diff --git a/v2rayN/PacLib/PacLib.csproj b/v2rayN/PacLib/PacLib.csproj index ed5be34b..ef8e4cda 100644 --- a/v2rayN/PacLib/PacLib.csproj +++ b/v2rayN/PacLib/PacLib.csproj @@ -1,20 +1,17 @@  - - net6.0-windows + net7.0 enable - True True Resources.resx - + ResXFileCodeGenerator Resources.Designer.cs - - + \ No newline at end of file diff --git a/v2rayN/ProtosLib/ProtosLib.csproj b/v2rayN/ProtosLib/ProtosLib.csproj index fac333e5..697457b9 100644 --- a/v2rayN/ProtosLib/ProtosLib.csproj +++ b/v2rayN/ProtosLib/ProtosLib.csproj @@ -1,20 +1,17 @@  - net6.0-windows - enable + net7.0 + enable - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file diff --git a/v2rayN/ProtosLib/Tests.cs b/v2rayN/ProtosLib/Tests.cs index e1eb85b1..62a19b0f 100644 --- a/v2rayN/ProtosLib/Tests.cs +++ b/v2rayN/ProtosLib/Tests.cs @@ -1,13 +1,12 @@ using ProtosLib.Statistics; -namespace ProtosLib -{ - public class Tests - { - private StatsService.StatsServiceClient client_; +namespace ProtosLib; - public Tests() - { - } +public class Tests +{ + private StatsService.StatsServiceClient client_; + + public Tests() + { } } \ No newline at end of file diff --git a/v2rayN/v2rayN/App.xaml.cs b/v2rayN/v2rayN/App.xaml.cs index 37ca3478..75ede10d 100644 --- a/v2rayN/v2rayN/App.xaml.cs +++ b/v2rayN/v2rayN/App.xaml.cs @@ -4,87 +4,86 @@ using v2rayN.Handler; using v2rayN.Mode; using v2rayN.Tool; -namespace v2rayN +namespace v2rayN; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application { - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application + public static EventWaitHandle ProgramStarted; + private static Config _config; + + public App() { - public static EventWaitHandle ProgramStarted; - private static Config _config; + // Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly()); + this.DispatcherUnhandledException += App_DispatcherUnhandledException; + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; + } - public App() + /// + /// 只打开一个进程 + /// + /// + protected override void OnStartup(StartupEventArgs e) + { + Global.ExePathKey = Utils.GetMD5(Utils.GetExePath()); + + var rebootas = (e.Args ?? new string[] { }).Any(t => t == Global.RebootAs); + ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, Global.ExePathKey, out bool bCreatedNew); + if (!rebootas && !bCreatedNew) { - // Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly()); - this.DispatcherUnhandledException += App_DispatcherUnhandledException; - AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; + ProgramStarted.Set(); + Current.Shutdown(); + Environment.Exit(0); + return; } - /// - /// 只打开一个进程 - /// - /// - protected override void OnStartup(StartupEventArgs e) + Global.processJob = new Job(); + + Logging.Setup(); + Init(); + Logging.LoggingEnabled(_config.guiItem.enableLog); + Utils.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}"); + Logging.ClearLogs(); + + Thread.CurrentThread.CurrentUICulture = new(_config.uiItem.currentLanguage); + + base.OnStartup(e); + } + + private void Init() + { + if (ConfigHandler.LoadConfig(ref _config) != 0) { - Global.ExePathKey = Utils.GetMD5(Utils.GetExePath()); - - var rebootas = (e.Args ?? new string[] { }).Any(t => t == Global.RebootAs); - ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, Global.ExePathKey, out bool bCreatedNew); - if (!rebootas && !bCreatedNew) - { - ProgramStarted.Set(); - Current.Shutdown(); - Environment.Exit(0); - return; - } - - Global.processJob = new Job(); - - Logging.Setup(); - Init(); - Logging.LoggingEnabled(_config.guiItem.enableLog); - Utils.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}"); - Logging.ClearLogs(); - - Thread.CurrentThread.CurrentUICulture = new(_config.uiItem.currentLanguage); - - base.OnStartup(e); + UI.ShowWarning($"Loading GUI configuration file is abnormal,please restart the application{Environment.NewLine}加载GUI配置文件异常,请重启应用"); + Application.Current.Shutdown(); + Environment.Exit(0); + return; } + //if (RuntimeInformation.ProcessArchitecture != Architecture.X86 && RuntimeInformation.ProcessArchitecture != Architecture.X64) + //{ + // _config.guiItem.enableStatistics = false; + //} + } - private void Init() - { - if (ConfigHandler.LoadConfig(ref _config) != 0) - { - UI.ShowWarning($"Loading GUI configuration file is abnormal,please restart the application{Environment.NewLine}加载GUI配置文件异常,请重启应用"); - Application.Current.Shutdown(); - Environment.Exit(0); - return; - } - //if (RuntimeInformation.ProcessArchitecture != Architecture.X86 && RuntimeInformation.ProcessArchitecture != Architecture.X64) - //{ - // _config.guiItem.enableStatistics = false; - //} - } + private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) + { + Utils.SaveLog("App_DispatcherUnhandledException", e.Exception); + e.Handled = true; + } - private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) + private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + if (e.ExceptionObject != null) { - Utils.SaveLog("App_DispatcherUnhandledException", e.Exception); - e.Handled = true; - } - - private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) - { - if (e.ExceptionObject != null) - { - Utils.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!); - } - } - - private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) - { - Utils.SaveLog("TaskScheduler_UnobservedTaskException", e.Exception); + Utils.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!); } } + + private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) + { + Utils.SaveLog("TaskScheduler_UnobservedTaskException", e.Exception); + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Base/DownloaderHelper.cs b/v2rayN/v2rayN/Base/DownloaderHelper.cs index 51a7840b..3bdb7afc 100644 --- a/v2rayN/v2rayN/Base/DownloaderHelper.cs +++ b/v2rayN/v2rayN/Base/DownloaderHelper.cs @@ -2,180 +2,179 @@ using System.IO; using System.Net; -namespace v2rayN.Base +namespace v2rayN.Base; + +internal class DownloaderHelper { - internal class DownloaderHelper + private static readonly Lazy _instance = new(() => new()); + public static DownloaderHelper Instance => _instance.Value; + + public async Task DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout) { - private static readonly Lazy _instance = new(() => new()); - public static DownloaderHelper Instance => _instance.Value; - - public async Task DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout) + if (string.IsNullOrEmpty(url)) { - if (string.IsNullOrEmpty(url)) - { - return null; - } - - Uri uri = new(url); - //Authorization Header - var headers = new WebHeaderCollection(); - if (!Utils.IsNullOrEmpty(uri.UserInfo)) - { - headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo)); - } - - var downloadOpt = new DownloadConfiguration() - { - Timeout = timeout * 1000, - MaxTryAgainOnFailover = 2, - RequestConfiguration = - { - Headers = headers, - UserAgent = userAgent, - Timeout = timeout * 1000, - Proxy = webProxy - } - }; - - using var downloader = new DownloadService(downloadOpt); - downloader.DownloadFileCompleted += (sender, value) => - { - if (value.Error != null) - { - throw value.Error; - } - }; - - using var cts = new CancellationTokenSource(); - using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token); - using StreamReader reader = new(stream); - - downloadOpt = null; - - return reader.ReadToEnd(); + return null; } - public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress progress, int timeout) + Uri uri = new(url); + //Authorization Header + var headers = new WebHeaderCollection(); + if (!Utils.IsNullOrEmpty(uri.UserInfo)) { - if (string.IsNullOrEmpty(url)) - { - throw new ArgumentNullException(nameof(url)); - } - - var downloadOpt = new DownloadConfiguration() - { - Timeout = timeout * 1000, - MaxTryAgainOnFailover = 2, - RequestConfiguration = - { - Timeout= timeout * 1000, - Proxy = webProxy - } - }; - - DateTime totalDatetime = DateTime.Now; - int totalSecond = 0; - var hasValue = false; - double maxSpeed = 0; - using var downloader = new DownloadService(downloadOpt); - //downloader.DownloadStarted += (sender, value) => - //{ - // if (progress != null) - // { - // progress.Report("Start download data..."); - // } - //}; - downloader.DownloadProgressChanged += (sender, value) => - { - TimeSpan ts = (DateTime.Now - totalDatetime); - if (progress != null && ts.Seconds > totalSecond) - { - hasValue = true; - totalSecond = ts.Seconds; - if (value.BytesPerSecondSpeed > maxSpeed) - { - maxSpeed = value.BytesPerSecondSpeed; - var speed = (maxSpeed / 1000 / 1000).ToString("#0.0"); - progress.Report(speed); - } - } - }; - downloader.DownloadFileCompleted += (sender, value) => - { - if (progress != null) - { - if (!hasValue && value.Error != null) - { - progress.Report(value.Error?.Message); - } - } - }; - //progress.Report("......"); - using var cts = new CancellationTokenSource(); - cts.CancelAfter(timeout * 1000); - using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token); - - downloadOpt = null; + headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo)); } - public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress progress, int timeout) + var downloadOpt = new DownloadConfiguration() { - if (string.IsNullOrEmpty(url)) - { - throw new ArgumentNullException(nameof(url)); - } - if (string.IsNullOrEmpty(fileName)) - { - throw new ArgumentNullException(nameof(fileName)); - } - if (File.Exists(fileName)) - { - File.Delete(fileName); - } - - var downloadOpt = new DownloadConfiguration() + Timeout = timeout * 1000, + MaxTryAgainOnFailover = 2, + RequestConfiguration = { + Headers = headers, + UserAgent = userAgent, Timeout = timeout * 1000, - MaxTryAgainOnFailover = 2, - RequestConfiguration = - { - Timeout= timeout * 1000, - Proxy = webProxy - } - }; + Proxy = webProxy + } + }; - var progressPercentage = 0; - var hasValue = false; - using var downloader = new DownloadService(downloadOpt); - downloader.DownloadStarted += (sender, value) => + using var downloader = new DownloadService(downloadOpt); + downloader.DownloadFileCompleted += (sender, value) => + { + if (value.Error != null) { - progress?.Report(0); - }; - downloader.DownloadProgressChanged += (sender, value) => + throw value.Error; + } + }; + + using var cts = new CancellationTokenSource(); + using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token); + using StreamReader reader = new(stream); + + downloadOpt = null; + + return reader.ReadToEnd(); + } + + public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress progress, int timeout) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException(nameof(url)); + } + + var downloadOpt = new DownloadConfiguration() + { + Timeout = timeout * 1000, + MaxTryAgainOnFailover = 2, + RequestConfiguration = + { + Timeout= timeout * 1000, + Proxy = webProxy + } + }; + + DateTime totalDatetime = DateTime.Now; + int totalSecond = 0; + var hasValue = false; + double maxSpeed = 0; + using var downloader = new DownloadService(downloadOpt); + //downloader.DownloadStarted += (sender, value) => + //{ + // if (progress != null) + // { + // progress.Report("Start download data..."); + // } + //}; + downloader.DownloadProgressChanged += (sender, value) => + { + TimeSpan ts = (DateTime.Now - totalDatetime); + if (progress != null && ts.Seconds > totalSecond) { hasValue = true; - var percent = (int)value.ProgressPercentage;// Convert.ToInt32((totalRead * 1d) / (total * 1d) * 100); - if (progressPercentage != percent && percent % 10 == 0) + totalSecond = ts.Seconds; + if (value.BytesPerSecondSpeed > maxSpeed) { - progressPercentage = percent; - progress.Report(percent); + maxSpeed = value.BytesPerSecondSpeed; + var speed = (maxSpeed / 1000 / 1000).ToString("#0.0"); + progress.Report(speed); } - }; - downloader.DownloadFileCompleted += (sender, value) => + } + }; + downloader.DownloadFileCompleted += (sender, value) => + { + if (progress != null) { - if (progress != null) + if (!hasValue && value.Error != null) { - if (hasValue && value.Error == null) - { - progress.Report(101); - } + progress.Report(value.Error?.Message); } - }; + } + }; + //progress.Report("......"); + using var cts = new CancellationTokenSource(); + cts.CancelAfter(timeout * 1000); + using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token); - using var cts = new CancellationTokenSource(); - await downloader.DownloadFileTaskAsync(url, fileName, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token); + downloadOpt = null; + } - downloadOpt = null; + public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress progress, int timeout) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException(nameof(url)); } + if (string.IsNullOrEmpty(fileName)) + { + throw new ArgumentNullException(nameof(fileName)); + } + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + + var downloadOpt = new DownloadConfiguration() + { + Timeout = timeout * 1000, + MaxTryAgainOnFailover = 2, + RequestConfiguration = + { + Timeout= timeout * 1000, + Proxy = webProxy + } + }; + + var progressPercentage = 0; + var hasValue = false; + using var downloader = new DownloadService(downloadOpt); + downloader.DownloadStarted += (sender, value) => + { + progress?.Report(0); + }; + downloader.DownloadProgressChanged += (sender, value) => + { + hasValue = true; + var percent = (int)value.ProgressPercentage;// Convert.ToInt32((totalRead * 1d) / (total * 1d) * 100); + if (progressPercentage != percent && percent % 10 == 0) + { + progressPercentage = percent; + progress.Report(percent); + } + }; + downloader.DownloadFileCompleted += (sender, value) => + { + if (progress != null) + { + if (hasValue && value.Error == null) + { + progress.Report(101); + } + } + }; + + using var cts = new CancellationTokenSource(); + await downloader.DownloadFileTaskAsync(url, fileName, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token); + + downloadOpt = null; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Base/HttpClientHelper.cs b/v2rayN/v2rayN/Base/HttpClientHelper.cs index 72d9d3c6..a1c31ab9 100644 --- a/v2rayN/v2rayN/Base/HttpClientHelper.cs +++ b/v2rayN/v2rayN/Base/HttpClientHelper.cs @@ -3,154 +3,153 @@ using System.Net.Http; using System.Net.Mime; using System.Text; -namespace v2rayN.Base +namespace v2rayN.Base; + +/// +/// +public class HttpClientHelper { - /// - /// - public class HttpClientHelper + private static readonly Lazy _instance = new(() => { - private static readonly Lazy _instance = new(() => + HttpClientHandler handler = new() { UseCookies = false }; + HttpClientHelper helper = new(new HttpClient(handler)); + return helper; + }); + + public static HttpClientHelper Instance => _instance.Value; + private readonly HttpClient httpClient; + + private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient; + + public async Task GetAsync(string url) + { + if (string.IsNullOrEmpty(url)) return null; + return await httpClient.GetStringAsync(url); + } + + public async Task GetAsync(HttpClient client, string url, CancellationToken token = default) + { + if (string.IsNullOrWhiteSpace(url)) return null; + return await client.GetStringAsync(url, token); + } + + public async Task PutAsync(string url, Dictionary headers) + { + var jsonContent = Utils.ToJson(headers); + var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json); + + var result = await httpClient.PutAsync(url, content); + } + + public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress? progress, CancellationToken token = default) + { + ArgumentNullException.ThrowIfNull(url); + ArgumentNullException.ThrowIfNull(fileName); + if (File.Exists(fileName)) File.Delete(fileName); + + using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); + + if (!response.IsSuccessStatusCode) throw new Exception(response.StatusCode.ToString()); + + var total = response.Content.Headers.ContentLength ?? -1L; + var canReportProgress = total != -1 && progress != null; + + using var stream = await response.Content.ReadAsStreamAsync(token); + using var file = File.Create(fileName); + var totalRead = 0L; + var buffer = new byte[1024 * 1024]; + var progressPercentage = 0; + + while (true) { - HttpClientHandler handler = new() { UseCookies = false }; - HttpClientHelper helper = new(new HttpClient(handler)); - return helper; - }); + token.ThrowIfCancellationRequested(); - public static HttpClientHelper Instance => _instance.Value; - private readonly HttpClient httpClient; + var read = await stream.ReadAsync(buffer, token); + totalRead += read; - private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient; + if (read == 0) break; + file.Write(buffer, 0, read); - public async Task GetAsync(string url) - { - if (string.IsNullOrEmpty(url)) return null; - return await httpClient.GetStringAsync(url); - } - - public async Task GetAsync(HttpClient client, string url, CancellationToken token = default) - { - if (string.IsNullOrWhiteSpace(url)) return null; - return await client.GetStringAsync(url, token); - } - - public async Task PutAsync(string url, Dictionary headers) - { - var jsonContent = Utils.ToJson(headers); - var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json); - - var result = await httpClient.PutAsync(url, content); - } - - public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress? progress, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(url); - ArgumentNullException.ThrowIfNull(fileName); - if (File.Exists(fileName)) File.Delete(fileName); - - using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); - - if (!response.IsSuccessStatusCode) throw new Exception(response.StatusCode.ToString()); - - var total = response.Content.Headers.ContentLength ?? -1L; - var canReportProgress = total != -1 && progress != null; - - using var stream = await response.Content.ReadAsStreamAsync(token); - using var file = File.Create(fileName); - var totalRead = 0L; - var buffer = new byte[1024 * 1024]; - var progressPercentage = 0; - - while (true) - { - token.ThrowIfCancellationRequested(); - - var read = await stream.ReadAsync(buffer, token); - totalRead += read; - - if (read == 0) break; - file.Write(buffer, 0, read); - - if (canReportProgress) - { - var percent = (int)(100.0 * totalRead / total); - //if (progressPercentage != percent && percent % 10 == 0) - { - progressPercentage = percent; - progress!.Report(percent); - } - } - } if (canReportProgress) { - progress!.Report(101); + var percent = (int)(100.0 * totalRead / total); + //if (progressPercentage != percent && percent % 10 == 0) + { + progressPercentage = percent; + progress!.Report(percent); + } } } - - public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress progress, CancellationToken token = default) + if (canReportProgress) { - if (string.IsNullOrEmpty(url)) + progress!.Report(101); + } + } + + public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress progress, CancellationToken token = default) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException(nameof(url)); + } + + var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); + + if (!response.IsSuccessStatusCode) + { + throw new Exception(response.StatusCode.ToString()); + } + + //var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L; + //var canReportProgress = total != -1 && progress != null; + + using var stream = await response.Content.ReadAsStreamAsync(token); + var totalRead = 0L; + var buffer = new byte[1024 * 64]; + var isMoreToRead = true; + string progressSpeed = string.Empty; + DateTime totalDatetime = DateTime.Now; + int totalSecond = 0; + + do + { + if (token.IsCancellationRequested) { - throw new ArgumentNullException(nameof(url)); - } - - var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token); - - if (!response.IsSuccessStatusCode) - { - throw new Exception(response.StatusCode.ToString()); - } - - //var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L; - //var canReportProgress = total != -1 && progress != null; - - using var stream = await response.Content.ReadAsStreamAsync(token); - var totalRead = 0L; - var buffer = new byte[1024 * 64]; - var isMoreToRead = true; - string progressSpeed = string.Empty; - DateTime totalDatetime = DateTime.Now; - int totalSecond = 0; - - do - { - if (token.IsCancellationRequested) + if (totalRead > 0) { - if (totalRead > 0) - { - return; - } - else - { - token.ThrowIfCancellationRequested(); - } - } - - var read = await stream.ReadAsync(buffer, token); - - if (read == 0) - { - isMoreToRead = false; + return; } else { - var data = new byte[read]; - buffer.ToList().CopyTo(0, data, 0, read); + token.ThrowIfCancellationRequested(); + } + } - totalRead += read; + var read = await stream.ReadAsync(buffer, token); - TimeSpan ts = (DateTime.Now - totalDatetime); - if (progress != null && ts.Seconds > totalSecond) + if (read == 0) + { + isMoreToRead = false; + } + else + { + var data = new byte[read]; + buffer.ToList().CopyTo(0, data, 0, read); + + totalRead += read; + + TimeSpan ts = (DateTime.Now - totalDatetime); + if (progress != null && ts.Seconds > totalSecond) + { + totalSecond = ts.Seconds; + var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0"); + if (progressSpeed != speed) { - totalSecond = ts.Seconds; - var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0"); - if (progressSpeed != speed) - { - progressSpeed = speed; - progress.Report(speed); - } + progressSpeed = speed; + progress.Report(speed); } } - } while (isMoreToRead); - } + } + } while (isMoreToRead); } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Base/MyDGTextColumn.cs b/v2rayN/v2rayN/Base/MyDGTextColumn.cs index 427ffacc..c46ebb05 100644 --- a/v2rayN/v2rayN/Base/MyDGTextColumn.cs +++ b/v2rayN/v2rayN/Base/MyDGTextColumn.cs @@ -1,9 +1,8 @@ using System.Windows.Controls; -namespace v2rayN.Base +namespace v2rayN.Base; + +internal class MyDGTextColumn : DataGridTextColumn { - internal class MyDGTextColumn : DataGridTextColumn - { - public string ExName { get; set; } - } + public string ExName { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Base/SqliteHelper.cs b/v2rayN/v2rayN/Base/SqliteHelper.cs index 7903f3f1..925ed879 100644 --- a/v2rayN/v2rayN/Base/SqliteHelper.cs +++ b/v2rayN/v2rayN/Base/SqliteHelper.cs @@ -1,122 +1,121 @@ using SQLite; using System.Collections; -namespace v2rayN.Base +namespace v2rayN.Base; + +public sealed class SqliteHelper { - public sealed class SqliteHelper + private static readonly Lazy _instance = new(() => new()); + public static SqliteHelper Instance => _instance.Value; + private string _connstr; + private SQLiteConnection _db; + private SQLiteAsyncConnection _dbAsync; + private static readonly object objLock = new(); + + public SqliteHelper() { - private static readonly Lazy _instance = new(() => new()); - public static SqliteHelper Instance => _instance.Value; - private string _connstr; - private SQLiteConnection _db; - private SQLiteAsyncConnection _dbAsync; - private static readonly object objLock = new(); + _connstr = Utils.GetConfigPath(Global.ConfigDB); + _db = new SQLiteConnection(_connstr, false); + _dbAsync = new SQLiteAsyncConnection(_connstr, false); + } - public SqliteHelper() - { - _connstr = Utils.GetConfigPath(Global.ConfigDB); - _db = new SQLiteConnection(_connstr, false); - _dbAsync = new SQLiteAsyncConnection(_connstr, false); - } + public CreateTableResult CreateTable() + { + return _db.CreateTable(); + } - public CreateTableResult CreateTable() - { - return _db.CreateTable(); - } + public int Insert(object model) + { + return _db.Insert(model); + } - public int Insert(object model) + public int InsertAll(IEnumerable models) + { + lock (objLock) { - return _db.Insert(model); - } - - public int InsertAll(IEnumerable models) - { - lock (objLock) - { - return _db.InsertAll(models); - } - } - - public async Task InsertAsync(object model) - { - return await _dbAsync.InsertAsync(model); - } - - public int Replace(object model) - { - lock (objLock) - { - return _db.InsertOrReplace(model); - } - } - - public async Task Replacesync(object model) - { - return await _dbAsync.InsertOrReplaceAsync(model); - } - - public int Update(object model) - { - lock (objLock) - { - return _db.Update(model); - } - } - - public async Task UpdateAsync(object model) - { - return await _dbAsync.UpdateAsync(model); - } - - public int UpdateAll(IEnumerable models) - { - lock (objLock) - { - return _db.UpdateAll(models); - } - } - - public int Delete(object model) - { - lock (objLock) - { - return _db.Delete(model); - } - } - - public async Task DeleteAsync(object model) - { - return await _dbAsync.DeleteAsync(model); - } - - public List Query(string sql) where T : new() - { - return _db.Query(sql); - } - - public async Task> QueryAsync(string sql) where T : new() - { - return await _dbAsync.QueryAsync(sql); - } - - public int Execute(string sql) - { - return _db.Execute(sql); - } - - public async Task ExecuteAsync(string sql) - { - return await _dbAsync.ExecuteAsync(sql); - } - - public TableQuery Table() where T : new() - { - return _db.Table(); - } - - public AsyncTableQuery TableAsync() where T : new() - { - return _dbAsync.Table(); + return _db.InsertAll(models); } } + + public async Task InsertAsync(object model) + { + return await _dbAsync.InsertAsync(model); + } + + public int Replace(object model) + { + lock (objLock) + { + return _db.InsertOrReplace(model); + } + } + + public async Task Replacesync(object model) + { + return await _dbAsync.InsertOrReplaceAsync(model); + } + + public int Update(object model) + { + lock (objLock) + { + return _db.Update(model); + } + } + + public async Task UpdateAsync(object model) + { + return await _dbAsync.UpdateAsync(model); + } + + public int UpdateAll(IEnumerable models) + { + lock (objLock) + { + return _db.UpdateAll(models); + } + } + + public int Delete(object model) + { + lock (objLock) + { + return _db.Delete(model); + } + } + + public async Task DeleteAsync(object model) + { + return await _dbAsync.DeleteAsync(model); + } + + public List Query(string sql) where T : new() + { + return _db.Query(sql); + } + + public async Task> QueryAsync(string sql) where T : new() + { + return await _dbAsync.QueryAsync(sql); + } + + public int Execute(string sql) + { + return _db.Execute(sql); + } + + public async Task ExecuteAsync(string sql) + { + return await _dbAsync.ExecuteAsync(sql); + } + + public TableQuery Table() where T : new() + { + return _db.Table(); + } + + public AsyncTableQuery TableAsync() where T : new() + { + return _dbAsync.Table(); + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Base/StringEx.cs b/v2rayN/v2rayN/Base/StringEx.cs index cf0f9106..0fa0e827 100644 --- a/v2rayN/v2rayN/Base/StringEx.cs +++ b/v2rayN/v2rayN/Base/StringEx.cs @@ -1,84 +1,83 @@ using System.Diagnostics.CodeAnalysis; using System.IO; -namespace v2rayN.Base +namespace v2rayN.Base; + +internal static class StringEx { - internal static class StringEx + public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) { - public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) + return string.IsNullOrEmpty(value); + } + + public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value) + { + return string.IsNullOrWhiteSpace(value); + } + + public static bool BeginWithAny(this string s, IEnumerable chars) + { + if (s.IsNullOrEmpty()) return false; + return chars.Contains(s[0]); + } + + public static bool IsWhiteSpace(this string value) + { + foreach (char c in value) { - return string.IsNullOrEmpty(value); + if (char.IsWhiteSpace(c)) continue; + + return false; } + return true; + } - public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value) + public static IEnumerable NonWhiteSpaceLines(this TextReader reader) + { + string? line; + while ((line = reader.ReadLine()) != null) { - return string.IsNullOrWhiteSpace(value); - } - - public static bool BeginWithAny(this string s, IEnumerable chars) - { - if (s.IsNullOrEmpty()) return false; - return chars.Contains(s[0]); - } - - public static bool IsWhiteSpace(this string value) - { - foreach (char c in value) - { - if (char.IsWhiteSpace(c)) continue; - - return false; - } - return true; - } - - public static IEnumerable NonWhiteSpaceLines(this TextReader reader) - { - string? line; - while ((line = reader.ReadLine()) != null) - { - if (line.IsWhiteSpace()) continue; - yield return line; - } - } - - public static string TrimEx(this string? value) - { - return value == null ? string.Empty : value.Trim(); - } - - public static string RemovePrefix(this string value, char prefix) - { - if (value.StartsWith(prefix)) - { - return value.Substring(1); - } - else - { - return value; - } - } - - public static string RemovePrefix(this string value, string prefix) - { - if (value.StartsWith(prefix)) - { - return value.Substring(prefix.Length); - } - else - { - return value; - } - } - - public static string UpperFirstChar(this string value) - { - if (string.IsNullOrEmpty(value)) - { - return string.Empty; - } - - return char.ToUpper(value[0]) + value.Substring(1); + if (line.IsWhiteSpace()) continue; + yield return line; } } + + public static string TrimEx(this string? value) + { + return value == null ? string.Empty : value.Trim(); + } + + public static string RemovePrefix(this string value, char prefix) + { + if (value.StartsWith(prefix)) + { + return value.Substring(1); + } + else + { + return value; + } + } + + public static string RemovePrefix(this string value, string prefix) + { + if (value.StartsWith(prefix)) + { + return value.Substring(prefix.Length); + } + else + { + return value; + } + } + + public static string UpperFirstChar(this string value) + { + if (string.IsNullOrEmpty(value)) + { + return string.Empty; + } + + return char.ToUpper(value[0]) + value.Substring(1); + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Converters/DelayColorConverter.cs b/v2rayN/v2rayN/Converters/DelayColorConverter.cs index b5c91565..09da3473 100644 --- a/v2rayN/v2rayN/Converters/DelayColorConverter.cs +++ b/v2rayN/v2rayN/Converters/DelayColorConverter.cs @@ -1,25 +1,24 @@ using System.Windows.Data; using System.Windows.Media; -namespace v2rayN.Converters +namespace v2rayN.Converters; + +public class DelayColorConverter : IValueConverter { - public class DelayColorConverter : IValueConverter + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) - { - int.TryParse(value.ToString(), out var delay); + int.TryParse(value.ToString(), out var delay); - if (delay <= 0) - return new SolidColorBrush(Colors.Red); - if (delay <= 200) - return new SolidColorBrush(Colors.Green); - else - return new SolidColorBrush(Colors.IndianRed); - } + if (delay <= 0) + return new SolidColorBrush(Colors.Red); + if (delay <= 200) + return new SolidColorBrush(Colors.Green); + else + return new SolidColorBrush(Colors.IndianRed); + } - public object? ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) - { - return null; - } + public object? ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return null; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Converters/InverseBooleanConverter.cs b/v2rayN/v2rayN/Converters/InverseBooleanConverter.cs index d5809a37..2b3f8b34 100644 --- a/v2rayN/v2rayN/Converters/InverseBooleanConverter.cs +++ b/v2rayN/v2rayN/Converters/InverseBooleanConverter.cs @@ -1,24 +1,23 @@ using System.Globalization; using System.Windows.Data; -namespace v2rayN.Converters +namespace v2rayN.Converters; + +[ValueConversion(typeof(bool), typeof(bool))] +public class InverseBooleanConverter : IValueConverter { - [ValueConversion(typeof(bool), typeof(bool))] - public class InverseBooleanConverter : IValueConverter + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + if (targetType != typeof(bool)) { - if (targetType != typeof(bool)) - { - throw new InvalidOperationException("The target must be a boolean"); - } - - return !(bool)value; + throw new InvalidOperationException("The target must be a boolean"); } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } + return !(bool)value; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Converters/MaterialDesignFonts.cs b/v2rayN/v2rayN/Converters/MaterialDesignFonts.cs index 0b6908d1..4e9ebcc2 100644 --- a/v2rayN/v2rayN/Converters/MaterialDesignFonts.cs +++ b/v2rayN/v2rayN/Converters/MaterialDesignFonts.cs @@ -1,27 +1,26 @@ using System.Windows.Media; using v2rayN.Handler; -namespace v2rayN.Converters -{ - public class MaterialDesignFonts - { - public static FontFamily MyFont { get; } +namespace v2rayN.Converters; - static MaterialDesignFonts() +public class MaterialDesignFonts +{ + public static FontFamily MyFont { get; } + + static MaterialDesignFonts() + { + try { - try + var fontFamily = LazyConfig.Instance.GetConfig().uiItem.currentFontFamily; + if (!string.IsNullOrEmpty(fontFamily)) { - var fontFamily = LazyConfig.Instance.GetConfig().uiItem.currentFontFamily; - if (!string.IsNullOrEmpty(fontFamily)) - { - var fontPath = Utils.GetFontsPath(); - MyFont = new FontFamily(new Uri(@$"file:///{fontPath}\"), $"./#{fontFamily}"); - } + var fontPath = Utils.GetFontsPath(); + MyFont = new FontFamily(new Uri(@$"file:///{fontPath}\"), $"./#{fontFamily}"); } - catch - { - } - MyFont ??= new FontFamily("Microsoft YaHei"); } + catch + { + } + MyFont ??= new FontFamily("Microsoft YaHei"); } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Global.cs b/v2rayN/v2rayN/Global.cs index 4af96a07..32c00bb6 100644 --- a/v2rayN/v2rayN/Global.cs +++ b/v2rayN/v2rayN/Global.cs @@ -1,174 +1,173 @@ -namespace v2rayN +namespace v2rayN; + +internal class Global { - internal class Global + #region const + + public const string githubUrl = "https://github.com"; + public const string githubApiUrl = "https://api.github.com/repos"; + public const string v2rayWebsiteUrl = @"https://www.v2fly.org/"; + public const string AboutUrl = @"https://github.com/2dust/v2rayN"; + public const string UpdateUrl = AboutUrl + @"/releases"; + public const string v2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases"; + public const string xrayCoreUrl = "https://github.com/XTLS/Xray-core/releases"; + public const string SagerNetCoreUrl = "https://github.com/SagerNet/v2ray-core/releases"; + public const string NUrl = @"https://github.com/2dust/v2rayN/releases"; + public const string clashCoreUrl = "https://github.com/Dreamacro/clash/releases"; + public const string clashMetaCoreUrl = "https://github.com/MetaCubeX/Clash.Meta/releases"; + public const string hysteriaCoreUrl = "https://github.com/apernet/hysteria/releases"; + public const string naiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases"; + public const string tuicCoreUrl = "https://github.com/EAimTY/tuic/releases"; + public const string singboxCoreUrl = "https://github.com/SagerNet/sing-box/releases"; + public const string geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat"; + public const string singboxGeoUrl = "https://github.com/soffchen/sing-{0}/releases/latest/download/{0}.db"; + public const string SpeedPingTestUrl = @"https://www.google.com/generate_204"; + public const string juicityCoreUrl = "https://github.com/juicity/juicity/releases"; + public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"; + + public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; + public const string ConfigFileName = "guiNConfig.json"; + public const string ConfigDB = "guiNDB.db"; + public const string coreConfigFileName = "config.json"; + public const string corePreConfigFileName = "configPre.json"; + + public const string v2raySampleClient = "v2rayN.Sample.SampleClientConfig"; + public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig"; + public const string v2raySampleHttprequestFileName = "v2rayN.Sample.SampleHttprequest"; + public const string v2raySampleHttpresponseFileName = "v2rayN.Sample.SampleHttpresponse"; + public const string v2raySampleInbound = "v2rayN.Sample.SampleInbound"; + public const string CustomRoutingFileName = "v2rayN.Sample.custom_routing_"; + + public const string TunSingboxDNSFileName = "v2rayN.Sample.tun_singbox_dns"; + public const string TunSingboxInboundFileName = "v2rayN.Sample.tun_singbox_inbound"; + public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules"; + + public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal"; + public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal"; + + public const string DefaultSecurity = "auto"; + public const string DefaultNetwork = "tcp"; + public const string TcpHeaderHttp = "http"; + public const string None = "none"; + public const string agentTag = "proxy"; + public const string directTag = "direct"; + public const string blockTag = "block"; + public const string StreamSecurity = "tls"; + public const string StreamSecurityReality = "reality"; + public const string InboundSocks = "socks"; + public const string InboundHttp = "http"; + public const string InboundSocks2 = "socks2"; + public const string InboundHttp2 = "http2"; + public const string Loopback = "127.0.0.1"; + public const string InboundAPITagName = "api"; + public const string InboundAPIProtocal = "dokodemo-door"; + + public const string vmessProtocol = "vmess://"; + public const string vmessProtocolLite = "vmess"; + public const string ssProtocol = "ss://"; + public const string ssProtocolLite = "shadowsocks"; + public const string socksProtocol = "socks://"; + public const string socksProtocolLite = "socks"; + public const string httpProtocol = "http://"; + public const string httpsProtocol = "https://"; + public const string vlessProtocol = "vless://"; + public const string vlessProtocolLite = "vless"; + public const string trojanProtocol = "trojan://"; + public const string trojanProtocolLite = "trojan"; + + public const string userEMail = "t@t.tt"; + public const string MyRegPath = "Software\\v2rayNGUI"; + public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run"; + public const string AutoRunName = "v2rayNAutoRun"; + public const string MyRegKeyLanguage = "CurrentLanguage"; + public const string CustomIconName = "v2rayN.ico"; + public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*"; + public const string RoutingRuleComma = ""; + public const string GrpcgunMode = "gun"; + public const string GrpcmultiMode = "multi"; + public const int MaxPort = 65536; + public const string CommandClearMsg = "CommandClearMsg"; + public const string DelayUnit = ""; + public const string SpeedUnit = ""; + public const int MinFontSize = 10; + public const string RebootAs = "rebootas"; + + public static readonly List IEProxyProtocols = new() { + "{ip}:{http_port}", + "socks={ip}:{socks_port}", + "http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}", + "http=http://{ip}:{http_port};https=http://{ip}:{http_port}", + "" + }; + + public static readonly List SubConvertUrls = new List { + @"https://sub.xeton.dev/sub?url={0}", + @"https://api.dler.io/sub?url={0}", + @"http://127.0.0.1:25500/sub?url={0}", + "" + }; + + public static readonly List SubConvertConfig = new List { + @"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini" + }; + + public static readonly List SubConvertTargets = new List { + "", + "mixed", + "v2ray", + "clash", + "ss", + }; + + public static readonly List SpeedTestUrls = new() { + @"http://cachefly.cachefly.net/100mb.test", + @"http://cachefly.cachefly.net/10mb.test" + }; + + public static readonly Dictionary userAgentTxt = new() { - #region const + {"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" }, + {"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" }, + {"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" }, + {"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" }, + {"none",""} + }; - public const string githubUrl = "https://github.com"; - public const string githubApiUrl = "https://api.github.com/repos"; - public const string v2rayWebsiteUrl = @"https://www.v2fly.org/"; - public const string AboutUrl = @"https://github.com/2dust/v2rayN"; - public const string UpdateUrl = AboutUrl + @"/releases"; - public const string v2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases"; - public const string xrayCoreUrl = "https://github.com/XTLS/Xray-core/releases"; - public const string SagerNetCoreUrl = "https://github.com/SagerNet/v2ray-core/releases"; - public const string NUrl = @"https://github.com/2dust/v2rayN/releases"; - public const string clashCoreUrl = "https://github.com/Dreamacro/clash/releases"; - public const string clashMetaCoreUrl = "https://github.com/MetaCubeX/Clash.Meta/releases"; - public const string hysteriaCoreUrl = "https://github.com/apernet/hysteria/releases"; - public const string naiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases"; - public const string tuicCoreUrl = "https://github.com/EAimTY/tuic/releases"; - public const string singboxCoreUrl = "https://github.com/SagerNet/sing-box/releases"; - public const string geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat"; - public const string singboxGeoUrl = "https://github.com/soffchen/sing-{0}/releases/latest/download/{0}.db"; - public const string SpeedPingTestUrl = @"https://www.google.com/generate_204"; - public const string juicityCoreUrl = "https://github.com/juicity/juicity/releases"; - public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"; + public static readonly List vmessSecuritys = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" }; + public static readonly List ssSecuritys = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" }; + public static readonly List ssSecuritysInSagerNet = new() { "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "rc4", "rc4-md5", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-cfb8", "aes-192-cfb8", "aes-256-cfb8", "aes-128-ofb", "aes-192-ofb", "aes-256-ofb", "bf-cfb", "cast5-cfb", "des-cfb", "idea-cfb", "rc2-cfb", "seed-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "camellia-128-cfb8", "camellia-192-cfb8", "camellia-256-cfb8", "salsa20", "chacha20", "chacha20-ietf", "xchacha20" }; + public static readonly List ssSecuritysInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" }; + public static readonly List flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" }; + public static readonly List networks = new() { "tcp", "kcp", "ws", "h2", "quic", "grpc" }; + public static readonly List kcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" }; + public static readonly List coreTypes = new() { "v2fly", "SagerNet", "Xray", "v2fly_v5", "sing_box" }; + public static readonly List coreTypes4VLESS = new() { "Xray", "sing_box" }; + public static readonly List domainStrategys = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" }; + public static readonly List domainStrategys4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" }; + public static readonly List domainMatchers = new() { "linear", "mph", "" }; + public static readonly List fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" }; + public static readonly List userAgent = new() { "chrome", "firefox", "safari", "edge", "none" }; - public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; - public const string ConfigFileName = "guiNConfig.json"; - public const string ConfigDB = "guiNDB.db"; - public const string coreConfigFileName = "config.json"; - public const string corePreConfigFileName = "configPre.json"; + public static readonly List allowInsecures = new() { "true", "false", "" }; + public static readonly List domainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" }; + public static readonly List Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" }; + public static readonly List alpns = new() { "h2", "http/1.1", "h2,http/1.1", "" }; + public static readonly List LogLevel = new() { "debug", "info", "warning", "error", "none" }; + public static readonly List InboundTags = new() { "socks", "http", "socks2", "http2" }; + public static readonly List Protocols = new() { "http", "tls", "bittorrent" }; + public static readonly List TunMtus = new() { "9000", "1500" }; + public static readonly List TunStacks = new() { "gvisor", "system" }; + public static readonly List PresetMsgFilters = new() { "proxy", "direct", "block", "" }; + public static readonly List SingboxMuxs = new() { "h2mux", "smux", "yamux", "" }; - public const string v2raySampleClient = "v2rayN.Sample.SampleClientConfig"; - public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig"; - public const string v2raySampleHttprequestFileName = "v2rayN.Sample.SampleHttprequest"; - public const string v2raySampleHttpresponseFileName = "v2rayN.Sample.SampleHttpresponse"; - public const string v2raySampleInbound = "v2rayN.Sample.SampleInbound"; - public const string CustomRoutingFileName = "v2rayN.Sample.custom_routing_"; + #endregion const - public const string TunSingboxDNSFileName = "v2rayN.Sample.tun_singbox_dns"; - public const string TunSingboxInboundFileName = "v2rayN.Sample.tun_singbox_inbound"; - public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules"; + #region global variable - public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal"; - public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal"; + public static int statePort { get; set; } + public static Job processJob { get; set; } + public static bool ShowInTaskbar { get; set; } + public static string ExePathKey { get; set; } - public const string DefaultSecurity = "auto"; - public const string DefaultNetwork = "tcp"; - public const string TcpHeaderHttp = "http"; - public const string None = "none"; - public const string agentTag = "proxy"; - public const string directTag = "direct"; - public const string blockTag = "block"; - public const string StreamSecurity = "tls"; - public const string StreamSecurityReality = "reality"; - public const string InboundSocks = "socks"; - public const string InboundHttp = "http"; - public const string InboundSocks2 = "socks2"; - public const string InboundHttp2 = "http2"; - public const string Loopback = "127.0.0.1"; - public const string InboundAPITagName = "api"; - public const string InboundAPIProtocal = "dokodemo-door"; - - public const string vmessProtocol = "vmess://"; - public const string vmessProtocolLite = "vmess"; - public const string ssProtocol = "ss://"; - public const string ssProtocolLite = "shadowsocks"; - public const string socksProtocol = "socks://"; - public const string socksProtocolLite = "socks"; - public const string httpProtocol = "http://"; - public const string httpsProtocol = "https://"; - public const string vlessProtocol = "vless://"; - public const string vlessProtocolLite = "vless"; - public const string trojanProtocol = "trojan://"; - public const string trojanProtocolLite = "trojan"; - - public const string userEMail = "t@t.tt"; - public const string MyRegPath = "Software\\v2rayNGUI"; - public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run"; - public const string AutoRunName = "v2rayNAutoRun"; - public const string MyRegKeyLanguage = "CurrentLanguage"; - public const string CustomIconName = "v2rayN.ico"; - public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*"; - public const string RoutingRuleComma = ""; - public const string GrpcgunMode = "gun"; - public const string GrpcmultiMode = "multi"; - public const int MaxPort = 65536; - public const string CommandClearMsg = "CommandClearMsg"; - public const string DelayUnit = ""; - public const string SpeedUnit = ""; - public const int MinFontSize = 10; - public const string RebootAs = "rebootas"; - - public static readonly List IEProxyProtocols = new() { - "{ip}:{http_port}", - "socks={ip}:{socks_port}", - "http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}", - "http=http://{ip}:{http_port};https=http://{ip}:{http_port}", - "" - }; - - public static readonly List SubConvertUrls = new List { - @"https://sub.xeton.dev/sub?url={0}", - @"https://api.dler.io/sub?url={0}", - @"http://127.0.0.1:25500/sub?url={0}", - "" - }; - - public static readonly List SubConvertConfig = new List { - @"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini" - }; - - public static readonly List SubConvertTargets = new List { - "", - "mixed", - "v2ray", - "clash", - "ss", - }; - - public static readonly List SpeedTestUrls = new() { - @"http://cachefly.cachefly.net/100mb.test", - @"http://cachefly.cachefly.net/10mb.test" - }; - - public static readonly Dictionary userAgentTxt = new() - { - {"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" }, - {"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" }, - {"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" }, - {"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" }, - {"none",""} - }; - - public static readonly List vmessSecuritys = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" }; - public static readonly List ssSecuritys = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" }; - public static readonly List ssSecuritysInSagerNet = new() { "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "rc4", "rc4-md5", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-cfb8", "aes-192-cfb8", "aes-256-cfb8", "aes-128-ofb", "aes-192-ofb", "aes-256-ofb", "bf-cfb", "cast5-cfb", "des-cfb", "idea-cfb", "rc2-cfb", "seed-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "camellia-128-cfb8", "camellia-192-cfb8", "camellia-256-cfb8", "salsa20", "chacha20", "chacha20-ietf", "xchacha20" }; - public static readonly List ssSecuritysInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" }; - public static readonly List flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" }; - public static readonly List networks = new() { "tcp", "kcp", "ws", "h2", "quic", "grpc" }; - public static readonly List kcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" }; - public static readonly List coreTypes = new() { "v2fly", "SagerNet", "Xray", "v2fly_v5", "sing_box" }; - public static readonly List coreTypes4VLESS = new() { "Xray", "sing_box" }; - public static readonly List domainStrategys = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" }; - public static readonly List domainStrategys4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" }; - public static readonly List domainMatchers = new() { "linear", "mph", "" }; - public static readonly List fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" }; - public static readonly List userAgent = new() { "chrome", "firefox", "safari", "edge", "none" }; - - public static readonly List allowInsecures = new() { "true", "false", "" }; - public static readonly List domainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" }; - public static readonly List Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" }; - public static readonly List alpns = new() { "h2", "http/1.1", "h2,http/1.1", "" }; - public static readonly List LogLevel = new() { "debug", "info", "warning", "error", "none" }; - public static readonly List InboundTags = new() { "socks", "http", "socks2", "http2" }; - public static readonly List Protocols = new() { "http", "tls", "bittorrent" }; - public static readonly List TunMtus = new() { "9000", "1500" }; - public static readonly List TunStacks = new() { "gvisor", "system" }; - public static readonly List PresetMsgFilters = new() { "proxy", "direct", "block", "" }; - public static readonly List SingboxMuxs = new() { "h2mux", "smux", "yamux", "" }; - - #endregion const - - #region global variable - - public static int statePort { get; set; } - public static Job processJob { get; set; } - public static bool ShowInTaskbar { get; set; } - public static string ExePathKey { get; set; } - - #endregion global variable - } + #endregion global variable } diff --git a/v2rayN/v2rayN/Handler/ConfigHandler.cs b/v2rayN/v2rayN/Handler/ConfigHandler.cs index 8bd6892d..8efa46b3 100644 --- a/v2rayN/v2rayN/Handler/ConfigHandler.cs +++ b/v2rayN/v2rayN/Handler/ConfigHandler.cs @@ -5,1621 +5,1620 @@ using v2rayN.Base; using v2rayN.Mode; using v2rayN.Tool; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +/// +/// 本软件配置文件处理类 +/// +internal class ConfigHandler { + private static string configRes = Global.ConfigFileName; + private static readonly object objLock = new(); + + #region ConfigHandler + /// - /// 本软件配置文件处理类 + /// 载入配置文件 /// - internal class ConfigHandler + /// + /// + public static int LoadConfig(ref Config config) { - private static string configRes = Global.ConfigFileName; - private static readonly object objLock = new(); - - #region ConfigHandler - - /// - /// 载入配置文件 - /// - /// - /// - public static int LoadConfig(ref Config config) + //载入配置文件 + string result = Utils.LoadResource(Utils.GetConfigPath(configRes)); + if (!Utils.IsNullOrEmpty(result)) { - //载入配置文件 - string result = Utils.LoadResource(Utils.GetConfigPath(configRes)); - if (!Utils.IsNullOrEmpty(result)) - { - //转成Json - config = Utils.FromJson(result); - } - else - { - if (File.Exists(Utils.GetConfigPath(configRes))) - { - Utils.SaveLog("LoadConfig Exception"); - return -1; - } - } - - if (config == null) - { - config = new Config - { - }; - } - if (config.coreBasicItem == null) - { - config.coreBasicItem = new() - { - logEnabled = false, - loglevel = "warning", - - muxEnabled = false, - }; - } - - //本地监听 - if (config.inbound == null) - { - config.inbound = new List(); - InItem inItem = new() - { - protocol = Global.InboundSocks, - localPort = 10808, - udpEnabled = true, - sniffingEnabled = true, - routeOnly = false, - }; - - config.inbound.Add(inItem); - - //inItem = new InItem(); - //inItem.protocol = "http"; - //inItem.localPort = 1081; - //inItem.udpEnabled = true; - - //config.inbound.Add(inItem); - } - else - { - if (config.inbound.Count > 0) - { - config.inbound[0].protocol = Global.InboundSocks; - } - } - if (config.routingBasicItem == null) - { - config.routingBasicItem = new() - { - enableRoutingAdvanced = true - }; - } - //路由规则 - if (Utils.IsNullOrEmpty(config.routingBasicItem.domainStrategy)) - { - config.routingBasicItem.domainStrategy = Global.domainStrategys[0];//"IPIfNonMatch"; - } - //if (Utils.IsNullOrEmpty(config.domainMatcher)) - //{ - // config.domainMatcher = "linear"; - //} - - //kcp - if (config.kcpItem == null) - { - config.kcpItem = new KcpItem - { - mtu = 1350, - tti = 50, - uplinkCapacity = 12, - downlinkCapacity = 100, - readBufferSize = 2, - writeBufferSize = 2, - congestion = false - }; - } - if (config.grpcItem == null) - { - config.grpcItem = new GrpcItem - { - idle_timeout = 60, - health_check_timeout = 20, - permit_without_stream = false, - initial_windows_size = 0, - }; - } - if (config.tunModeItem == null) - { - config.tunModeItem = new TunModeItem - { - enableTun = false, - mtu = 9000, - }; - } - if (config.guiItem == null) - { - config.guiItem = new() - { - enableStatistics = false, - }; - } - if (config.uiItem == null) - { - config.uiItem = new UIItem() - { - enableAutoAdjustMainLvColWidth = true - }; - } - if (config.uiItem.mainColumnItem == null) - { - config.uiItem.mainColumnItem = new(); - } - if (Utils.IsNullOrEmpty(config.uiItem.currentLanguage)) - { - config.uiItem.currentLanguage = Global.Languages[0]; - } - - if (config.constItem == null) - { - config.constItem = new ConstItem(); - } - if (Utils.IsNullOrEmpty(config.constItem.defIEProxyExceptions)) - { - config.constItem.defIEProxyExceptions = Global.IEProxyExceptions; - } - - if (config.speedTestItem == null) - { - config.speedTestItem = new(); - } - if (config.speedTestItem.speedTestTimeout < 10) - { - config.speedTestItem.speedTestTimeout = 10; - } - if (Utils.IsNullOrEmpty(config.speedTestItem.speedTestUrl)) - { - config.speedTestItem.speedTestUrl = Global.SpeedTestUrls[0]; - } - if (Utils.IsNullOrEmpty(config.speedTestItem.speedPingTestUrl)) - { - config.speedTestItem.speedPingTestUrl = Global.SpeedPingTestUrl; - } - - if (config.mux4Sbox == null) - { - config.mux4Sbox = new() - { - protocol = Global.SingboxMuxs[0], - max_connections = 4, - min_streams = 4, - max_streams = 0, - padding = true - }; - } - - LazyConfig.Instance.SetConfig(config); - return 0; + //转成Json + config = Utils.FromJson(result); } - - /// - /// 保参数 - /// - /// - /// - public static int SaveConfig(ref Config config, bool reload = true) + else { - ToJsonFile(config); - - return 0; - } - - /// - /// 存储文件 - /// - /// - private static void ToJsonFile(Config config) - { - lock (objLock) - { - try - { - //save temp file - var resPath = Utils.GetConfigPath(configRes); - var tempPath = $"{resPath}_temp"; - if (Utils.ToJsonFile(config, tempPath) != 0) - { - return; - } - - if (File.Exists(resPath)) - { - File.Delete(resPath); - } - //rename - File.Move(tempPath, resPath); - } - catch (Exception ex) - { - Utils.SaveLog("ToJsonFile", ex); - } - } - } - - public static int ImportOldGuiConfig(ref Config config, string fileName) - { - string result = Utils.LoadResource(fileName); - if (Utils.IsNullOrEmpty(result)) + if (File.Exists(Utils.GetConfigPath(configRes))) { + Utils.SaveLog("LoadConfig Exception"); return -1; } - - var configOld = Utils.FromJson(result); - if (configOld == null) - { - return -1; - } - - var subItem = Utils.FromJson>(Utils.ToJson(configOld.subItem)); - foreach (var it in subItem) - { - if (Utils.IsNullOrEmpty(it.id)) - { - it.id = Utils.GetGUID(false); - } - SqliteHelper.Instance.Replace(it); - } - - var profileItems = Utils.FromJson>(Utils.ToJson(configOld.vmess)); - foreach (var it in profileItems) - { - if (Utils.IsNullOrEmpty(it.indexId)) - { - it.indexId = Utils.GetGUID(false); - } - SqliteHelper.Instance.Replace(it); - } - - foreach (var it in configOld.routings) - { - if (it.locked) - { - continue; - } - var routing = Utils.FromJson(Utils.ToJson(it)); - foreach (var it2 in it.rules) - { - it2.id = Utils.GetGUID(false); - } - routing.ruleNum = it.rules.Count; - routing.ruleSet = Utils.ToJson(it.rules, false); - - if (Utils.IsNullOrEmpty(routing.id)) - { - routing.id = Utils.GetGUID(false); - } - SqliteHelper.Instance.Replace(routing); - } - - config = Utils.FromJson(Utils.ToJson(configOld)); - - if (config.coreBasicItem == null) - { - config.coreBasicItem = new() - { - logEnabled = configOld.logEnabled, - loglevel = configOld.loglevel, - muxEnabled = configOld.muxEnabled, - }; - } - - if (config.routingBasicItem == null) - { - config.routingBasicItem = new() - { - enableRoutingAdvanced = configOld.enableRoutingAdvanced, - domainStrategy = configOld.domainStrategy - }; - } - - if (config.guiItem == null) - { - config.guiItem = new() - { - enableStatistics = configOld.enableStatistics, - keepOlderDedupl = configOld.keepOlderDedupl, - ignoreGeoUpdateCore = configOld.ignoreGeoUpdateCore, - autoUpdateInterval = configOld.autoUpdateInterval, - checkPreReleaseUpdate = configOld.checkPreReleaseUpdate, - enableSecurityProtocolTls13 = configOld.enableSecurityProtocolTls13, - trayMenuServersLimit = configOld.trayMenuServersLimit, - }; - } - - GetDefaultServer(ref config); - GetDefaultRouting(ref config); - SaveConfig(ref config); - LoadConfig(ref config); - - return 0; } - #endregion ConfigHandler - - #region Server - - /// - /// 添加服务器或编辑 - /// - /// - /// - /// - public static int AddServer(ref Config config, ProfileItem profileItem, bool toFile = true) + if (config == null) { - profileItem.configType = EConfigType.VMess; - - profileItem.address = profileItem.address.TrimEx(); - profileItem.id = profileItem.id.TrimEx(); - profileItem.security = profileItem.security.TrimEx(); - profileItem.network = profileItem.network.TrimEx(); - profileItem.headerType = profileItem.headerType.TrimEx(); - profileItem.requestHost = profileItem.requestHost.TrimEx(); - profileItem.path = profileItem.path.TrimEx(); - profileItem.streamSecurity = profileItem.streamSecurity.TrimEx(); - - if (!Global.vmessSecuritys.Contains(profileItem.security)) + config = new Config { - return -1; - } - if (profileItem.id.IsNullOrEmpty()) - { - return -1; - } - - AddServerCommon(ref config, profileItem, toFile); - - return 0; + }; } - - /// - /// 移除服务器 - /// - /// - /// - /// - public static int RemoveServer(Config config, List indexs) + if (config.coreBasicItem == null) { - var subid = "TempRemoveSubId"; - foreach (var item in indexs) + config.coreBasicItem = new() { - item.subid = subid; - } + logEnabled = false, + loglevel = "warning", - SqliteHelper.Instance.UpdateAll(indexs); - RemoveServerViaSubid(ref config, subid, false); - - return 0; + muxEnabled = false, + }; } - /// - /// 克隆服务器 - /// - /// - /// - /// - public static int CopyServer(ref Config config, List indexs) + //本地监听 + if (config.inbound == null) { - foreach (var it in indexs) + config.inbound = new List(); + InItem inItem = new() { - var item = LazyConfig.Instance.GetProfileItem(it.indexId); - if (item is null) - { - continue; - } + protocol = Global.InboundSocks, + localPort = 10808, + udpEnabled = true, + sniffingEnabled = true, + routeOnly = false, + }; - ProfileItem profileItem = Utils.DeepCopy(item); - profileItem.indexId = string.Empty; - profileItem.remarks = $"{item.remarks}-clone"; + config.inbound.Add(inItem); - if (profileItem.configType == EConfigType.Custom) - { - profileItem.address = Utils.GetConfigPath(profileItem.address); - if (AddCustomServer(ref config, profileItem, false) == 0) - { - } - } - else - { - AddServerCommon(ref config, profileItem, true); - } - } + //inItem = new InItem(); + //inItem.protocol = "http"; + //inItem.localPort = 1081; + //inItem.udpEnabled = true; - return 0; + //config.inbound.Add(inItem); } - - /// - /// 设置活动服务器 - /// - /// - /// - /// - public static int SetDefaultServerIndex(ref Config config, string? indexId) + else { - if (Utils.IsNullOrEmpty(indexId)) + if (config.inbound.Count > 0) { - return -1; + config.inbound[0].protocol = Global.InboundSocks; } - - config.indexId = indexId; - - ToJsonFile(config); - - return 0; } - - public static int SetDefaultServer(Config config, List lstProfile) + if (config.routingBasicItem == null) { - if (lstProfile.Exists(t => t.indexId == config.indexId)) + config.routingBasicItem = new() { - return 0; - } - if (SqliteHelper.Instance.Table().Where(t => t.indexId == config.indexId).Any()) - { - return 0; - } - if (lstProfile.Count > 0) - { - return SetDefaultServerIndex(ref config, lstProfile.Where(t => t.port > 0).FirstOrDefault()?.indexId); - } - return SetDefaultServerIndex(ref config, SqliteHelper.Instance.Table().Where(t => t.port > 0).Select(t => t.indexId).FirstOrDefault()); + enableRoutingAdvanced = true + }; } - - public static ProfileItem? GetDefaultServer(ref Config config) + //路由规则 + if (Utils.IsNullOrEmpty(config.routingBasicItem.domainStrategy)) { - var item = LazyConfig.Instance.GetProfileItem(config.indexId); - if (item is null) - { - var item2 = SqliteHelper.Instance.Table().FirstOrDefault(); - SetDefaultServerIndex(ref config, item2?.indexId); - return item2; - } - - return item; + config.routingBasicItem.domainStrategy = Global.domainStrategys[0];//"IPIfNonMatch"; } + //if (Utils.IsNullOrEmpty(config.domainMatcher)) + //{ + // config.domainMatcher = "linear"; + //} - /// - /// 移动服务器 - /// - /// - /// - /// - /// - /// - public static int MoveServer(ref Config config, ref List lstProfile, int index, EMove eMove, int pos = -1) + //kcp + if (config.kcpItem == null) { - int count = lstProfile.Count; - if (index < 0 || index > lstProfile.Count - 1) + config.kcpItem = new KcpItem { - return -1; - } - - for (int i = 0; i < lstProfile.Count; i++) - { - ProfileExHandler.Instance.SetSort(lstProfile[i].indexId, (i + 1) * 10); - } - - var sort = 0; - switch (eMove) - { - case EMove.Top: - { - if (index == 0) - { - return 0; - } - sort = ProfileExHandler.Instance.GetSort(lstProfile[0].indexId) - 1; - - break; - } - case EMove.Up: - { - if (index == 0) - { - return 0; - } - sort = ProfileExHandler.Instance.GetSort(lstProfile[index - 1].indexId) - 1; - - break; - } - - case EMove.Down: - { - if (index == count - 1) - { - return 0; - } - sort = ProfileExHandler.Instance.GetSort(lstProfile[index + 1].indexId) + 1; - - break; - } - case EMove.Bottom: - { - if (index == count - 1) - { - return 0; - } - sort = ProfileExHandler.Instance.GetSort(lstProfile[^1].indexId) + 1; - - break; - } - case EMove.Position: - sort = pos * 10 + 1; - break; - } - - ProfileExHandler.Instance.SetSort(lstProfile[index].indexId, sort); - return 0; + mtu = 1350, + tti = 50, + uplinkCapacity = 12, + downlinkCapacity = 100, + readBufferSize = 2, + writeBufferSize = 2, + congestion = false + }; } - - /// - /// 添加自定义服务器 - /// - /// - /// - /// - public static int AddCustomServer(ref Config config, ProfileItem profileItem, bool blDelete) + if (config.grpcItem == null) { - var fileName = profileItem.address; - if (!File.Exists(fileName)) + config.grpcItem = new GrpcItem { - return -1; - } - var ext = Path.GetExtension(fileName); - string newFileName = $"{Utils.GetGUID()}{ext}"; - //newFileName = Path.Combine(Utils.GetTempPath(), newFileName); - - try - { - File.Copy(fileName, Utils.GetConfigPath(newFileName)); - if (blDelete) - { - File.Delete(fileName); - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - return -1; - } - - profileItem.address = newFileName; - profileItem.configType = EConfigType.Custom; - if (Utils.IsNullOrEmpty(profileItem.remarks)) - { - profileItem.remarks = $"import custom@{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}"; - } - - AddServerCommon(ref config, profileItem, true); - - return 0; + idle_timeout = 60, + health_check_timeout = 20, + permit_without_stream = false, + initial_windows_size = 0, + }; } - - /// - /// 添加服务器或编辑 - /// - /// - /// - /// - public static int EditCustomServer(ref Config config, ProfileItem profileItem) + if (config.tunModeItem == null) { - if (SqliteHelper.Instance.Update(profileItem) > 0) + config.tunModeItem = new TunModeItem { - return 0; - } - else - { - return -1; - } - - //ToJsonFile(config); + enableTun = false, + mtu = 9000, + }; } - - /// - /// 添加服务器或编辑 - /// - /// - /// - /// - public static int AddShadowsocksServer(ref Config config, ProfileItem profileItem, bool toFile = true) + if (config.guiItem == null) { - profileItem.configType = EConfigType.Shadowsocks; - - profileItem.address = profileItem.address.TrimEx(); - profileItem.id = profileItem.id.TrimEx(); - profileItem.security = profileItem.security.TrimEx(); - - if (!LazyConfig.Instance.GetShadowsocksSecuritys(profileItem).Contains(profileItem.security)) + config.guiItem = new() { - return -1; - } - if (profileItem.id.IsNullOrEmpty()) - { - return -1; - } - - AddServerCommon(ref config, profileItem, toFile); - - return 0; + enableStatistics = false, + }; } - - /// - /// 添加服务器或编辑 - /// - /// - /// - /// - public static int AddSocksServer(ref Config config, ProfileItem profileItem, bool toFile = true) + if (config.uiItem == null) { - profileItem.configType = EConfigType.Socks; - - profileItem.address = profileItem.address.TrimEx(); - - AddServerCommon(ref config, profileItem, toFile); - - return 0; + config.uiItem = new UIItem() + { + enableAutoAdjustMainLvColWidth = true + }; } - - /// - /// 添加服务器或编辑 - /// - /// - /// - /// - public static int AddTrojanServer(ref Config config, ProfileItem profileItem, bool toFile = true) + if (config.uiItem.mainColumnItem == null) { - profileItem.configType = EConfigType.Trojan; - - profileItem.address = profileItem.address.TrimEx(); - profileItem.id = profileItem.id.TrimEx(); - if (Utils.IsNullOrEmpty(profileItem.streamSecurity)) - { - profileItem.streamSecurity = Global.StreamSecurity; - } - if (profileItem.id.IsNullOrEmpty()) - { - return -1; - } - - AddServerCommon(ref config, profileItem, toFile); - - return 0; + config.uiItem.mainColumnItem = new(); } - - public static int SortServers(ref Config config, string subId, string colName, bool asc) + if (Utils.IsNullOrEmpty(config.uiItem.currentLanguage)) { - var lstModel = LazyConfig.Instance.ProfileItems(subId, ""); - if (lstModel.Count <= 0) - { - return -1; - } - var lstProfileExs = ProfileExHandler.Instance.ProfileExs; - var lstProfile = (from t in lstModel - join t3 in lstProfileExs on t.indexId equals t3.indexId into t3b - from t33 in t3b.DefaultIfEmpty() - select new ProfileItemModel - { - indexId = t.indexId, - configType = t.configType, - remarks = t.remarks, - address = t.address, - port = t.port, - security = t.security, - network = t.network, - streamSecurity = t.streamSecurity, - delay = t33 == null ? 0 : t33.delay, - speed = t33 == null ? 0 : t33.speed, - sort = t33 == null ? 0 : t33.sort - }).ToList(); - - Enum.TryParse(colName, true, out EServerColName name); - var propertyName = string.Empty; - switch (name) - { - case EServerColName.configType: - case EServerColName.remarks: - case EServerColName.address: - case EServerColName.port: - case EServerColName.security: - case EServerColName.network: - case EServerColName.streamSecurity: - propertyName = name.ToString(); - break; - - case EServerColName.delayVal: - propertyName = "delay"; - break; - - case EServerColName.speedVal: - propertyName = "speed"; - break; - - case EServerColName.subRemarks: - propertyName = "subid"; - break; - - default: - return -1; - } - - var items = lstProfile.AsQueryable(); - - if (asc) - { - lstProfile = items.OrderBy(propertyName).ToList(); - } - else - { - lstProfile = items.OrderByDescending(propertyName).ToList(); - } - for (int i = 0; i < lstProfile.Count; i++) - { - ProfileExHandler.Instance.SetSort(lstProfile[i].indexId, (i + 1) * 10); - } - if (name == EServerColName.delayVal) - { - var maxSort = lstProfile.Max(t => t.sort) + 10; - foreach (var item in lstProfile) - { - if (item.delay <= 0) - { - ProfileExHandler.Instance.SetSort(item.indexId, maxSort); - } - } - } - if (name == EServerColName.speedVal) - { - var maxSort = lstProfile.Max(t => t.sort) + 10; - foreach (var item in lstProfile) - { - if (item.speed <= 0) - { - ProfileExHandler.Instance.SetSort(item.indexId, maxSort); - } - } - } - - return 0; + config.uiItem.currentLanguage = Global.Languages[0]; } - /// - /// 添加服务器或编辑 - /// - /// - /// - /// - public static int AddVlessServer(ref Config config, ProfileItem profileItem, bool toFile = true) + if (config.constItem == null) { - profileItem.configType = EConfigType.VLESS; - - profileItem.address = profileItem.address.TrimEx(); - profileItem.id = profileItem.id.TrimEx(); - profileItem.security = profileItem.security.TrimEx(); - profileItem.network = profileItem.network.TrimEx(); - profileItem.headerType = profileItem.headerType.TrimEx(); - profileItem.requestHost = profileItem.requestHost.TrimEx(); - profileItem.path = profileItem.path.TrimEx(); - profileItem.streamSecurity = profileItem.streamSecurity.TrimEx(); - - if (!Global.flows.Contains(profileItem.flow)) - { - profileItem.flow = Global.flows.First(); - } - if (profileItem.id.IsNullOrEmpty()) - { - return -1; - } - - AddServerCommon(ref config, profileItem, toFile); - - return 0; + config.constItem = new ConstItem(); } - - public static Tuple DedupServerList(Config config, string subId) + if (Utils.IsNullOrEmpty(config.constItem.defIEProxyExceptions)) { - var lstProfile = LazyConfig.Instance.ProfileItems(subId); - - List lstKeep = new(); - List lstRemove = new(); - if (!config.guiItem.keepOlderDedupl) lstProfile.Reverse(); - - foreach (ProfileItem item in lstProfile) - { - if (!lstKeep.Exists(i => CompareProfileItem(i, item, false))) - { - lstKeep.Add(item); - } - else - { - lstRemove.Add(item); - } - } - RemoveServer(config, lstRemove); - - return new Tuple(lstProfile.Count, lstKeep.Count); + config.constItem.defIEProxyExceptions = Global.IEProxyExceptions; } - public static int AddServerCommon(ref Config config, ProfileItem profileItem, bool toFile = true) + if (config.speedTestItem == null) { - profileItem.configVersion = 2; - - if (!Utils.IsNullOrEmpty(profileItem.streamSecurity)) - { - if (Utils.IsNullOrEmpty(profileItem.allowInsecure)) - { - profileItem.allowInsecure = config.coreBasicItem.defAllowInsecure.ToString().ToLower(); - } - if (Utils.IsNullOrEmpty(profileItem.fingerprint)) - { - profileItem.fingerprint = config.coreBasicItem.defFingerprint; - } - } - - if (!Utils.IsNullOrEmpty(profileItem.network) && !Global.networks.Contains(profileItem.network)) - { - profileItem.network = Global.DefaultNetwork; - } - - var maxSort = -1; - if (Utils.IsNullOrEmpty(profileItem.indexId)) - { - profileItem.indexId = Utils.GetGUID(false); - maxSort = ProfileExHandler.Instance.GetMaxSort(); - } - if (!toFile && maxSort < 0) - { - maxSort = ProfileExHandler.Instance.GetMaxSort(); - } - if (maxSort > 0) - { - ProfileExHandler.Instance.SetSort(profileItem.indexId, maxSort + 1); - } - - if (toFile) - { - SqliteHelper.Instance.Replace(profileItem); - } - return 0; + config.speedTestItem = new(); } - - private static bool CompareProfileItem(ProfileItem o, ProfileItem n, bool remarks) + if (config.speedTestItem.speedTestTimeout < 10) { - if (o == null || n == null) - { - return false; - } - - return o.configType == n.configType - && o.address == n.address - && o.port == n.port - && o.id == n.id - && o.alterId == n.alterId - && o.security == n.security - && o.network == n.network - && o.headerType == n.headerType - && o.requestHost == n.requestHost - && o.path == n.path - && (o.configType == EConfigType.Trojan || o.streamSecurity == n.streamSecurity) - && o.flow == n.flow - && o.sni == n.sni - && (!remarks || o.remarks == n.remarks); + config.speedTestItem.speedTestTimeout = 10; + } + if (Utils.IsNullOrEmpty(config.speedTestItem.speedTestUrl)) + { + config.speedTestItem.speedTestUrl = Global.SpeedTestUrls[0]; + } + if (Utils.IsNullOrEmpty(config.speedTestItem.speedPingTestUrl)) + { + config.speedTestItem.speedPingTestUrl = Global.SpeedPingTestUrl; } - private static int RemoveProfileItem(Config config, string indexId) + if (config.mux4Sbox == null) + { + config.mux4Sbox = new() + { + protocol = Global.SingboxMuxs[0], + max_connections = 4, + min_streams = 4, + max_streams = 0, + padding = true + }; + } + + LazyConfig.Instance.SetConfig(config); + return 0; + } + + /// + /// 保参数 + /// + /// + /// + public static int SaveConfig(ref Config config, bool reload = true) + { + ToJsonFile(config); + + return 0; + } + + /// + /// 存储文件 + /// + /// + private static void ToJsonFile(Config config) + { + lock (objLock) { try { - var item = LazyConfig.Instance.GetProfileItem(indexId); - if (item == null) + //save temp file + var resPath = Utils.GetConfigPath(configRes); + var tempPath = $"{resPath}_temp"; + if (Utils.ToJsonFile(config, tempPath) != 0) { - return 0; - } - if (item.configType == EConfigType.Custom) - { - File.Delete(Utils.GetConfigPath(item.address)); + return; } - SqliteHelper.Instance.Delete(item); + if (File.Exists(resPath)) + { + File.Delete(resPath); + } + //rename + File.Move(tempPath, resPath); } catch (Exception ex) { - Utils.SaveLog("Remove Item", ex); - } - - return 0; - } - - #endregion Server - - #region Batch add servers - - /// - /// 批量添加服务器 - /// - /// - /// - /// - /// 成功导入的数量 - private static int AddBatchServers(ref Config config, string clipboardData, string subid, bool isSub, List lstOriSub) - { - if (Utils.IsNullOrEmpty(clipboardData)) - { - return -1; - } - - string subFilter = string.Empty; - //remove sub items - if (isSub && !Utils.IsNullOrEmpty(subid)) - { - RemoveServerViaSubid(ref config, subid, isSub); - subFilter = LazyConfig.Instance.GetSubItem(subid)?.filter ?? ""; - } - - int countServers = 0; - //Check for duplicate indexId - List? lstDbIndexId = null; - List lstAdd = new(); - var arrData = clipboardData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty()); - if (isSub) - { - arrData = arrData.Distinct(); - } - foreach (string str in arrData) - { - //maybe sub - if (!isSub && (str.StartsWith(Global.httpsProtocol) || str.StartsWith(Global.httpProtocol))) - { - if (AddSubItem(ref config, str) == 0) - { - countServers++; - } - continue; - } - ProfileItem profileItem = ShareHandler.ImportFromClipboardConfig(str, out string msg); - if (profileItem == null) - { - continue; - } - - //exist sub items - if (isSub && !Utils.IsNullOrEmpty(subid)) - { - var existItem = lstOriSub?.FirstOrDefault(t => t.isSub == isSub && CompareProfileItem(t, profileItem, true)); - if (existItem != null) - { - //Check for duplicate indexId - if (lstDbIndexId is null) - { - lstDbIndexId = LazyConfig.Instance.ProfileItemIndexs(""); - } - if (lstAdd.Any(t => t.indexId == existItem.indexId) - || lstDbIndexId.Any(t => t == existItem.indexId)) - { - profileItem.indexId = string.Empty; - } - else - { - profileItem.indexId = existItem.indexId; - } - } - //filter - if (!Utils.IsNullOrEmpty(subFilter)) - { - if (!Regex.IsMatch(profileItem.remarks, subFilter)) - { - continue; - } - } - } - profileItem.subid = subid; - profileItem.isSub = isSub; - var addStatus = -1; - - if (profileItem.configType == EConfigType.VMess) - { - addStatus = AddServer(ref config, profileItem, false); - } - else if (profileItem.configType == EConfigType.Shadowsocks) - { - addStatus = AddShadowsocksServer(ref config, profileItem, false); - } - else if (profileItem.configType == EConfigType.Socks) - { - addStatus = AddSocksServer(ref config, profileItem, false); - } - else if (profileItem.configType == EConfigType.Trojan) - { - addStatus = AddTrojanServer(ref config, profileItem, false); - } - else if (profileItem.configType == EConfigType.VLESS) - { - addStatus = AddVlessServer(ref config, profileItem, false); - } - - if (addStatus == 0) - { - countServers++; - lstAdd.Add(profileItem); - } - } - - if (lstAdd.Count > 0) - { - SqliteHelper.Instance.InsertAll(lstAdd); - } - - ToJsonFile(config); - return countServers; - } - - private static int AddBatchServers4Custom(ref Config config, string clipboardData, string subid, bool isSub, List lstOriSub) - { - if (Utils.IsNullOrEmpty(clipboardData)) - { - return -1; - } - - //判断str是否包含s的任意一个字符串 - static bool Containss(string str, params string[] s) - { - foreach (var item in s) - { - if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true; - } - return false; - } - - ProfileItem profileItem = new(); - //Is v2ray configuration - V2rayConfig? v2rayConfig = Utils.FromJson(clipboardData); - if (v2rayConfig?.inbounds?.Count > 0 - && v2rayConfig.outbounds?.Count > 0) - { - var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); - File.WriteAllText(fileName, clipboardData); - - profileItem.coreType = ECoreType.Xray; - profileItem.address = fileName; - profileItem.remarks = "v2ray_custom"; - } - //Is Clash configuration - else if (Containss(clipboardData, "port", "socks-port", "proxies")) - { - var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.yaml"); - File.WriteAllText(fileName, clipboardData); - - profileItem.coreType = ECoreType.clash; - profileItem.address = fileName; - profileItem.remarks = "clash_custom"; - } - //Is hysteria configuration - else if (Containss(clipboardData, "server", "up", "down", "listen", "", "")) - { - var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); - File.WriteAllText(fileName, clipboardData); - - profileItem.coreType = ECoreType.hysteria; - profileItem.address = fileName; - profileItem.remarks = "hysteria_custom"; - } - //Is naiveproxy configuration - else if (Containss(clipboardData, "listen", "proxy", "", "")) - { - var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); - File.WriteAllText(fileName, clipboardData); - - profileItem.coreType = ECoreType.naiveproxy; - profileItem.address = fileName; - profileItem.remarks = "naiveproxy_custom"; - } - //Is Other configuration - else - { - return -1; - //var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.txt"); - //File.WriteAllText(fileName, clipboardData); - - //profileItem.address = fileName; - //profileItem.remarks = "other_custom"; - } - - if (isSub && !Utils.IsNullOrEmpty(subid)) - { - RemoveServerViaSubid(ref config, subid, isSub); - } - if (isSub && lstOriSub?.Count == 1) - { - profileItem.indexId = lstOriSub[0].indexId; - } - profileItem.subid = subid; - profileItem.isSub = isSub; - - if (Utils.IsNullOrEmpty(profileItem.address)) - { - return -1; - } - - if (AddCustomServer(ref config, profileItem, true) == 0) - { - return 1; - } - else - { - return -1; + Utils.SaveLog("ToJsonFile", ex); } } + } - private static int AddBatchServers4SsSIP008(ref Config config, string clipboardData, string subid, bool isSub, List lstOriSub) + public static int ImportOldGuiConfig(ref Config config, string fileName) + { + string result = Utils.LoadResource(fileName); + if (Utils.IsNullOrEmpty(result)) { - if (Utils.IsNullOrEmpty(clipboardData)) - { - return -1; - } - - if (isSub && !Utils.IsNullOrEmpty(subid)) - { - RemoveServerViaSubid(ref config, subid, isSub); - } - - //SsSIP008 - var lstSsServer = Utils.FromJson>(clipboardData); - if (lstSsServer?.Count <= 0) - { - var ssSIP008 = Utils.FromJson(clipboardData); - if (ssSIP008?.servers?.Count > 0) - { - lstSsServer = ssSIP008.servers; - } - } - - if (lstSsServer?.Count > 0) - { - int counter = 0; - foreach (var it in lstSsServer) - { - var ssItem = new ProfileItem() - { - subid = subid, - remarks = it.remarks, - security = it.method, - id = it.password, - address = it.server, - port = Utils.ToInt(it.server_port) - }; - ssItem.subid = subid; - ssItem.isSub = isSub; - if (AddShadowsocksServer(ref config, ssItem) == 0) - { - counter++; - } - } - ToJsonFile(config); - return counter; - } - return -1; } - public static int AddBatchServers(ref Config config, string clipboardData, string subid, bool isSub) + var configOld = Utils.FromJson(result); + if (configOld == null) { - List? lstOriSub = null; - if (isSub && !Utils.IsNullOrEmpty(subid)) - { - lstOriSub = LazyConfig.Instance.ProfileItems(subid); - } - - var counter = 0; - if (Utils.IsBase64String(clipboardData)) - { - counter = AddBatchServers(ref config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub); - } - if (counter < 1) - { - counter = AddBatchServers(ref config, clipboardData, subid, isSub, lstOriSub); - } - if (counter < 1) - { - counter = AddBatchServers(ref config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub); - } - - if (counter < 1) - { - counter = AddBatchServers4SsSIP008(ref config, clipboardData, subid, isSub, lstOriSub); - } - - //maybe other sub - if (counter < 1) - { - counter = AddBatchServers4Custom(ref config, clipboardData, subid, isSub, lstOriSub); - } - - return counter; + return -1; } - #endregion Batch add servers - - #region Sub & Group - - /// - /// add sub - /// - /// - /// - /// - public static int AddSubItem(ref Config config, string url) + var subItem = Utils.FromJson>(Utils.ToJson(configOld.subItem)); + foreach (var it in subItem) { - //already exists - if (SqliteHelper.Instance.Table().Where(e => e.url == url).Count() > 0) + if (Utils.IsNullOrEmpty(it.id)) { - return 0; + it.id = Utils.GetGUID(false); } + SqliteHelper.Instance.Replace(it); + } - SubItem subItem = new() + var profileItems = Utils.FromJson>(Utils.ToJson(configOld.vmess)); + foreach (var it in profileItems) + { + if (Utils.IsNullOrEmpty(it.indexId)) { - id = string.Empty, - remarks = "import_sub", - url = url + it.indexId = Utils.GetGUID(false); + } + SqliteHelper.Instance.Replace(it); + } + + foreach (var it in configOld.routings) + { + if (it.locked) + { + continue; + } + var routing = Utils.FromJson(Utils.ToJson(it)); + foreach (var it2 in it.rules) + { + it2.id = Utils.GetGUID(false); + } + routing.ruleNum = it.rules.Count; + routing.ruleSet = Utils.ToJson(it.rules, false); + + if (Utils.IsNullOrEmpty(routing.id)) + { + routing.id = Utils.GetGUID(false); + } + SqliteHelper.Instance.Replace(routing); + } + + config = Utils.FromJson(Utils.ToJson(configOld)); + + if (config.coreBasicItem == null) + { + config.coreBasicItem = new() + { + logEnabled = configOld.logEnabled, + loglevel = configOld.loglevel, + muxEnabled = configOld.muxEnabled, }; - - return AddSubItem(ref config, subItem); } - public static int AddSubItem(ref Config config, SubItem subItem) + if (config.routingBasicItem == null) { - if (Utils.IsNullOrEmpty(subItem.id)) + config.routingBasicItem = new() { - subItem.id = Utils.GetGUID(false); + enableRoutingAdvanced = configOld.enableRoutingAdvanced, + domainStrategy = configOld.domainStrategy + }; + } - if (subItem.sort <= 0) + if (config.guiItem == null) + { + config.guiItem = new() + { + enableStatistics = configOld.enableStatistics, + keepOlderDedupl = configOld.keepOlderDedupl, + ignoreGeoUpdateCore = configOld.ignoreGeoUpdateCore, + autoUpdateInterval = configOld.autoUpdateInterval, + checkPreReleaseUpdate = configOld.checkPreReleaseUpdate, + enableSecurityProtocolTls13 = configOld.enableSecurityProtocolTls13, + trayMenuServersLimit = configOld.trayMenuServersLimit, + }; + } + + GetDefaultServer(ref config); + GetDefaultRouting(ref config); + SaveConfig(ref config); + LoadConfig(ref config); + + return 0; + } + + #endregion ConfigHandler + + #region Server + + /// + /// 添加服务器或编辑 + /// + /// + /// + /// + public static int AddServer(ref Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.configType = EConfigType.VMess; + + profileItem.address = profileItem.address.TrimEx(); + profileItem.id = profileItem.id.TrimEx(); + profileItem.security = profileItem.security.TrimEx(); + profileItem.network = profileItem.network.TrimEx(); + profileItem.headerType = profileItem.headerType.TrimEx(); + profileItem.requestHost = profileItem.requestHost.TrimEx(); + profileItem.path = profileItem.path.TrimEx(); + profileItem.streamSecurity = profileItem.streamSecurity.TrimEx(); + + if (!Global.vmessSecuritys.Contains(profileItem.security)) + { + return -1; + } + if (profileItem.id.IsNullOrEmpty()) + { + return -1; + } + + AddServerCommon(ref config, profileItem, toFile); + + return 0; + } + + /// + /// 移除服务器 + /// + /// + /// + /// + public static int RemoveServer(Config config, List indexs) + { + var subid = "TempRemoveSubId"; + foreach (var item in indexs) + { + item.subid = subid; + } + + SqliteHelper.Instance.UpdateAll(indexs); + RemoveServerViaSubid(ref config, subid, false); + + return 0; + } + + /// + /// 克隆服务器 + /// + /// + /// + /// + public static int CopyServer(ref Config config, List indexs) + { + foreach (var it in indexs) + { + var item = LazyConfig.Instance.GetProfileItem(it.indexId); + if (item is null) + { + continue; + } + + ProfileItem profileItem = Utils.DeepCopy(item); + profileItem.indexId = string.Empty; + profileItem.remarks = $"{item.remarks}-clone"; + + if (profileItem.configType == EConfigType.Custom) + { + profileItem.address = Utils.GetConfigPath(profileItem.address); + if (AddCustomServer(ref config, profileItem, false) == 0) { - var maxSort = 0; - if (SqliteHelper.Instance.Table().Count() > 0) - { - maxSort = SqliteHelper.Instance.Table().Max(t => t == null ? 0 : t.sort); - } - subItem.sort = maxSort + 1; } } - if (SqliteHelper.Instance.Replace(subItem) > 0) - { - return 0; - } else { - return -1; + AddServerCommon(ref config, profileItem, true); } } - /// - /// 移除服务器 - /// - /// - /// - /// - public static int RemoveServerViaSubid(ref Config config, string subid, bool isSub) + return 0; + } + + /// + /// 设置活动服务器 + /// + /// + /// + /// + public static int SetDefaultServerIndex(ref Config config, string? indexId) + { + if (Utils.IsNullOrEmpty(indexId)) { - if (Utils.IsNullOrEmpty(subid)) + return -1; + } + + config.indexId = indexId; + + ToJsonFile(config); + + return 0; + } + + public static int SetDefaultServer(Config config, List lstProfile) + { + if (lstProfile.Exists(t => t.indexId == config.indexId)) + { + return 0; + } + if (SqliteHelper.Instance.Table().Where(t => t.indexId == config.indexId).Any()) + { + return 0; + } + if (lstProfile.Count > 0) + { + return SetDefaultServerIndex(ref config, lstProfile.Where(t => t.port > 0).FirstOrDefault()?.indexId); + } + return SetDefaultServerIndex(ref config, SqliteHelper.Instance.Table().Where(t => t.port > 0).Select(t => t.indexId).FirstOrDefault()); + } + + public static ProfileItem? GetDefaultServer(ref Config config) + { + var item = LazyConfig.Instance.GetProfileItem(config.indexId); + if (item is null) + { + var item2 = SqliteHelper.Instance.Table().FirstOrDefault(); + SetDefaultServerIndex(ref config, item2?.indexId); + return item2; + } + + return item; + } + + /// + /// 移动服务器 + /// + /// + /// + /// + /// + /// + public static int MoveServer(ref Config config, ref List lstProfile, int index, EMove eMove, int pos = -1) + { + int count = lstProfile.Count; + if (index < 0 || index > lstProfile.Count - 1) + { + return -1; + } + + for (int i = 0; i < lstProfile.Count; i++) + { + ProfileExHandler.Instance.SetSort(lstProfile[i].indexId, (i + 1) * 10); + } + + var sort = 0; + switch (eMove) + { + case EMove.Top: + { + if (index == 0) + { + return 0; + } + sort = ProfileExHandler.Instance.GetSort(lstProfile[0].indexId) - 1; + + break; + } + case EMove.Up: + { + if (index == 0) + { + return 0; + } + sort = ProfileExHandler.Instance.GetSort(lstProfile[index - 1].indexId) - 1; + + break; + } + + case EMove.Down: + { + if (index == count - 1) + { + return 0; + } + sort = ProfileExHandler.Instance.GetSort(lstProfile[index + 1].indexId) + 1; + + break; + } + case EMove.Bottom: + { + if (index == count - 1) + { + return 0; + } + sort = ProfileExHandler.Instance.GetSort(lstProfile[^1].indexId) + 1; + + break; + } + case EMove.Position: + sort = pos * 10 + 1; + break; + } + + ProfileExHandler.Instance.SetSort(lstProfile[index].indexId, sort); + return 0; + } + + /// + /// 添加自定义服务器 + /// + /// + /// + /// + public static int AddCustomServer(ref Config config, ProfileItem profileItem, bool blDelete) + { + var fileName = profileItem.address; + if (!File.Exists(fileName)) + { + return -1; + } + var ext = Path.GetExtension(fileName); + string newFileName = $"{Utils.GetGUID()}{ext}"; + //newFileName = Path.Combine(Utils.GetTempPath(), newFileName); + + try + { + File.Copy(fileName, Utils.GetConfigPath(newFileName)); + if (blDelete) { - return -1; + File.Delete(fileName); } - var customProfile = SqliteHelper.Instance.Table().Where(t => t.subid == subid && t.configType == EConfigType.Custom).ToList(); - if (isSub) + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + return -1; + } + + profileItem.address = newFileName; + profileItem.configType = EConfigType.Custom; + if (Utils.IsNullOrEmpty(profileItem.remarks)) + { + profileItem.remarks = $"import custom@{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}"; + } + + AddServerCommon(ref config, profileItem, true); + + return 0; + } + + /// + /// 添加服务器或编辑 + /// + /// + /// + /// + public static int EditCustomServer(ref Config config, ProfileItem profileItem) + { + if (SqliteHelper.Instance.Update(profileItem) > 0) + { + return 0; + } + else + { + return -1; + } + + //ToJsonFile(config); + } + + /// + /// 添加服务器或编辑 + /// + /// + /// + /// + public static int AddShadowsocksServer(ref Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.configType = EConfigType.Shadowsocks; + + profileItem.address = profileItem.address.TrimEx(); + profileItem.id = profileItem.id.TrimEx(); + profileItem.security = profileItem.security.TrimEx(); + + if (!LazyConfig.Instance.GetShadowsocksSecuritys(profileItem).Contains(profileItem.security)) + { + return -1; + } + if (profileItem.id.IsNullOrEmpty()) + { + return -1; + } + + AddServerCommon(ref config, profileItem, toFile); + + return 0; + } + + /// + /// 添加服务器或编辑 + /// + /// + /// + /// + public static int AddSocksServer(ref Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.configType = EConfigType.Socks; + + profileItem.address = profileItem.address.TrimEx(); + + AddServerCommon(ref config, profileItem, toFile); + + return 0; + } + + /// + /// 添加服务器或编辑 + /// + /// + /// + /// + public static int AddTrojanServer(ref Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.configType = EConfigType.Trojan; + + profileItem.address = profileItem.address.TrimEx(); + profileItem.id = profileItem.id.TrimEx(); + if (Utils.IsNullOrEmpty(profileItem.streamSecurity)) + { + profileItem.streamSecurity = Global.StreamSecurity; + } + if (profileItem.id.IsNullOrEmpty()) + { + return -1; + } + + AddServerCommon(ref config, profileItem, toFile); + + return 0; + } + + public static int SortServers(ref Config config, string subId, string colName, bool asc) + { + var lstModel = LazyConfig.Instance.ProfileItems(subId, ""); + if (lstModel.Count <= 0) + { + return -1; + } + var lstProfileExs = ProfileExHandler.Instance.ProfileExs; + var lstProfile = (from t in lstModel + join t3 in lstProfileExs on t.indexId equals t3.indexId into t3b + from t33 in t3b.DefaultIfEmpty() + select new ProfileItemModel + { + indexId = t.indexId, + configType = t.configType, + remarks = t.remarks, + address = t.address, + port = t.port, + security = t.security, + network = t.network, + streamSecurity = t.streamSecurity, + delay = t33 == null ? 0 : t33.delay, + speed = t33 == null ? 0 : t33.speed, + sort = t33 == null ? 0 : t33.sort + }).ToList(); + + Enum.TryParse(colName, true, out EServerColName name); + var propertyName = string.Empty; + switch (name) + { + case EServerColName.configType: + case EServerColName.remarks: + case EServerColName.address: + case EServerColName.port: + case EServerColName.security: + case EServerColName.network: + case EServerColName.streamSecurity: + propertyName = name.ToString(); + break; + + case EServerColName.delayVal: + propertyName = "delay"; + break; + + case EServerColName.speedVal: + propertyName = "speed"; + break; + + case EServerColName.subRemarks: + propertyName = "subid"; + break; + + default: + return -1; + } + + var items = lstProfile.AsQueryable(); + + if (asc) + { + lstProfile = items.OrderBy(propertyName).ToList(); + } + else + { + lstProfile = items.OrderByDescending(propertyName).ToList(); + } + for (int i = 0; i < lstProfile.Count; i++) + { + ProfileExHandler.Instance.SetSort(lstProfile[i].indexId, (i + 1) * 10); + } + if (name == EServerColName.delayVal) + { + var maxSort = lstProfile.Max(t => t.sort) + 10; + foreach (var item in lstProfile) { - SqliteHelper.Instance.Execute($"delete from ProfileItem where isSub = 1 and subid = '{subid}'"); + if (item.delay <= 0) + { + ProfileExHandler.Instance.SetSort(item.indexId, maxSort); + } + } + } + if (name == EServerColName.speedVal) + { + var maxSort = lstProfile.Max(t => t.sort) + 10; + foreach (var item in lstProfile) + { + if (item.speed <= 0) + { + ProfileExHandler.Instance.SetSort(item.indexId, maxSort); + } + } + } + + return 0; + } + + /// + /// 添加服务器或编辑 + /// + /// + /// + /// + public static int AddVlessServer(ref Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.configType = EConfigType.VLESS; + + profileItem.address = profileItem.address.TrimEx(); + profileItem.id = profileItem.id.TrimEx(); + profileItem.security = profileItem.security.TrimEx(); + profileItem.network = profileItem.network.TrimEx(); + profileItem.headerType = profileItem.headerType.TrimEx(); + profileItem.requestHost = profileItem.requestHost.TrimEx(); + profileItem.path = profileItem.path.TrimEx(); + profileItem.streamSecurity = profileItem.streamSecurity.TrimEx(); + + if (!Global.flows.Contains(profileItem.flow)) + { + profileItem.flow = Global.flows.First(); + } + if (profileItem.id.IsNullOrEmpty()) + { + return -1; + } + + AddServerCommon(ref config, profileItem, toFile); + + return 0; + } + + public static Tuple DedupServerList(Config config, string subId) + { + var lstProfile = LazyConfig.Instance.ProfileItems(subId); + + List lstKeep = new(); + List lstRemove = new(); + if (!config.guiItem.keepOlderDedupl) lstProfile.Reverse(); + + foreach (ProfileItem item in lstProfile) + { + if (!lstKeep.Exists(i => CompareProfileItem(i, item, false))) + { + lstKeep.Add(item); } else { - SqliteHelper.Instance.Execute($"delete from ProfileItem where subid = '{subid}'"); + lstRemove.Add(item); } - foreach (var item in customProfile) + } + RemoveServer(config, lstRemove); + + return new Tuple(lstProfile.Count, lstKeep.Count); + } + + public static int AddServerCommon(ref Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.configVersion = 2; + + if (!Utils.IsNullOrEmpty(profileItem.streamSecurity)) + { + if (Utils.IsNullOrEmpty(profileItem.allowInsecure)) + { + profileItem.allowInsecure = config.coreBasicItem.defAllowInsecure.ToString().ToLower(); + } + if (Utils.IsNullOrEmpty(profileItem.fingerprint)) + { + profileItem.fingerprint = config.coreBasicItem.defFingerprint; + } + } + + if (!Utils.IsNullOrEmpty(profileItem.network) && !Global.networks.Contains(profileItem.network)) + { + profileItem.network = Global.DefaultNetwork; + } + + var maxSort = -1; + if (Utils.IsNullOrEmpty(profileItem.indexId)) + { + profileItem.indexId = Utils.GetGUID(false); + maxSort = ProfileExHandler.Instance.GetMaxSort(); + } + if (!toFile && maxSort < 0) + { + maxSort = ProfileExHandler.Instance.GetMaxSort(); + } + if (maxSort > 0) + { + ProfileExHandler.Instance.SetSort(profileItem.indexId, maxSort + 1); + } + + if (toFile) + { + SqliteHelper.Instance.Replace(profileItem); + } + return 0; + } + + private static bool CompareProfileItem(ProfileItem o, ProfileItem n, bool remarks) + { + if (o == null || n == null) + { + return false; + } + + return o.configType == n.configType + && o.address == n.address + && o.port == n.port + && o.id == n.id + && o.alterId == n.alterId + && o.security == n.security + && o.network == n.network + && o.headerType == n.headerType + && o.requestHost == n.requestHost + && o.path == n.path + && (o.configType == EConfigType.Trojan || o.streamSecurity == n.streamSecurity) + && o.flow == n.flow + && o.sni == n.sni + && (!remarks || o.remarks == n.remarks); + } + + private static int RemoveProfileItem(Config config, string indexId) + { + try + { + var item = LazyConfig.Instance.GetProfileItem(indexId); + if (item == null) + { + return 0; + } + if (item.configType == EConfigType.Custom) { File.Delete(Utils.GetConfigPath(item.address)); } - return 0; - } - - public static int DeleteSubItem(ref Config config, string id) - { - var item = LazyConfig.Instance.GetSubItem(id); - if (item is null) - { - return 0; - } SqliteHelper.Instance.Delete(item); - RemoveServerViaSubid(ref config, id, false); - - return 0; + } + catch (Exception ex) + { + Utils.SaveLog("Remove Item", ex); } - public static int MoveToGroup(Config config, List lstProfile, string subid) - { - foreach (var item in lstProfile) - { - item.subid = subid; - } - SqliteHelper.Instance.UpdateAll(lstProfile); + return 0; + } - return 0; + #endregion Server + + #region Batch add servers + + /// + /// 批量添加服务器 + /// + /// + /// + /// + /// 成功导入的数量 + private static int AddBatchServers(ref Config config, string clipboardData, string subid, bool isSub, List lstOriSub) + { + if (Utils.IsNullOrEmpty(clipboardData)) + { + return -1; } - #endregion Sub & Group - - #region Routing - - public static int SaveRoutingItem(ref Config config, RoutingItem item) + string subFilter = string.Empty; + //remove sub items + if (isSub && !Utils.IsNullOrEmpty(subid)) { - if (Utils.IsNullOrEmpty(item.id)) - { - item.id = Utils.GetGUID(false); - } - - if (SqliteHelper.Instance.Replace(item) > 0) - { - return 0; - } - else - { - return -1; - } + RemoveServerViaSubid(ref config, subid, isSub); + subFilter = LazyConfig.Instance.GetSubItem(subid)?.filter ?? ""; } - /// - /// AddBatchRoutingRules - /// - /// - /// - /// - public static int AddBatchRoutingRules(ref RoutingItem routingItem, string clipboardData) + int countServers = 0; + //Check for duplicate indexId + List? lstDbIndexId = null; + List lstAdd = new(); + var arrData = clipboardData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty()); + if (isSub) { - if (Utils.IsNullOrEmpty(clipboardData)) - { - return -1; - } - - var lstRules = Utils.FromJson>(clipboardData); - if (lstRules == null) - { - return -1; - } - - foreach (var item in lstRules) - { - item.id = Utils.GetGUID(false); - } - routingItem.ruleNum = lstRules.Count; - routingItem.ruleSet = Utils.ToJson(lstRules, false); - - if (Utils.IsNullOrEmpty(routingItem.id)) - { - routingItem.id = Utils.GetGUID(false); - } - - if (SqliteHelper.Instance.Replace(routingItem) > 0) - { - return 0; - } - else - { - return -1; - } + arrData = arrData.Distinct(); } - - /// - /// MoveRoutingRule - /// - /// - /// - /// - /// - public static int MoveRoutingRule(List rules, int index, EMove eMove, int pos = -1) + foreach (string str in arrData) { - int count = rules.Count; - if (index < 0 || index > rules.Count - 1) + //maybe sub + if (!isSub && (str.StartsWith(Global.httpsProtocol) || str.StartsWith(Global.httpProtocol))) { - return -1; - } - switch (eMove) - { - case EMove.Top: - { - if (index == 0) - { - return 0; - } - var item = Utils.DeepCopy(rules[index]); - rules.RemoveAt(index); - rules.Insert(0, item); - - break; - } - case EMove.Up: - { - if (index == 0) - { - return 0; - } - var item = Utils.DeepCopy(rules[index]); - rules.RemoveAt(index); - rules.Insert(index - 1, item); - - break; - } - - case EMove.Down: - { - if (index == count - 1) - { - return 0; - } - var item = Utils.DeepCopy(rules[index]); - rules.RemoveAt(index); - rules.Insert(index + 1, item); - - break; - } - case EMove.Bottom: - { - if (index == count - 1) - { - return 0; - } - var item = Utils.DeepCopy(rules[index]); - rules.RemoveAt(index); - rules.Add(item); - - break; - } - case EMove.Position: - { - var removeItem = rules[index]; - var item = Utils.DeepCopy(rules[index]); - rules.Insert(pos, item); - rules.Remove(removeItem); - break; - } - } - return 0; - } - - public static int SetDefaultRouting(ref Config config, RoutingItem routingItem) - { - if (SqliteHelper.Instance.Table().Where(t => t.id == routingItem.id).Count() > 0) - { - config.routingBasicItem.routingIndexId = routingItem.id; - } - - ToJsonFile(config); - - return 0; - } - - public static RoutingItem GetDefaultRouting(ref Config config) - { - var item = LazyConfig.Instance.GetRoutingItem(config.routingBasicItem.routingIndexId); - if (item is null) - { - var item2 = SqliteHelper.Instance.Table().FirstOrDefault(t => t.locked == false); - SetDefaultRouting(ref config, item2); - return item2; - } - - return item; - } - - public static int InitBuiltinRouting(ref Config config, bool blImportAdvancedRules = false) - { - var items = LazyConfig.Instance.RoutingItems(); - if (blImportAdvancedRules || items.Count <= 0) - { - var maxSort = items.Count; - //Bypass the mainland - var item2 = new RoutingItem() + if (AddSubItem(ref config, str) == 0) { - remarks = "绕过大陆(Whitelist)", - url = string.Empty, - sort = maxSort + 1, - }; - AddBatchRoutingRules(ref item2, Utils.GetEmbedText(Global.CustomRoutingFileName + "white")); + countServers++; + } + continue; + } + ProfileItem profileItem = ShareHandler.ImportFromClipboardConfig(str, out string msg); + if (profileItem == null) + { + continue; + } - //Blacklist - var item3 = new RoutingItem() + //exist sub items + if (isSub && !Utils.IsNullOrEmpty(subid)) + { + var existItem = lstOriSub?.FirstOrDefault(t => t.isSub == isSub && CompareProfileItem(t, profileItem, true)); + if (existItem != null) { - remarks = "黑名单(Blacklist)", - url = string.Empty, - sort = maxSort + 2, - }; - AddBatchRoutingRules(ref item3, Utils.GetEmbedText(Global.CustomRoutingFileName + "black")); - - //Global - var item1 = new RoutingItem() + //Check for duplicate indexId + if (lstDbIndexId is null) + { + lstDbIndexId = LazyConfig.Instance.ProfileItemIndexs(""); + } + if (lstAdd.Any(t => t.indexId == existItem.indexId) + || lstDbIndexId.Any(t => t == existItem.indexId)) + { + profileItem.indexId = string.Empty; + } + else + { + profileItem.indexId = existItem.indexId; + } + } + //filter + if (!Utils.IsNullOrEmpty(subFilter)) { - remarks = "全局(Global)", - url = string.Empty, - sort = maxSort + 3, - }; - AddBatchRoutingRules(ref item1, Utils.GetEmbedText(Global.CustomRoutingFileName + "global")); - - if (!blImportAdvancedRules) - { - SetDefaultRouting(ref config, item2); + if (!Regex.IsMatch(profileItem.remarks, subFilter)) + { + continue; + } } } + profileItem.subid = subid; + profileItem.isSub = isSub; + var addStatus = -1; - if (GetLockedRoutingItem(ref config) == null) + if (profileItem.configType == EConfigType.VMess) { - var item1 = new RoutingItem() - { - remarks = "locked", - url = string.Empty, - locked = true, - }; - AddBatchRoutingRules(ref item1, Utils.GetEmbedText(Global.CustomRoutingFileName + "locked")); + addStatus = AddServer(ref config, profileItem, false); + } + else if (profileItem.configType == EConfigType.Shadowsocks) + { + addStatus = AddShadowsocksServer(ref config, profileItem, false); + } + else if (profileItem.configType == EConfigType.Socks) + { + addStatus = AddSocksServer(ref config, profileItem, false); + } + else if (profileItem.configType == EConfigType.Trojan) + { + addStatus = AddTrojanServer(ref config, profileItem, false); + } + else if (profileItem.configType == EConfigType.VLESS) + { + addStatus = AddVlessServer(ref config, profileItem, false); + } + + if (addStatus == 0) + { + countServers++; + lstAdd.Add(profileItem); } - return 0; } - public static RoutingItem GetLockedRoutingItem(ref Config config) + if (lstAdd.Count > 0) { - return SqliteHelper.Instance.Table().FirstOrDefault(it => it.locked == true); + SqliteHelper.Instance.InsertAll(lstAdd); } - public static void RemoveRoutingItem(RoutingItem routingItem) - { - SqliteHelper.Instance.Delete(routingItem); - } - - #endregion Routing - - #region DNS - - public static int InitBuiltinDNS(Config config) - { - var items = LazyConfig.Instance.DNSItems(); - if (items.Count <= 0) - { - var item = new DNSItem() - { - remarks = "V2ray", - coreType = ECoreType.Xray, - }; - SaveDNSItems(config, item); - - var item2 = new DNSItem() - { - remarks = "sing-box", - coreType = ECoreType.sing_box, - }; - SaveDNSItems(config, item2); - } - - return 0; - } - - public static int SaveDNSItems(Config config, DNSItem item) - { - if (Utils.IsNullOrEmpty(item.id)) - { - item.id = Utils.GetGUID(false); - } - - if (SqliteHelper.Instance.Replace(item) > 0) - { - return 0; - } - else - { - return -1; - } - } - - #endregion DNS + ToJsonFile(config); + return countServers; } + + private static int AddBatchServers4Custom(ref Config config, string clipboardData, string subid, bool isSub, List lstOriSub) + { + if (Utils.IsNullOrEmpty(clipboardData)) + { + return -1; + } + + //判断str是否包含s的任意一个字符串 + static bool Containss(string str, params string[] s) + { + foreach (var item in s) + { + if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true; + } + return false; + } + + ProfileItem profileItem = new(); + //Is v2ray configuration + V2rayConfig? v2rayConfig = Utils.FromJson(clipboardData); + if (v2rayConfig?.inbounds?.Count > 0 + && v2rayConfig.outbounds?.Count > 0) + { + var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); + File.WriteAllText(fileName, clipboardData); + + profileItem.coreType = ECoreType.Xray; + profileItem.address = fileName; + profileItem.remarks = "v2ray_custom"; + } + //Is Clash configuration + else if (Containss(clipboardData, "port", "socks-port", "proxies")) + { + var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.yaml"); + File.WriteAllText(fileName, clipboardData); + + profileItem.coreType = ECoreType.clash; + profileItem.address = fileName; + profileItem.remarks = "clash_custom"; + } + //Is hysteria configuration + else if (Containss(clipboardData, "server", "up", "down", "listen", "", "")) + { + var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); + File.WriteAllText(fileName, clipboardData); + + profileItem.coreType = ECoreType.hysteria; + profileItem.address = fileName; + profileItem.remarks = "hysteria_custom"; + } + //Is naiveproxy configuration + else if (Containss(clipboardData, "listen", "proxy", "", "")) + { + var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); + File.WriteAllText(fileName, clipboardData); + + profileItem.coreType = ECoreType.naiveproxy; + profileItem.address = fileName; + profileItem.remarks = "naiveproxy_custom"; + } + //Is Other configuration + else + { + return -1; + //var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.txt"); + //File.WriteAllText(fileName, clipboardData); + + //profileItem.address = fileName; + //profileItem.remarks = "other_custom"; + } + + if (isSub && !Utils.IsNullOrEmpty(subid)) + { + RemoveServerViaSubid(ref config, subid, isSub); + } + if (isSub && lstOriSub?.Count == 1) + { + profileItem.indexId = lstOriSub[0].indexId; + } + profileItem.subid = subid; + profileItem.isSub = isSub; + + if (Utils.IsNullOrEmpty(profileItem.address)) + { + return -1; + } + + if (AddCustomServer(ref config, profileItem, true) == 0) + { + return 1; + } + else + { + return -1; + } + } + + private static int AddBatchServers4SsSIP008(ref Config config, string clipboardData, string subid, bool isSub, List lstOriSub) + { + if (Utils.IsNullOrEmpty(clipboardData)) + { + return -1; + } + + if (isSub && !Utils.IsNullOrEmpty(subid)) + { + RemoveServerViaSubid(ref config, subid, isSub); + } + + //SsSIP008 + var lstSsServer = Utils.FromJson>(clipboardData); + if (lstSsServer?.Count <= 0) + { + var ssSIP008 = Utils.FromJson(clipboardData); + if (ssSIP008?.servers?.Count > 0) + { + lstSsServer = ssSIP008.servers; + } + } + + if (lstSsServer?.Count > 0) + { + int counter = 0; + foreach (var it in lstSsServer) + { + var ssItem = new ProfileItem() + { + subid = subid, + remarks = it.remarks, + security = it.method, + id = it.password, + address = it.server, + port = Utils.ToInt(it.server_port) + }; + ssItem.subid = subid; + ssItem.isSub = isSub; + if (AddShadowsocksServer(ref config, ssItem) == 0) + { + counter++; + } + } + ToJsonFile(config); + return counter; + } + + return -1; + } + + public static int AddBatchServers(ref Config config, string clipboardData, string subid, bool isSub) + { + List? lstOriSub = null; + if (isSub && !Utils.IsNullOrEmpty(subid)) + { + lstOriSub = LazyConfig.Instance.ProfileItems(subid); + } + + var counter = 0; + if (Utils.IsBase64String(clipboardData)) + { + counter = AddBatchServers(ref config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub); + } + if (counter < 1) + { + counter = AddBatchServers(ref config, clipboardData, subid, isSub, lstOriSub); + } + if (counter < 1) + { + counter = AddBatchServers(ref config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub); + } + + if (counter < 1) + { + counter = AddBatchServers4SsSIP008(ref config, clipboardData, subid, isSub, lstOriSub); + } + + //maybe other sub + if (counter < 1) + { + counter = AddBatchServers4Custom(ref config, clipboardData, subid, isSub, lstOriSub); + } + + return counter; + } + + #endregion Batch add servers + + #region Sub & Group + + /// + /// add sub + /// + /// + /// + /// + public static int AddSubItem(ref Config config, string url) + { + //already exists + if (SqliteHelper.Instance.Table().Where(e => e.url == url).Count() > 0) + { + return 0; + } + + SubItem subItem = new() + { + id = string.Empty, + remarks = "import_sub", + url = url + }; + + return AddSubItem(ref config, subItem); + } + + public static int AddSubItem(ref Config config, SubItem subItem) + { + if (Utils.IsNullOrEmpty(subItem.id)) + { + subItem.id = Utils.GetGUID(false); + + if (subItem.sort <= 0) + { + var maxSort = 0; + if (SqliteHelper.Instance.Table().Count() > 0) + { + maxSort = SqliteHelper.Instance.Table().Max(t => t == null ? 0 : t.sort); + } + subItem.sort = maxSort + 1; + } + } + if (SqliteHelper.Instance.Replace(subItem) > 0) + { + return 0; + } + else + { + return -1; + } + } + + /// + /// 移除服务器 + /// + /// + /// + /// + public static int RemoveServerViaSubid(ref Config config, string subid, bool isSub) + { + if (Utils.IsNullOrEmpty(subid)) + { + return -1; + } + var customProfile = SqliteHelper.Instance.Table().Where(t => t.subid == subid && t.configType == EConfigType.Custom).ToList(); + if (isSub) + { + SqliteHelper.Instance.Execute($"delete from ProfileItem where isSub = 1 and subid = '{subid}'"); + } + else + { + SqliteHelper.Instance.Execute($"delete from ProfileItem where subid = '{subid}'"); + } + foreach (var item in customProfile) + { + File.Delete(Utils.GetConfigPath(item.address)); + } + + return 0; + } + + public static int DeleteSubItem(ref Config config, string id) + { + var item = LazyConfig.Instance.GetSubItem(id); + if (item is null) + { + return 0; + } + SqliteHelper.Instance.Delete(item); + RemoveServerViaSubid(ref config, id, false); + + return 0; + } + + public static int MoveToGroup(Config config, List lstProfile, string subid) + { + foreach (var item in lstProfile) + { + item.subid = subid; + } + SqliteHelper.Instance.UpdateAll(lstProfile); + + return 0; + } + + #endregion Sub & Group + + #region Routing + + public static int SaveRoutingItem(ref Config config, RoutingItem item) + { + if (Utils.IsNullOrEmpty(item.id)) + { + item.id = Utils.GetGUID(false); + } + + if (SqliteHelper.Instance.Replace(item) > 0) + { + return 0; + } + else + { + return -1; + } + } + + /// + /// AddBatchRoutingRules + /// + /// + /// + /// + public static int AddBatchRoutingRules(ref RoutingItem routingItem, string clipboardData) + { + if (Utils.IsNullOrEmpty(clipboardData)) + { + return -1; + } + + var lstRules = Utils.FromJson>(clipboardData); + if (lstRules == null) + { + return -1; + } + + foreach (var item in lstRules) + { + item.id = Utils.GetGUID(false); + } + routingItem.ruleNum = lstRules.Count; + routingItem.ruleSet = Utils.ToJson(lstRules, false); + + if (Utils.IsNullOrEmpty(routingItem.id)) + { + routingItem.id = Utils.GetGUID(false); + } + + if (SqliteHelper.Instance.Replace(routingItem) > 0) + { + return 0; + } + else + { + return -1; + } + } + + /// + /// MoveRoutingRule + /// + /// + /// + /// + /// + public static int MoveRoutingRule(List rules, int index, EMove eMove, int pos = -1) + { + int count = rules.Count; + if (index < 0 || index > rules.Count - 1) + { + return -1; + } + switch (eMove) + { + case EMove.Top: + { + if (index == 0) + { + return 0; + } + var item = Utils.DeepCopy(rules[index]); + rules.RemoveAt(index); + rules.Insert(0, item); + + break; + } + case EMove.Up: + { + if (index == 0) + { + return 0; + } + var item = Utils.DeepCopy(rules[index]); + rules.RemoveAt(index); + rules.Insert(index - 1, item); + + break; + } + + case EMove.Down: + { + if (index == count - 1) + { + return 0; + } + var item = Utils.DeepCopy(rules[index]); + rules.RemoveAt(index); + rules.Insert(index + 1, item); + + break; + } + case EMove.Bottom: + { + if (index == count - 1) + { + return 0; + } + var item = Utils.DeepCopy(rules[index]); + rules.RemoveAt(index); + rules.Add(item); + + break; + } + case EMove.Position: + { + var removeItem = rules[index]; + var item = Utils.DeepCopy(rules[index]); + rules.Insert(pos, item); + rules.Remove(removeItem); + break; + } + } + return 0; + } + + public static int SetDefaultRouting(ref Config config, RoutingItem routingItem) + { + if (SqliteHelper.Instance.Table().Where(t => t.id == routingItem.id).Count() > 0) + { + config.routingBasicItem.routingIndexId = routingItem.id; + } + + ToJsonFile(config); + + return 0; + } + + public static RoutingItem GetDefaultRouting(ref Config config) + { + var item = LazyConfig.Instance.GetRoutingItem(config.routingBasicItem.routingIndexId); + if (item is null) + { + var item2 = SqliteHelper.Instance.Table().FirstOrDefault(t => t.locked == false); + SetDefaultRouting(ref config, item2); + return item2; + } + + return item; + } + + public static int InitBuiltinRouting(ref Config config, bool blImportAdvancedRules = false) + { + var items = LazyConfig.Instance.RoutingItems(); + if (blImportAdvancedRules || items.Count <= 0) + { + var maxSort = items.Count; + //Bypass the mainland + var item2 = new RoutingItem() + { + remarks = "绕过大陆(Whitelist)", + url = string.Empty, + sort = maxSort + 1, + }; + AddBatchRoutingRules(ref item2, Utils.GetEmbedText(Global.CustomRoutingFileName + "white")); + + //Blacklist + var item3 = new RoutingItem() + { + remarks = "黑名单(Blacklist)", + url = string.Empty, + sort = maxSort + 2, + }; + AddBatchRoutingRules(ref item3, Utils.GetEmbedText(Global.CustomRoutingFileName + "black")); + + //Global + var item1 = new RoutingItem() + { + remarks = "全局(Global)", + url = string.Empty, + sort = maxSort + 3, + }; + AddBatchRoutingRules(ref item1, Utils.GetEmbedText(Global.CustomRoutingFileName + "global")); + + if (!blImportAdvancedRules) + { + SetDefaultRouting(ref config, item2); + } + } + + if (GetLockedRoutingItem(ref config) == null) + { + var item1 = new RoutingItem() + { + remarks = "locked", + url = string.Empty, + locked = true, + }; + AddBatchRoutingRules(ref item1, Utils.GetEmbedText(Global.CustomRoutingFileName + "locked")); + } + return 0; + } + + public static RoutingItem GetLockedRoutingItem(ref Config config) + { + return SqliteHelper.Instance.Table().FirstOrDefault(it => it.locked == true); + } + + public static void RemoveRoutingItem(RoutingItem routingItem) + { + SqliteHelper.Instance.Delete(routingItem); + } + + #endregion Routing + + #region DNS + + public static int InitBuiltinDNS(Config config) + { + var items = LazyConfig.Instance.DNSItems(); + if (items.Count <= 0) + { + var item = new DNSItem() + { + remarks = "V2ray", + coreType = ECoreType.Xray, + }; + SaveDNSItems(config, item); + + var item2 = new DNSItem() + { + remarks = "sing-box", + coreType = ECoreType.sing_box, + }; + SaveDNSItems(config, item2); + } + + return 0; + } + + public static int SaveDNSItems(Config config, DNSItem item) + { + if (Utils.IsNullOrEmpty(item.id)) + { + item.id = Utils.GetGUID(false); + } + + if (SqliteHelper.Instance.Replace(item) > 0) + { + return 0; + } + else + { + return -1; + } + } + + #endregion DNS } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/CoreConfigHandler.cs b/v2rayN/v2rayN/Handler/CoreConfigHandler.cs index c0dc712e..01b91108 100644 --- a/v2rayN/v2rayN/Handler/CoreConfigHandler.cs +++ b/v2rayN/v2rayN/Handler/CoreConfigHandler.cs @@ -2,155 +2,154 @@ using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +/// +/// Core configuration file processing class +/// +internal class CoreConfigHandler { - /// - /// Core configuration file processing class - /// - internal class CoreConfigHandler + public static int GenerateClientConfig(ProfileItem node, string? fileName, out string msg, out string content) { - public static int GenerateClientConfig(ProfileItem node, string? fileName, out string msg, out string content) + content = string.Empty; + try { - content = string.Empty; - try + if (node == null) { - if (node == null) + msg = ResUI.CheckServerSettings; + return -1; + } + var config = LazyConfig.Instance.GetConfig(); + + msg = ResUI.InitialConfiguration; + if (node.configType == EConfigType.Custom) + { + return GenerateClientCustomConfig(node, fileName, out msg); + } + else if (config.tunModeItem.enableTun || LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box) + { + var configGenSingbox = new CoreConfigSingbox(config); + if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0) { - msg = ResUI.CheckServerSettings; return -1; } - var config = LazyConfig.Instance.GetConfig(); - - msg = ResUI.InitialConfiguration; - if (node.configType == EConfigType.Custom) + if (Utils.IsNullOrEmpty(fileName)) { - return GenerateClientCustomConfig(node, fileName, out msg); - } - else if (config.tunModeItem.enableTun || LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box) - { - var configGenSingbox = new CoreConfigSingbox(config); - if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0) - { - return -1; - } - if (Utils.IsNullOrEmpty(fileName)) - { - content = Utils.ToJson(singboxConfig); - } - else - { - Utils.ToJsonFile(singboxConfig, fileName, false); - } + content = Utils.ToJson(singboxConfig); } else { - var coreConfigV2ray = new CoreConfigV2ray(config); - if (coreConfigV2ray.GenerateClientConfigContent(node, out V2rayConfig? v2rayConfig, out msg) != 0) - { - return -1; - } - if (Utils.IsNullOrEmpty(fileName)) - { - content = Utils.ToJson(v2rayConfig); - } - else - { - Utils.ToJsonFile(v2rayConfig, fileName, false); - } + Utils.ToJsonFile(singboxConfig, fileName, false); } } - catch (Exception ex) + else + { + var coreConfigV2ray = new CoreConfigV2ray(config); + if (coreConfigV2ray.GenerateClientConfigContent(node, out V2rayConfig? v2rayConfig, out msg) != 0) + { + return -1; + } + if (Utils.IsNullOrEmpty(fileName)) + { + content = Utils.ToJson(v2rayConfig); + } + else + { + Utils.ToJsonFile(v2rayConfig, fileName, false); + } + } + } + catch (Exception ex) + { + Utils.SaveLog("GenerateClientConfig", ex); + msg = ResUI.FailedGenDefaultConfiguration; + return -1; + } + return 0; + } + + private static int GenerateClientCustomConfig(ProfileItem node, string? fileName, out string msg) + { + try + { + if (node == null || fileName is null) + { + msg = ResUI.CheckServerSettings; + return -1; + } + + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + + string addressFileName = node.address; + if (!File.Exists(addressFileName)) + { + addressFileName = Utils.GetConfigPath(addressFileName); + } + if (!File.Exists(addressFileName)) { - Utils.SaveLog("GenerateClientConfig", ex); msg = ResUI.FailedGenDefaultConfiguration; return -1; } - return 0; - } + File.Copy(addressFileName, fileName); - private static int GenerateClientCustomConfig(ProfileItem node, string? fileName, out string msg) - { - try + //check again + if (!File.Exists(fileName)) { - if (node == null || fileName is null) - { - msg = ResUI.CheckServerSettings; - return -1; - } - - if (File.Exists(fileName)) - { - File.Delete(fileName); - } - - string addressFileName = node.address; - if (!File.Exists(addressFileName)) - { - addressFileName = Utils.GetConfigPath(addressFileName); - } - if (!File.Exists(addressFileName)) - { - msg = ResUI.FailedGenDefaultConfiguration; - return -1; - } - File.Copy(addressFileName, fileName); - - //check again - if (!File.Exists(fileName)) - { - msg = ResUI.FailedGenDefaultConfiguration; - return -1; - } - - //overwrite port - if (node.preSocksPort <= 0) - { - var fileContent = File.ReadAllLines(fileName).ToList(); - var coreType = LazyConfig.Instance.GetCoreType(node, node.configType); - switch (coreType) - { - case ECoreType.v2fly: - case ECoreType.SagerNet: - case ECoreType.Xray: - case ECoreType.v2fly_v5: - break; - - case ECoreType.clash: - case ECoreType.clash_meta: - //remove the original - var indexPort = fileContent.FindIndex(t => t.Contains("port:")); - if (indexPort >= 0) - { - fileContent.RemoveAt(indexPort); - } - indexPort = fileContent.FindIndex(t => t.Contains("socks-port:")); - if (indexPort >= 0) - { - fileContent.RemoveAt(indexPort); - } - - fileContent.Add($"port: {LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}"); - fileContent.Add($"socks-port: {LazyConfig.Instance.GetLocalPort(Global.InboundSocks)}"); - break; - } - File.WriteAllLines(fileName, fileContent); - } - - msg = string.Format(ResUI.SuccessfulConfiguration, ""); - } - catch (Exception ex) - { - Utils.SaveLog("GenerateClientCustomConfig", ex); msg = ResUI.FailedGenDefaultConfiguration; return -1; } - return 0; - } - public static string GenerateClientSpeedtestConfigString(Config config, List selecteds, out string msg) - { - var coreConfigV2ray = new CoreConfigV2ray(config); - return coreConfigV2ray.GenerateClientSpeedtestConfigString(selecteds, out msg); + //overwrite port + if (node.preSocksPort <= 0) + { + var fileContent = File.ReadAllLines(fileName).ToList(); + var coreType = LazyConfig.Instance.GetCoreType(node, node.configType); + switch (coreType) + { + case ECoreType.v2fly: + case ECoreType.SagerNet: + case ECoreType.Xray: + case ECoreType.v2fly_v5: + break; + + case ECoreType.clash: + case ECoreType.clash_meta: + //remove the original + var indexPort = fileContent.FindIndex(t => t.Contains("port:")); + if (indexPort >= 0) + { + fileContent.RemoveAt(indexPort); + } + indexPort = fileContent.FindIndex(t => t.Contains("socks-port:")); + if (indexPort >= 0) + { + fileContent.RemoveAt(indexPort); + } + + fileContent.Add($"port: {LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}"); + fileContent.Add($"socks-port: {LazyConfig.Instance.GetLocalPort(Global.InboundSocks)}"); + break; + } + File.WriteAllLines(fileName, fileContent); + } + + msg = string.Format(ResUI.SuccessfulConfiguration, ""); } + catch (Exception ex) + { + Utils.SaveLog("GenerateClientCustomConfig", ex); + msg = ResUI.FailedGenDefaultConfiguration; + return -1; + } + return 0; + } + + public static string GenerateClientSpeedtestConfigString(Config config, List selecteds, out string msg) + { + var coreConfigV2ray = new CoreConfigV2ray(config); + return coreConfigV2ray.GenerateClientSpeedtestConfigString(selecteds, out msg); } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs b/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs index 7d64b7b8..87b58721 100644 --- a/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs +++ b/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs @@ -2,739 +2,738 @@ using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class CoreConfigSingbox { - internal class CoreConfigSingbox + private string SampleClient = Global.SingboxSampleClient; + private Config _config; + + public CoreConfigSingbox(Config config) { - private string SampleClient = Global.SingboxSampleClient; - private Config _config; + _config = config; + } - public CoreConfigSingbox(Config config) + public int GenerateClientConfigContent(ProfileItem node, out SingboxConfig? singboxConfig, out string msg) + { + singboxConfig = null; + try { - _config = config; - } - - public int GenerateClientConfigContent(ProfileItem node, out SingboxConfig? singboxConfig, out string msg) - { - singboxConfig = null; - try + if (node == null + || node.port <= 0) { - if (node == null - || node.port <= 0) - { - msg = ResUI.CheckServerSettings; - return -1; - } - - msg = ResUI.InitialConfiguration; - - string result = Utils.GetEmbedText(SampleClient); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedGetDefaultConfiguration; - return -1; - } - - singboxConfig = Utils.FromJson(result); - if (singboxConfig == null) - { - msg = ResUI.FailedGenDefaultConfiguration; - return -1; - } - - log(singboxConfig); - - inbound(singboxConfig); - - outbound(node, singboxConfig); - - routing(singboxConfig); - - dns(node, singboxConfig); - - statistic(singboxConfig); - - msg = string.Format(ResUI.SuccessfulConfiguration, ""); + msg = ResUI.CheckServerSettings; + return -1; } - catch (Exception ex) + + msg = ResUI.InitialConfiguration; + + string result = Utils.GetEmbedText(SampleClient); + if (Utils.IsNullOrEmpty(result)) + { + msg = ResUI.FailedGetDefaultConfiguration; + return -1; + } + + singboxConfig = Utils.FromJson(result); + if (singboxConfig == null) { - Utils.SaveLog("GenerateClientConfig4Singbox", ex); msg = ResUI.FailedGenDefaultConfiguration; return -1; } - return 0; + + log(singboxConfig); + + inbound(singboxConfig); + + outbound(node, singboxConfig); + + routing(singboxConfig); + + dns(node, singboxConfig); + + statistic(singboxConfig); + + msg = string.Format(ResUI.SuccessfulConfiguration, ""); } - - private int log(SingboxConfig singboxConfig) + catch (Exception ex) { - try - { - switch (_config.coreBasicItem.loglevel) - { - case "debug": - case "info": - case "error": - singboxConfig.log.level = _config.coreBasicItem.loglevel; - break; - - case "warning": - singboxConfig.log.level = "warn"; - break; - - default: - break; - } - if (_config.coreBasicItem.loglevel == "none") - { - singboxConfig.log.disabled = true; - } - if (_config.coreBasicItem.logEnabled) - { - var dtNow = DateTime.Now; - singboxConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt"); - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; + Utils.SaveLog("GenerateClientConfig4Singbox", ex); + msg = ResUI.FailedGenDefaultConfiguration; + return -1; } + return 0; + } - #region inbound private - - private int inbound(SingboxConfig singboxConfig) + private int log(SingboxConfig singboxConfig) + { + try { - try + switch (_config.coreBasicItem.loglevel) { - singboxConfig.inbounds.Clear(); + case "debug": + case "info": + case "error": + singboxConfig.log.level = _config.coreBasicItem.loglevel; + break; - if (!_config.tunModeItem.enableTun || (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound)) - { - var inbound = new Inbound4Sbox() - { - type = Global.InboundSocks, - tag = Global.InboundSocks, - listen = Global.Loopback, - }; - singboxConfig.inbounds.Add(inbound); + case "warning": + singboxConfig.log.level = "warn"; + break; - inbound.listen_port = LazyConfig.Instance.GetLocalPort(Global.InboundSocks); - inbound.sniff = _config.inbound[0].sniffingEnabled; - inbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled; - inbound.domain_strategy = Utils.IsNullOrEmpty(_config.routingBasicItem.domainStrategy4Singbox) ? null : _config.routingBasicItem.domainStrategy4Singbox; - - if (_config.routingBasicItem.enableRoutingAdvanced) - { - var routing = ConfigHandler.GetDefaultRouting(ref _config); - if (!Utils.IsNullOrEmpty(routing.domainStrategy4Singbox)) - { - inbound.domain_strategy = routing.domainStrategy4Singbox; - } - } - - //http - var inbound2 = GetInbound(inbound, Global.InboundHttp, 1, false); - singboxConfig.inbounds.Add(inbound2); - - if (_config.inbound[0].allowLANConn) - { - if (_config.inbound[0].newPort4LAN) - { - var inbound3 = GetInbound(inbound, Global.InboundSocks2, 2, true); - inbound3.listen = "::"; - singboxConfig.inbounds.Add(inbound3); - - var inbound4 = GetInbound(inbound, Global.InboundHttp2, 3, false); - inbound4.listen = "::"; - singboxConfig.inbounds.Add(inbound4); - - //auth - if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass)) - { - inbound3.users = new() { new() { username = _config.inbound[0].user, password = _config.inbound[0].pass } }; - inbound4.users = new() { new() { username = _config.inbound[0].user, password = _config.inbound[0].pass } }; - } - } - else - { - inbound.listen = "::"; - inbound2.listen = "::"; - } - } - } - - if (_config.tunModeItem.enableTun) - { - if (_config.tunModeItem.mtu <= 0) - { - _config.tunModeItem.mtu = Convert.ToInt32(Global.TunMtus[0]); - } - if (Utils.IsNullOrEmpty(_config.tunModeItem.stack)) - { - _config.tunModeItem.stack = Global.TunStacks[0]; - } - - var tunInbound = Utils.FromJson(Utils.GetEmbedText(Global.TunSingboxInboundFileName)); - tunInbound.mtu = _config.tunModeItem.mtu; - tunInbound.strict_route = _config.tunModeItem.strictRoute; - tunInbound.stack = _config.tunModeItem.stack; - - singboxConfig.inbounds.Add(tunInbound); - } + default: + break; } - catch (Exception ex) + if (_config.coreBasicItem.loglevel == "none") { - Utils.SaveLog(ex.Message, ex); + singboxConfig.log.disabled = true; + } + if (_config.coreBasicItem.logEnabled) + { + var dtNow = DateTime.Now; + singboxConfig.log.output = Utils.GetLogPath($"sbox_{dtNow:yyyy-MM-dd}.txt"); } - return 0; } - - private Inbound4Sbox? GetInbound(Inbound4Sbox inItem, string tag, int offset, bool bSocks) + catch (Exception ex) { - var inbound = Utils.DeepCopy(inItem); - inbound.tag = tag; - inbound.listen_port = inItem.listen_port + offset; - inbound.type = bSocks ? Global.InboundSocks : Global.InboundHttp; - return inbound; + Utils.SaveLog(ex.Message, ex); } + return 0; + } - #endregion inbound private + #region inbound private - #region outbound private - - private int outbound(ProfileItem node, SingboxConfig singboxConfig) + private int inbound(SingboxConfig singboxConfig) + { + try { - try + singboxConfig.inbounds.Clear(); + + if (!_config.tunModeItem.enableTun || (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound)) { - if (_config.tunModeItem.enableTun) + var inbound = new Inbound4Sbox() { - singboxConfig.outbounds.Add(new() - { - type = "dns", - tag = "dns_out" - }); - } + type = Global.InboundSocks, + tag = Global.InboundSocks, + listen = Global.Loopback, + }; + singboxConfig.inbounds.Add(inbound); - var outbound = singboxConfig.outbounds[0]; - outbound.server = node.address; - outbound.server_port = node.port; - - if (node.configType == EConfigType.VMess) - { - outbound.type = Global.vmessProtocolLite; - - outbound.uuid = node.id; - outbound.alter_id = node.alterId; - if (Global.vmessSecuritys.Contains(node.security)) - { - outbound.security = node.security; - } - else - { - outbound.security = Global.DefaultSecurity; - } - - outboundMux(node, outbound); - } - else if (node.configType == EConfigType.Shadowsocks) - { - outbound.type = Global.ssProtocolLite; - - outbound.method = LazyConfig.Instance.GetShadowsocksSecuritys(node).Contains(node.security) ? node.security : "none"; - outbound.password = node.id; - - outboundMux(node, outbound); - } - else if (node.configType == EConfigType.Socks) - { - outbound.type = Global.socksProtocolLite; - - outbound.version = "5"; - if (!Utils.IsNullOrEmpty(node.security) - && !Utils.IsNullOrEmpty(node.id)) - { - outbound.username = node.security; - outbound.password = node.id; - } - } - else if (node.configType == EConfigType.VLESS) - { - outbound.type = Global.vlessProtocolLite; - - outbound.uuid = node.id; - - outbound.packet_encoding = "xudp"; - - if (Utils.IsNullOrEmpty(node.flow)) - { - outboundMux(node, outbound); - } - else - { - outbound.flow = node.flow; - } - } - else if (node.configType == EConfigType.Trojan) - { - outbound.type = Global.trojanProtocolLite; - - outbound.password = node.id; - - outboundMux(node, outbound); - } - - outboundTls(node, outbound); - - outboundTransport(node, outbound); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private int outboundMux(ProfileItem node, Outbound4Sbox outbound) - { - try - { - //if (_config.coreBasicItem.muxEnabled) - //{ - // var mux = new Multiplex4Sbox() - // { - // enabled = true, - // protocol = _config.mux4Sbox.protocol, - // max_connections = _config.mux4Sbox.max_connections, - // min_streams = _config.mux4Sbox.min_streams, - // max_streams = _config.mux4Sbox.max_streams, - // padding = _config.mux4Sbox.padding - // }; - // outbound.multiplex = mux; - //} - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private int outboundTls(ProfileItem node, Outbound4Sbox outbound) - { - try - { - if (node.streamSecurity == Global.StreamSecurityReality || node.streamSecurity == Global.StreamSecurity) - { - var server_name = string.Empty; - if (!string.IsNullOrWhiteSpace(node.sni)) - { - server_name = node.sni; - } - else if (!string.IsNullOrWhiteSpace(node.requestHost)) - { - server_name = Utils.String2List(node.requestHost)[0]; - } - var tls = new Tls4Sbox() - { - enabled = true, - server_name = server_name, - insecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? _config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure), - alpn = node.GetAlpn(), - }; - if (!Utils.IsNullOrEmpty(node.fingerprint)) - { - tls.utls = new Utls4Sbox() - { - enabled = true, - fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint - }; - } - if (node.streamSecurity == Global.StreamSecurityReality) - { - tls.reality = new Reality4Sbox() - { - enabled = true, - public_key = node.publicKey, - short_id = node.shortId - }; - tls.insecure = false; - } - outbound.tls = tls; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private int outboundTransport(ProfileItem node, Outbound4Sbox outbound) - { - try - { - var transport = new Transport4Sbox(); - - switch (node.GetNetwork()) - { - case "h2": - transport.type = "http"; - transport.host = Utils.IsNullOrEmpty(node.requestHost) ? null : Utils.String2List(node.requestHost); - transport.path = Utils.IsNullOrEmpty(node.path) ? null : node.path; - break; - - case "ws": - transport.type = "ws"; - transport.path = Utils.IsNullOrEmpty(node.path) ? null : node.path; - if (!Utils.IsNullOrEmpty(node.requestHost)) - { - transport.headers = new() - { - Host = node.requestHost - }; - } - break; - - case "quic": - transport.type = "quic"; - break; - - case "grpc": - transport.type = "grpc"; - transport.service_name = node.path; - transport.idle_timeout = _config.grpcItem.idle_timeout.ToString("##s"); - transport.ping_timeout = _config.grpcItem.health_check_timeout.ToString("##s"); - transport.permit_without_stream = _config.grpcItem.permit_without_stream; - break; - - default: - transport = null; - break; - } - - outbound.transport = transport; - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - #endregion outbound private - - #region routing rule private - - private int routing(SingboxConfig singboxConfig) - { - try - { - if (_config.tunModeItem.enableTun) - { - singboxConfig.route.auto_detect_interface = true; - - var tunRules = Utils.FromJson>(Utils.GetEmbedText(Global.TunSingboxRulesFileName)); - singboxConfig.route.rules.AddRange(tunRules); - - routingDirectExe(out List lstDnsExe, out List lstDirectExe); - singboxConfig.route.rules.Add(new() - { - port = new() { 53 }, - outbound = "dns_out", - process_name = lstDnsExe - }); - - singboxConfig.route.rules.Add(new() - { - outbound = "direct", - process_name = lstDirectExe - }); - } + inbound.listen_port = LazyConfig.Instance.GetLocalPort(Global.InboundSocks); + inbound.sniff = _config.inbound[0].sniffingEnabled; + inbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled; + inbound.domain_strategy = Utils.IsNullOrEmpty(_config.routingBasicItem.domainStrategy4Singbox) ? null : _config.routingBasicItem.domainStrategy4Singbox; if (_config.routingBasicItem.enableRoutingAdvanced) { var routing = ConfigHandler.GetDefaultRouting(ref _config); - if (routing != null) + if (!Utils.IsNullOrEmpty(routing.domainStrategy4Singbox)) { - var rules = Utils.FromJson>(routing.ruleSet); - foreach (var item in rules!) + inbound.domain_strategy = routing.domainStrategy4Singbox; + } + } + + //http + var inbound2 = GetInbound(inbound, Global.InboundHttp, 1, false); + singboxConfig.inbounds.Add(inbound2); + + if (_config.inbound[0].allowLANConn) + { + if (_config.inbound[0].newPort4LAN) + { + var inbound3 = GetInbound(inbound, Global.InboundSocks2, 2, true); + inbound3.listen = "::"; + singboxConfig.inbounds.Add(inbound3); + + var inbound4 = GetInbound(inbound, Global.InboundHttp2, 3, false); + inbound4.listen = "::"; + singboxConfig.inbounds.Add(inbound4); + + //auth + if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass)) { - if (item.enabled) - { - routingUserRule(item, singboxConfig.route.rules); - } + inbound3.users = new() { new() { username = _config.inbound[0].user, password = _config.inbound[0].pass } }; + inbound4.users = new() { new() { username = _config.inbound[0].user, password = _config.inbound[0].pass } }; } } + else + { + inbound.listen = "::"; + inbound2.listen = "::"; + } + } + } + + if (_config.tunModeItem.enableTun) + { + if (_config.tunModeItem.mtu <= 0) + { + _config.tunModeItem.mtu = Convert.ToInt32(Global.TunMtus[0]); + } + if (Utils.IsNullOrEmpty(_config.tunModeItem.stack)) + { + _config.tunModeItem.stack = Global.TunStacks[0]; + } + + var tunInbound = Utils.FromJson(Utils.GetEmbedText(Global.TunSingboxInboundFileName)); + tunInbound.mtu = _config.tunModeItem.mtu; + tunInbound.strict_route = _config.tunModeItem.strictRoute; + tunInbound.stack = _config.tunModeItem.stack; + + singboxConfig.inbounds.Add(tunInbound); + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private Inbound4Sbox? GetInbound(Inbound4Sbox inItem, string tag, int offset, bool bSocks) + { + var inbound = Utils.DeepCopy(inItem); + inbound.tag = tag; + inbound.listen_port = inItem.listen_port + offset; + inbound.type = bSocks ? Global.InboundSocks : Global.InboundHttp; + return inbound; + } + + #endregion inbound private + + #region outbound private + + private int outbound(ProfileItem node, SingboxConfig singboxConfig) + { + try + { + if (_config.tunModeItem.enableTun) + { + singboxConfig.outbounds.Add(new() + { + type = "dns", + tag = "dns_out" + }); + } + + var outbound = singboxConfig.outbounds[0]; + outbound.server = node.address; + outbound.server_port = node.port; + + if (node.configType == EConfigType.VMess) + { + outbound.type = Global.vmessProtocolLite; + + outbound.uuid = node.id; + outbound.alter_id = node.alterId; + if (Global.vmessSecuritys.Contains(node.security)) + { + outbound.security = node.security; } else { - var lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config); - if (lockedItem != null) + outbound.security = Global.DefaultSecurity; + } + + outboundMux(node, outbound); + } + else if (node.configType == EConfigType.Shadowsocks) + { + outbound.type = Global.ssProtocolLite; + + outbound.method = LazyConfig.Instance.GetShadowsocksSecuritys(node).Contains(node.security) ? node.security : "none"; + outbound.password = node.id; + + outboundMux(node, outbound); + } + else if (node.configType == EConfigType.Socks) + { + outbound.type = Global.socksProtocolLite; + + outbound.version = "5"; + if (!Utils.IsNullOrEmpty(node.security) + && !Utils.IsNullOrEmpty(node.id)) + { + outbound.username = node.security; + outbound.password = node.id; + } + } + else if (node.configType == EConfigType.VLESS) + { + outbound.type = Global.vlessProtocolLite; + + outbound.uuid = node.id; + + outbound.packet_encoding = "xudp"; + + if (Utils.IsNullOrEmpty(node.flow)) + { + outboundMux(node, outbound); + } + else + { + outbound.flow = node.flow; + } + } + else if (node.configType == EConfigType.Trojan) + { + outbound.type = Global.trojanProtocolLite; + + outbound.password = node.id; + + outboundMux(node, outbound); + } + + outboundTls(node, outbound); + + outboundTransport(node, outbound); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outboundMux(ProfileItem node, Outbound4Sbox outbound) + { + try + { + //if (_config.coreBasicItem.muxEnabled) + //{ + // var mux = new Multiplex4Sbox() + // { + // enabled = true, + // protocol = _config.mux4Sbox.protocol, + // max_connections = _config.mux4Sbox.max_connections, + // min_streams = _config.mux4Sbox.min_streams, + // max_streams = _config.mux4Sbox.max_streams, + // padding = _config.mux4Sbox.padding + // }; + // outbound.multiplex = mux; + //} + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outboundTls(ProfileItem node, Outbound4Sbox outbound) + { + try + { + if (node.streamSecurity == Global.StreamSecurityReality || node.streamSecurity == Global.StreamSecurity) + { + var server_name = string.Empty; + if (!string.IsNullOrWhiteSpace(node.sni)) + { + server_name = node.sni; + } + else if (!string.IsNullOrWhiteSpace(node.requestHost)) + { + server_name = Utils.String2List(node.requestHost)[0]; + } + var tls = new Tls4Sbox() + { + enabled = true, + server_name = server_name, + insecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? _config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure), + alpn = node.GetAlpn(), + }; + if (!Utils.IsNullOrEmpty(node.fingerprint)) + { + tls.utls = new Utls4Sbox() { - var rules = Utils.FromJson>(lockedItem.ruleSet); - foreach (var item in rules!) + enabled = true, + fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint + }; + } + if (node.streamSecurity == Global.StreamSecurityReality) + { + tls.reality = new Reality4Sbox() + { + enabled = true, + public_key = node.publicKey, + short_id = node.shortId + }; + tls.insecure = false; + } + outbound.tls = tls; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outboundTransport(ProfileItem node, Outbound4Sbox outbound) + { + try + { + var transport = new Transport4Sbox(); + + switch (node.GetNetwork()) + { + case "h2": + transport.type = "http"; + transport.host = Utils.IsNullOrEmpty(node.requestHost) ? null : Utils.String2List(node.requestHost); + transport.path = Utils.IsNullOrEmpty(node.path) ? null : node.path; + break; + + case "ws": + transport.type = "ws"; + transport.path = Utils.IsNullOrEmpty(node.path) ? null : node.path; + if (!Utils.IsNullOrEmpty(node.requestHost)) + { + transport.headers = new() + { + Host = node.requestHost + }; + } + break; + + case "quic": + transport.type = "quic"; + break; + + case "grpc": + transport.type = "grpc"; + transport.service_name = node.path; + transport.idle_timeout = _config.grpcItem.idle_timeout.ToString("##s"); + transport.ping_timeout = _config.grpcItem.health_check_timeout.ToString("##s"); + transport.permit_without_stream = _config.grpcItem.permit_without_stream; + break; + + default: + transport = null; + break; + } + + outbound.transport = transport; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + #endregion outbound private + + #region routing rule private + + private int routing(SingboxConfig singboxConfig) + { + try + { + if (_config.tunModeItem.enableTun) + { + singboxConfig.route.auto_detect_interface = true; + + var tunRules = Utils.FromJson>(Utils.GetEmbedText(Global.TunSingboxRulesFileName)); + singboxConfig.route.rules.AddRange(tunRules); + + routingDirectExe(out List lstDnsExe, out List lstDirectExe); + singboxConfig.route.rules.Add(new() + { + port = new() { 53 }, + outbound = "dns_out", + process_name = lstDnsExe + }); + + singboxConfig.route.rules.Add(new() + { + outbound = "direct", + process_name = lstDirectExe + }); + } + + if (_config.routingBasicItem.enableRoutingAdvanced) + { + var routing = ConfigHandler.GetDefaultRouting(ref _config); + if (routing != null) + { + var rules = Utils.FromJson>(routing.ruleSet); + foreach (var item in rules!) + { + if (item.enabled) { routingUserRule(item, singboxConfig.route.rules); } } } } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private void routingDirectExe(out List lstDnsExe, out List lstDirectExe) - { - lstDnsExe = new(); - lstDirectExe = new(); - var coreInfos = LazyConfig.Instance.GetCoreInfos(); - foreach (var it in coreInfos) - { - if (it.coreType == ECoreType.v2rayN) - { - continue; - } - foreach (var it2 in it.coreExes) - { - if (!lstDnsExe.Contains(it2) && it.coreType != ECoreType.sing_box) - { - lstDnsExe.Add($"{it2}.exe"); - } - - if (!lstDirectExe.Contains(it2)) - { - lstDirectExe.Add($"{it2}.exe"); - } - } - } - } - - private int routingUserRule(RulesItem item, List rules) - { - try - { - if (item == null) - { - return 0; - } - - var rule = new Rule4Sbox() - { - outbound = item.outboundTag, - }; - - if (!Utils.IsNullOrEmpty(item.port)) - { - if (item.port.Contains("-")) - { - rule.port_range = new List { item.port.Replace("-", ":") }; - } - else - { - rule.port = new List { Utils.ToInt(item.port) }; - } - } - if (item.protocol?.Count > 0) - { - rule.protocol = item.protocol; - } - if (item.inboundTag?.Count >= 0) - { - rule.inbound = item.inboundTag; - } - var rule2 = Utils.DeepCopy(rule); - var rule3 = Utils.DeepCopy(rule); - - var hasDomainIp = false; - if (item.domain?.Count > 0) - { - foreach (var it in item.domain) - { - parseV2Domain(it, rule); - } - rules.Add(rule); - hasDomainIp = true; - } - - if (item.ip?.Count > 0) - { - foreach (var it in item.ip) - { - parseV2Address(it, rule2); - } - rules.Add(rule2); - hasDomainIp = true; - } - - if (_config.tunModeItem.enableTun && item.process?.Count > 0) - { - rule3.process_name = item.process; - rules.Add(rule3); - hasDomainIp = true; - } - - if (!hasDomainIp) - { - rules.Add(rule); - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private void parseV2Domain(string domain, Rule4Sbox rule) - { - if (domain.StartsWith("ext:") || domain.StartsWith("ext-domain:")) - { - return; - } - else if (domain.StartsWith("geosite:")) - { - if (rule.geosite is null) { rule.geosite = new(); } - rule.geosite?.Add(domain.Substring(8)); - } - else if (domain.StartsWith("regexp:")) - { - if (rule.domain_regex is null) { rule.domain_regex = new(); } - rule.domain_regex?.Add(domain.Replace(Global.RoutingRuleComma, ",").Substring(7)); - } - else if (domain.StartsWith("domain:")) - { - if (rule.domain is null) { rule.domain = new(); } - if (rule.domain_suffix is null) { rule.domain_suffix = new(); } - rule.domain?.Add(domain.Substring(7)); - rule.domain_suffix?.Add("." + domain.Substring(7)); - } - else if (domain.StartsWith("full:")) - { - if (rule.domain is null) { rule.domain = new(); } - rule.domain?.Add(domain.Substring(5)); - } - else if (domain.StartsWith("keyword:")) - { - if (rule.domain_keyword is null) { rule.domain_keyword = new(); } - rule.domain_keyword?.Add(domain.Substring(8)); - } else { - if (rule.domain_keyword is null) { rule.domain_keyword = new(); } - rule.domain_keyword?.Add(domain); - } - } - - private void parseV2Address(string address, Rule4Sbox rule) - { - if (address.StartsWith("ext:") || address.StartsWith("ext-ip:")) - { - return; - } - else if (address.StartsWith("geoip:!")) - { - return; - } - else if (address.StartsWith("geoip:")) - { - if (rule.geoip is null) { rule.geoip = new(); } - rule.geoip?.Add(address.Substring(6)); - } - else - { - if (rule.ip_cidr is null) { rule.ip_cidr = new(); } - rule.ip_cidr?.Add(address); - } - } - - #endregion routing rule private - - #region dns private - - private int dns(ProfileItem node, SingboxConfig singboxConfig) - { - try - { - Dns4Sbox? dns4Sbox; - if (_config.tunModeItem.enableTun) + var lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config); + if (lockedItem != null) { - var item = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box); - var tunDNS = item?.tunDNS; - if (string.IsNullOrWhiteSpace(tunDNS)) + var rules = Utils.FromJson>(lockedItem.ruleSet); + foreach (var item in rules!) { - tunDNS = Utils.GetEmbedText(Global.TunSingboxDNSFileName); + routingUserRule(item, singboxConfig.route.rules); } - dns4Sbox = Utils.FromJson(tunDNS); + } + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private void routingDirectExe(out List lstDnsExe, out List lstDirectExe) + { + lstDnsExe = new(); + lstDirectExe = new(); + var coreInfos = LazyConfig.Instance.GetCoreInfos(); + foreach (var it in coreInfos) + { + if (it.coreType == ECoreType.v2rayN) + { + continue; + } + foreach (var it2 in it.coreExes) + { + if (!lstDnsExe.Contains(it2) && it.coreType != ECoreType.sing_box) + { + lstDnsExe.Add($"{it2}.exe"); + } + + if (!lstDirectExe.Contains(it2)) + { + lstDirectExe.Add($"{it2}.exe"); + } + } + } + } + + private int routingUserRule(RulesItem item, List rules) + { + try + { + if (item == null) + { + return 0; + } + + var rule = new Rule4Sbox() + { + outbound = item.outboundTag, + }; + + if (!Utils.IsNullOrEmpty(item.port)) + { + if (item.port.Contains("-")) + { + rule.port_range = new List { item.port.Replace("-", ":") }; } else { - var item = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box); - var normalDNS = item?.normalDNS; - if (string.IsNullOrWhiteSpace(normalDNS)) - { - normalDNS = "{\"servers\":[{\"address\":\"tcp://8.8.8.8\"}]}"; - } - - dns4Sbox = Utils.FromJson(normalDNS); + rule.port = new List { Utils.ToInt(item.port) }; } - if (dns4Sbox is null) - { - return 0; - } - //Add the dns of the remote server domain - if (dns4Sbox.rules is null) - { - dns4Sbox.rules = new(); - } - dns4Sbox.servers.Add(new() - { - tag = "local_local", - address = "223.5.5.5", - detour = "direct" - }); - dns4Sbox.rules.Add(new() - { - server = "local_local", - outbound = "any" - }); - - singboxConfig.dns = dns4Sbox; } - catch (Exception ex) + if (item.protocol?.Count > 0) { - Utils.SaveLog(ex.Message, ex); + rule.protocol = item.protocol; + } + if (item.inboundTag?.Count >= 0) + { + rule.inbound = item.inboundTag; + } + var rule2 = Utils.DeepCopy(rule); + var rule3 = Utils.DeepCopy(rule); + + var hasDomainIp = false; + if (item.domain?.Count > 0) + { + foreach (var it in item.domain) + { + parseV2Domain(it, rule); + } + rules.Add(rule); + hasDomainIp = true; + } + + if (item.ip?.Count > 0) + { + foreach (var it in item.ip) + { + parseV2Address(it, rule2); + } + rules.Add(rule2); + hasDomainIp = true; + } + + if (_config.tunModeItem.enableTun && item.process?.Count > 0) + { + rule3.process_name = item.process; + rules.Add(rule3); + hasDomainIp = true; + } + + if (!hasDomainIp) + { + rules.Add(rule); } - return 0; } - - #endregion dns private - - private int statistic(SingboxConfig singboxConfig) + catch (Exception ex) { - if (_config.guiItem.enableStatistics) - { - singboxConfig.experimental = new Experimental4Sbox() - { - //v2ray_api = new V2ray_Api4Sbox() - //{ - // listen = $"{Global.Loopback}:{Global.statePort}", - // stats = new Stats4Sbox() - // { - // enabled = true, - // } - //} - clash_api = new Clash_Api4Sbox() - { - external_controller = $"{Global.Loopback}:{Global.statePort}", - store_selected = true - } - }; - } - return 0; + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private void parseV2Domain(string domain, Rule4Sbox rule) + { + if (domain.StartsWith("ext:") || domain.StartsWith("ext-domain:")) + { + return; + } + else if (domain.StartsWith("geosite:")) + { + if (rule.geosite is null) { rule.geosite = new(); } + rule.geosite?.Add(domain.Substring(8)); + } + else if (domain.StartsWith("regexp:")) + { + if (rule.domain_regex is null) { rule.domain_regex = new(); } + rule.domain_regex?.Add(domain.Replace(Global.RoutingRuleComma, ",").Substring(7)); + } + else if (domain.StartsWith("domain:")) + { + if (rule.domain is null) { rule.domain = new(); } + if (rule.domain_suffix is null) { rule.domain_suffix = new(); } + rule.domain?.Add(domain.Substring(7)); + rule.domain_suffix?.Add("." + domain.Substring(7)); + } + else if (domain.StartsWith("full:")) + { + if (rule.domain is null) { rule.domain = new(); } + rule.domain?.Add(domain.Substring(5)); + } + else if (domain.StartsWith("keyword:")) + { + if (rule.domain_keyword is null) { rule.domain_keyword = new(); } + rule.domain_keyword?.Add(domain.Substring(8)); + } + else + { + if (rule.domain_keyword is null) { rule.domain_keyword = new(); } + rule.domain_keyword?.Add(domain); } } + + private void parseV2Address(string address, Rule4Sbox rule) + { + if (address.StartsWith("ext:") || address.StartsWith("ext-ip:")) + { + return; + } + else if (address.StartsWith("geoip:!")) + { + return; + } + else if (address.StartsWith("geoip:")) + { + if (rule.geoip is null) { rule.geoip = new(); } + rule.geoip?.Add(address.Substring(6)); + } + else + { + if (rule.ip_cidr is null) { rule.ip_cidr = new(); } + rule.ip_cidr?.Add(address); + } + } + + #endregion routing rule private + + #region dns private + + private int dns(ProfileItem node, SingboxConfig singboxConfig) + { + try + { + Dns4Sbox? dns4Sbox; + if (_config.tunModeItem.enableTun) + { + var item = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box); + var tunDNS = item?.tunDNS; + if (string.IsNullOrWhiteSpace(tunDNS)) + { + tunDNS = Utils.GetEmbedText(Global.TunSingboxDNSFileName); + } + dns4Sbox = Utils.FromJson(tunDNS); + } + else + { + var item = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box); + var normalDNS = item?.normalDNS; + if (string.IsNullOrWhiteSpace(normalDNS)) + { + normalDNS = "{\"servers\":[{\"address\":\"tcp://8.8.8.8\"}]}"; + } + + dns4Sbox = Utils.FromJson(normalDNS); + } + if (dns4Sbox is null) + { + return 0; + } + //Add the dns of the remote server domain + if (dns4Sbox.rules is null) + { + dns4Sbox.rules = new(); + } + dns4Sbox.servers.Add(new() + { + tag = "local_local", + address = "223.5.5.5", + detour = "direct" + }); + dns4Sbox.rules.Add(new() + { + server = "local_local", + outbound = "any" + }); + + singboxConfig.dns = dns4Sbox; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + #endregion dns private + + private int statistic(SingboxConfig singboxConfig) + { + if (_config.guiItem.enableStatistics) + { + singboxConfig.experimental = new Experimental4Sbox() + { + //v2ray_api = new V2ray_Api4Sbox() + //{ + // listen = $"{Global.Loopback}:{Global.statePort}", + // stats = new Stats4Sbox() + // { + // enabled = true, + // } + //} + clash_api = new Clash_Api4Sbox() + { + external_controller = $"{Global.Loopback}:{Global.statePort}", + store_selected = true + } + }; + } + return 0; + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs b/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs index 87253e54..cd14a021 100644 --- a/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs +++ b/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs @@ -4,202 +4,188 @@ using v2rayN.Base; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class CoreConfigV2ray { - internal class CoreConfigV2ray + private string SampleClient = Global.v2raySampleClient; + private Config _config; + + public CoreConfigV2ray(Config config) { - private string SampleClient = Global.v2raySampleClient; - private Config _config; + _config = config; + } - public CoreConfigV2ray(Config config) + public int GenerateClientConfigContent(ProfileItem node, out V2rayConfig? v2rayConfig, out string msg) + { + v2rayConfig = null; + try { - _config = config; - } - - public int GenerateClientConfigContent(ProfileItem node, out V2rayConfig? v2rayConfig, out string msg) - { - v2rayConfig = null; - try + if (node == null + || node.port <= 0) { - if (node == null - || node.port <= 0) - { - msg = ResUI.CheckServerSettings; - return -1; - } - - msg = ResUI.InitialConfiguration; - - string result = Utils.GetEmbedText(SampleClient); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedGetDefaultConfiguration; - return -1; - } - - v2rayConfig = Utils.FromJson(result); - if (v2rayConfig == null) - { - msg = ResUI.FailedGenDefaultConfiguration; - return -1; - } - - log(v2rayConfig); - - inbound(v2rayConfig); - - routing(v2rayConfig); - - outbound(node, v2rayConfig); - - dns(v2rayConfig); - - statistic(v2rayConfig); - - msg = string.Format(ResUI.SuccessfulConfiguration, ""); + msg = ResUI.CheckServerSettings; + return -1; } - catch (Exception ex) + + msg = ResUI.InitialConfiguration; + + string result = Utils.GetEmbedText(SampleClient); + if (Utils.IsNullOrEmpty(result)) + { + msg = ResUI.FailedGetDefaultConfiguration; + return -1; + } + + v2rayConfig = Utils.FromJson(result); + if (v2rayConfig == null) { - Utils.SaveLog("GenerateClientConfig4V2ray", ex); msg = ResUI.FailedGenDefaultConfiguration; return -1; } - return 0; - } - private int log(V2rayConfig v2rayConfig) + log(v2rayConfig); + + inbound(v2rayConfig); + + routing(v2rayConfig); + + outbound(node, v2rayConfig); + + dns(v2rayConfig); + + statistic(v2rayConfig); + + msg = string.Format(ResUI.SuccessfulConfiguration, ""); + } + catch (Exception ex) { - try + Utils.SaveLog("GenerateClientConfig4V2ray", ex); + msg = ResUI.FailedGenDefaultConfiguration; + return -1; + } + return 0; + } + + private int log(V2rayConfig v2rayConfig) + { + try + { + if (_config.coreBasicItem.logEnabled) { - if (_config.coreBasicItem.logEnabled) + var dtNow = DateTime.Now; + v2rayConfig.log.loglevel = _config.coreBasicItem.loglevel; + v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt"); + v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt"); + } + else + { + v2rayConfig.log.loglevel = _config.coreBasicItem.loglevel; + v2rayConfig.log.access = ""; + v2rayConfig.log.error = ""; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int inbound(V2rayConfig v2rayConfig) + { + try + { + v2rayConfig.inbounds = new List(); + + Inbounds4Ray? inbound = GetInbound(_config.inbound[0], Global.InboundSocks, 0, true); + v2rayConfig.inbounds.Add(inbound); + + //http + Inbounds4Ray? inbound2 = GetInbound(_config.inbound[0], Global.InboundHttp, 1, false); + v2rayConfig.inbounds.Add(inbound2); + + if (_config.inbound[0].allowLANConn) + { + if (_config.inbound[0].newPort4LAN) { - var dtNow = DateTime.Now; - v2rayConfig.log.loglevel = _config.coreBasicItem.loglevel; - v2rayConfig.log.access = Utils.GetLogPath($"Vaccess_{dtNow:yyyy-MM-dd}.txt"); - v2rayConfig.log.error = Utils.GetLogPath($"Verror_{dtNow:yyyy-MM-dd}.txt"); + Inbounds4Ray inbound3 = GetInbound(_config.inbound[0], Global.InboundSocks2, 2, true); + inbound3.listen = "0.0.0.0"; + v2rayConfig.inbounds.Add(inbound3); + + Inbounds4Ray inbound4 = GetInbound(_config.inbound[0], Global.InboundHttp2, 3, false); + inbound4.listen = "0.0.0.0"; + v2rayConfig.inbounds.Add(inbound4); + + //auth + if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass)) + { + inbound3.settings.auth = "password"; + inbound3.settings.accounts = new List { new AccountsItem4Ray() { user = _config.inbound[0].user, pass = _config.inbound[0].pass } }; + + inbound4.settings.auth = "password"; + inbound4.settings.accounts = new List { new AccountsItem4Ray() { user = _config.inbound[0].user, pass = _config.inbound[0].pass } }; + } } else { - v2rayConfig.log.loglevel = _config.coreBasicItem.loglevel; - v2rayConfig.log.access = ""; - v2rayConfig.log.error = ""; + inbound.listen = "0.0.0.0"; + inbound2.listen = "0.0.0.0"; } } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private Inbounds4Ray? GetInbound(InItem inItem, string tag, int offset, bool bSocks) + { + string result = Utils.GetEmbedText(Global.v2raySampleInbound); + if (Utils.IsNullOrEmpty(result)) + { + return null; } - private int inbound(V2rayConfig v2rayConfig) + var inbound = Utils.FromJson(result); + if (inbound == null) { - try + return null; + } + inbound.tag = tag; + inbound.port = inItem.localPort + offset; + inbound.protocol = bSocks ? Global.InboundSocks : Global.InboundHttp; + inbound.settings.udp = inItem.udpEnabled; + inbound.sniffing.enabled = inItem.sniffingEnabled; + inbound.sniffing.routeOnly = inItem.routeOnly; + + return inbound; + } + + private int routing(V2rayConfig v2rayConfig) + { + try + { + if (v2rayConfig.routing?.rules != null) { - v2rayConfig.inbounds = new List(); + v2rayConfig.routing.domainStrategy = _config.routingBasicItem.domainStrategy; + v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(_config.routingBasicItem.domainMatcher) ? null : _config.routingBasicItem.domainMatcher; - Inbounds4Ray? inbound = GetInbound(_config.inbound[0], Global.InboundSocks, 0, true); - v2rayConfig.inbounds.Add(inbound); - - //http - Inbounds4Ray? inbound2 = GetInbound(_config.inbound[0], Global.InboundHttp, 1, false); - v2rayConfig.inbounds.Add(inbound2); - - if (_config.inbound[0].allowLANConn) + if (_config.routingBasicItem.enableRoutingAdvanced) { - if (_config.inbound[0].newPort4LAN) + var routing = ConfigHandler.GetDefaultRouting(ref _config); + if (routing != null) { - Inbounds4Ray inbound3 = GetInbound(_config.inbound[0], Global.InboundSocks2, 2, true); - inbound3.listen = "0.0.0.0"; - v2rayConfig.inbounds.Add(inbound3); - - Inbounds4Ray inbound4 = GetInbound(_config.inbound[0], Global.InboundHttp2, 3, false); - inbound4.listen = "0.0.0.0"; - v2rayConfig.inbounds.Add(inbound4); - - //auth - if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass)) + if (!Utils.IsNullOrEmpty(routing.domainStrategy)) { - inbound3.settings.auth = "password"; - inbound3.settings.accounts = new List { new AccountsItem4Ray() { user = _config.inbound[0].user, pass = _config.inbound[0].pass } }; - - inbound4.settings.auth = "password"; - inbound4.settings.accounts = new List { new AccountsItem4Ray() { user = _config.inbound[0].user, pass = _config.inbound[0].pass } }; + v2rayConfig.routing.domainStrategy = routing.domainStrategy; } - } - else - { - inbound.listen = "0.0.0.0"; - inbound2.listen = "0.0.0.0"; - } - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private Inbounds4Ray? GetInbound(InItem inItem, string tag, int offset, bool bSocks) - { - string result = Utils.GetEmbedText(Global.v2raySampleInbound); - if (Utils.IsNullOrEmpty(result)) - { - return null; - } - - var inbound = Utils.FromJson(result); - if (inbound == null) - { - return null; - } - inbound.tag = tag; - inbound.port = inItem.localPort + offset; - inbound.protocol = bSocks ? Global.InboundSocks : Global.InboundHttp; - inbound.settings.udp = inItem.udpEnabled; - inbound.sniffing.enabled = inItem.sniffingEnabled; - inbound.sniffing.routeOnly = inItem.routeOnly; - - return inbound; - } - - private int routing(V2rayConfig v2rayConfig) - { - try - { - if (v2rayConfig.routing?.rules != null) - { - v2rayConfig.routing.domainStrategy = _config.routingBasicItem.domainStrategy; - v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(_config.routingBasicItem.domainMatcher) ? null : _config.routingBasicItem.domainMatcher; - - if (_config.routingBasicItem.enableRoutingAdvanced) - { - var routing = ConfigHandler.GetDefaultRouting(ref _config); - if (routing != null) + var rules = Utils.FromJson>(routing.ruleSet); + foreach (var item in rules) { - if (!Utils.IsNullOrEmpty(routing.domainStrategy)) - { - v2rayConfig.routing.domainStrategy = routing.domainStrategy; - } - var rules = Utils.FromJson>(routing.ruleSet); - foreach (var item in rules) - { - if (item.enabled) - { - var item2 = Utils.FromJson(Utils.ToJson(item)); - routingUserRule(item2, v2rayConfig); - } - } - } - } - else - { - var lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config); - if (lockedItem != null) - { - var rules = Utils.FromJson>(lockedItem.ruleSet); - foreach (var item in rules) + if (item.enabled) { var item2 = Utils.FromJson(Utils.ToJson(item)); routingUserRule(item2, v2rayConfig); @@ -207,743 +193,756 @@ namespace v2rayN.Handler } } } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private int routingUserRule(RulesItem4Ray rules, V2rayConfig v2rayConfig) - { - try - { - if (rules == null) + else { - return 0; - } - if (Utils.IsNullOrEmpty(rules.port)) - { - rules.port = null; - } - if (rules.domain?.Count == 0) - { - rules.domain = null; - } - if (rules.ip?.Count == 0) - { - rules.ip = null; - } - if (rules.protocol?.Count == 0) - { - rules.protocol = null; - } - if (rules.inboundTag?.Count == 0) - { - rules.inboundTag = null; - } - - var hasDomainIp = false; - if (rules.domain?.Count > 0) - { - var it = Utils.DeepCopy(rules); - it.ip = null; - it.type = "field"; - for (int k = it.domain.Count - 1; k >= 0; k--) + var lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config); + if (lockedItem != null) { - if (it.domain[k].StartsWith("#")) + var rules = Utils.FromJson>(lockedItem.ruleSet); + foreach (var item in rules) { - it.domain.RemoveAt(k); - } - it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ","); - } - v2rayConfig.routing.rules.Add(it); - hasDomainIp = true; - } - if (rules.ip?.Count > 0) - { - var it = Utils.DeepCopy(rules); - it.domain = null; - it.type = "field"; - v2rayConfig.routing.rules.Add(it); - hasDomainIp = true; - } - if (!hasDomainIp) - { - if (!Utils.IsNullOrEmpty(rules.port) - || (rules.protocol?.Count > 0) - || (rules.inboundTag?.Count > 0) - ) - { - var it = Utils.DeepCopy(rules); - it.type = "field"; - v2rayConfig.routing.rules.Add(it); - } - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; - } - - private int outbound(ProfileItem node, V2rayConfig v2rayConfig) - { - try - { - Outbounds4Ray outbound = v2rayConfig.outbounds[0]; - if (node.configType == EConfigType.VMess) - { - VnextItem4Ray vnextItem; - if (outbound.settings.vnext.Count <= 0) - { - vnextItem = new VnextItem4Ray(); - outbound.settings.vnext.Add(vnextItem); - } - else - { - vnextItem = outbound.settings.vnext[0]; - } - vnextItem.address = node.address; - vnextItem.port = node.port; - - UsersItem4Ray usersItem; - if (vnextItem.users.Count <= 0) - { - usersItem = new UsersItem4Ray(); - vnextItem.users.Add(usersItem); - } - else - { - usersItem = vnextItem.users[0]; - } - //远程服务器用户ID - usersItem.id = node.id; - usersItem.alterId = node.alterId; - usersItem.email = Global.userEMail; - if (Global.vmessSecuritys.Contains(node.security)) - { - usersItem.security = node.security; - } - else - { - usersItem.security = Global.DefaultSecurity; - } - - outboundMux(node, outbound, _config.coreBasicItem.muxEnabled); - - outbound.protocol = Global.vmessProtocolLite; - outbound.settings.servers = null; - } - else if (node.configType == EConfigType.Shadowsocks) - { - ServersItem4Ray serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem4Ray(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers[0]; - } - serversItem.address = node.address; - serversItem.port = node.port; - serversItem.password = node.id; - serversItem.method = LazyConfig.Instance.GetShadowsocksSecuritys(node).Contains(node.security) ? node.security : "none"; - - serversItem.ota = false; - serversItem.level = 1; - - outboundMux(node, outbound, false); - - outbound.protocol = Global.ssProtocolLite; - outbound.settings.vnext = null; - } - else if (node.configType == EConfigType.Socks) - { - ServersItem4Ray serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem4Ray(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers[0]; - } - serversItem.address = node.address; - serversItem.port = node.port; - serversItem.method = null; - serversItem.password = null; - - if (!Utils.IsNullOrEmpty(node.security) - && !Utils.IsNullOrEmpty(node.id)) - { - SocksUsersItem4Ray socksUsersItem = new() - { - user = node.security, - pass = node.id, - level = 1 - }; - - serversItem.users = new List() { socksUsersItem }; - } - - outboundMux(node, outbound, false); - - outbound.protocol = Global.socksProtocolLite; - outbound.settings.vnext = null; - } - else if (node.configType == EConfigType.VLESS) - { - VnextItem4Ray vnextItem; - if (outbound.settings.vnext.Count <= 0) - { - vnextItem = new VnextItem4Ray(); - outbound.settings.vnext.Add(vnextItem); - } - else - { - vnextItem = outbound.settings.vnext[0]; - } - vnextItem.address = node.address; - vnextItem.port = node.port; - - UsersItem4Ray usersItem; - if (vnextItem.users.Count <= 0) - { - usersItem = new UsersItem4Ray(); - vnextItem.users.Add(usersItem); - } - else - { - usersItem = vnextItem.users[0]; - } - usersItem.id = node.id; - usersItem.email = Global.userEMail; - usersItem.encryption = node.security; - - outboundMux(node, outbound, _config.coreBasicItem.muxEnabled); - - if (node.streamSecurity == Global.StreamSecurityReality - || node.streamSecurity == Global.StreamSecurity) - { - if (!Utils.IsNullOrEmpty(node.flow)) - { - usersItem.flow = node.flow; - - outboundMux(node, outbound, false); + var item2 = Utils.FromJson(Utils.ToJson(item)); + routingUserRule(item2, v2rayConfig); } } - if (node.streamSecurity == Global.StreamSecurityReality && Utils.IsNullOrEmpty(node.flow)) - { - outboundMux(node, outbound, _config.coreBasicItem.muxEnabled); - } - - outbound.protocol = Global.vlessProtocolLite; - outbound.settings.servers = null; } - else if (node.configType == EConfigType.Trojan) - { - ServersItem4Ray serversItem; - if (outbound.settings.servers.Count <= 0) - { - serversItem = new ServersItem4Ray(); - outbound.settings.servers.Add(serversItem); - } - else - { - serversItem = outbound.settings.servers[0]; - } - serversItem.address = node.address; - serversItem.port = node.port; - serversItem.password = node.id; - - serversItem.ota = false; - serversItem.level = 1; - - outboundMux(node, outbound, false); - - outbound.protocol = Global.trojanProtocolLite; - outbound.settings.vnext = null; - } - boundStreamSettings(node, outbound.streamSettings); } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; } - - private int outboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabled) + catch (Exception ex) { - try + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int routingUserRule(RulesItem4Ray rules, V2rayConfig v2rayConfig) + { + try + { + if (rules == null) { - if (enabled) + return 0; + } + if (Utils.IsNullOrEmpty(rules.port)) + { + rules.port = null; + } + if (rules.domain?.Count == 0) + { + rules.domain = null; + } + if (rules.ip?.Count == 0) + { + rules.ip = null; + } + if (rules.protocol?.Count == 0) + { + rules.protocol = null; + } + if (rules.inboundTag?.Count == 0) + { + rules.inboundTag = null; + } + + var hasDomainIp = false; + if (rules.domain?.Count > 0) + { + var it = Utils.DeepCopy(rules); + it.ip = null; + it.type = "field"; + for (int k = it.domain.Count - 1; k >= 0; k--) { - outbound.mux.enabled = true; - outbound.mux.concurrency = 8; + if (it.domain[k].StartsWith("#")) + { + it.domain.RemoveAt(k); + } + it.domain[k] = it.domain[k].Replace(Global.RoutingRuleComma, ","); + } + v2rayConfig.routing.rules.Add(it); + hasDomainIp = true; + } + if (rules.ip?.Count > 0) + { + var it = Utils.DeepCopy(rules); + it.domain = null; + it.type = "field"; + v2rayConfig.routing.rules.Add(it); + hasDomainIp = true; + } + if (!hasDomainIp) + { + if (!Utils.IsNullOrEmpty(rules.port) + || (rules.protocol?.Count > 0) + || (rules.inboundTag?.Count > 0) + ) + { + var it = Utils.DeepCopy(rules); + it.type = "field"; + v2rayConfig.routing.rules.Add(it); + } + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outbound(ProfileItem node, V2rayConfig v2rayConfig) + { + try + { + Outbounds4Ray outbound = v2rayConfig.outbounds[0]; + if (node.configType == EConfigType.VMess) + { + VnextItem4Ray vnextItem; + if (outbound.settings.vnext.Count <= 0) + { + vnextItem = new VnextItem4Ray(); + outbound.settings.vnext.Add(vnextItem); } else { - outbound.mux.enabled = false; - outbound.mux.concurrency = -1; + vnextItem = outbound.settings.vnext[0]; } + vnextItem.address = node.address; + vnextItem.port = node.port; + + UsersItem4Ray usersItem; + if (vnextItem.users.Count <= 0) + { + usersItem = new UsersItem4Ray(); + vnextItem.users.Add(usersItem); + } + else + { + usersItem = vnextItem.users[0]; + } + //远程服务器用户ID + usersItem.id = node.id; + usersItem.alterId = node.alterId; + usersItem.email = Global.userEMail; + if (Global.vmessSecuritys.Contains(node.security)) + { + usersItem.security = node.security; + } + else + { + usersItem.security = Global.DefaultSecurity; + } + + outboundMux(node, outbound, _config.coreBasicItem.muxEnabled); + + outbound.protocol = Global.vmessProtocolLite; + outbound.settings.servers = null; } - catch (Exception ex) + else if (node.configType == EConfigType.Shadowsocks) { - Utils.SaveLog(ex.Message, ex); + ServersItem4Ray serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem4Ray(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers[0]; + } + serversItem.address = node.address; + serversItem.port = node.port; + serversItem.password = node.id; + serversItem.method = LazyConfig.Instance.GetShadowsocksSecuritys(node).Contains(node.security) ? node.security : "none"; + + serversItem.ota = false; + serversItem.level = 1; + + outboundMux(node, outbound, false); + + outbound.protocol = Global.ssProtocolLite; + outbound.settings.vnext = null; } - return 0; + else if (node.configType == EConfigType.Socks) + { + ServersItem4Ray serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem4Ray(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers[0]; + } + serversItem.address = node.address; + serversItem.port = node.port; + serversItem.method = null; + serversItem.password = null; + + if (!Utils.IsNullOrEmpty(node.security) + && !Utils.IsNullOrEmpty(node.id)) + { + SocksUsersItem4Ray socksUsersItem = new() + { + user = node.security, + pass = node.id, + level = 1 + }; + + serversItem.users = new List() { socksUsersItem }; + } + + outboundMux(node, outbound, false); + + outbound.protocol = Global.socksProtocolLite; + outbound.settings.vnext = null; + } + else if (node.configType == EConfigType.VLESS) + { + VnextItem4Ray vnextItem; + if (outbound.settings.vnext.Count <= 0) + { + vnextItem = new VnextItem4Ray(); + outbound.settings.vnext.Add(vnextItem); + } + else + { + vnextItem = outbound.settings.vnext[0]; + } + vnextItem.address = node.address; + vnextItem.port = node.port; + + UsersItem4Ray usersItem; + if (vnextItem.users.Count <= 0) + { + usersItem = new UsersItem4Ray(); + vnextItem.users.Add(usersItem); + } + else + { + usersItem = vnextItem.users[0]; + } + usersItem.id = node.id; + usersItem.email = Global.userEMail; + usersItem.encryption = node.security; + + outboundMux(node, outbound, _config.coreBasicItem.muxEnabled); + + if (node.streamSecurity == Global.StreamSecurityReality + || node.streamSecurity == Global.StreamSecurity) + { + if (!Utils.IsNullOrEmpty(node.flow)) + { + usersItem.flow = node.flow; + + outboundMux(node, outbound, false); + } + } + if (node.streamSecurity == Global.StreamSecurityReality && Utils.IsNullOrEmpty(node.flow)) + { + outboundMux(node, outbound, _config.coreBasicItem.muxEnabled); + } + + outbound.protocol = Global.vlessProtocolLite; + outbound.settings.servers = null; + } + else if (node.configType == EConfigType.Trojan) + { + ServersItem4Ray serversItem; + if (outbound.settings.servers.Count <= 0) + { + serversItem = new ServersItem4Ray(); + outbound.settings.servers.Add(serversItem); + } + else + { + serversItem = outbound.settings.servers[0]; + } + serversItem.address = node.address; + serversItem.port = node.port; + serversItem.password = node.id; + + serversItem.ota = false; + serversItem.level = 1; + + outboundMux(node, outbound, false); + + outbound.protocol = Global.trojanProtocolLite; + outbound.settings.vnext = null; + } + boundStreamSettings(node, outbound.streamSettings); } - - private int boundStreamSettings(ProfileItem node, StreamSettings4Ray streamSettings) + catch (Exception ex) { - try + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int outboundMux(ProfileItem node, Outbounds4Ray outbound, bool enabled) + { + try + { + if (enabled) { - streamSettings.network = node.GetNetwork(); - string host = node.requestHost.TrimEx(); - string sni = node.sni; - string useragent = ""; - if (!_config.coreBasicItem.defUserAgent.IsNullOrEmpty()) + outbound.mux.enabled = true; + outbound.mux.concurrency = 8; + } + else + { + outbound.mux.enabled = false; + outbound.mux.concurrency = -1; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int boundStreamSettings(ProfileItem node, StreamSettings4Ray streamSettings) + { + try + { + streamSettings.network = node.GetNetwork(); + string host = node.requestHost.TrimEx(); + string sni = node.sni; + string useragent = ""; + if (!_config.coreBasicItem.defUserAgent.IsNullOrEmpty()) + { + try { - try - { - useragent = Global.userAgentTxt[_config.coreBasicItem.defUserAgent]; - } - catch (KeyNotFoundException) - { - useragent = _config.coreBasicItem.defUserAgent; - } + useragent = Global.userAgentTxt[_config.coreBasicItem.defUserAgent]; } - - //if tls - if (node.streamSecurity == Global.StreamSecurity) + catch (KeyNotFoundException) { - streamSettings.security = node.streamSecurity; - - TlsSettings4Ray tlsSettings = new() - { - allowInsecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? _config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure), - alpn = node.GetAlpn(), - fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint - }; - if (!string.IsNullOrWhiteSpace(sni)) - { - tlsSettings.serverName = sni; - } - else if (!string.IsNullOrWhiteSpace(host)) - { - tlsSettings.serverName = Utils.String2List(host)[0]; - } - streamSettings.tlsSettings = tlsSettings; + useragent = _config.coreBasicItem.defUserAgent; } + } - //if Reality - if (node.streamSecurity == Global.StreamSecurityReality) + //if tls + if (node.streamSecurity == Global.StreamSecurity) + { + streamSettings.security = node.streamSecurity; + + TlsSettings4Ray tlsSettings = new() { - streamSettings.security = node.streamSecurity; + allowInsecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? _config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure), + alpn = node.GetAlpn(), + fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint + }; + if (!string.IsNullOrWhiteSpace(sni)) + { + tlsSettings.serverName = sni; + } + else if (!string.IsNullOrWhiteSpace(host)) + { + tlsSettings.serverName = Utils.String2List(host)[0]; + } + streamSettings.tlsSettings = tlsSettings; + } - TlsSettings4Ray realitySettings = new() + //if Reality + if (node.streamSecurity == Global.StreamSecurityReality) + { + streamSettings.security = node.streamSecurity; + + TlsSettings4Ray realitySettings = new() + { + fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint, + serverName = sni, + publicKey = node.publicKey, + shortId = node.shortId, + spiderX = node.spiderX, + }; + + streamSettings.realitySettings = realitySettings; + } + + //streamSettings + switch (node.GetNetwork()) + { + case "kcp": + KcpSettings4Ray kcpSettings = new() { - fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint, - serverName = sni, - publicKey = node.publicKey, - shortId = node.shortId, - spiderX = node.spiderX, + mtu = _config.kcpItem.mtu, + tti = _config.kcpItem.tti }; - streamSettings.realitySettings = realitySettings; - } + kcpSettings.uplinkCapacity = _config.kcpItem.uplinkCapacity; + kcpSettings.downlinkCapacity = _config.kcpItem.downlinkCapacity; - //streamSettings - switch (node.GetNetwork()) - { - case "kcp": - KcpSettings4Ray kcpSettings = new() - { - mtu = _config.kcpItem.mtu, - tti = _config.kcpItem.tti - }; + kcpSettings.congestion = _config.kcpItem.congestion; + kcpSettings.readBufferSize = _config.kcpItem.readBufferSize; + kcpSettings.writeBufferSize = _config.kcpItem.writeBufferSize; + kcpSettings.header = new Header4Ray + { + type = node.headerType + }; + if (!Utils.IsNullOrEmpty(node.path)) + { + kcpSettings.seed = node.path; + } + streamSettings.kcpSettings = kcpSettings; + break; + //ws + case "ws": + WsSettings4Ray wsSettings = new(); + wsSettings.headers = new Headers4Ray(); + string path = node.path; + if (!string.IsNullOrWhiteSpace(host)) + { + wsSettings.headers.Host = host; + } + if (!string.IsNullOrWhiteSpace(path)) + { + wsSettings.path = path; + } + if (!string.IsNullOrWhiteSpace(useragent)) + { + wsSettings.headers.UserAgent = useragent; + } + streamSettings.wsSettings = wsSettings; - kcpSettings.uplinkCapacity = _config.kcpItem.uplinkCapacity; - kcpSettings.downlinkCapacity = _config.kcpItem.downlinkCapacity; + break; + //h2 + case "h2": + HttpSettings4Ray httpSettings = new(); - kcpSettings.congestion = _config.kcpItem.congestion; - kcpSettings.readBufferSize = _config.kcpItem.readBufferSize; - kcpSettings.writeBufferSize = _config.kcpItem.writeBufferSize; - kcpSettings.header = new Header4Ray + if (!string.IsNullOrWhiteSpace(host)) + { + httpSettings.host = Utils.String2List(host); + } + httpSettings.path = node.path; + + streamSettings.httpSettings = httpSettings; + + break; + //quic + case "quic": + QuicSettings4Ray quicsettings = new() + { + security = host, + key = node.path, + header = new Header4Ray { type = node.headerType - }; - if (!Utils.IsNullOrEmpty(node.path)) - { - kcpSettings.seed = node.path; } - streamSettings.kcpSettings = kcpSettings; - break; - //ws - case "ws": - WsSettings4Ray wsSettings = new(); - wsSettings.headers = new Headers4Ray(); - string path = node.path; - if (!string.IsNullOrWhiteSpace(host)) + }; + streamSettings.quicSettings = quicsettings; + if (node.streamSecurity == Global.StreamSecurity) + { + if (!string.IsNullOrWhiteSpace(sni)) { - wsSettings.headers.Host = host; + streamSettings.tlsSettings.serverName = sni; } - if (!string.IsNullOrWhiteSpace(path)) + else { - wsSettings.path = path; + streamSettings.tlsSettings.serverName = node.address; } - if (!string.IsNullOrWhiteSpace(useragent)) - { - wsSettings.headers.UserAgent = useragent; - } - streamSettings.wsSettings = wsSettings; + } + break; - break; - //h2 - case "h2": - HttpSettings4Ray httpSettings = new(); + case "grpc": + GrpcSettings4Ray grpcSettings = new() + { + serviceName = node.path, + multiMode = (node.headerType == Global.GrpcmultiMode), + idle_timeout = _config.grpcItem.idle_timeout, + health_check_timeout = _config.grpcItem.health_check_timeout, + permit_without_stream = _config.grpcItem.permit_without_stream, + initial_windows_size = _config.grpcItem.initial_windows_size, + }; + streamSettings.grpcSettings = grpcSettings; + break; - if (!string.IsNullOrWhiteSpace(host)) + default: + //tcp + if (node.headerType == Global.TcpHeaderHttp) + { + TcpSettings4Ray tcpSettings = new() { - httpSettings.host = Utils.String2List(host); - } - httpSettings.path = node.path; - - streamSettings.httpSettings = httpSettings; - - break; - //quic - case "quic": - QuicSettings4Ray quicsettings = new() - { - security = host, - key = node.path, header = new Header4Ray { type = node.headerType } }; - streamSettings.quicSettings = quicsettings; - if (node.streamSecurity == Global.StreamSecurity) + + //request Host + string request = Utils.GetEmbedText(Global.v2raySampleHttprequestFileName); + string[] arrHost = host.Split(','); + string host2 = string.Join("\",\"", arrHost); + request = request.Replace("$requestHost$", $"\"{host2}\""); + //request = request.Replace("$requestHost$", string.Format("\"{0}\"", config.requestHost())); + request = request.Replace("$requestUserAgent$", $"\"{useragent}\""); + //Path + string pathHttp = @"/"; + if (!Utils.IsNullOrEmpty(node.path)) { - if (!string.IsNullOrWhiteSpace(sni)) - { - streamSettings.tlsSettings.serverName = sni; - } - else - { - streamSettings.tlsSettings.serverName = node.address; - } + string[] arrPath = node.path.Split(','); + pathHttp = string.Join("\",\"", arrPath); } - break; + request = request.Replace("$requestPath$", $"\"{pathHttp}\""); + tcpSettings.header.request = Utils.FromJson(request); - case "grpc": - GrpcSettings4Ray grpcSettings = new() - { - serviceName = node.path, - multiMode = (node.headerType == Global.GrpcmultiMode), - idle_timeout = _config.grpcItem.idle_timeout, - health_check_timeout = _config.grpcItem.health_check_timeout, - permit_without_stream = _config.grpcItem.permit_without_stream, - initial_windows_size = _config.grpcItem.initial_windows_size, - }; - streamSettings.grpcSettings = grpcSettings; - break; - - default: - //tcp - if (node.headerType == Global.TcpHeaderHttp) - { - TcpSettings4Ray tcpSettings = new() - { - header = new Header4Ray - { - type = node.headerType - } - }; - - //request Host - string request = Utils.GetEmbedText(Global.v2raySampleHttprequestFileName); - string[] arrHost = host.Split(','); - string host2 = string.Join("\",\"", arrHost); - request = request.Replace("$requestHost$", $"\"{host2}\""); - //request = request.Replace("$requestHost$", string.Format("\"{0}\"", config.requestHost())); - request = request.Replace("$requestUserAgent$", $"\"{useragent}\""); - //Path - string pathHttp = @"/"; - if (!Utils.IsNullOrEmpty(node.path)) - { - string[] arrPath = node.path.Split(','); - pathHttp = string.Join("\",\"", arrPath); - } - request = request.Replace("$requestPath$", $"\"{pathHttp}\""); - tcpSettings.header.request = Utils.FromJson(request); - - streamSettings.tcpSettings = tcpSettings; - } - break; - } + streamSettings.tcpSettings = tcpSettings; + } + break; } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; } - - private int dns(V2rayConfig v2rayConfig) + catch (Exception ex) { - try - { - var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray); - var normalDNS = item?.normalDNS; - var domainStrategy4Freedom = item?.domainStrategy4Freedom; - if (string.IsNullOrWhiteSpace(normalDNS)) - { - normalDNS = "1.1.1.1,8.8.8.8"; - } - - //Outbound Freedom domainStrategy - if (!string.IsNullOrWhiteSpace(domainStrategy4Freedom)) - { - var outbound = v2rayConfig.outbounds[1]; - outbound.settings.domainStrategy = domainStrategy4Freedom; - outbound.settings.userLevel = 0; - } - - var obj = Utils.ParseJson(normalDNS); - if (obj?.ContainsKey("servers") == true) - { - v2rayConfig.dns = obj; - } - else - { - List servers = new(); - - string[] arrDNS = normalDNS.Split(','); - foreach (string str in arrDNS) - { - //if (Utils.IsIP(str)) - //{ - servers.Add(str); - //} - } - //servers.Add("localhost"); - v2rayConfig.dns = new Mode.Dns4Ray - { - servers = servers - }; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return 0; + Utils.SaveLog(ex.Message, ex); } + return 0; + } - private int statistic(V2rayConfig v2rayConfig) + private int dns(V2rayConfig v2rayConfig) + { + try { - if (_config.guiItem.enableStatistics) + var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray); + var normalDNS = item?.normalDNS; + var domainStrategy4Freedom = item?.domainStrategy4Freedom; + if (string.IsNullOrWhiteSpace(normalDNS)) { - string tag = Global.InboundAPITagName; - API4Ray apiObj = new(); - Policy4Ray policyObj = new(); - SystemPolicy4Ray policySystemSetting = new(); - - string[] services = { "StatsService" }; - - v2rayConfig.stats = new Stats4Ray(); - - apiObj.tag = tag; - apiObj.services = services.ToList(); - v2rayConfig.api = apiObj; - - policySystemSetting.statsOutboundDownlink = true; - policySystemSetting.statsOutboundUplink = true; - policyObj.system = policySystemSetting; - v2rayConfig.policy = policyObj; - - if (!v2rayConfig.inbounds.Exists(item => item.tag == tag)) - { - Inbounds4Ray apiInbound = new(); - Inboundsettings4Ray apiInboundSettings = new(); - apiInbound.tag = tag; - apiInbound.listen = Global.Loopback; - apiInbound.port = Global.statePort; - apiInbound.protocol = Global.InboundAPIProtocal; - apiInboundSettings.address = Global.Loopback; - apiInbound.settings = apiInboundSettings; - v2rayConfig.inbounds.Add(apiInbound); - } - - if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag)) - { - RulesItem4Ray apiRoutingRule = new() - { - inboundTag = new List { tag }, - outboundTag = tag, - type = "field" - }; - - v2rayConfig.routing.rules.Add(apiRoutingRule); - } + normalDNS = "1.1.1.1,8.8.8.8"; + } + + //Outbound Freedom domainStrategy + if (!string.IsNullOrWhiteSpace(domainStrategy4Freedom)) + { + var outbound = v2rayConfig.outbounds[1]; + outbound.settings.domainStrategy = domainStrategy4Freedom; + outbound.settings.userLevel = 0; + } + + var obj = Utils.ParseJson(normalDNS); + if (obj?.ContainsKey("servers") == true) + { + v2rayConfig.dns = obj; + } + else + { + List servers = new(); + + string[] arrDNS = normalDNS.Split(','); + foreach (string str in arrDNS) + { + //if (Utils.IsIP(str)) + //{ + servers.Add(str); + //} + } + //servers.Add("localhost"); + v2rayConfig.dns = new Mode.Dns4Ray + { + servers = servers + }; } - return 0; } - - #region Gen speedtest config - - public string GenerateClientSpeedtestConfigString(List selecteds, out string msg) + catch (Exception ex) { - try + Utils.SaveLog(ex.Message, ex); + } + return 0; + } + + private int statistic(V2rayConfig v2rayConfig) + { + if (_config.guiItem.enableStatistics) + { + string tag = Global.InboundAPITagName; + API4Ray apiObj = new(); + Policy4Ray policyObj = new(); + SystemPolicy4Ray policySystemSetting = new(); + + string[] services = { "StatsService" }; + + v2rayConfig.stats = new Stats4Ray(); + + apiObj.tag = tag; + apiObj.services = services.ToList(); + v2rayConfig.api = apiObj; + + policySystemSetting.statsOutboundDownlink = true; + policySystemSetting.statsOutboundUplink = true; + policyObj.system = policySystemSetting; + v2rayConfig.policy = policyObj; + + if (!v2rayConfig.inbounds.Exists(item => item.tag == tag)) { - if (_config == null) - { - msg = ResUI.CheckServerSettings; - return ""; - } - - msg = ResUI.InitialConfiguration; - - Config configCopy = Utils.DeepCopy(_config); - - string result = Utils.GetEmbedText(SampleClient); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedGetDefaultConfiguration; - return ""; - } - - V2rayConfig? v2rayConfig = Utils.FromJson(result); - if (v2rayConfig == null) - { - msg = ResUI.FailedGenDefaultConfiguration; - return ""; - } - List lstIpEndPoints = new(); - List lstTcpConns = new(); - try - { - lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); - lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); - lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - - log(v2rayConfig); - v2rayConfig.inbounds.Clear(); // Remove "proxy" service for speedtest, avoiding port conflicts. - - int httpPort = LazyConfig.Instance.GetLocalPort("speedtest"); - - foreach (var it in selecteds) - { - if (it.configType == EConfigType.Custom) - { - continue; - } - if (it.port <= 0) - { - continue; - } - if (it.configType is EConfigType.VMess or EConfigType.VLESS) - { - var item2 = LazyConfig.Instance.GetProfileItem(it.indexId); - if (item2 is null || Utils.IsNullOrEmpty(item2.id) || !Utils.IsGuidByParse(item2.id)) - { - continue; - } - } - - //find unuse port - var port = httpPort; - for (int k = httpPort; k < Global.MaxPort; k++) - { - if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) - { - continue; - } - if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0) - { - continue; - } - //found - port = k; - httpPort = port + 1; - break; - } - - //Port In Used - if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0) - { - continue; - } - it.port = port; - it.allowTest = true; - - //inbound - Inbounds4Ray inbound = new() - { - listen = Global.Loopback, - port = port, - protocol = Global.InboundHttp - }; - inbound.tag = Global.InboundHttp + inbound.port.ToString(); - v2rayConfig.inbounds.Add(inbound); - - //outbound - V2rayConfig? v2rayConfigCopy = Utils.FromJson(result); - var item = LazyConfig.Instance.GetProfileItem(it.indexId); - if (item is null) - { - continue; - } - if (item.configType == EConfigType.Shadowsocks - && !Global.ssSecuritysInXray.Contains(item.security)) - { - continue; - } - if (item.configType == EConfigType.VLESS - && !Global.flows.Contains(item.flow)) - { - continue; - } - - outbound(item, v2rayConfigCopy); - v2rayConfigCopy.outbounds[0].tag = Global.agentTag + inbound.port.ToString(); - v2rayConfig.outbounds.Add(v2rayConfigCopy.outbounds[0]); - - //rule - RulesItem4Ray rule = new() - { - inboundTag = new List { inbound.tag }, - outboundTag = v2rayConfigCopy.outbounds[0].tag, - type = "field" - }; - v2rayConfig.routing.rules.Add(rule); - } - - //msg = string.Format(ResUI.SuccessfulConfiguration"), node.getSummary()); - return Utils.ToJson(v2rayConfig); + Inbounds4Ray apiInbound = new(); + Inboundsettings4Ray apiInboundSettings = new(); + apiInbound.tag = tag; + apiInbound.listen = Global.Loopback; + apiInbound.port = Global.statePort; + apiInbound.protocol = Global.InboundAPIProtocal; + apiInboundSettings.address = Global.Loopback; + apiInbound.settings = apiInboundSettings; + v2rayConfig.inbounds.Add(apiInbound); } - catch (Exception ex) + + if (!v2rayConfig.routing.rules.Exists(item => item.outboundTag == tag)) + { + RulesItem4Ray apiRoutingRule = new() + { + inboundTag = new List { tag }, + outboundTag = tag, + type = "field" + }; + + v2rayConfig.routing.rules.Add(apiRoutingRule); + } + } + return 0; + } + + #region Gen speedtest config + + public string GenerateClientSpeedtestConfigString(List selecteds, out string msg) + { + try + { + if (_config == null) + { + msg = ResUI.CheckServerSettings; + return ""; + } + + msg = ResUI.InitialConfiguration; + + Config configCopy = Utils.DeepCopy(_config); + + string result = Utils.GetEmbedText(SampleClient); + if (Utils.IsNullOrEmpty(result)) + { + msg = ResUI.FailedGetDefaultConfiguration; + return ""; + } + + V2rayConfig? v2rayConfig = Utils.FromJson(result); + if (v2rayConfig == null) { - Utils.SaveLog(ex.Message, ex); msg = ResUI.FailedGenDefaultConfiguration; return ""; } - } + List lstIpEndPoints = new(); + List lstTcpConns = new(); + try + { + lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); + lstIpEndPoints.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners()); + lstTcpConns.AddRange(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } - #endregion Gen speedtest config + log(v2rayConfig); + v2rayConfig.inbounds.Clear(); // Remove "proxy" service for speedtest, avoiding port conflicts. + + int httpPort = LazyConfig.Instance.GetLocalPort("speedtest"); + + foreach (var it in selecteds) + { + if (it.configType == EConfigType.Custom) + { + continue; + } + if (it.port <= 0) + { + continue; + } + if (it.configType is EConfigType.VMess or EConfigType.VLESS) + { + var item2 = LazyConfig.Instance.GetProfileItem(it.indexId); + if (item2 is null || Utils.IsNullOrEmpty(item2.id) || !Utils.IsGuidByParse(item2.id)) + { + continue; + } + } + + //find unuse port + var port = httpPort; + for (int k = httpPort; k < Global.MaxPort; k++) + { + if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0) + { + continue; + } + if (lstTcpConns?.FindIndex(_it => _it.LocalEndPoint.Port == k) >= 0) + { + continue; + } + //found + port = k; + httpPort = port + 1; + break; + } + + //Port In Used + if (lstIpEndPoints?.FindIndex(_it => _it.Port == port) >= 0) + { + continue; + } + it.port = port; + it.allowTest = true; + + //inbound + Inbounds4Ray inbound = new() + { + listen = Global.Loopback, + port = port, + protocol = Global.InboundHttp + }; + inbound.tag = Global.InboundHttp + inbound.port.ToString(); + v2rayConfig.inbounds.Add(inbound); + + //outbound + V2rayConfig? v2rayConfigCopy = Utils.FromJson(result); + var item = LazyConfig.Instance.GetProfileItem(it.indexId); + if (item is null) + { + continue; + } + if (item.configType == EConfigType.Shadowsocks + && !Global.ssSecuritysInXray.Contains(item.security)) + { + continue; + } + if (item.configType == EConfigType.VLESS + && !Global.flows.Contains(item.flow)) + { + continue; + } + + outbound(item, v2rayConfigCopy); + v2rayConfigCopy.outbounds[0].tag = Global.agentTag + inbound.port.ToString(); + v2rayConfig.outbounds.Add(v2rayConfigCopy.outbounds[0]); + + //rule + RulesItem4Ray rule = new() + { + inboundTag = new List { inbound.tag }, + outboundTag = v2rayConfigCopy.outbounds[0].tag, + type = "field" + }; + v2rayConfig.routing.rules.Add(rule); + } + + //msg = string.Format(ResUI.SuccessfulConfiguration"), node.getSummary()); + return Utils.ToJson(v2rayConfig); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + msg = ResUI.FailedGenDefaultConfiguration; + return ""; + } } + + #endregion Gen speedtest config } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/CoreHandler.cs b/v2rayN/v2rayN/Handler/CoreHandler.cs index 60da3037..e56d8760 100644 --- a/v2rayN/v2rayN/Handler/CoreHandler.cs +++ b/v2rayN/v2rayN/Handler/CoreHandler.cs @@ -4,359 +4,358 @@ using System.Text; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +/// +/// Core process processing class +/// +internal class CoreHandler { - /// - /// Core process processing class - /// - internal class CoreHandler + private Config _config; + private Process? _process; + private Process? _processPre; + private Action _updateFunc; + + public CoreHandler(Config config, Action update) { - private Config _config; - private Process? _process; - private Process? _processPre; - private Action _updateFunc; + _config = config; + _updateFunc = update; - public CoreHandler(Config config, Action update) + Environment.SetEnvironmentVariable("v2ray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); + } + + public void LoadCore() + { + var node = ConfigHandler.GetDefaultServer(ref _config); + if (node == null) { - _config = config; - _updateFunc = update; - - Environment.SetEnvironmentVariable("v2ray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); - Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process); + ShowMsg(false, ResUI.CheckServerSettings); + return; } - public void LoadCore() + string fileName = Utils.GetConfigPath(Global.coreConfigFileName); + if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0) { - var node = ConfigHandler.GetDefaultServer(ref _config); - if (node == null) - { - ShowMsg(false, ResUI.CheckServerSettings); - return; - } - - string fileName = Utils.GetConfigPath(Global.coreConfigFileName); - if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0) - { - ShowMsg(false, msg); - } - else - { - ShowMsg(false, msg); - ShowMsg(true, $"{node.GetSummary()}"); - CoreStop(); - CoreStart(node); - } + ShowMsg(false, msg); } - - public int LoadCoreConfigString(List _selecteds) + else { - int pid = -1; - string configStr = CoreConfigHandler.GenerateClientSpeedtestConfigString(_config, _selecteds, out string msg); - if (configStr == "") - { - ShowMsg(false, msg); - } - else - { - ShowMsg(false, msg); - pid = CoreStartViaString(configStr); - } - return pid; + ShowMsg(false, msg); + ShowMsg(true, $"{node.GetSummary()}"); + CoreStop(); + CoreStart(node); } + } - public void CoreStop() + public int LoadCoreConfigString(List _selecteds) + { + int pid = -1; + string configStr = CoreConfigHandler.GenerateClientSpeedtestConfigString(_config, _selecteds, out string msg); + if (configStr == "") { - try + ShowMsg(false, msg); + } + else + { + ShowMsg(false, msg); + pid = CoreStartViaString(configStr); + } + return pid; + } + + public void CoreStop() + { + try + { + bool hasProc = false; + if (_process != null) { - bool hasProc = false; - if (_process != null) - { - KillProcess(_process); - _process.Dispose(); - _process = null; - hasProc = true; - } + KillProcess(_process); + _process.Dispose(); + _process = null; + hasProc = true; + } - if (_processPre != null) - { - KillProcess(_processPre); - _processPre.Dispose(); - _processPre = null; - hasProc = true; - } + if (_processPre != null) + { + KillProcess(_processPre); + _processPre.Dispose(); + _processPre = null; + hasProc = true; + } - if (!hasProc) + if (!hasProc) + { + var coreInfos = LazyConfig.Instance.GetCoreInfos(); + foreach (var it in coreInfos) { - var coreInfos = LazyConfig.Instance.GetCoreInfos(); - foreach (var it in coreInfos) + if (it.coreType == ECoreType.v2rayN) { - if (it.coreType == ECoreType.v2rayN) + continue; + } + foreach (string vName in it.coreExes) + { + Process[] existing = Process.GetProcessesByName(vName); + foreach (Process p in existing) { - continue; - } - foreach (string vName in it.coreExes) - { - Process[] existing = Process.GetProcessesByName(vName); - foreach (Process p in existing) + string? path = p.MainModule?.FileName; + if (path == $"{Utils.GetBinPath(vName, it.coreType)}.exe") { - string? path = p.MainModule?.FileName; - if (path == $"{Utils.GetBinPath(vName, it.coreType)}.exe") - { - KillProcess(p); - } + KillProcess(p); } } } } } - catch (Exception ex) + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + } + + public void CoreStopPid(int pid) + { + try + { + Process _p = Process.GetProcessById(pid); + KillProcess(_p); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + } + + private string CoreFindexe(CoreInfo coreInfo) + { + string fileName = string.Empty; + foreach (string name in coreInfo.coreExes) + { + string vName = $"{name}.exe"; + vName = Utils.GetBinPath(vName, coreInfo.coreType); + if (File.Exists(vName)) { - Utils.SaveLog(ex.Message, ex); + fileName = vName; + break; } } - - public void CoreStopPid(int pid) + if (Utils.IsNullOrEmpty(fileName)) { - try - { - Process _p = Process.GetProcessById(pid); - KillProcess(_p); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } + string msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.coreType), string.Join(", ", coreInfo.coreExes.ToArray()), coreInfo.coreUrl); + Utils.SaveLog(msg); + ShowMsg(false, msg); } + return fileName; + } - private string CoreFindexe(CoreInfo coreInfo) + private void CoreStart(ProfileItem node) + { + ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); + + ECoreType coreType; + if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun) { - string fileName = string.Empty; - foreach (string name in coreInfo.coreExes) + coreType = ECoreType.sing_box; + } + else + { + coreType = LazyConfig.Instance.GetCoreType(node, node.configType); + } + var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType); + + var displayLog = node.configType != EConfigType.Custom || node.displayLog; + var proc = RunProcess(node, coreInfo, "", displayLog, ShowMsg); + if (proc is null) + { + return; + } + _process = proc; + + //start a socks service + if (_process != null && !_process.HasExited) + { + if ((node.configType == EConfigType.Custom && node.preSocksPort > 0)) { - string vName = $"{name}.exe"; - vName = Utils.GetBinPath(vName, coreInfo.coreType); - if (File.Exists(vName)) + var itemSocks = new ProfileItem() { - fileName = vName; - break; + coreType = ECoreType.sing_box, + configType = EConfigType.Socks, + address = Global.Loopback, + port = node.preSocksPort + }; + string fileName2 = Utils.GetConfigPath(Global.corePreConfigFileName); + if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0) + { + var coreInfo2 = LazyConfig.Instance.GetCoreInfo(ECoreType.sing_box); + var proc2 = RunProcess(node, coreInfo2, $" -c {Global.corePreConfigFileName}", true, ShowMsg); + if (proc2 is not null) + { + _processPre = proc2; + } } } + } + } + + private int CoreStartViaString(string configStr) + { + ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); + + try + { + var coreInfo = LazyConfig.Instance.GetCoreInfo(ECoreType.Xray); + string fileName = CoreFindexe(coreInfo); + if (fileName == "") return -1; + + Process p = new() + { + StartInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = "-config stdin:", + WorkingDirectory = Utils.GetConfigPath(), + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8 + } + }; + p.OutputDataReceived += (sender, e) => + { + if (!String.IsNullOrEmpty(e.Data)) + { + string msg = e.Data + Environment.NewLine; + ShowMsg(false, msg); + } + }; + p.ErrorDataReceived += (sender, e) => + { + if (!string.IsNullOrEmpty(e.Data)) + { + string msg = e.Data + Environment.NewLine; + ShowMsg(false, msg); + } + }; + p.Start(); + p.BeginOutputReadLine(); + p.BeginErrorReadLine(); + + p.StandardInput.Write(configStr); + p.StandardInput.Close(); + + if (p.WaitForExit(1000)) + { + throw new Exception(p.StandardError.ReadToEnd()); + } + + Global.processJob.AddProcess(p.Handle); + return p.Id; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + string msg = ex.Message; + ShowMsg(false, msg); + return -1; + } + } + + private void ShowMsg(bool updateToTrayTooltip, string msg) + { + _updateFunc(updateToTrayTooltip, msg); + } + + #region Process + + private Process? RunProcess(ProfileItem node, CoreInfo coreInfo, string configPath, bool displayLog, Action update) + { + try + { + string fileName = CoreFindexe(coreInfo); if (Utils.IsNullOrEmpty(fileName)) { - string msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.coreType), string.Join(", ", coreInfo.coreExes.ToArray()), coreInfo.coreUrl); - Utils.SaveLog(msg); - ShowMsg(false, msg); + return null; } - return fileName; - } - - private void CoreStart(ProfileItem node) - { - ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); - - ECoreType coreType; - if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun) + Process proc = new() { - coreType = ECoreType.sing_box; - } - else - { - coreType = LazyConfig.Instance.GetCoreType(node, node.configType); - } - var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType); - - var displayLog = node.configType != EConfigType.Custom || node.displayLog; - var proc = RunProcess(node, coreInfo, "", displayLog, ShowMsg); - if (proc is null) - { - return; - } - _process = proc; - - //start a socks service - if (_process != null && !_process.HasExited) - { - if ((node.configType == EConfigType.Custom && node.preSocksPort > 0)) + StartInfo = new ProcessStartInfo { - var itemSocks = new ProfileItem() - { - coreType = ECoreType.sing_box, - configType = EConfigType.Socks, - address = Global.Loopback, - port = node.preSocksPort - }; - string fileName2 = Utils.GetConfigPath(Global.corePreConfigFileName); - if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0) - { - var coreInfo2 = LazyConfig.Instance.GetCoreInfo(ECoreType.sing_box); - var proc2 = RunProcess(node, coreInfo2, $" -c {Global.corePreConfigFileName}", true, ShowMsg); - if (proc2 is not null) - { - _processPre = proc2; - } - } + FileName = fileName, + Arguments = string.Format(coreInfo.arguments, configPath), + WorkingDirectory = Utils.GetConfigPath(), + UseShellExecute = false, + RedirectStandardOutput = displayLog, + RedirectStandardError = displayLog, + CreateNoWindow = true, + StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, + StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, } - } - } - - private int CoreStartViaString(string configStr) - { - ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); - - try + }; + if (displayLog) { - var coreInfo = LazyConfig.Instance.GetCoreInfo(ECoreType.Xray); - string fileName = CoreFindexe(coreInfo); - if (fileName == "") return -1; - - Process p = new() - { - StartInfo = new ProcessStartInfo - { - FileName = fileName, - Arguments = "-config stdin:", - WorkingDirectory = Utils.GetConfigPath(), - UseShellExecute = false, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - StandardOutputEncoding = Encoding.UTF8, - StandardErrorEncoding = Encoding.UTF8 - } - }; - p.OutputDataReceived += (sender, e) => - { - if (!String.IsNullOrEmpty(e.Data)) - { - string msg = e.Data + Environment.NewLine; - ShowMsg(false, msg); - } - }; - p.ErrorDataReceived += (sender, e) => + proc.OutputDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { string msg = e.Data + Environment.NewLine; - ShowMsg(false, msg); + update(false, msg); } }; - p.Start(); - p.BeginOutputReadLine(); - p.BeginErrorReadLine(); - - p.StandardInput.Write(configStr); - p.StandardInput.Close(); - - if (p.WaitForExit(1000)) + proc.ErrorDataReceived += (sender, e) => { - throw new Exception(p.StandardError.ReadToEnd()); - } - - Global.processJob.AddProcess(p.Handle); - return p.Id; - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - string msg = ex.Message; - ShowMsg(false, msg); - return -1; - } - } - - private void ShowMsg(bool updateToTrayTooltip, string msg) - { - _updateFunc(updateToTrayTooltip, msg); - } - - #region Process - - private Process? RunProcess(ProfileItem node, CoreInfo coreInfo, string configPath, bool displayLog, Action update) - { - try - { - string fileName = CoreFindexe(coreInfo); - if (Utils.IsNullOrEmpty(fileName)) - { - return null; - } - Process proc = new() - { - StartInfo = new ProcessStartInfo + if (!string.IsNullOrEmpty(e.Data)) { - FileName = fileName, - Arguments = string.Format(coreInfo.arguments, configPath), - WorkingDirectory = Utils.GetConfigPath(), - UseShellExecute = false, - RedirectStandardOutput = displayLog, - RedirectStandardError = displayLog, - CreateNoWindow = true, - StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, - StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, + string msg = e.Data + Environment.NewLine; + update(false, msg); } }; - if (displayLog) - { - proc.OutputDataReceived += (sender, e) => - { - if (!string.IsNullOrEmpty(e.Data)) - { - string msg = e.Data + Environment.NewLine; - update(false, msg); - } - }; - proc.ErrorDataReceived += (sender, e) => - { - if (!string.IsNullOrEmpty(e.Data)) - { - string msg = e.Data + Environment.NewLine; - update(false, msg); - } - }; - } - proc.Start(); - if (displayLog) - { - proc.BeginOutputReadLine(); - proc.BeginErrorReadLine(); - } - - if (proc.WaitForExit(1000)) - { - throw new Exception(displayLog ? proc.StandardError.ReadToEnd() : "启动进程失败并退出 (Failed to start the process and exited)"); - } - - Global.processJob.AddProcess(proc.Handle); - return proc; } - catch (Exception ex) + proc.Start(); + if (displayLog) { - Utils.SaveLog(ex.Message, ex); - string msg = ex.Message; - update(true, msg); - return null; + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); } - } - private void KillProcess(Process p) + if (proc.WaitForExit(1000)) + { + throw new Exception(displayLog ? proc.StandardError.ReadToEnd() : "启动进程失败并退出 (Failed to start the process and exited)"); + } + + Global.processJob.AddProcess(proc.Handle); + return proc; + } + catch (Exception ex) { - try - { - p.CloseMainWindow(); - p.WaitForExit(100); - if (!p.HasExited) - { - p.Kill(); - p.WaitForExit(100); - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } + Utils.SaveLog(ex.Message, ex); + string msg = ex.Message; + update(true, msg); + return null; } - - #endregion Process } + + private void KillProcess(Process p) + { + try + { + p.CloseMainWindow(); + p.WaitForExit(100); + if (!p.HasExited) + { + p.Kill(); + p.WaitForExit(100); + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + } + + #endregion Process } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/DownloadHandle.cs b/v2rayN/v2rayN/Handler/DownloadHandle.cs index 54edadf7..ec1ccdf6 100644 --- a/v2rayN/v2rayN/Handler/DownloadHandle.cs +++ b/v2rayN/v2rayN/Handler/DownloadHandle.cs @@ -7,272 +7,266 @@ using System.Net.Sockets; using v2rayN.Base; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +/// +///Download +/// +internal class DownloadHandle { - /// - ///Download - /// - internal class DownloadHandle + public event EventHandler? UpdateCompleted; + + public event ErrorEventHandler? Error; + + public class ResultEventArgs : EventArgs { - public event EventHandler? UpdateCompleted; + public bool Success; + public string Msg; - public event ErrorEventHandler? Error; - - public class ResultEventArgs : EventArgs + public ResultEventArgs(bool success, string msg) { - public bool Success; - public string Msg; - - public ResultEventArgs(bool success, string msg) - { - Success = success; - Msg = msg; - } + Success = success; + Msg = msg; } + } - public async Task DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action update) - { - try - { - Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); - - var progress = new Progress(); - progress.ProgressChanged += (sender, value) => - { - if (update != null) - { - string msg = $"{value}"; - update(false, msg); - } - }; - - await DownloaderHelper.Instance.DownloadDataAsync4Speed(webProxy, - url, - progress, - downloadTimeout); - } - catch (Exception ex) - { - update(false, ex.Message); - if (ex.InnerException != null) - { - update(false, ex.InnerException.Message); - } - } - return 0; - } - - public async Task DownloadFileAsync(string url, bool blProxy, int downloadTimeout) - { - try - { - Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); - UpdateCompleted?.Invoke(this, new ResultEventArgs(false, $"{ResUI.Downloading} {url}")); - - var progress = new Progress(); - progress.ProgressChanged += (sender, value) => - { - UpdateCompleted?.Invoke(this, new ResultEventArgs(value > 100, $"...{value}%")); - }; - - var webProxy = GetWebProxy(blProxy); - await DownloaderHelper.Instance.DownloadFileAsync(webProxy, - url, - Utils.GetTempPath(Utils.GetDownloadFileName(url)), - progress, - downloadTimeout); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - - Error?.Invoke(this, new ErrorEventArgs(ex)); - if (ex.InnerException != null) - { - Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); - } - } - } - - public async Task UrlRedirectAsync(string url, bool blProxy) + public async Task DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action update) + { + try { Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); - var webRequestHandler = new SocketsHttpHandler + + var progress = new Progress(); + progress.ProgressChanged += (sender, value) => { - AllowAutoRedirect = false, - Proxy = GetWebProxy(blProxy) + if (update != null) + { + string msg = $"{value}"; + update(false, msg); + } }; - HttpClient client = new(webRequestHandler); - HttpResponseMessage response = await client.GetAsync(url); - if (response.StatusCode == HttpStatusCode.Redirect && response.Headers.Location is not null) + await DownloaderHelper.Instance.DownloadDataAsync4Speed(webProxy, + url, + progress, + downloadTimeout); + } + catch (Exception ex) + { + update(false, ex.Message); + if (ex.InnerException != null) { - return response.Headers.Location.ToString(); - } - else - { - Utils.SaveLog("StatusCode error: " + url); - return null; + update(false, ex.InnerException.Message); } } + return 0; + } - public async Task TryDownloadString(string url, bool blProxy, string userAgent) + public async Task DownloadFileAsync(string url, bool blProxy, int downloadTimeout) + { + try { - try - { - var result1 = await DownloadStringAsync(url, blProxy, userAgent); - if (!Utils.IsNullOrEmpty(result1)) - { - return result1; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - Error?.Invoke(this, new ErrorEventArgs(ex)); - if (ex.InnerException != null) - { - Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); - } - } + Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); + UpdateCompleted?.Invoke(this, new ResultEventArgs(false, $"{ResUI.Downloading} {url}")); - try + var progress = new Progress(); + progress.ProgressChanged += (sender, value) => { - var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent); - if (!Utils.IsNullOrEmpty(result2)) - { - return result2; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - Error?.Invoke(this, new ErrorEventArgs(ex)); - if (ex.InnerException != null) - { - Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); - } - } + UpdateCompleted?.Invoke(this, new ResultEventArgs(value > 100, $"...{value}%")); + }; - try - { - using var wc = new WebClient(); - wc.Proxy = GetWebProxy(blProxy); - var result3 = await wc.DownloadStringTaskAsync(url); - if (!Utils.IsNullOrEmpty(result3)) - { - return result3; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - Error?.Invoke(this, new ErrorEventArgs(ex)); - if (ex.InnerException != null) - { - Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); - } - } + var webProxy = GetWebProxy(blProxy); + await DownloaderHelper.Instance.DownloadFileAsync(webProxy, + url, + Utils.GetTempPath(Utils.GetDownloadFileName(url)), + progress, + downloadTimeout); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + Error?.Invoke(this, new ErrorEventArgs(ex)); + if (ex.InnerException != null) + { + Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); + } + } + } + + public async Task UrlRedirectAsync(string url, bool blProxy) + { + Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); + var webRequestHandler = new SocketsHttpHandler + { + AllowAutoRedirect = false, + Proxy = GetWebProxy(blProxy) + }; + HttpClient client = new(webRequestHandler); + + HttpResponseMessage response = await client.GetAsync(url); + if (response.StatusCode == HttpStatusCode.Redirect && response.Headers.Location is not null) + { + return response.Headers.Location.ToString(); + } + else + { + Utils.SaveLog("StatusCode error: " + url); return null; } + } - /// - /// DownloadString - /// - /// - public async Task DownloadStringAsync(string url, bool blProxy, string userAgent) + public async Task TryDownloadString(string url, bool blProxy, string userAgent) + { + try { - try + var result1 = await DownloadStringAsync(url, blProxy, userAgent); + if (!Utils.IsNullOrEmpty(result1)) { - Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); - var webProxy = GetWebProxy(blProxy); - var client = new HttpClient(new SocketsHttpHandler() - { - Proxy = webProxy, - UseProxy = webProxy != null - }); - - if (Utils.IsNullOrEmpty(userAgent)) - { - userAgent = Utils.GetVersion(false); - } - client.DefaultRequestHeaders.UserAgent.TryParseAdd(userAgent); - - Uri uri = new(url); - //Authorization Header - if (!Utils.IsNullOrEmpty(uri.UserInfo)) - { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo)); - } - - using var cts = new CancellationTokenSource(); - var result = await HttpClientHelper.Instance.GetAsync(client, url, cts.Token).WaitAsync(TimeSpan.FromSeconds(30), cts.Token); - return result; + return result1; } - catch (Exception ex) + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + Error?.Invoke(this, new ErrorEventArgs(ex)); + if (ex.InnerException != null) { - Utils.SaveLog(ex.Message, ex); - Error?.Invoke(this, new ErrorEventArgs(ex)); - if (ex.InnerException != null) - { - Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); - } + Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); } - return null; } - /// - /// DownloadString - /// - /// - public async Task DownloadStringViaDownloader(string url, bool blProxy, string userAgent) + try { - try + var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent); + if (!Utils.IsNullOrEmpty(result2)) { - Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); - - var webProxy = GetWebProxy(blProxy); - - if (Utils.IsNullOrEmpty(userAgent)) - { - userAgent = Utils.GetVersion(false); - } - var result = await DownloaderHelper.Instance.DownloadStringAsync(webProxy, url, userAgent, 30); - return result; + return result2; } - catch (Exception ex) + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + Error?.Invoke(this, new ErrorEventArgs(ex)); + if (ex.InnerException != null) { - Utils.SaveLog(ex.Message, ex); - Error?.Invoke(this, new ErrorEventArgs(ex)); - if (ex.InnerException != null) - { - Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); - } + Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); } - return null; } - public async Task RunAvailabilityCheck(IWebProxy? webProxy) + try { + using var wc = new WebClient(); + wc.Proxy = GetWebProxy(blProxy); + var result3 = await wc.DownloadStringTaskAsync(url); + if (!Utils.IsNullOrEmpty(result3)) + { + return result3; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + Error?.Invoke(this, new ErrorEventArgs(ex)); + if (ex.InnerException != null) + { + Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); + } + } + + return null; + } + + /// + /// DownloadString + /// + /// + public async Task DownloadStringAsync(string url, bool blProxy, string userAgent) + { + try + { + Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); + var webProxy = GetWebProxy(blProxy); + var client = new HttpClient(new SocketsHttpHandler() + { + Proxy = webProxy, + UseProxy = webProxy != null + }); + + if (Utils.IsNullOrEmpty(userAgent)) + { + userAgent = Utils.GetVersion(false); + } + client.DefaultRequestHeaders.UserAgent.TryParseAdd(userAgent); + + Uri uri = new(url); + //Authorization Header + if (!Utils.IsNullOrEmpty(uri.UserInfo)) + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo)); + } + + using var cts = new CancellationTokenSource(); + var result = await HttpClientHelper.Instance.GetAsync(client, url, cts.Token).WaitAsync(TimeSpan.FromSeconds(30), cts.Token); + return result; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + Error?.Invoke(this, new ErrorEventArgs(ex)); + if (ex.InnerException != null) + { + Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); + } + } + return null; + } + + /// + /// DownloadString + /// + /// + public async Task DownloadStringViaDownloader(string url, bool blProxy, string userAgent) + { + try + { + Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13); + + var webProxy = GetWebProxy(blProxy); + + if (Utils.IsNullOrEmpty(userAgent)) + { + userAgent = Utils.GetVersion(false); + } + var result = await DownloaderHelper.Instance.DownloadStringAsync(webProxy, url, userAgent, 30); + return result; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + Error?.Invoke(this, new ErrorEventArgs(ex)); + if (ex.InnerException != null) + { + Error?.Invoke(this, new ErrorEventArgs(ex.InnerException)); + } + } + return null; + } + + public async Task RunAvailabilityCheck(IWebProxy? webProxy) + { + try + { + if (webProxy == null) + { + webProxy = GetWebProxy(true); + } + try { - if (webProxy == null) - { - webProxy = GetWebProxy(true); - } - - try - { - var config = LazyConfig.Instance.GetConfig(); - int responseTime = await GetRealPingTime(config.speedTestItem.speedPingTestUrl, webProxy, 10); - return responseTime; - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - return -1; - } + var config = LazyConfig.Instance.GetConfig(); + int responseTime = await GetRealPingTime(config.speedTestItem.speedPingTestUrl, webProxy, 10); + return responseTime; } catch (Exception ex) { @@ -280,60 +274,65 @@ namespace v2rayN.Handler return -1; } } - - public async Task GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout) + catch (Exception ex) { - int responseTime = -1; - try - { - Stopwatch timer = Stopwatch.StartNew(); + Utils.SaveLog(ex.Message, ex); + return -1; + } + } - using var cts = new CancellationTokenSource(); - cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout)); - using var client = new HttpClient(new SocketsHttpHandler() - { - Proxy = webProxy, - UseProxy = webProxy != null - }); - await client.GetAsync(url, cts.Token); + public async Task GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout) + { + int responseTime = -1; + try + { + Stopwatch timer = Stopwatch.StartNew(); - responseTime = timer.Elapsed.Milliseconds; - } - catch (Exception ex) + using var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout)); + using var client = new HttpClient(new SocketsHttpHandler() { - //Utils.SaveLog(ex.Message, ex); - } - return responseTime; + Proxy = webProxy, + UseProxy = webProxy != null + }); + await client.GetAsync(url, cts.Token); + + responseTime = timer.Elapsed.Milliseconds; + } + catch (Exception ex) + { + //Utils.SaveLog(ex.Message, ex); + } + return responseTime; + } + + private WebProxy? GetWebProxy(bool blProxy) + { + if (!blProxy) + { + return null; + } + var httpPort = LazyConfig.Instance.GetLocalPort(Global.InboundHttp); + if (!SocketCheck(Global.Loopback, httpPort)) + { + return null; } - private WebProxy? GetWebProxy(bool blProxy) - { - if (!blProxy) - { - return null; - } - var httpPort = LazyConfig.Instance.GetLocalPort(Global.InboundHttp); - if (!SocketCheck(Global.Loopback, httpPort)) - { - return null; - } + return new WebProxy(Global.Loopback, httpPort); + } - return new WebProxy(Global.Loopback, httpPort); + private bool SocketCheck(string ip, int port) + { + try + { + IPEndPoint point = new(IPAddress.Parse(ip), port); + using Socket? sock = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + sock.Connect(point); + return true; } - - private bool SocketCheck(string ip, int port) + catch (Exception) { - try - { - IPEndPoint point = new(IPAddress.Parse(ip), port); - using Socket? sock = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - sock.Connect(point); - return true; - } - catch (Exception) - { - return false; - } + return false; } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/HotkeyHandler.cs b/v2rayN/v2rayN/Handler/HotkeyHandler.cs index f6543392..b5eb916a 100644 --- a/v2rayN/v2rayN/Handler/HotkeyHandler.cs +++ b/v2rayN/v2rayN/Handler/HotkeyHandler.cs @@ -7,174 +7,173 @@ using System.Windows.Interop; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +public sealed class HotkeyHandler { - public sealed class HotkeyHandler + private static readonly Lazy _instance = new(() => new()); + public static HotkeyHandler Instance = _instance.Value; + + private const int WmHotkey = 0x0312; + + private Config _config { - private static readonly Lazy _instance = new(() => new()); - public static HotkeyHandler Instance = _instance.Value; + get => LazyConfig.Instance.GetConfig(); + } - private const int WmHotkey = 0x0312; + private Dictionary> _hotkeyTriggerDic; - private Config _config + public bool IsPause { get; set; } = false; + + public event Action? UpdateViewEvent; + + public event Action? HotkeyTriggerEvent; + + public HotkeyHandler() + { + _hotkeyTriggerDic = new(); + ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage; + Init(); + } + + private void Init() + { + _hotkeyTriggerDic.Clear(); + if (_config.globalHotkeys == null) return; + foreach (var item in _config.globalHotkeys) { - get => LazyConfig.Instance.GetConfig(); - } - - private Dictionary> _hotkeyTriggerDic; - - public bool IsPause { get; set; } = false; - - public event Action? UpdateViewEvent; - - public event Action? HotkeyTriggerEvent; - - public HotkeyHandler() - { - _hotkeyTriggerDic = new(); - ComponentDispatcher.ThreadPreprocessMessage += OnThreadPreProcessMessage; - Init(); - } - - private void Init() - { - _hotkeyTriggerDic.Clear(); - if (_config.globalHotkeys == null) return; - foreach (var item in _config.globalHotkeys) + if (item.KeyCode != null && item.KeyCode != Key.None) { - if (item.KeyCode != null && item.KeyCode != Key.None) + int key = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode); + KeyModifiers modifiers = KeyModifiers.None; + if (item.Control) modifiers |= KeyModifiers.Ctrl; + if (item.Shift) modifiers |= KeyModifiers.Shift; + if (item.Alt) modifiers |= KeyModifiers.Alt; + key = (key << 16) | (int)modifiers; + if (!_hotkeyTriggerDic.ContainsKey(key)) { - int key = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode); - KeyModifiers modifiers = KeyModifiers.None; - if (item.Control) modifiers |= KeyModifiers.Ctrl; - if (item.Shift) modifiers |= KeyModifiers.Shift; - if (item.Alt) modifiers |= KeyModifiers.Alt; - key = (key << 16) | (int)modifiers; - if (!_hotkeyTriggerDic.ContainsKey(key)) - { - _hotkeyTriggerDic.Add(key, new() { item.eGlobalHotkey }); - } - else - { - if (!_hotkeyTriggerDic[key].Contains(item.eGlobalHotkey)) - _hotkeyTriggerDic[key].Add(item.eGlobalHotkey); - } + _hotkeyTriggerDic.Add(key, new() { item.eGlobalHotkey }); + } + else + { + if (!_hotkeyTriggerDic[key].Contains(item.eGlobalHotkey)) + _hotkeyTriggerDic[key].Add(item.eGlobalHotkey); } } } - - public void Load() - { - foreach (var _hotkeyCode in _hotkeyTriggerDic.Keys) - { - var hotkeyInfo = GetHotkeyInfo(_hotkeyCode); - bool isSuccess = false; - string msg; - - Application.Current.Dispatcher.Invoke(() => - { - isSuccess = RegisterHotKey(IntPtr.Zero, _hotkeyCode, hotkeyInfo.fsModifiers, hotkeyInfo.vKey); - }); - foreach (var name in hotkeyInfo.Names) - { - if (isSuccess) - { - msg = string.Format(ResUI.RegisterGlobalHotkeySuccessfully, $"{name}({hotkeyInfo.hotkeyStr})"); - } - else - { - var errInfo = new Win32Exception(Marshal.GetLastWin32Error()).Message; - msg = string.Format(ResUI.RegisterGlobalHotkeyFailed, $"{name}({hotkeyInfo.hotkeyStr})", errInfo); - } - UpdateViewEvent?.Invoke(false, msg); - } - } - } - - public void ReLoad() - { - foreach (var hotkey in _hotkeyTriggerDic.Keys) - { - Application.Current.Dispatcher.Invoke(() => - { - UnregisterHotKey(IntPtr.Zero, hotkey); - }); - } - Init(); - Load(); - } - - private (int fsModifiers, int vKey, string hotkeyStr, List Names) GetHotkeyInfo(int hotkeycode) - { - var _fsModifiers = hotkeycode & 0xffff; - var _vkey = (hotkeycode >> 16) & 0xffff; - var _hotkeyStr = new StringBuilder(); - var _names = new List(); - - var mdif = (KeyModifiers)_fsModifiers; - var key = KeyInterop.KeyFromVirtualKey(_vkey); - if ((mdif & KeyModifiers.Ctrl) == KeyModifiers.Ctrl) _hotkeyStr.Append($"{KeyModifiers.Ctrl}+"); - if ((mdif & KeyModifiers.Alt) == KeyModifiers.Alt) _hotkeyStr.Append($"{KeyModifiers.Alt}+"); - if ((mdif & KeyModifiers.Shift) == KeyModifiers.Shift) _hotkeyStr.Append($"{KeyModifiers.Shift}+"); - _hotkeyStr.Append(key.ToString()); - - foreach (var name in _hotkeyTriggerDic[hotkeycode]) - { - _names.Add(name.ToString()); - } - - return (_fsModifiers, _vkey, _hotkeyStr.ToString(), _names); - } - - private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled) - { - if (msg.message != WmHotkey || !_hotkeyTriggerDic.ContainsKey((int)msg.lParam)) - { - return; - } - handled = true; - var _hotKeyCode = (int)msg.lParam; - if (IsPause) - { - Application.Current.Dispatcher.Invoke(() => - { - UIElement? element = Keyboard.FocusedElement as UIElement; - if (element != null) - { - var _keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, - PresentationSource.FromVisual(element), 0, - KeyInterop.KeyFromVirtualKey(GetHotkeyInfo(_hotKeyCode).vKey)) - { - RoutedEvent = UIElement.KeyDownEvent - }; - element.RaiseEvent(_keyEventArgs); - } - }); - } - else - { - foreach (var keyEvent in _hotkeyTriggerDic[(int)msg.lParam]) - { - HotkeyTriggerEvent?.Invoke(keyEvent); - } - } - } - - [DllImport("user32.dll", SetLastError = true)] - private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc); - - [DllImport("user32.dll", SetLastError = true)] - private static extern bool UnregisterHotKey(IntPtr hWnd, int id); - - [Flags] - private enum KeyModifiers - { - None = 0x0000, - Alt = 0x0001, - Ctrl = 0x0002, - Shift = 0x0004, - Win = 0x0008, - NoRepeat = 0x4000 - } + } + + public void Load() + { + foreach (var _hotkeyCode in _hotkeyTriggerDic.Keys) + { + var hotkeyInfo = GetHotkeyInfo(_hotkeyCode); + bool isSuccess = false; + string msg; + + Application.Current.Dispatcher.Invoke(() => + { + isSuccess = RegisterHotKey(IntPtr.Zero, _hotkeyCode, hotkeyInfo.fsModifiers, hotkeyInfo.vKey); + }); + foreach (var name in hotkeyInfo.Names) + { + if (isSuccess) + { + msg = string.Format(ResUI.RegisterGlobalHotkeySuccessfully, $"{name}({hotkeyInfo.hotkeyStr})"); + } + else + { + var errInfo = new Win32Exception(Marshal.GetLastWin32Error()).Message; + msg = string.Format(ResUI.RegisterGlobalHotkeyFailed, $"{name}({hotkeyInfo.hotkeyStr})", errInfo); + } + UpdateViewEvent?.Invoke(false, msg); + } + } + } + + public void ReLoad() + { + foreach (var hotkey in _hotkeyTriggerDic.Keys) + { + Application.Current.Dispatcher.Invoke(() => + { + UnregisterHotKey(IntPtr.Zero, hotkey); + }); + } + Init(); + Load(); + } + + private (int fsModifiers, int vKey, string hotkeyStr, List Names) GetHotkeyInfo(int hotkeycode) + { + var _fsModifiers = hotkeycode & 0xffff; + var _vkey = (hotkeycode >> 16) & 0xffff; + var _hotkeyStr = new StringBuilder(); + var _names = new List(); + + var mdif = (KeyModifiers)_fsModifiers; + var key = KeyInterop.KeyFromVirtualKey(_vkey); + if ((mdif & KeyModifiers.Ctrl) == KeyModifiers.Ctrl) _hotkeyStr.Append($"{KeyModifiers.Ctrl}+"); + if ((mdif & KeyModifiers.Alt) == KeyModifiers.Alt) _hotkeyStr.Append($"{KeyModifiers.Alt}+"); + if ((mdif & KeyModifiers.Shift) == KeyModifiers.Shift) _hotkeyStr.Append($"{KeyModifiers.Shift}+"); + _hotkeyStr.Append(key.ToString()); + + foreach (var name in _hotkeyTriggerDic[hotkeycode]) + { + _names.Add(name.ToString()); + } + + return (_fsModifiers, _vkey, _hotkeyStr.ToString(), _names); + } + + private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled) + { + if (msg.message != WmHotkey || !_hotkeyTriggerDic.ContainsKey((int)msg.lParam)) + { + return; + } + handled = true; + var _hotKeyCode = (int)msg.lParam; + if (IsPause) + { + Application.Current.Dispatcher.Invoke(() => + { + UIElement? element = Keyboard.FocusedElement as UIElement; + if (element != null) + { + var _keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice, + PresentationSource.FromVisual(element), 0, + KeyInterop.KeyFromVirtualKey(GetHotkeyInfo(_hotKeyCode).vKey)) + { + RoutedEvent = UIElement.KeyDownEvent + }; + element.RaiseEvent(_keyEventArgs); + } + }); + } + else + { + foreach (var keyEvent in _hotkeyTriggerDic[(int)msg.lParam]) + { + HotkeyTriggerEvent?.Invoke(keyEvent); + } + } + } + + [DllImport("user32.dll", SetLastError = true)] + private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc); + + [DllImport("user32.dll", SetLastError = true)] + private static extern bool UnregisterHotKey(IntPtr hWnd, int id); + + [Flags] + private enum KeyModifiers + { + None = 0x0000, + Alt = 0x0001, + Ctrl = 0x0002, + Shift = 0x0004, + Win = 0x0008, + NoRepeat = 0x4000 } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/LazyConfig.cs b/v2rayN/v2rayN/Handler/LazyConfig.cs index 3d2d9c23..a9cc5f05 100644 --- a/v2rayN/v2rayN/Handler/LazyConfig.cs +++ b/v2rayN/v2rayN/Handler/LazyConfig.cs @@ -1,368 +1,367 @@ using v2rayN.Base; using v2rayN.Mode; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +public sealed class LazyConfig { - public sealed class LazyConfig + private static readonly Lazy _instance = new(() => new()); + private Config _config; + private List coreInfos; + + public static LazyConfig Instance => _instance.Value; + + public LazyConfig() { - private static readonly Lazy _instance = new(() => new()); - private Config _config; - private List coreInfos; + SqliteHelper.Instance.CreateTable(); + SqliteHelper.Instance.CreateTable(); + SqliteHelper.Instance.CreateTable(); + SqliteHelper.Instance.CreateTable(); + SqliteHelper.Instance.CreateTable(); + SqliteHelper.Instance.CreateTable(); + } - public static LazyConfig Instance => _instance.Value; + #region Config - public LazyConfig() + public void SetConfig(Config config) + { + _config = config; + } + + public Config GetConfig() + { + return _config; + } + + public int GetLocalPort(string protocol) + { + int localPort = _config.inbound.FirstOrDefault(t => t.protocol == Global.InboundSocks).localPort; + if (protocol == Global.InboundSocks) { - SqliteHelper.Instance.CreateTable(); - SqliteHelper.Instance.CreateTable(); - SqliteHelper.Instance.CreateTable(); - SqliteHelper.Instance.CreateTable(); - SqliteHelper.Instance.CreateTable(); - SqliteHelper.Instance.CreateTable(); - } - - #region Config - - public void SetConfig(Config config) - { - _config = config; - } - - public Config GetConfig() - { - return _config; - } - - public int GetLocalPort(string protocol) - { - int localPort = _config.inbound.FirstOrDefault(t => t.protocol == Global.InboundSocks).localPort; - if (protocol == Global.InboundSocks) - { - return localPort; - } - else if (protocol == Global.InboundHttp) - { - return localPort + 1; - } - else if (protocol == Global.InboundSocks2) - { - return localPort + 2; - } - else if (protocol == Global.InboundHttp2) - { - return localPort + 3; - } - else if (protocol == ESysProxyType.Pac.ToString()) - { - return localPort + 4; - } - else if (protocol == "speedtest") - { - return localPort + 103; - } return localPort; } - - public List SubItems() + else if (protocol == Global.InboundHttp) { - return SqliteHelper.Instance.Table().ToList(); + return localPort + 1; } - - public SubItem GetSubItem(string subid) + else if (protocol == Global.InboundSocks2) { - return SqliteHelper.Instance.Table().FirstOrDefault(t => t.id == subid); + return localPort + 2; } - - public List ProfileItems(string subid) + else if (protocol == Global.InboundHttp2) { - if (Utils.IsNullOrEmpty(subid)) - { - return SqliteHelper.Instance.Table().ToList(); - } - else - { - return SqliteHelper.Instance.Table().Where(t => t.subid == subid).ToList(); - } + return localPort + 3; } - - public List ProfileItemIndexs(string subid) + else if (protocol == ESysProxyType.Pac.ToString()) { - if (Utils.IsNullOrEmpty(subid)) - { - return SqliteHelper.Instance.Table().Select(t => t.indexId).ToList(); - } - else - { - return SqliteHelper.Instance.Table().Where(t => t.subid == subid).Select(t => t.indexId).ToList(); - } + return localPort + 4; } - - public List ProfileItems(string subid, string filter) + else if (protocol == "speedtest") { - var sql = @$"select a.* + return localPort + 103; + } + return localPort; + } + + public List SubItems() + { + return SqliteHelper.Instance.Table().ToList(); + } + + public SubItem GetSubItem(string subid) + { + return SqliteHelper.Instance.Table().FirstOrDefault(t => t.id == subid); + } + + public List ProfileItems(string subid) + { + if (Utils.IsNullOrEmpty(subid)) + { + return SqliteHelper.Instance.Table().ToList(); + } + else + { + return SqliteHelper.Instance.Table().Where(t => t.subid == subid).ToList(); + } + } + + public List ProfileItemIndexs(string subid) + { + if (Utils.IsNullOrEmpty(subid)) + { + return SqliteHelper.Instance.Table().Select(t => t.indexId).ToList(); + } + else + { + return SqliteHelper.Instance.Table().Where(t => t.subid == subid).Select(t => t.indexId).ToList(); + } + } + + public List ProfileItems(string subid, string filter) + { + var sql = @$"select a.* ,b.remarks subRemarks from ProfileItem a left join SubItem b on a.subid = b.id where 1=1 "; - if (!Utils.IsNullOrEmpty(subid)) - { - sql += $" and a.subid = '{subid}'"; - } - if (!Utils.IsNullOrEmpty(filter)) - { - if (filter.Contains('\'')) - { - filter = filter.Replace("'", ""); - } - sql += String.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter); - } - - return SqliteHelper.Instance.Query(sql).ToList(); - } - - public ProfileItem? GetProfileItem(string indexId) + if (!Utils.IsNullOrEmpty(subid)) { - if (Utils.IsNullOrEmpty(indexId)) + sql += $" and a.subid = '{subid}'"; + } + if (!Utils.IsNullOrEmpty(filter)) + { + if (filter.Contains('\'')) { - return null; + filter = filter.Replace("'", ""); } - return SqliteHelper.Instance.Table().FirstOrDefault(it => it.indexId == indexId); + sql += String.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter); } - public List RoutingItems() - { - return SqliteHelper.Instance.Table().Where(it => it.locked == false).OrderBy(t => t.sort).ToList(); - } - - public RoutingItem GetRoutingItem(string id) - { - return SqliteHelper.Instance.Table().FirstOrDefault(it => it.locked == false && it.id == id); - } - - public List DNSItems() - { - return SqliteHelper.Instance.Table().ToList(); - } - - public DNSItem GetDNSItem(ECoreType eCoreType) - { - return SqliteHelper.Instance.Table().FirstOrDefault(it => it.coreType == eCoreType); - } - - #endregion Config - - #region Core Type - - public List GetShadowsocksSecuritys(ProfileItem profileItem) - { - if (GetCoreType(profileItem, EConfigType.Shadowsocks) == ECoreType.v2fly) - { - return Global.ssSecuritys; - } - if (GetCoreType(profileItem, EConfigType.Shadowsocks) == ECoreType.Xray) - { - return Global.ssSecuritysInXray; - } - - return Global.ssSecuritysInSagerNet; - } - - public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType) - { - if (profileItem?.coreType != null) - { - return (ECoreType)profileItem.coreType; - } - - if (_config.coreTypeItem == null) - { - return ECoreType.Xray; - } - var item = _config.coreTypeItem.FirstOrDefault(it => it.configType == eConfigType); - if (item == null) - { - return ECoreType.Xray; - } - return item.coreType; - } - - public CoreInfo? GetCoreInfo(ECoreType coreType) - { - if (coreInfos == null) - { - InitCoreInfo(); - } - return coreInfos!.FirstOrDefault(t => t.coreType == coreType); - } - - public List? GetCoreInfos() - { - if (coreInfos == null) - { - InitCoreInfo(); - } - return coreInfos; - } - - private void InitCoreInfo() - { - coreInfos = new(16); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.v2rayN, - coreUrl = Global.NUrl, - coreReleaseApiUrl = Global.NUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.NUrl + "/download/{0}/v2rayN-32.zip", - coreDownloadUrl64 = Global.NUrl + "/download/{0}/v2rayN.zip", - coreDownloadUrlArm64 = Global.NUrl + "/download/{0}/v2rayN-arm64.zip" - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.v2fly, - coreExes = new List { "wv2ray", "v2ray" }, - arguments = "", - coreUrl = Global.v2flyCoreUrl, - coreReleaseApiUrl = Global.v2flyCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - coreDownloadUrl64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - coreDownloadUrlArm64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - match = "V2Ray", - versionArg = "-version", - redirectInfo = true, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.SagerNet, - coreExes = new List { "SagerNet", "v2ray" }, - arguments = "run", - coreUrl = Global.SagerNetCoreUrl, - coreReleaseApiUrl = Global.SagerNetCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - coreDownloadUrl64 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - coreDownloadUrlArm64 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - match = "V2Ray", - versionArg = "version", - redirectInfo = true, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.v2fly_v5, - coreExes = new List { "v2ray" }, - arguments = "run", - coreUrl = Global.v2flyCoreUrl, - coreReleaseApiUrl = Global.v2flyCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - coreDownloadUrl64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - coreDownloadUrlArm64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", - match = "V2Ray", - versionArg = "version", - redirectInfo = true, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.Xray, - coreExes = new List { "xray", "wxray" }, - arguments = "", - coreUrl = Global.xrayCoreUrl, - coreReleaseApiUrl = Global.xrayCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip", - coreDownloadUrl64 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip", - coreDownloadUrlArm64 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip", - match = "Xray", - versionArg = "-version", - redirectInfo = true, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.clash, - coreExes = new List { "clash-windows-amd64-v3", "clash-windows-amd64", "clash-windows-386", "clash" }, - arguments = "-f config.json", - coreUrl = Global.clashCoreUrl, - coreReleaseApiUrl = Global.clashCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.clashCoreUrl + "/download/{0}/clash-windows-386-{0}.zip", - coreDownloadUrl64 = Global.clashCoreUrl + "/download/{0}/clash-windows-amd64-{0}.zip", - coreDownloadUrlArm64 = Global.clashCoreUrl + "/download/{0}/clash-windows-arm64-{0}.zip", - match = "v", - versionArg = "-v", - redirectInfo = true, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.clash_meta, - coreExes = new List { "Clash.Meta-windows-amd64-compatible", "Clash.Meta-windows-amd64", "Clash.Meta-windows-386", "Clash.Meta", "clash" }, - arguments = "-f config.json", - coreUrl = Global.clashMetaCoreUrl, - coreReleaseApiUrl = Global.clashMetaCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-386-{0}.zip", - coreDownloadUrl64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-amd64-compatible-{0}.zip", - coreDownloadUrlArm64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-arm64-{0}.zip", - match = "v", - versionArg = "-v", - redirectInfo = true, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.hysteria, - coreExes = new List { "hysteria-windows-amd64", "hysteria-windows-386", "hysteria" }, - arguments = "", - coreUrl = Global.hysteriaCoreUrl, - coreReleaseApiUrl = Global.hysteriaCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-386.exe", - coreDownloadUrl64 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-amd64.exe", - coreDownloadUrlArm64 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-arm64.exe", - redirectInfo = true, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.naiveproxy, - coreExes = new List { "naiveproxy", "naive" }, - arguments = "config.json", - coreUrl = Global.naiveproxyCoreUrl, - redirectInfo = false, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.tuic, - coreExes = new List { "tuic-client", "tuic" }, - arguments = "-c config.json", - coreUrl = Global.tuicCoreUrl, - redirectInfo = true, - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.sing_box, - coreExes = new List { "sing-box-client", "sing-box" }, - arguments = "run{0}", - coreUrl = Global.singboxCoreUrl, - redirectInfo = true, - coreReleaseApiUrl = Global.singboxCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), - coreDownloadUrl32 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-386.zip", - coreDownloadUrl64 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip", - coreDownloadUrlArm64 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip", - match = "sing-box", - versionArg = "version", - }); - - coreInfos.Add(new CoreInfo - { - coreType = ECoreType.juicity, - coreExes = new List { "juicity-client", "juicity" }, - arguments = "run -c config.json", - coreUrl = Global.juicityCoreUrl - }); - } - - #endregion Core Type + return SqliteHelper.Instance.Query(sql).ToList(); } + + public ProfileItem? GetProfileItem(string indexId) + { + if (Utils.IsNullOrEmpty(indexId)) + { + return null; + } + return SqliteHelper.Instance.Table().FirstOrDefault(it => it.indexId == indexId); + } + + public List RoutingItems() + { + return SqliteHelper.Instance.Table().Where(it => it.locked == false).OrderBy(t => t.sort).ToList(); + } + + public RoutingItem GetRoutingItem(string id) + { + return SqliteHelper.Instance.Table().FirstOrDefault(it => it.locked == false && it.id == id); + } + + public List DNSItems() + { + return SqliteHelper.Instance.Table().ToList(); + } + + public DNSItem GetDNSItem(ECoreType eCoreType) + { + return SqliteHelper.Instance.Table().FirstOrDefault(it => it.coreType == eCoreType); + } + + #endregion Config + + #region Core Type + + public List GetShadowsocksSecuritys(ProfileItem profileItem) + { + if (GetCoreType(profileItem, EConfigType.Shadowsocks) == ECoreType.v2fly) + { + return Global.ssSecuritys; + } + if (GetCoreType(profileItem, EConfigType.Shadowsocks) == ECoreType.Xray) + { + return Global.ssSecuritysInXray; + } + + return Global.ssSecuritysInSagerNet; + } + + public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType) + { + if (profileItem?.coreType != null) + { + return (ECoreType)profileItem.coreType; + } + + if (_config.coreTypeItem == null) + { + return ECoreType.Xray; + } + var item = _config.coreTypeItem.FirstOrDefault(it => it.configType == eConfigType); + if (item == null) + { + return ECoreType.Xray; + } + return item.coreType; + } + + public CoreInfo? GetCoreInfo(ECoreType coreType) + { + if (coreInfos == null) + { + InitCoreInfo(); + } + return coreInfos!.FirstOrDefault(t => t.coreType == coreType); + } + + public List? GetCoreInfos() + { + if (coreInfos == null) + { + InitCoreInfo(); + } + return coreInfos; + } + + private void InitCoreInfo() + { + coreInfos = new(16); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.v2rayN, + coreUrl = Global.NUrl, + coreReleaseApiUrl = Global.NUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.NUrl + "/download/{0}/v2rayN-32.zip", + coreDownloadUrl64 = Global.NUrl + "/download/{0}/v2rayN.zip", + coreDownloadUrlArm64 = Global.NUrl + "/download/{0}/v2rayN-arm64.zip" + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.v2fly, + coreExes = new List { "wv2ray", "v2ray" }, + arguments = "", + coreUrl = Global.v2flyCoreUrl, + coreReleaseApiUrl = Global.v2flyCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + coreDownloadUrl64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + coreDownloadUrlArm64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + match = "V2Ray", + versionArg = "-version", + redirectInfo = true, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.SagerNet, + coreExes = new List { "SagerNet", "v2ray" }, + arguments = "run", + coreUrl = Global.SagerNetCoreUrl, + coreReleaseApiUrl = Global.SagerNetCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + coreDownloadUrl64 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + coreDownloadUrlArm64 = Global.SagerNetCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + match = "V2Ray", + versionArg = "version", + redirectInfo = true, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.v2fly_v5, + coreExes = new List { "v2ray" }, + arguments = "run", + coreUrl = Global.v2flyCoreUrl, + coreReleaseApiUrl = Global.v2flyCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + coreDownloadUrl64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + coreDownloadUrlArm64 = Global.v2flyCoreUrl + "/download/{0}/v2ray-windows-{1}.zip", + match = "V2Ray", + versionArg = "version", + redirectInfo = true, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.Xray, + coreExes = new List { "xray", "wxray" }, + arguments = "", + coreUrl = Global.xrayCoreUrl, + coreReleaseApiUrl = Global.xrayCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip", + coreDownloadUrl64 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip", + coreDownloadUrlArm64 = Global.xrayCoreUrl + "/download/{0}/Xray-windows-{1}.zip", + match = "Xray", + versionArg = "-version", + redirectInfo = true, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.clash, + coreExes = new List { "clash-windows-amd64-v3", "clash-windows-amd64", "clash-windows-386", "clash" }, + arguments = "-f config.json", + coreUrl = Global.clashCoreUrl, + coreReleaseApiUrl = Global.clashCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.clashCoreUrl + "/download/{0}/clash-windows-386-{0}.zip", + coreDownloadUrl64 = Global.clashCoreUrl + "/download/{0}/clash-windows-amd64-{0}.zip", + coreDownloadUrlArm64 = Global.clashCoreUrl + "/download/{0}/clash-windows-arm64-{0}.zip", + match = "v", + versionArg = "-v", + redirectInfo = true, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.clash_meta, + coreExes = new List { "Clash.Meta-windows-amd64-compatible", "Clash.Meta-windows-amd64", "Clash.Meta-windows-386", "Clash.Meta", "clash" }, + arguments = "-f config.json", + coreUrl = Global.clashMetaCoreUrl, + coreReleaseApiUrl = Global.clashMetaCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-386-{0}.zip", + coreDownloadUrl64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-amd64-compatible-{0}.zip", + coreDownloadUrlArm64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-arm64-{0}.zip", + match = "v", + versionArg = "-v", + redirectInfo = true, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.hysteria, + coreExes = new List { "hysteria-windows-amd64", "hysteria-windows-386", "hysteria" }, + arguments = "", + coreUrl = Global.hysteriaCoreUrl, + coreReleaseApiUrl = Global.hysteriaCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-386.exe", + coreDownloadUrl64 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-amd64.exe", + coreDownloadUrlArm64 = Global.hysteriaCoreUrl + "/download/{0}/hysteria-windows-arm64.exe", + redirectInfo = true, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.naiveproxy, + coreExes = new List { "naiveproxy", "naive" }, + arguments = "config.json", + coreUrl = Global.naiveproxyCoreUrl, + redirectInfo = false, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.tuic, + coreExes = new List { "tuic-client", "tuic" }, + arguments = "-c config.json", + coreUrl = Global.tuicCoreUrl, + redirectInfo = true, + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.sing_box, + coreExes = new List { "sing-box-client", "sing-box" }, + arguments = "run{0}", + coreUrl = Global.singboxCoreUrl, + redirectInfo = true, + coreReleaseApiUrl = Global.singboxCoreUrl.Replace(Global.githubUrl, Global.githubApiUrl), + coreDownloadUrl32 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-386.zip", + coreDownloadUrl64 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip", + coreDownloadUrlArm64 = Global.singboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip", + match = "sing-box", + versionArg = "version", + }); + + coreInfos.Add(new CoreInfo + { + coreType = ECoreType.juicity, + coreExes = new List { "juicity-client", "juicity" }, + arguments = "run -c config.json", + coreUrl = Global.juicityCoreUrl + }); + } + + #endregion Core Type } diff --git a/v2rayN/v2rayN/Handler/MainFormHandler.cs b/v2rayN/v2rayN/Handler/MainFormHandler.cs index e3e9ae8f..2db95adf 100644 --- a/v2rayN/v2rayN/Handler/MainFormHandler.cs +++ b/v2rayN/v2rayN/Handler/MainFormHandler.cs @@ -5,224 +5,223 @@ using System.Windows.Media.Imaging; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +public sealed class MainFormHandler { - public sealed class MainFormHandler + private static readonly Lazy instance = new(() => new()); + public static MainFormHandler Instance => instance.Value; + + public Icon GetNotifyIcon(Config config) { - private static readonly Lazy instance = new(() => new()); - public static MainFormHandler Instance => instance.Value; - - public Icon GetNotifyIcon(Config config) + try { - try + int index = (int)config.sysProxyType; + + //Load from routing setting + var createdIcon = GetNotifyIcon4Routing(config); + if (createdIcon != null) { - int index = (int)config.sysProxyType; - - //Load from routing setting - var createdIcon = GetNotifyIcon4Routing(config); - if (createdIcon != null) - { - return createdIcon; - } - - //Load from local file - var fileName = Utils.GetPath($"NotifyIcon{index + 1}.ico"); - if (File.Exists(fileName)) - { - return new Icon(fileName); - } - return index switch - { - 0 => Properties.Resources.NotifyIcon1, - 1 => Properties.Resources.NotifyIcon2, - 2 => Properties.Resources.NotifyIcon3, - 3 => Properties.Resources.NotifyIcon2, - _ => Properties.Resources.NotifyIcon1, // default - }; - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - return Properties.Resources.NotifyIcon1; - } - } - - public System.Windows.Media.ImageSource GetAppIcon(Config config) - { - int index = 1; - switch ((int)config.sysProxyType) - { - case 0: - index = 1; - break; - - case 1: - case 3: - index = 2; - break; - - case 2: - index = 3; - break; - } - return BitmapFrame.Create(new Uri($"pack://application:,,,/Resources/NotifyIcon{index}.ico", UriKind.RelativeOrAbsolute)); - } - - private Icon? GetNotifyIcon4Routing(Config config) - { - try - { - if (!config.routingBasicItem.enableRoutingAdvanced) - { - return null; - } - - var item = ConfigHandler.GetDefaultRouting(ref config); - if (item == null || Utils.IsNullOrEmpty(item.customIcon) || !File.Exists(item.customIcon)) - { - return null; - } - - Color color = ColorTranslator.FromHtml("#3399CC"); - int index = (int)config.sysProxyType; - if (index > 0) - { - color = (new[] { Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.DarkSlateBlue, Color.RoyalBlue })[index - 1]; - } - - int width = 128; - int height = 128; - - Bitmap bitmap = new(width, height); - Graphics graphics = Graphics.FromImage(bitmap); - SolidBrush drawBrush = new(color); - - graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; - //graphics.FillRectangle(drawBrush, new Rectangle(0, 0, width, height)); - graphics.DrawImage(new Bitmap(item.customIcon), 0, 0, width, height); - graphics.FillEllipse(drawBrush, width / 2, width / 2, width / 2, width / 2); - - Icon createdIcon = Icon.FromHandle(bitmap.GetHicon()); - - drawBrush.Dispose(); - graphics.Dispose(); - bitmap.Dispose(); - return createdIcon; } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - return null; - } - } - public void Export2ClientConfig(ProfileItem item, Config config) - { - if (item == null) + //Load from local file + var fileName = Utils.GetPath($"NotifyIcon{index + 1}.ico"); + if (File.Exists(fileName)) { - return; + return new Icon(fileName); } - if (item.configType == EConfigType.Custom) + return index switch { - UI.Show(ResUI.NonVmessService); - return; - } - - SaveFileDialog fileDialog = new() - { - Filter = "Config|*.json", - FilterIndex = 2, - RestoreDirectory = true + 0 => Properties.Resources.NotifyIcon1, + 1 => Properties.Resources.NotifyIcon2, + 2 => Properties.Resources.NotifyIcon3, + 3 => Properties.Resources.NotifyIcon2, + _ => Properties.Resources.NotifyIcon1, // default }; - if (fileDialog.ShowDialog() != true) - { - return; - } - string fileName = fileDialog.FileName; - if (Utils.IsNullOrEmpty(fileName)) - { - return; - } - if (CoreConfigHandler.GenerateClientConfig(item, fileName, out string msg, out string content) != 0) - { - UI.Show(msg); - } - else - { - UI.ShowWarning(string.Format(ResUI.SaveClientConfigurationIn, fileName)); - } } - - public void UpdateTask(Config config, Action update) + catch (Exception ex) { - Task.Run(() => UpdateTaskRunSubscription(config, update)); - Task.Run(() => UpdateTaskRunGeo(config, update)); - } - - private async Task UpdateTaskRunSubscription(Config config, Action update) - { - await Task.Delay(60000); - Utils.SaveLog("UpdateTaskRunSubscription"); - - var updateHandle = new UpdateHandle(); - while (true) - { - var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); - var lstSubs = LazyConfig.Instance.SubItems() - .Where(t => t.autoUpdateInterval > 0) - .Where(t => updateTime - t.updateTime >= t.autoUpdateInterval * 60) - .ToList(); - - foreach (var item in lstSubs) - { - updateHandle.UpdateSubscriptionProcess(config, item.id, true, (bool success, string msg) => - { - update(success, msg); - if (success) - Utils.SaveLog("subscription" + msg); - }); - item.updateTime = updateTime; - ConfigHandler.AddSubItem(ref config, item); - - await Task.Delay(5000); - } - await Task.Delay(60000); - } - } - - private async Task UpdateTaskRunGeo(Config config, Action update) - { - var autoUpdateGeoTime = DateTime.Now; - - await Task.Delay(1000 * 120); - Utils.SaveLog("UpdateTaskRunGeo"); - - var updateHandle = new UpdateHandle(); - while (true) - { - var dtNow = DateTime.Now; - if (config.guiItem.autoUpdateInterval > 0) - { - if ((dtNow - autoUpdateGeoTime).Hours % config.guiItem.autoUpdateInterval == 0) - { - updateHandle.UpdateGeoFileAll(config, (bool success, string msg) => - { - update(false, msg); - }); - autoUpdateGeoTime = dtNow; - } - } - - await Task.Delay(1000 * 3600); - } - } - - public void RegisterGlobalHotkey(Config config, Action handler, Action update) - { - HotkeyHandler.Instance.UpdateViewEvent += update; - HotkeyHandler.Instance.HotkeyTriggerEvent += handler; - HotkeyHandler.Instance.Load(); + Utils.SaveLog(ex.Message, ex); + return Properties.Resources.NotifyIcon1; } } + + public System.Windows.Media.ImageSource GetAppIcon(Config config) + { + int index = 1; + switch ((int)config.sysProxyType) + { + case 0: + index = 1; + break; + + case 1: + case 3: + index = 2; + break; + + case 2: + index = 3; + break; + } + return BitmapFrame.Create(new Uri($"pack://application:,,,/Resources/NotifyIcon{index}.ico", UriKind.RelativeOrAbsolute)); + } + + private Icon? GetNotifyIcon4Routing(Config config) + { + try + { + if (!config.routingBasicItem.enableRoutingAdvanced) + { + return null; + } + + var item = ConfigHandler.GetDefaultRouting(ref config); + if (item == null || Utils.IsNullOrEmpty(item.customIcon) || !File.Exists(item.customIcon)) + { + return null; + } + + Color color = ColorTranslator.FromHtml("#3399CC"); + int index = (int)config.sysProxyType; + if (index > 0) + { + color = (new[] { Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.DarkSlateBlue, Color.RoyalBlue })[index - 1]; + } + + int width = 128; + int height = 128; + + Bitmap bitmap = new(width, height); + Graphics graphics = Graphics.FromImage(bitmap); + SolidBrush drawBrush = new(color); + + graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; + //graphics.FillRectangle(drawBrush, new Rectangle(0, 0, width, height)); + graphics.DrawImage(new Bitmap(item.customIcon), 0, 0, width, height); + graphics.FillEllipse(drawBrush, width / 2, width / 2, width / 2, width / 2); + + Icon createdIcon = Icon.FromHandle(bitmap.GetHicon()); + + drawBrush.Dispose(); + graphics.Dispose(); + bitmap.Dispose(); + + return createdIcon; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + return null; + } + } + + public void Export2ClientConfig(ProfileItem item, Config config) + { + if (item == null) + { + return; + } + if (item.configType == EConfigType.Custom) + { + UI.Show(ResUI.NonVmessService); + return; + } + + SaveFileDialog fileDialog = new() + { + Filter = "Config|*.json", + FilterIndex = 2, + RestoreDirectory = true + }; + if (fileDialog.ShowDialog() != true) + { + return; + } + string fileName = fileDialog.FileName; + if (Utils.IsNullOrEmpty(fileName)) + { + return; + } + if (CoreConfigHandler.GenerateClientConfig(item, fileName, out string msg, out string content) != 0) + { + UI.Show(msg); + } + else + { + UI.ShowWarning(string.Format(ResUI.SaveClientConfigurationIn, fileName)); + } + } + + public void UpdateTask(Config config, Action update) + { + Task.Run(() => UpdateTaskRunSubscription(config, update)); + Task.Run(() => UpdateTaskRunGeo(config, update)); + } + + private async Task UpdateTaskRunSubscription(Config config, Action update) + { + await Task.Delay(60000); + Utils.SaveLog("UpdateTaskRunSubscription"); + + var updateHandle = new UpdateHandle(); + while (true) + { + var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); + var lstSubs = LazyConfig.Instance.SubItems() + .Where(t => t.autoUpdateInterval > 0) + .Where(t => updateTime - t.updateTime >= t.autoUpdateInterval * 60) + .ToList(); + + foreach (var item in lstSubs) + { + updateHandle.UpdateSubscriptionProcess(config, item.id, true, (bool success, string msg) => + { + update(success, msg); + if (success) + Utils.SaveLog("subscription" + msg); + }); + item.updateTime = updateTime; + ConfigHandler.AddSubItem(ref config, item); + + await Task.Delay(5000); + } + await Task.Delay(60000); + } + } + + private async Task UpdateTaskRunGeo(Config config, Action update) + { + var autoUpdateGeoTime = DateTime.Now; + + await Task.Delay(1000 * 120); + Utils.SaveLog("UpdateTaskRunGeo"); + + var updateHandle = new UpdateHandle(); + while (true) + { + var dtNow = DateTime.Now; + if (config.guiItem.autoUpdateInterval > 0) + { + if ((dtNow - autoUpdateGeoTime).Hours % config.guiItem.autoUpdateInterval == 0) + { + updateHandle.UpdateGeoFileAll(config, (bool success, string msg) => + { + update(false, msg); + }); + autoUpdateGeoTime = dtNow; + } + } + + await Task.Delay(1000 * 3600); + } + } + + public void RegisterGlobalHotkey(Config config, Action handler, Action update) + { + HotkeyHandler.Instance.UpdateViewEvent += update; + HotkeyHandler.Instance.HotkeyTriggerEvent += handler; + HotkeyHandler.Instance.Load(); + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/NoticeHandler.cs b/v2rayN/v2rayN/Handler/NoticeHandler.cs index f74f8e65..b688e0b7 100644 --- a/v2rayN/v2rayN/Handler/NoticeHandler.cs +++ b/v2rayN/v2rayN/Handler/NoticeHandler.cs @@ -1,33 +1,32 @@ using MaterialDesignThemes.Wpf; using ReactiveUI; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +public class NoticeHandler { - public class NoticeHandler + private readonly ISnackbarMessageQueue _snackbarMessageQueue; + + public NoticeHandler(ISnackbarMessageQueue snackbarMessageQueue) { - private readonly ISnackbarMessageQueue _snackbarMessageQueue; + _snackbarMessageQueue = snackbarMessageQueue ?? throw new ArgumentNullException(nameof(snackbarMessageQueue)); - public NoticeHandler(ISnackbarMessageQueue snackbarMessageQueue) - { - _snackbarMessageQueue = snackbarMessageQueue ?? throw new ArgumentNullException(nameof(snackbarMessageQueue)); + //_snackbarMessageQueue = snackbarMessageQueue; + } - //_snackbarMessageQueue = snackbarMessageQueue; - } + public void Enqueue(object content) + { + _snackbarMessageQueue?.Enqueue(content); + } - public void Enqueue(object content) - { - _snackbarMessageQueue?.Enqueue(content); - } + public void SendMessage(string msg) + { + MessageBus.Current.SendMessage(msg, "MsgView"); + } - public void SendMessage(string msg) - { - MessageBus.Current.SendMessage(msg, "MsgView"); - } - - public void SendMessage(string msg, bool time) - { - msg = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} {msg}"; - MessageBus.Current.SendMessage(msg, "MsgView"); - } + public void SendMessage(string msg, bool time) + { + msg = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} {msg}"; + MessageBus.Current.SendMessage(msg, "MsgView"); } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/ProfileExHandler.cs b/v2rayN/v2rayN/Handler/ProfileExHandler.cs index 421b2c7f..fc9d5262 100644 --- a/v2rayN/v2rayN/Handler/ProfileExHandler.cs +++ b/v2rayN/v2rayN/Handler/ProfileExHandler.cs @@ -3,143 +3,142 @@ using System.Reactive.Linq; using v2rayN.Base; using v2rayN.Mode; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class ProfileExHandler { - internal class ProfileExHandler + private static readonly Lazy _instance = new(() => new()); + private ConcurrentBag _lstProfileEx; + private Queue _queIndexIds = new(); + public ConcurrentBag ProfileExs => _lstProfileEx; + public static ProfileExHandler Instance => _instance.Value; + + public ProfileExHandler() { - private static readonly Lazy _instance = new(() => new()); - private ConcurrentBag _lstProfileEx; - private Queue _queIndexIds = new(); - public ConcurrentBag ProfileExs => _lstProfileEx; - public static ProfileExHandler Instance => _instance.Value; + Init(); + } - public ProfileExHandler() + private void Init() + { + SqliteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )"); + + _lstProfileEx = new(SqliteHelper.Instance.Table()); + + Task.Run(async () => { - Init(); - } - - private void Init() - { - SqliteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )"); - - _lstProfileEx = new(SqliteHelper.Instance.Table()); - - Task.Run(async () => + while (true) { - while (true) + var cnt = _queIndexIds.Count; + for (int i = 0; i < cnt; i++) { - var cnt = _queIndexIds.Count; - for (int i = 0; i < cnt; i++) + var id = _queIndexIds.Dequeue(); + var item = _lstProfileEx.FirstOrDefault(t => t.indexId == id); + if (item is not null) { - var id = _queIndexIds.Dequeue(); - var item = _lstProfileEx.FirstOrDefault(t => t.indexId == id); - if (item is not null) - { - SqliteHelper.Instance.Replace(item); - } + SqliteHelper.Instance.Replace(item); } - await Task.Delay(1000 * 60); } - }); - } - - private void IndexIdEnqueue(string indexId) - { - if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId)) - { - _queIndexIds.Enqueue(indexId); + await Task.Delay(1000 * 60); } - } + }); + } - private void AddProfileEx(string indexId, ref ProfileExItem profileEx) + private void IndexIdEnqueue(string indexId) + { + if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId)) { - profileEx = new() - { - indexId = indexId, - delay = 0, - speed = 0, - sort = 0 - }; - _lstProfileEx.Add(profileEx); - IndexIdEnqueue(indexId); - } - - public void ClearAll() - { - SqliteHelper.Instance.Execute($"delete from ProfileExItem "); - _lstProfileEx = new(); - } - - public void SaveTo() - { - try - { - //foreach (var item in _lstProfileEx) - //{ - // SqliteHelper.Instance.Replace(item); - //} - SqliteHelper.Instance.UpdateAll(_lstProfileEx); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - } - - public void SetTestDelay(string indexId, string delayVal) - { - var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId); - if (profileEx == null) - { - AddProfileEx(indexId, ref profileEx); - } - - int.TryParse(delayVal, out int delay); - profileEx.delay = delay; - IndexIdEnqueue(indexId); - } - - public void SetTestSpeed(string indexId, string speedVal) - { - var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId); - if (profileEx == null) - { - AddProfileEx(indexId, ref profileEx); - } - - decimal.TryParse(speedVal, out decimal speed); - profileEx.speed = speed; - IndexIdEnqueue(indexId); - } - - public void SetSort(string indexId, int sort) - { - var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId); - if (profileEx == null) - { - AddProfileEx(indexId, ref profileEx); - } - profileEx.sort = sort; - IndexIdEnqueue(indexId); - } - - public int GetSort(string indexId) - { - var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId); - if (profileEx == null) - { - return 0; - } - return profileEx.sort; - } - - public int GetMaxSort() - { - if (_lstProfileEx.Count <= 0) - { - return 0; - } - return _lstProfileEx.Max(t => t == null ? 0 : t.sort); + _queIndexIds.Enqueue(indexId); } } + + private void AddProfileEx(string indexId, ref ProfileExItem profileEx) + { + profileEx = new() + { + indexId = indexId, + delay = 0, + speed = 0, + sort = 0 + }; + _lstProfileEx.Add(profileEx); + IndexIdEnqueue(indexId); + } + + public void ClearAll() + { + SqliteHelper.Instance.Execute($"delete from ProfileExItem "); + _lstProfileEx = new(); + } + + public void SaveTo() + { + try + { + //foreach (var item in _lstProfileEx) + //{ + // SqliteHelper.Instance.Replace(item); + //} + SqliteHelper.Instance.UpdateAll(_lstProfileEx); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + } + + public void SetTestDelay(string indexId, string delayVal) + { + var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId); + if (profileEx == null) + { + AddProfileEx(indexId, ref profileEx); + } + + int.TryParse(delayVal, out int delay); + profileEx.delay = delay; + IndexIdEnqueue(indexId); + } + + public void SetTestSpeed(string indexId, string speedVal) + { + var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId); + if (profileEx == null) + { + AddProfileEx(indexId, ref profileEx); + } + + decimal.TryParse(speedVal, out decimal speed); + profileEx.speed = speed; + IndexIdEnqueue(indexId); + } + + public void SetSort(string indexId, int sort) + { + var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId); + if (profileEx == null) + { + AddProfileEx(indexId, ref profileEx); + } + profileEx.sort = sort; + IndexIdEnqueue(indexId); + } + + public int GetSort(string indexId) + { + var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId); + if (profileEx == null) + { + return 0; + } + return profileEx.sort; + } + + public int GetMaxSort() + { + if (_lstProfileEx.Count <= 0) + { + return 0; + } + return _lstProfileEx.Max(t => t == null ? 0 : t.sort); + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/ProxySetting.cs b/v2rayN/v2rayN/Handler/ProxySetting.cs index 67541e15..08918db0 100644 --- a/v2rayN/v2rayN/Handler/ProxySetting.cs +++ b/v2rayN/v2rayN/Handler/ProxySetting.cs @@ -1,216 +1,215 @@ using Microsoft.Win32; using System.Runtime.InteropServices; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class ProxySetting { - internal class ProxySetting + public static bool UnsetProxy() { - public static bool UnsetProxy() + return SetProxy(null, null, 1); + } + + public static bool SetProxy(string? strProxy, string? exceptions, int type) + { + InternetPerConnOptionList list = new(); + + int optionCount = 1; + if (type == 1) { - return SetProxy(null, null, 1); + optionCount = 1; + } + else if (type is 2 or 4) + { + optionCount = Utils.IsNullOrEmpty(exceptions) ? 2 : 3; } - public static bool SetProxy(string? strProxy, string? exceptions, int type) + int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT; + PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS; + if (type == 2) { - InternetPerConnOptionList list = new(); - - int optionCount = 1; - if (type == 1) - { - optionCount = 1; - } - else if (type is 2 or 4) - { - optionCount = Utils.IsNullOrEmpty(exceptions) ? 2 : 3; - } - - int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT; - PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS; - if (type == 2) - { - m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY); - m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER; - } - else if (type == 4) - { - m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL); - m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL; - } - - //int optionCount = Utils.IsNullOrEmpty(strProxy) ? 1 : (Utils.IsNullOrEmpty(exceptions) ? 2 : 3); - InternetConnectionOption[] options = new InternetConnectionOption[optionCount]; - // USE a proxy server ... - options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS; - //options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY)); - options[0].m_Value.m_Int = m_Int; - // use THIS proxy server - if (optionCount > 1) - { - options[1].m_Option = m_Option; - options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy); - // except for these addresses ... - if (optionCount > 2) - { - options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS; - options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions); - } - } - - // default stuff - list.dwSize = Marshal.SizeOf(list); - list.szConnection = IntPtr.Zero; - list.dwOptionCount = options.Length; - list.dwOptionError = 0; - - int optSize = Marshal.SizeOf(typeof(InternetConnectionOption)); - // make a pointer out of all that ... - IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); - // copy the array over into that spot in memory ... - for (int i = 0; i < options.Length; ++i) - { - if (Environment.Is64BitOperatingSystem) - { - IntPtr opt = new(optionsPtr.ToInt64() + (i * optSize)); - Marshal.StructureToPtr(options[i], opt, false); - } - else - { - IntPtr opt = new(optionsPtr.ToInt32() + (i * optSize)); - Marshal.StructureToPtr(options[i], opt, false); - } - } - - list.options = optionsPtr; - - // and then make a pointer out of the whole list - IntPtr ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); - Marshal.StructureToPtr(list, ipcoListPtr, false); - - // and finally, call the API method! - int returnvalue = NativeMethods.InternetSetOption(IntPtr.Zero, - InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION, - ipcoListPtr, list.dwSize) ? -1 : 0; - if (returnvalue == 0) - { // get the error codes, they might be helpful - returnvalue = Marshal.GetLastWin32Error(); - } - // FREE the data ASAP - Marshal.FreeCoTaskMem(optionsPtr); - Marshal.FreeCoTaskMem(ipcoListPtr); - if (returnvalue > 0) - { // throw the error codes, they might be helpful - //throw new Win32Exception(Marshal.GetLastWin32Error()); - } - - return (returnvalue < 0); + m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY); + m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER; + } + else if (type == 4) + { + m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL); + m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL; } - #region WinInet structures - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public struct InternetPerConnOptionList + //int optionCount = Utils.IsNullOrEmpty(strProxy) ? 1 : (Utils.IsNullOrEmpty(exceptions) ? 2 : 3); + InternetConnectionOption[] options = new InternetConnectionOption[optionCount]; + // USE a proxy server ... + options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS; + //options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY)); + options[0].m_Value.m_Int = m_Int; + // use THIS proxy server + if (optionCount > 1) { - public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct - public IntPtr szConnection; // connection name to set/query options - public int dwOptionCount; // number of options to set/query - public int dwOptionError; // on error, which option failed - - //[MarshalAs(UnmanagedType.)] - public IntPtr options; - }; - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public struct InternetConnectionOption - { - private static readonly int Size; - public PerConnOption m_Option; - public InternetConnectionOptionValue m_Value; - - static InternetConnectionOption() + options[1].m_Option = m_Option; + options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy); + // except for these addresses ... + if (optionCount > 2) { - Size = Marshal.SizeOf(typeof(InternetConnectionOption)); - } - - // Nested Types - [StructLayout(LayoutKind.Explicit)] - public struct InternetConnectionOptionValue - { - // Fields - [FieldOffset(0)] - public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime; - - [FieldOffset(0)] - public int m_Int; - - [FieldOffset(0)] - public IntPtr m_StringPtr; + options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS; + options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions); } } - #endregion WinInet structures + // default stuff + list.dwSize = Marshal.SizeOf(list); + list.szConnection = IntPtr.Zero; + list.dwOptionCount = options.Length; + list.dwOptionError = 0; - #region WinInet enums - - // - // options manifests for Internet{Query|Set}Option - // - public enum InternetOption : uint + int optSize = Marshal.SizeOf(typeof(InternetConnectionOption)); + // make a pointer out of all that ... + IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); + // copy the array over into that spot in memory ... + for (int i = 0; i < options.Length; ++i) { - INTERNET_OPTION_PER_CONNECTION_OPTION = 75 - } - - // - // Options used in INTERNET_PER_CONN_OPTON struct - // - public enum PerConnOption - { - INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags - INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers. - INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server. - INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script. - } - - // - // PER_CONN_FLAGS - // - [Flags] - public enum PerConnFlags - { - PROXY_TYPE_DIRECT = 0x00000001, // direct to net - PROXY_TYPE_PROXY = 0x00000002, // via named proxy - PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL - PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection - } - - #endregion WinInet enums - - internal static class NativeMethods - { - [DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength); - } - - //判断是否使用代理 - public static bool UsedProxy() - { - using RegistryKey? rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true); - if (rk?.GetValue("ProxyEnable")?.ToString() == "1") + if (Environment.Is64BitOperatingSystem) { - return true; + IntPtr opt = new(optionsPtr.ToInt64() + (i * optSize)); + Marshal.StructureToPtr(options[i], opt, false); } else { - return false; + IntPtr opt = new(optionsPtr.ToInt32() + (i * optSize)); + Marshal.StructureToPtr(options[i], opt, false); } } - //获得代理的IP和端口 - public static string? GetProxyProxyServer() + list.options = optionsPtr; + + // and then make a pointer out of the whole list + IntPtr ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); + Marshal.StructureToPtr(list, ipcoListPtr, false); + + // and finally, call the API method! + int returnvalue = NativeMethods.InternetSetOption(IntPtr.Zero, + InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION, + ipcoListPtr, list.dwSize) ? -1 : 0; + if (returnvalue == 0) + { // get the error codes, they might be helpful + returnvalue = Marshal.GetLastWin32Error(); + } + // FREE the data ASAP + Marshal.FreeCoTaskMem(optionsPtr); + Marshal.FreeCoTaskMem(ipcoListPtr); + if (returnvalue > 0) + { // throw the error codes, they might be helpful + //throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + return (returnvalue < 0); + } + + #region WinInet structures + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct InternetPerConnOptionList + { + public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct + public IntPtr szConnection; // connection name to set/query options + public int dwOptionCount; // number of options to set/query + public int dwOptionError; // on error, which option failed + + //[MarshalAs(UnmanagedType.)] + public IntPtr options; + }; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct InternetConnectionOption + { + private static readonly int Size; + public PerConnOption m_Option; + public InternetConnectionOptionValue m_Value; + + static InternetConnectionOption() { - using RegistryKey? rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true); - string ProxyServer = rk.GetValue("ProxyServer").ToString(); - return ProxyServer; + Size = Marshal.SizeOf(typeof(InternetConnectionOption)); + } + + // Nested Types + [StructLayout(LayoutKind.Explicit)] + public struct InternetConnectionOptionValue + { + // Fields + [FieldOffset(0)] + public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime; + + [FieldOffset(0)] + public int m_Int; + + [FieldOffset(0)] + public IntPtr m_StringPtr; } } + + #endregion WinInet structures + + #region WinInet enums + + // + // options manifests for Internet{Query|Set}Option + // + public enum InternetOption : uint + { + INTERNET_OPTION_PER_CONNECTION_OPTION = 75 + } + + // + // Options used in INTERNET_PER_CONN_OPTON struct + // + public enum PerConnOption + { + INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags + INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers. + INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server. + INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script. + } + + // + // PER_CONN_FLAGS + // + [Flags] + public enum PerConnFlags + { + PROXY_TYPE_DIRECT = 0x00000001, // direct to net + PROXY_TYPE_PROXY = 0x00000002, // via named proxy + PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL + PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection + } + + #endregion WinInet enums + + internal static class NativeMethods + { + [DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength); + } + + //判断是否使用代理 + public static bool UsedProxy() + { + using RegistryKey? rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true); + if (rk?.GetValue("ProxyEnable")?.ToString() == "1") + { + return true; + } + else + { + return false; + } + } + + //获得代理的IP和端口 + public static string? GetProxyProxyServer() + { + using RegistryKey? rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true); + string ProxyServer = rk.GetValue("ProxyServer").ToString(); + return ProxyServer; + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/QRCodeHelper.cs b/v2rayN/v2rayN/Handler/QRCodeHelper.cs index 07af8856..05a49362 100644 --- a/v2rayN/v2rayN/Handler/QRCodeHelper.cs +++ b/v2rayN/v2rayN/Handler/QRCodeHelper.cs @@ -2,27 +2,22 @@ using QRCoder.Xaml; using System.Windows.Media; -namespace v2rayN.Handler +namespace v2rayN.Handler; +public class QRCodeHelper { - /// - /// 含有QR码的描述类和包装编码和渲染 - /// - public class QRCodeHelper + public static DrawingImage? GetQRCode(string strContent) { - public static DrawingImage? GetQRCode(string strContent) + try { - try - { - QRCodeGenerator qrGenerator = new(); - QRCodeData qrCodeData = qrGenerator.CreateQrCode(strContent, QRCodeGenerator.ECCLevel.H); - XamlQRCode qrCode = new(qrCodeData); - DrawingImage qrCodeAsXaml = qrCode.GetGraphic(40); - return qrCodeAsXaml; - } - catch - { - return null; - } + QRCodeGenerator qrGenerator = new(); + QRCodeData qrCodeData = qrGenerator.CreateQrCode(strContent, QRCodeGenerator.ECCLevel.H); + XamlQRCode qrCode = new(qrCodeData); + DrawingImage qrCodeAsXaml = qrCode.GetGraphic(40); + return qrCodeAsXaml; + } + catch + { + return null; } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/ShareHandler.cs b/v2rayN/v2rayN/Handler/ShareHandler.cs index 7186b17f..5ee52a02 100644 --- a/v2rayN/v2rayN/Handler/ShareHandler.cs +++ b/v2rayN/v2rayN/Handler/ShareHandler.cs @@ -5,813 +5,812 @@ using v2rayN.Base; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class ShareHandler { - internal class ShareHandler + #region GetShareUrl + + /// + /// GetShareUrl + /// + /// + /// + public static string? GetShareUrl(ProfileItem item) { - #region GetShareUrl - - /// - /// GetShareUrl - /// - /// - /// - public static string? GetShareUrl(ProfileItem item) + try { - try + string? url = string.Empty; + + url = item.configType switch { - string? url = string.Empty; - - url = item.configType switch - { - EConfigType.VMess => ShareVmess(item), - EConfigType.Shadowsocks => ShareShadowsocks(item), - EConfigType.Socks => ShareSocks(item), - EConfigType.Trojan => ShareTrojan(item), - EConfigType.VLESS => ShareVLESS(item), - _ => null, - }; - - return url; - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - return ""; - } - } - - private static string ShareVmess(ProfileItem item) - { - string url = string.Empty; - - VmessQRCode vmessQRCode = new() - { - v = item.configVersion.ToString(), - ps = item.remarks.TrimEx(), - add = item.address, - port = item.port.ToString(), - id = item.id, - aid = item.alterId.ToString(), - scy = item.security, - net = item.network, - type = item.headerType, - host = item.requestHost, - path = item.path, - tls = item.streamSecurity, - sni = item.sni, - alpn = item.alpn, - fp = item.fingerprint + EConfigType.VMess => ShareVmess(item), + EConfigType.Shadowsocks => ShareShadowsocks(item), + EConfigType.Socks => ShareSocks(item), + EConfigType.Trojan => ShareTrojan(item), + EConfigType.VLESS => ShareVLESS(item), + _ => null, }; - url = Utils.ToJson(vmessQRCode); - url = Utils.Base64Encode(url); - url = $"{Global.vmessProtocol}{url}"; - return url; } - - private static string ShareShadowsocks(ProfileItem item) + catch (Exception ex) { - string url = string.Empty; + Utils.SaveLog(ex.Message, ex); + return ""; + } + } - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - //url = string.Format("{0}:{1}@{2}:{3}", - // item.security, - // item.id, - // item.address, - // item.port); - //url = Utils.Base64Encode(url); - //new Sip002 - var pw = Utils.Base64Encode($"{item.security}:{item.id}"); - url = $"{pw}@{GetIpv6(item.address)}:{item.port}"; - url = $"{Global.ssProtocol}{url}{remark}"; - return url; + private static string ShareVmess(ProfileItem item) + { + string url = string.Empty; + + VmessQRCode vmessQRCode = new() + { + v = item.configVersion.ToString(), + ps = item.remarks.TrimEx(), + add = item.address, + port = item.port.ToString(), + id = item.id, + aid = item.alterId.ToString(), + scy = item.security, + net = item.network, + type = item.headerType, + host = item.requestHost, + path = item.path, + tls = item.streamSecurity, + sni = item.sni, + alpn = item.alpn, + fp = item.fingerprint + }; + + url = Utils.ToJson(vmessQRCode); + url = Utils.Base64Encode(url); + url = $"{Global.vmessProtocol}{url}"; + + return url; + } + + private static string ShareShadowsocks(ProfileItem item) + { + string url = string.Empty; + + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + //url = string.Format("{0}:{1}@{2}:{3}", + // item.security, + // item.id, + // item.address, + // item.port); + //url = Utils.Base64Encode(url); + //new Sip002 + var pw = Utils.Base64Encode($"{item.security}:{item.id}"); + url = $"{pw}@{GetIpv6(item.address)}:{item.port}"; + url = $"{Global.ssProtocol}{url}{remark}"; + return url; + } + + private static string ShareSocks(ProfileItem item) + { + string url = string.Empty; + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + //url = string.Format("{0}:{1}@{2}:{3}", + // item.security, + // item.id, + // item.address, + // item.port); + //url = Utils.Base64Encode(url); + //new + var pw = Utils.Base64Encode($"{item.security}:{item.id}"); + url = $"{pw}@{GetIpv6(item.address)}:{item.port}"; + url = $"{Global.socksProtocol}{url}{remark}"; + return url; + } + + private static string ShareTrojan(ProfileItem item) + { + string url = string.Empty; + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + var dicQuery = new Dictionary(); + GetStdTransport(item, null, ref dicQuery); + string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); + + url = string.Format("{0}@{1}:{2}", + item.id, + GetIpv6(item.address), + item.port); + url = $"{Global.trojanProtocol}{url}{query}{remark}"; + return url; + } + + private static string ShareVLESS(ProfileItem item) + { + string url = string.Empty; + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + var dicQuery = new Dictionary(); + if (!Utils.IsNullOrEmpty(item.security)) + { + dicQuery.Add("encryption", item.security); + } + else + { + dicQuery.Add("encryption", "none"); + } + GetStdTransport(item, "none", ref dicQuery); + string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); + + url = string.Format("{0}@{1}:{2}", + item.id, + GetIpv6(item.address), + item.port); + url = $"{Global.vlessProtocol}{url}{query}{remark}"; + return url; + } + + private static string GetIpv6(string address) + { + return Utils.IsIpv6(address) ? $"[{address}]" : address; + } + + private static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary dicQuery) + { + if (!Utils.IsNullOrEmpty(item.flow)) + { + dicQuery.Add("flow", item.flow); } - private static string ShareSocks(ProfileItem item) + if (!Utils.IsNullOrEmpty(item.streamSecurity)) { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) + dicQuery.Add("security", item.streamSecurity); + } + else + { + if (securityDef != null) { - remark = "#" + Utils.UrlEncode(item.remarks); + dicQuery.Add("security", securityDef); } - //url = string.Format("{0}:{1}@{2}:{3}", - // item.security, - // item.id, - // item.address, - // item.port); - //url = Utils.Base64Encode(url); - //new - var pw = Utils.Base64Encode($"{item.security}:{item.id}"); - url = $"{pw}@{GetIpv6(item.address)}:{item.port}"; - url = $"{Global.socksProtocol}{url}{remark}"; - return url; + } + if (!Utils.IsNullOrEmpty(item.sni)) + { + dicQuery.Add("sni", item.sni); + } + if (!Utils.IsNullOrEmpty(item.alpn)) + { + dicQuery.Add("alpn", Utils.UrlEncode(item.alpn)); + } + if (!Utils.IsNullOrEmpty(item.fingerprint)) + { + dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint)); + } + if (!Utils.IsNullOrEmpty(item.publicKey)) + { + dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey)); + } + if (!Utils.IsNullOrEmpty(item.shortId)) + { + dicQuery.Add("sid", Utils.UrlEncode(item.shortId)); + } + if (!Utils.IsNullOrEmpty(item.spiderX)) + { + dicQuery.Add("spx", Utils.UrlEncode(item.spiderX)); } - private static string ShareTrojan(ProfileItem item) - { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - var dicQuery = new Dictionary(); - GetStdTransport(item, null, ref dicQuery); - string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); + dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : "tcp"); - url = string.Format("{0}@{1}:{2}", - item.id, - GetIpv6(item.address), - item.port); - url = $"{Global.trojanProtocol}{url}{query}{remark}"; - return url; + switch (item.network) + { + case "tcp": + dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : "none"); + if (!Utils.IsNullOrEmpty(item.requestHost)) + { + dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); + } + break; + + case "kcp": + dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : "none"); + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("seed", Utils.UrlEncode(item.path)); + } + break; + + case "ws": + if (!Utils.IsNullOrEmpty(item.requestHost)) + { + dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); + } + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("path", Utils.UrlEncode(item.path)); + } + break; + + case "http": + case "h2": + dicQuery["type"] = "http"; + if (!Utils.IsNullOrEmpty(item.requestHost)) + { + dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); + } + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("path", Utils.UrlEncode(item.path)); + } + break; + + case "quic": + dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : "none"); + dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost)); + dicQuery.Add("key", Utils.UrlEncode(item.path)); + break; + + case "grpc": + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("serviceName", Utils.UrlEncode(item.path)); + if (item.headerType is Global.GrpcgunMode or Global.GrpcmultiMode) + { + dicQuery.Add("mode", Utils.UrlEncode(item.headerType)); + } + } + break; } + return 0; + } - private static string ShareVLESS(ProfileItem item) + #endregion GetShareUrl + + #region ImportShareUrl + + /// + /// 从剪贴板导入URL + /// + /// + /// + public static ProfileItem? ImportFromClipboardConfig(string clipboardData, out string msg) + { + msg = string.Empty; + + ProfileItem profileItem = new(); + + try { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) + //载入配置文件 + string result = clipboardData.TrimEx();// Utils.GetClipboardData(); + if (Utils.IsNullOrEmpty(result)) { - remark = "#" + Utils.UrlEncode(item.remarks); - } - var dicQuery = new Dictionary(); - if (!Utils.IsNullOrEmpty(item.security)) - { - dicQuery.Add("encryption", item.security); - } - else - { - dicQuery.Add("encryption", "none"); - } - GetStdTransport(item, "none", ref dicQuery); - string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); - - url = string.Format("{0}@{1}:{2}", - item.id, - GetIpv6(item.address), - item.port); - url = $"{Global.vlessProtocol}{url}{query}{remark}"; - return url; - } - - private static string GetIpv6(string address) - { - return Utils.IsIpv6(address) ? $"[{address}]" : address; - } - - private static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary dicQuery) - { - if (!Utils.IsNullOrEmpty(item.flow)) - { - dicQuery.Add("flow", item.flow); + msg = ResUI.FailedReadConfiguration; + return null; } - if (!Utils.IsNullOrEmpty(item.streamSecurity)) + if (result.StartsWith(Global.vmessProtocol)) { - dicQuery.Add("security", item.streamSecurity); - } - else - { - if (securityDef != null) + int indexSplit = result.IndexOf("?"); + if (indexSplit > 0) { - dicQuery.Add("security", securityDef); - } - } - if (!Utils.IsNullOrEmpty(item.sni)) - { - dicQuery.Add("sni", item.sni); - } - if (!Utils.IsNullOrEmpty(item.alpn)) - { - dicQuery.Add("alpn", Utils.UrlEncode(item.alpn)); - } - if (!Utils.IsNullOrEmpty(item.fingerprint)) - { - dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint)); - } - if (!Utils.IsNullOrEmpty(item.publicKey)) - { - dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey)); - } - if (!Utils.IsNullOrEmpty(item.shortId)) - { - dicQuery.Add("sid", Utils.UrlEncode(item.shortId)); - } - if (!Utils.IsNullOrEmpty(item.spiderX)) - { - dicQuery.Add("spx", Utils.UrlEncode(item.spiderX)); - } - - dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : "tcp"); - - switch (item.network) - { - case "tcp": - dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : "none"); - if (!Utils.IsNullOrEmpty(item.requestHost)) - { - dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); - } - break; - - case "kcp": - dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : "none"); - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("seed", Utils.UrlEncode(item.path)); - } - break; - - case "ws": - if (!Utils.IsNullOrEmpty(item.requestHost)) - { - dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); - } - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("path", Utils.UrlEncode(item.path)); - } - break; - - case "http": - case "h2": - dicQuery["type"] = "http"; - if (!Utils.IsNullOrEmpty(item.requestHost)) - { - dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); - } - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("path", Utils.UrlEncode(item.path)); - } - break; - - case "quic": - dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : "none"); - dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost)); - dicQuery.Add("key", Utils.UrlEncode(item.path)); - break; - - case "grpc": - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("serviceName", Utils.UrlEncode(item.path)); - if (item.headerType is Global.GrpcgunMode or Global.GrpcmultiMode) - { - dicQuery.Add("mode", Utils.UrlEncode(item.headerType)); - } - } - break; - } - return 0; - } - - #endregion GetShareUrl - - #region ImportShareUrl - - /// - /// 从剪贴板导入URL - /// - /// - /// - public static ProfileItem? ImportFromClipboardConfig(string clipboardData, out string msg) - { - msg = string.Empty; - - ProfileItem profileItem = new(); - - try - { - //载入配置文件 - string result = clipboardData.TrimEx();// Utils.GetClipboardData(); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedReadConfiguration; - return null; - } - - if (result.StartsWith(Global.vmessProtocol)) - { - int indexSplit = result.IndexOf("?"); - if (indexSplit > 0) - { - profileItem = ResolveStdVmess(result) ?? ResolveVmess4Kitsunebi(result); - } - else - { - profileItem = ResolveVmess(result, out msg); - } - } - else if (result.StartsWith(Global.ssProtocol)) - { - msg = ResUI.ConfigurationFormatIncorrect; - - profileItem = ResolveSSLegacy(result) ?? ResolveSip002(result); - if (profileItem == null) - { - return null; - } - if (profileItem.address.Length == 0 || profileItem.port == 0 || profileItem.security.Length == 0 || profileItem.id.Length == 0) - { - return null; - } - - profileItem.configType = EConfigType.Shadowsocks; - } - else if (result.StartsWith(Global.socksProtocol)) - { - msg = ResUI.ConfigurationFormatIncorrect; - - profileItem = ResolveSocksNew(result) ?? ResolveSocks(result); - if (profileItem == null) - { - return null; - } - if (profileItem.address.Length == 0 || profileItem.port == 0) - { - return null; - } - - profileItem.configType = EConfigType.Socks; - } - else if (result.StartsWith(Global.trojanProtocol)) - { - msg = ResUI.ConfigurationFormatIncorrect; - - profileItem = ResolveTrojan(result); - } - else if (result.StartsWith(Global.vlessProtocol)) - { - profileItem = ResolveStdVLESS(result); + profileItem = ResolveStdVmess(result) ?? ResolveVmess4Kitsunebi(result); } else { - msg = ResUI.NonvmessOrssProtocol; - return null; + profileItem = ResolveVmess(result, out msg); } } - catch (Exception ex) + else if (result.StartsWith(Global.ssProtocol)) { - Utils.SaveLog(ex.Message, ex); - msg = ResUI.Incorrectconfiguration; - return null; - } + msg = ResUI.ConfigurationFormatIncorrect; - return profileItem; - } - - private static ProfileItem? ResolveVmess(string result, out string msg) - { - msg = string.Empty; - var profileItem = new ProfileItem - { - configType = EConfigType.VMess - }; - - result = result[Global.vmessProtocol.Length..]; - result = Utils.Base64Decode(result); - - //转成Json - VmessQRCode? vmessQRCode = Utils.FromJson(result); - if (vmessQRCode == null) - { - msg = ResUI.FailedConversionConfiguration; - return null; - } - - profileItem.network = Global.DefaultNetwork; - profileItem.headerType = Global.None; - - profileItem.configVersion = Utils.ToInt(vmessQRCode.v); - profileItem.remarks = Utils.ToString(vmessQRCode.ps); - profileItem.address = Utils.ToString(vmessQRCode.add); - profileItem.port = Utils.ToInt(vmessQRCode.port); - profileItem.id = Utils.ToString(vmessQRCode.id); - profileItem.alterId = Utils.ToInt(vmessQRCode.aid); - profileItem.security = Utils.ToString(vmessQRCode.scy); - - profileItem.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity; - if (!Utils.IsNullOrEmpty(vmessQRCode.net)) - { - profileItem.network = vmessQRCode.net; - } - if (!Utils.IsNullOrEmpty(vmessQRCode.type)) - { - profileItem.headerType = vmessQRCode.type; - } - - profileItem.requestHost = Utils.ToString(vmessQRCode.host); - profileItem.path = Utils.ToString(vmessQRCode.path); - profileItem.streamSecurity = Utils.ToString(vmessQRCode.tls); - profileItem.sni = Utils.ToString(vmessQRCode.sni); - profileItem.alpn = Utils.ToString(vmessQRCode.alpn); - profileItem.fingerprint = Utils.ToString(vmessQRCode.fp); - - return profileItem; - } - - private static ProfileItem? ResolveVmess4Kitsunebi(string result) - { - ProfileItem profileItem = new() - { - configType = EConfigType.VMess - }; - result = result[Global.vmessProtocol.Length..]; - int indexSplit = result.IndexOf("?"); - if (indexSplit > 0) - { - result = result[..indexSplit]; - } - result = Utils.Base64Decode(result); - - string[] arr1 = result.Split('@'); - if (arr1.Length != 2) - { - return null; - } - string[] arr21 = arr1[0].Split(':'); - string[] arr22 = arr1[1].Split(':'); - if (arr21.Length != 2 || arr22.Length != 2) - { - return null; - } - - profileItem.address = arr22[0]; - profileItem.port = Utils.ToInt(arr22[1]); - profileItem.security = arr21[0]; - profileItem.id = arr21[1]; - - profileItem.network = Global.DefaultNetwork; - profileItem.headerType = Global.None; - profileItem.remarks = "Alien"; - - return profileItem; - } - - private static ProfileItem? ResolveStdVmess(string result) - { - ProfileItem i = new() - { - configType = EConfigType.VMess, - security = "auto" - }; - - Uri u = new(result); - - i.address = u.IdnHost; - i.port = u.Port; - i.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - var q = HttpUtility.ParseQueryString(u.Query); - - var m = StdVmessUserInfo.Match(u.UserInfo); - if (!m.Success) return null; - - i.id = m.Groups["id"].Value; - - if (m.Groups["streamSecurity"].Success) - { - i.streamSecurity = m.Groups["streamSecurity"].Value; - } - switch (i.streamSecurity) - { - case "tls": - break; - - default: - if (!string.IsNullOrWhiteSpace(i.streamSecurity)) - return null; - break; - } - - i.network = m.Groups["network"].Value; - switch (i.network) - { - case "tcp": - string t1 = q["type"] ?? "none"; - i.headerType = t1; - break; - - case "kcp": - i.headerType = q["type"] ?? "none"; - break; - - case "ws": - string p1 = q["path"] ?? "/"; - string h1 = q["host"] ?? ""; - i.requestHost = Utils.UrlDecode(h1); - i.path = p1; - break; - - case "http": - case "h2": - i.network = "h2"; - string p2 = q["path"] ?? "/"; - string h2 = q["host"] ?? ""; - i.requestHost = Utils.UrlDecode(h2); - i.path = p2; - break; - - case "quic": - string s = q["security"] ?? "none"; - string k = q["key"] ?? ""; - string t3 = q["type"] ?? "none"; - i.headerType = t3; - i.requestHost = Utils.UrlDecode(s); - i.path = k; - break; - - default: - return null; - } - - return i; - } - - private static ProfileItem? ResolveSip002(string result) - { - Uri parsedUrl; - try - { - parsedUrl = new Uri(result); - } - catch (UriFormatException) - { - return null; - } - ProfileItem server = new() - { - remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), - address = parsedUrl.IdnHost, - port = parsedUrl.Port, - }; - string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped); - //2022-blake3 - if (rawUserInfo.Contains(':')) - { - string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); - if (userInfoParts.Length != 2) + profileItem = ResolveSSLegacy(result) ?? ResolveSip002(result); + if (profileItem == null) { return null; } - server.security = userInfoParts[0]; - server.id = Utils.UrlDecode(userInfoParts[1]); + if (profileItem.address.Length == 0 || profileItem.port == 0 || profileItem.security.Length == 0 || profileItem.id.Length == 0) + { + return null; + } + + profileItem.configType = EConfigType.Shadowsocks; + } + else if (result.StartsWith(Global.socksProtocol)) + { + msg = ResUI.ConfigurationFormatIncorrect; + + profileItem = ResolveSocksNew(result) ?? ResolveSocks(result); + if (profileItem == null) + { + return null; + } + if (profileItem.address.Length == 0 || profileItem.port == 0) + { + return null; + } + + profileItem.configType = EConfigType.Socks; + } + else if (result.StartsWith(Global.trojanProtocol)) + { + msg = ResUI.ConfigurationFormatIncorrect; + + profileItem = ResolveTrojan(result); + } + else if (result.StartsWith(Global.vlessProtocol)) + { + profileItem = ResolveStdVLESS(result); } else { - // parse base64 UserInfo - string userInfo = Utils.Base64Decode(rawUserInfo); - string[] userInfoParts = userInfo.Split(new[] { ':' }, 2); - if (userInfoParts.Length != 2) - { + msg = ResUI.NonvmessOrssProtocol; + return null; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + msg = ResUI.Incorrectconfiguration; + return null; + } + + return profileItem; + } + + private static ProfileItem? ResolveVmess(string result, out string msg) + { + msg = string.Empty; + var profileItem = new ProfileItem + { + configType = EConfigType.VMess + }; + + result = result[Global.vmessProtocol.Length..]; + result = Utils.Base64Decode(result); + + //转成Json + VmessQRCode? vmessQRCode = Utils.FromJson(result); + if (vmessQRCode == null) + { + msg = ResUI.FailedConversionConfiguration; + return null; + } + + profileItem.network = Global.DefaultNetwork; + profileItem.headerType = Global.None; + + profileItem.configVersion = Utils.ToInt(vmessQRCode.v); + profileItem.remarks = Utils.ToString(vmessQRCode.ps); + profileItem.address = Utils.ToString(vmessQRCode.add); + profileItem.port = Utils.ToInt(vmessQRCode.port); + profileItem.id = Utils.ToString(vmessQRCode.id); + profileItem.alterId = Utils.ToInt(vmessQRCode.aid); + profileItem.security = Utils.ToString(vmessQRCode.scy); + + profileItem.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity; + if (!Utils.IsNullOrEmpty(vmessQRCode.net)) + { + profileItem.network = vmessQRCode.net; + } + if (!Utils.IsNullOrEmpty(vmessQRCode.type)) + { + profileItem.headerType = vmessQRCode.type; + } + + profileItem.requestHost = Utils.ToString(vmessQRCode.host); + profileItem.path = Utils.ToString(vmessQRCode.path); + profileItem.streamSecurity = Utils.ToString(vmessQRCode.tls); + profileItem.sni = Utils.ToString(vmessQRCode.sni); + profileItem.alpn = Utils.ToString(vmessQRCode.alpn); + profileItem.fingerprint = Utils.ToString(vmessQRCode.fp); + + return profileItem; + } + + private static ProfileItem? ResolveVmess4Kitsunebi(string result) + { + ProfileItem profileItem = new() + { + configType = EConfigType.VMess + }; + result = result[Global.vmessProtocol.Length..]; + int indexSplit = result.IndexOf("?"); + if (indexSplit > 0) + { + result = result[..indexSplit]; + } + result = Utils.Base64Decode(result); + + string[] arr1 = result.Split('@'); + if (arr1.Length != 2) + { + return null; + } + string[] arr21 = arr1[0].Split(':'); + string[] arr22 = arr1[1].Split(':'); + if (arr21.Length != 2 || arr22.Length != 2) + { + return null; + } + + profileItem.address = arr22[0]; + profileItem.port = Utils.ToInt(arr22[1]); + profileItem.security = arr21[0]; + profileItem.id = arr21[1]; + + profileItem.network = Global.DefaultNetwork; + profileItem.headerType = Global.None; + profileItem.remarks = "Alien"; + + return profileItem; + } + + private static ProfileItem? ResolveStdVmess(string result) + { + ProfileItem i = new() + { + configType = EConfigType.VMess, + security = "auto" + }; + + Uri u = new(result); + + i.address = u.IdnHost; + i.port = u.Port; + i.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + var q = HttpUtility.ParseQueryString(u.Query); + + var m = StdVmessUserInfo.Match(u.UserInfo); + if (!m.Success) return null; + + i.id = m.Groups["id"].Value; + + if (m.Groups["streamSecurity"].Success) + { + i.streamSecurity = m.Groups["streamSecurity"].Value; + } + switch (i.streamSecurity) + { + case "tls": + break; + + default: + if (!string.IsNullOrWhiteSpace(i.streamSecurity)) return null; - } - server.security = userInfoParts[0]; - server.id = userInfoParts[1]; - } - - NameValueCollection queryParameters = HttpUtility.ParseQueryString(parsedUrl.Query); - if (queryParameters["plugin"] != null) - { - //obfs-host exists - var obfsHost = queryParameters["plugin"].Split(';').FirstOrDefault(t => t.Contains("obfs-host")); - if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost)) - { - obfsHost = obfsHost.Replace("obfs-host=", ""); - server.network = Global.DefaultNetwork; - server.headerType = Global.TcpHeaderHttp; - server.requestHost = obfsHost; - } - else - { - return null; - } - } - - return server; + break; } - private static readonly Regex UrlFinder = new(@"ss://(?[A-Za-z0-9+-/=_]+)(?:#(?\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex DetailsParser = new(@"^((?.+?):(?.*)@(?.+?):(?\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled); - - private static ProfileItem? ResolveSSLegacy(string result) + i.network = m.Groups["network"].Value; + switch (i.network) { - var match = UrlFinder.Match(result); - if (!match.Success) - return null; + case "tcp": + string t1 = q["type"] ?? "none"; + i.headerType = t1; + break; - ProfileItem server = new(); - var base64 = match.Groups["base64"].Value.TrimEnd('/'); - var tag = match.Groups["tag"].Value; - if (!Utils.IsNullOrEmpty(tag)) - { - server.remarks = Utils.UrlDecode(tag); - } - Match details; - try - { - details = DetailsParser.Match(Utils.Base64Decode(base64)); - } - catch (FormatException) - { + case "kcp": + i.headerType = q["type"] ?? "none"; + break; + + case "ws": + string p1 = q["path"] ?? "/"; + string h1 = q["host"] ?? ""; + i.requestHost = Utils.UrlDecode(h1); + i.path = p1; + break; + + case "http": + case "h2": + i.network = "h2"; + string p2 = q["path"] ?? "/"; + string h2 = q["host"] ?? ""; + i.requestHost = Utils.UrlDecode(h2); + i.path = p2; + break; + + case "quic": + string s = q["security"] ?? "none"; + string k = q["key"] ?? ""; + string t3 = q["type"] ?? "none"; + i.headerType = t3; + i.requestHost = Utils.UrlDecode(s); + i.path = k; + break; + + default: return null; - } - if (!details.Success) - return null; - server.security = details.Groups["method"].Value; - server.id = details.Groups["password"].Value; - server.address = details.Groups["hostname"].Value; - server.port = Utils.ToInt(details.Groups["port"].Value); - return server; } - private static readonly Regex StdVmessUserInfo = new( - @"^(?[a-z]+)(\+(?[a-z]+))?:(?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", RegexOptions.Compiled); + return i; + } - private static ProfileItem? ResolveSocks(string result) + private static ProfileItem? ResolveSip002(string result) + { + Uri parsedUrl; + try { - ProfileItem profileItem = new() - { - configType = EConfigType.Socks - }; - result = result[Global.socksProtocol.Length..]; - //remark - int indexRemark = result.IndexOf("#"); - if (indexRemark > 0) - { - try - { - profileItem.remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1)); - } - catch { } - result = result[..indexRemark]; - } - //part decode - int indexS = result.IndexOf("@"); - if (indexS > 0) - { - } - else - { - result = Utils.Base64Decode(result); - } - - string[] arr1 = result.Split('@'); - if (arr1.Length != 2) - { - return null; - } - string[] arr21 = arr1[0].Split(':'); - //string[] arr22 = arr1[1].Split(':'); - int indexPort = arr1[1].LastIndexOf(":"); - if (arr21.Length != 2 || indexPort < 0) - { - return null; - } - profileItem.address = arr1[1][..indexPort]; - profileItem.port = Utils.ToInt(arr1[1][(indexPort + 1)..]); - profileItem.security = arr21[0]; - profileItem.id = arr21[1]; - - return profileItem; + parsedUrl = new Uri(result); } - - private static ProfileItem? ResolveSocksNew(string result) + catch (UriFormatException) { - Uri parsedUrl; - try - { - parsedUrl = new Uri(result); - } - catch (UriFormatException) + return null; + } + ProfileItem server = new() + { + remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + address = parsedUrl.IdnHost, + port = parsedUrl.Port, + }; + string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped); + //2022-blake3 + if (rawUserInfo.Contains(':')) + { + string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length != 2) { return null; } - ProfileItem server = new() - { - remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), - address = parsedUrl.IdnHost, - port = parsedUrl.Port, - }; - + server.security = userInfoParts[0]; + server.id = Utils.UrlDecode(userInfoParts[1]); + } + else + { // parse base64 UserInfo - string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped); string userInfo = Utils.Base64Decode(rawUserInfo); string[] userInfoParts = userInfo.Split(new[] { ':' }, 2); - if (userInfoParts.Length == 2) + if (userInfoParts.Length != 2) { - server.security = userInfoParts[0]; - server.id = userInfoParts[1]; + return null; } - - return server; + server.security = userInfoParts[0]; + server.id = userInfoParts[1]; } - private static ProfileItem ResolveTrojan(string result) + NameValueCollection queryParameters = HttpUtility.ParseQueryString(parsedUrl.Query); + if (queryParameters["plugin"] != null) { - ProfileItem item = new() + //obfs-host exists + var obfsHost = queryParameters["plugin"].Split(';').FirstOrDefault(t => t.Contains("obfs-host")); + if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost)) { - configType = EConfigType.Trojan - }; - - Uri url = new(result); - - item.address = url.IdnHost; - item.port = url.Port; - item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.id = url.UserInfo; - - var query = HttpUtility.ParseQueryString(url.Query); - ResolveStdTransport(query, ref item); - - return item; - } - - private static ProfileItem ResolveStdVLESS(string result) - { - ProfileItem item = new() - { - configType = EConfigType.VLESS, - security = "none" - }; - - Uri url = new(result); - - item.address = url.IdnHost; - item.port = url.Port; - item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.id = url.UserInfo; - - var query = HttpUtility.ParseQueryString(url.Query); - item.security = query["encryption"] ?? "none"; - item.streamSecurity = query["security"] ?? ""; - ResolveStdTransport(query, ref item); - - return item; - } - - private static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item) - { - item.flow = query["flow"] ?? ""; - item.streamSecurity = query["security"] ?? ""; - item.sni = query["sni"] ?? ""; - item.alpn = Utils.UrlDecode(query["alpn"] ?? ""); - item.fingerprint = Utils.UrlDecode(query["fp"] ?? ""); - item.publicKey = Utils.UrlDecode(query["pbk"] ?? ""); - item.shortId = Utils.UrlDecode(query["sid"] ?? ""); - item.spiderX = Utils.UrlDecode(query["spx"] ?? ""); - - item.network = query["type"] ?? "tcp"; - switch (item.network) - { - case "tcp": - item.headerType = query["headerType"] ?? "none"; - item.requestHost = Utils.UrlDecode(query["host"] ?? ""); - - break; - - case "kcp": - item.headerType = query["headerType"] ?? "none"; - item.path = Utils.UrlDecode(query["seed"] ?? ""); - break; - - case "ws": - item.requestHost = Utils.UrlDecode(query["host"] ?? ""); - item.path = Utils.UrlDecode(query["path"] ?? "/"); - break; - - case "http": - case "h2": - item.network = "h2"; - item.requestHost = Utils.UrlDecode(query["host"] ?? ""); - item.path = Utils.UrlDecode(query["path"] ?? "/"); - break; - - case "quic": - item.headerType = query["headerType"] ?? "none"; - item.requestHost = query["quicSecurity"] ?? "none"; - item.path = Utils.UrlDecode(query["key"] ?? ""); - break; - - case "grpc": - item.path = Utils.UrlDecode(query["serviceName"] ?? ""); - item.headerType = Utils.UrlDecode(query["mode"] ?? Global.GrpcgunMode); - break; - - default: - break; + obfsHost = obfsHost.Replace("obfs-host=", ""); + server.network = Global.DefaultNetwork; + server.headerType = Global.TcpHeaderHttp; + server.requestHost = obfsHost; + } + else + { + return null; } - return 0; } - #endregion ImportShareUrl + return server; } + + private static readonly Regex UrlFinder = new(@"ss://(?[A-Za-z0-9+-/=_]+)(?:#(?\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex DetailsParser = new(@"^((?.+?):(?.*)@(?.+?):(?\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static ProfileItem? ResolveSSLegacy(string result) + { + var match = UrlFinder.Match(result); + if (!match.Success) + return null; + + ProfileItem server = new(); + var base64 = match.Groups["base64"].Value.TrimEnd('/'); + var tag = match.Groups["tag"].Value; + if (!Utils.IsNullOrEmpty(tag)) + { + server.remarks = Utils.UrlDecode(tag); + } + Match details; + try + { + details = DetailsParser.Match(Utils.Base64Decode(base64)); + } + catch (FormatException) + { + return null; + } + if (!details.Success) + return null; + server.security = details.Groups["method"].Value; + server.id = details.Groups["password"].Value; + server.address = details.Groups["hostname"].Value; + server.port = Utils.ToInt(details.Groups["port"].Value); + return server; + } + + private static readonly Regex StdVmessUserInfo = new( + @"^(?[a-z]+)(\+(?[a-z]+))?:(?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", RegexOptions.Compiled); + + private static ProfileItem? ResolveSocks(string result) + { + ProfileItem profileItem = new() + { + configType = EConfigType.Socks + }; + result = result[Global.socksProtocol.Length..]; + //remark + int indexRemark = result.IndexOf("#"); + if (indexRemark > 0) + { + try + { + profileItem.remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1)); + } + catch { } + result = result[..indexRemark]; + } + //part decode + int indexS = result.IndexOf("@"); + if (indexS > 0) + { + } + else + { + result = Utils.Base64Decode(result); + } + + string[] arr1 = result.Split('@'); + if (arr1.Length != 2) + { + return null; + } + string[] arr21 = arr1[0].Split(':'); + //string[] arr22 = arr1[1].Split(':'); + int indexPort = arr1[1].LastIndexOf(":"); + if (arr21.Length != 2 || indexPort < 0) + { + return null; + } + profileItem.address = arr1[1][..indexPort]; + profileItem.port = Utils.ToInt(arr1[1][(indexPort + 1)..]); + profileItem.security = arr21[0]; + profileItem.id = arr21[1]; + + return profileItem; + } + + private static ProfileItem? ResolveSocksNew(string result) + { + Uri parsedUrl; + try + { + parsedUrl = new Uri(result); + } + catch (UriFormatException) + { + return null; + } + ProfileItem server = new() + { + remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + address = parsedUrl.IdnHost, + port = parsedUrl.Port, + }; + + // parse base64 UserInfo + string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped); + string userInfo = Utils.Base64Decode(rawUserInfo); + string[] userInfoParts = userInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length == 2) + { + server.security = userInfoParts[0]; + server.id = userInfoParts[1]; + } + + return server; + } + + private static ProfileItem ResolveTrojan(string result) + { + ProfileItem item = new() + { + configType = EConfigType.Trojan + }; + + Uri url = new(result); + + item.address = url.IdnHost; + item.port = url.Port; + item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + item.id = url.UserInfo; + + var query = HttpUtility.ParseQueryString(url.Query); + ResolveStdTransport(query, ref item); + + return item; + } + + private static ProfileItem ResolveStdVLESS(string result) + { + ProfileItem item = new() + { + configType = EConfigType.VLESS, + security = "none" + }; + + Uri url = new(result); + + item.address = url.IdnHost; + item.port = url.Port; + item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + item.id = url.UserInfo; + + var query = HttpUtility.ParseQueryString(url.Query); + item.security = query["encryption"] ?? "none"; + item.streamSecurity = query["security"] ?? ""; + ResolveStdTransport(query, ref item); + + return item; + } + + private static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item) + { + item.flow = query["flow"] ?? ""; + item.streamSecurity = query["security"] ?? ""; + item.sni = query["sni"] ?? ""; + item.alpn = Utils.UrlDecode(query["alpn"] ?? ""); + item.fingerprint = Utils.UrlDecode(query["fp"] ?? ""); + item.publicKey = Utils.UrlDecode(query["pbk"] ?? ""); + item.shortId = Utils.UrlDecode(query["sid"] ?? ""); + item.spiderX = Utils.UrlDecode(query["spx"] ?? ""); + + item.network = query["type"] ?? "tcp"; + switch (item.network) + { + case "tcp": + item.headerType = query["headerType"] ?? "none"; + item.requestHost = Utils.UrlDecode(query["host"] ?? ""); + + break; + + case "kcp": + item.headerType = query["headerType"] ?? "none"; + item.path = Utils.UrlDecode(query["seed"] ?? ""); + break; + + case "ws": + item.requestHost = Utils.UrlDecode(query["host"] ?? ""); + item.path = Utils.UrlDecode(query["path"] ?? "/"); + break; + + case "http": + case "h2": + item.network = "h2"; + item.requestHost = Utils.UrlDecode(query["host"] ?? ""); + item.path = Utils.UrlDecode(query["path"] ?? "/"); + break; + + case "quic": + item.headerType = query["headerType"] ?? "none"; + item.requestHost = query["quicSecurity"] ?? "none"; + item.path = Utils.UrlDecode(query["key"] ?? ""); + break; + + case "grpc": + item.path = Utils.UrlDecode(query["serviceName"] ?? ""); + item.headerType = Utils.UrlDecode(query["mode"] ?? Global.GrpcgunMode); + break; + + default: + break; + } + return 0; + } + + #endregion ImportShareUrl } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/SpeedtestHandler.cs b/v2rayN/v2rayN/Handler/SpeedtestHandler.cs index bb1d3b66..b6b32e82 100644 --- a/v2rayN/v2rayN/Handler/SpeedtestHandler.cs +++ b/v2rayN/v2rayN/Handler/SpeedtestHandler.cs @@ -5,423 +5,422 @@ using System.Net.Sockets; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.Handler -{ - internal class SpeedtestHandler - { - private Config _config; - private CoreHandler _coreHandler; - private List _selecteds; - private ESpeedActionType _actionType; - private Action _updateFunc; +namespace v2rayN.Handler; - public SpeedtestHandler(Config config) +internal class SpeedtestHandler +{ + private Config _config; + private CoreHandler _coreHandler; + private List _selecteds; + private ESpeedActionType _actionType; + private Action _updateFunc; + + public SpeedtestHandler(Config config) + { + _config = config; + } + + public SpeedtestHandler(Config config, CoreHandler coreHandler, List selecteds, ESpeedActionType actionType, Action update) + { + _config = config; + _coreHandler = coreHandler; + _actionType = actionType; + _updateFunc = update; + + _selecteds = new List(); + foreach (var it in selecteds) { - _config = config; + if (it.configType == EConfigType.Custom) + { + continue; + } + if (it.port <= 0) + { + continue; + } + _selecteds.Add(new ServerTestItem() + { + indexId = it.indexId, + address = it.address, + port = it.port, + configType = it.configType + }); + } + //clear test result + foreach (var it in _selecteds) + { + switch (actionType) + { + case ESpeedActionType.Ping: + case ESpeedActionType.Tcping: + case ESpeedActionType.Realping: + UpdateFunc(it.indexId, ResUI.Speedtesting, ""); + ProfileExHandler.Instance.SetTestDelay(it.indexId, "0"); + break; + + case ESpeedActionType.Speedtest: + UpdateFunc(it.indexId, "", ResUI.SpeedtestingWait); + ProfileExHandler.Instance.SetTestSpeed(it.indexId, "0"); + break; + + case ESpeedActionType.Mixedtest: + UpdateFunc(it.indexId, ResUI.Speedtesting, ResUI.SpeedtestingWait); + ProfileExHandler.Instance.SetTestDelay(it.indexId, "0"); + ProfileExHandler.Instance.SetTestSpeed(it.indexId, "0"); + break; + } } - public SpeedtestHandler(Config config, CoreHandler coreHandler, List selecteds, ESpeedActionType actionType, Action update) + switch (actionType) { - _config = config; - _coreHandler = coreHandler; - _actionType = actionType; - _updateFunc = update; + case ESpeedActionType.Ping: + Task.Run(RunPing); + break; - _selecteds = new List(); - foreach (var it in selecteds) + case ESpeedActionType.Tcping: + Task.Run(RunTcping); + break; + + case ESpeedActionType.Realping: + Task.Run(RunRealPing); + break; + + case ESpeedActionType.Speedtest: + Task.Run(RunSpeedTestAsync); + break; + + case ESpeedActionType.Mixedtest: + Task.Run(RunMixedtestAsync); + break; + } + } + + private async Task RunPingSubAsync(Action updateFun) + { + try + { + foreach (var it in _selecteds.Where(it => it.configType != EConfigType.Custom)) { + try + { + Task.Run(() => updateFun(it)); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + } + + await Task.Delay(10); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + } + + private async void RunPing() + { + await RunPingSubAsync((ServerTestItem it) => + { + long time = Ping(it.address); + var output = FormatOut(time, Global.DelayUnit); + + ProfileExHandler.Instance.SetTestDelay(it.indexId, output); + UpdateFunc(it.indexId, output); + }); + } + + private async void RunTcping() + { + await RunPingSubAsync((ServerTestItem it) => + { + int time = GetTcpingTime(it.address, it.port); + var output = FormatOut(time, Global.DelayUnit); + + ProfileExHandler.Instance.SetTestDelay(it.indexId, output); + UpdateFunc(it.indexId, output); + }); + } + + private Task RunRealPing() + { + int pid = -1; + try + { + string msg = string.Empty; + + pid = _coreHandler.LoadCoreConfigString(_selecteds); + if (pid < 0) + { + UpdateFunc("", ResUI.FailedToRunCore); + return Task.CompletedTask; + } + + DownloadHandle downloadHandle = new DownloadHandle(); + + List tasks = new(); + foreach (var it in _selecteds) + { + if (!it.allowTest) + { + continue; + } if (it.configType == EConfigType.Custom) { continue; } - if (it.port <= 0) - { - continue; - } - _selecteds.Add(new ServerTestItem() - { - indexId = it.indexId, - address = it.address, - port = it.port, - configType = it.configType - }); - } - //clear test result - foreach (var it in _selecteds) - { - switch (actionType) - { - case ESpeedActionType.Ping: - case ESpeedActionType.Tcping: - case ESpeedActionType.Realping: - UpdateFunc(it.indexId, ResUI.Speedtesting, ""); - ProfileExHandler.Instance.SetTestDelay(it.indexId, "0"); - break; - - case ESpeedActionType.Speedtest: - UpdateFunc(it.indexId, "", ResUI.SpeedtestingWait); - ProfileExHandler.Instance.SetTestSpeed(it.indexId, "0"); - break; - - case ESpeedActionType.Mixedtest: - UpdateFunc(it.indexId, ResUI.Speedtesting, ResUI.SpeedtestingWait); - ProfileExHandler.Instance.SetTestDelay(it.indexId, "0"); - ProfileExHandler.Instance.SetTestSpeed(it.indexId, "0"); - break; - } - } - - switch (actionType) - { - case ESpeedActionType.Ping: - Task.Run(RunPing); - break; - - case ESpeedActionType.Tcping: - Task.Run(RunTcping); - break; - - case ESpeedActionType.Realping: - Task.Run(RunRealPing); - break; - - case ESpeedActionType.Speedtest: - Task.Run(RunSpeedTestAsync); - break; - - case ESpeedActionType.Mixedtest: - Task.Run(RunMixedtestAsync); - break; - } - } - - private async Task RunPingSubAsync(Action updateFun) - { - try - { - foreach (var it in _selecteds.Where(it => it.configType != EConfigType.Custom)) + tasks.Add(Task.Run(async () => { try { - Task.Run(() => updateFun(it)); + WebProxy webProxy = new(Global.Loopback, it.port); + string output = await GetRealPingTime(downloadHandle, webProxy); + + ProfileExHandler.Instance.SetTestDelay(it.indexId, output); + UpdateFunc(it.indexId, output); + int.TryParse(output, out int delay); + it.delay = delay; } catch (Exception ex) { Utils.SaveLog(ex.Message, ex); } + })); + } + Task.WaitAll(tasks.ToArray()); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + finally + { + if (pid > 0) _coreHandler.CoreStopPid(pid); + ProfileExHandler.Instance.SaveTo(); + } + + return Task.CompletedTask; + } + + private async Task RunSpeedTestAsync() + { + int pid = -1; + //if (_actionType == ESpeedActionType.Mixedtest) + //{ + // _selecteds = _selecteds.OrderBy(t => t.delay).ToList(); + //} + + pid = _coreHandler.LoadCoreConfigString(_selecteds); + if (pid < 0) + { + UpdateFunc("", ResUI.FailedToRunCore); + return; + } + + string url = _config.speedTestItem.speedTestUrl; + var timeout = _config.speedTestItem.speedTestTimeout; + + DownloadHandle downloadHandle = new(); + + foreach (var it in _selecteds) + { + if (!it.allowTest) + { + continue; + } + if (it.configType == EConfigType.Custom) + { + continue; + } + //if (it.delay < 0) + //{ + // UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip); + // continue; + //} + ProfileExHandler.Instance.SetTestSpeed(it.indexId, "-1"); + UpdateFunc(it.indexId, "", ResUI.Speedtesting); + + var item = LazyConfig.Instance.GetProfileItem(it.indexId); + if (item is null) continue; + + WebProxy webProxy = new(Global.Loopback, it.port); + + await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) => + { + decimal.TryParse(msg, out decimal dec); + if (dec > 0) + { + ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg); } - - await Task.Delay(10); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - } - - private async void RunPing() - { - await RunPingSubAsync((ServerTestItem it) => - { - long time = Ping(it.address); - var output = FormatOut(time, Global.DelayUnit); - - ProfileExHandler.Instance.SetTestDelay(it.indexId, output); - UpdateFunc(it.indexId, output); - }); - } - - private async void RunTcping() - { - await RunPingSubAsync((ServerTestItem it) => - { - int time = GetTcpingTime(it.address, it.port); - var output = FormatOut(time, Global.DelayUnit); - - ProfileExHandler.Instance.SetTestDelay(it.indexId, output); - UpdateFunc(it.indexId, output); + UpdateFunc(it.indexId, "", msg); }); } - private Task RunRealPing() + if (pid > 0) { - int pid = -1; - try + _coreHandler.CoreStopPid(pid); + } + UpdateFunc("", ResUI.SpeedtestingCompleted); + ProfileExHandler.Instance.SaveTo(); + } + + private async Task RunSpeedTestMulti() + { + int pid = -1; + pid = _coreHandler.LoadCoreConfigString(_selecteds); + if (pid < 0) + { + UpdateFunc("", ResUI.FailedToRunCore); + return; + } + + string url = _config.speedTestItem.speedTestUrl; + var timeout = _config.speedTestItem.speedTestTimeout; + + DownloadHandle downloadHandle = new(); + + foreach (var it in _selecteds) + { + if (!it.allowTest) { - string msg = string.Empty; + continue; + } + if (it.configType == EConfigType.Custom) + { + continue; + } + if (it.delay < 0) + { + UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip); + continue; + } + ProfileExHandler.Instance.SetTestSpeed(it.indexId, "-1"); + UpdateFunc(it.indexId, "", ResUI.Speedtesting); - pid = _coreHandler.LoadCoreConfigString(_selecteds); - if (pid < 0) + var item = LazyConfig.Instance.GetProfileItem(it.indexId); + if (item is null) continue; + + WebProxy webProxy = new(Global.Loopback, it.port); + _ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) => + { + decimal.TryParse(msg, out decimal dec); + if (dec > 0) { - UpdateFunc("", ResUI.FailedToRunCore); - return Task.CompletedTask; + ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg); } + UpdateFunc(it.indexId, "", msg); + }); + await Task.Delay(2000); + } - DownloadHandle downloadHandle = new DownloadHandle(); + await Task.Delay((timeout + 2) * 1000); - List tasks = new(); - foreach (var it in _selecteds) + if (pid > 0) + { + _coreHandler.CoreStopPid(pid); + } + UpdateFunc("", ResUI.SpeedtestingCompleted); + ProfileExHandler.Instance.SaveTo(); + } + + private async Task RunMixedtestAsync() + { + await RunRealPing(); + + await Task.Delay(1000); + + await RunSpeedTestMulti(); + } + + public async Task GetRealPingTime(DownloadHandle downloadHandle, IWebProxy webProxy) + { + int responseTime = await downloadHandle.GetRealPingTime(_config.speedTestItem.speedPingTestUrl, webProxy, 10); + //string output = Utils.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status; + return FormatOut(responseTime, Global.DelayUnit); + } + + private int GetTcpingTime(string url, int port) + { + int responseTime = -1; + + try + { + if (!IPAddress.TryParse(url, out IPAddress ipAddress)) + { + IPHostEntry ipHostInfo = System.Net.Dns.GetHostEntry(url); + ipAddress = ipHostInfo.AddressList[0]; + } + + Stopwatch timer = new(); + timer.Start(); + + IPEndPoint endPoint = new(ipAddress, port); + using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + + IAsyncResult result = clientSocket.BeginConnect(endPoint, null, null); + if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5))) + throw new TimeoutException("connect timeout (5s): " + url); + clientSocket.EndConnect(result); + + timer.Stop(); + responseTime = timer.Elapsed.Milliseconds; + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return responseTime; + } + + /// + /// Ping + /// + /// + /// + public long Ping(string host) + { + long roundtripTime = -1; + try + { + int timeout = 30; + int echoNum = 2; + using Ping pingSender = new(); + for (int i = 0; i < echoNum; i++) + { + PingReply reply = pingSender.Send(host, timeout); + if (reply.Status == IPStatus.Success) { - if (!it.allowTest) + if (reply.RoundtripTime < 0) { continue; } - if (it.configType == EConfigType.Custom) + if (roundtripTime < 0 || reply.RoundtripTime < roundtripTime) { - continue; - } - tasks.Add(Task.Run(async () => - { - try - { - WebProxy webProxy = new(Global.Loopback, it.port); - string output = await GetRealPingTime(downloadHandle, webProxy); - - ProfileExHandler.Instance.SetTestDelay(it.indexId, output); - UpdateFunc(it.indexId, output); - int.TryParse(output, out int delay); - it.delay = delay; - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - })); - } - Task.WaitAll(tasks.ToArray()); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - finally - { - if (pid > 0) _coreHandler.CoreStopPid(pid); - ProfileExHandler.Instance.SaveTo(); - } - - return Task.CompletedTask; - } - - private async Task RunSpeedTestAsync() - { - int pid = -1; - //if (_actionType == ESpeedActionType.Mixedtest) - //{ - // _selecteds = _selecteds.OrderBy(t => t.delay).ToList(); - //} - - pid = _coreHandler.LoadCoreConfigString(_selecteds); - if (pid < 0) - { - UpdateFunc("", ResUI.FailedToRunCore); - return; - } - - string url = _config.speedTestItem.speedTestUrl; - var timeout = _config.speedTestItem.speedTestTimeout; - - DownloadHandle downloadHandle = new(); - - foreach (var it in _selecteds) - { - if (!it.allowTest) - { - continue; - } - if (it.configType == EConfigType.Custom) - { - continue; - } - //if (it.delay < 0) - //{ - // UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip); - // continue; - //} - ProfileExHandler.Instance.SetTestSpeed(it.indexId, "-1"); - UpdateFunc(it.indexId, "", ResUI.Speedtesting); - - var item = LazyConfig.Instance.GetProfileItem(it.indexId); - if (item is null) continue; - - WebProxy webProxy = new(Global.Loopback, it.port); - - await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) => - { - decimal.TryParse(msg, out decimal dec); - if (dec > 0) - { - ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg); - } - UpdateFunc(it.indexId, "", msg); - }); - } - - if (pid > 0) - { - _coreHandler.CoreStopPid(pid); - } - UpdateFunc("", ResUI.SpeedtestingCompleted); - ProfileExHandler.Instance.SaveTo(); - } - - private async Task RunSpeedTestMulti() - { - int pid = -1; - pid = _coreHandler.LoadCoreConfigString(_selecteds); - if (pid < 0) - { - UpdateFunc("", ResUI.FailedToRunCore); - return; - } - - string url = _config.speedTestItem.speedTestUrl; - var timeout = _config.speedTestItem.speedTestTimeout; - - DownloadHandle downloadHandle = new(); - - foreach (var it in _selecteds) - { - if (!it.allowTest) - { - continue; - } - if (it.configType == EConfigType.Custom) - { - continue; - } - if (it.delay < 0) - { - UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip); - continue; - } - ProfileExHandler.Instance.SetTestSpeed(it.indexId, "-1"); - UpdateFunc(it.indexId, "", ResUI.Speedtesting); - - var item = LazyConfig.Instance.GetProfileItem(it.indexId); - if (item is null) continue; - - WebProxy webProxy = new(Global.Loopback, it.port); - _ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (bool success, string msg) => - { - decimal.TryParse(msg, out decimal dec); - if (dec > 0) - { - ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg); - } - UpdateFunc(it.indexId, "", msg); - }); - await Task.Delay(2000); - } - - await Task.Delay((timeout + 2) * 1000); - - if (pid > 0) - { - _coreHandler.CoreStopPid(pid); - } - UpdateFunc("", ResUI.SpeedtestingCompleted); - ProfileExHandler.Instance.SaveTo(); - } - - private async Task RunMixedtestAsync() - { - await RunRealPing(); - - await Task.Delay(1000); - - await RunSpeedTestMulti(); - } - - public async Task GetRealPingTime(DownloadHandle downloadHandle, IWebProxy webProxy) - { - int responseTime = await downloadHandle.GetRealPingTime(_config.speedTestItem.speedPingTestUrl, webProxy, 10); - //string output = Utils.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status; - return FormatOut(responseTime, Global.DelayUnit); - } - - private int GetTcpingTime(string url, int port) - { - int responseTime = -1; - - try - { - if (!IPAddress.TryParse(url, out IPAddress ipAddress)) - { - IPHostEntry ipHostInfo = System.Net.Dns.GetHostEntry(url); - ipAddress = ipHostInfo.AddressList[0]; - } - - Stopwatch timer = new(); - timer.Start(); - - IPEndPoint endPoint = new(ipAddress, port); - using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - - IAsyncResult result = clientSocket.BeginConnect(endPoint, null, null); - if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5))) - throw new TimeoutException("connect timeout (5s): " + url); - clientSocket.EndConnect(result); - - timer.Stop(); - responseTime = timer.Elapsed.Milliseconds; - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return responseTime; - } - - /// - /// Ping - /// - /// - /// - public long Ping(string host) - { - long roundtripTime = -1; - try - { - int timeout = 30; - int echoNum = 2; - using Ping pingSender = new(); - for (int i = 0; i < echoNum; i++) - { - PingReply reply = pingSender.Send(host, timeout); - if (reply.Status == IPStatus.Success) - { - if (reply.RoundtripTime < 0) - { - continue; - } - if (roundtripTime < 0 || reply.RoundtripTime < roundtripTime) - { - roundtripTime = reply.RoundtripTime; - } + roundtripTime = reply.RoundtripTime; } } } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - return -1; - } - return roundtripTime; } - - private string FormatOut(object time, string unit) + catch (Exception ex) { - //if (time.ToString().Equals("-1")) - //{ - // return "Timeout"; - //} - return $"{time}"; + Utils.SaveLog(ex.Message, ex); + return -1; } + return roundtripTime; + } - private void UpdateFunc(string indexId, string delay, string speed = "") - { - _updateFunc(indexId, delay, speed); - } + private string FormatOut(object time, string unit) + { + //if (time.ToString().Equals("-1")) + //{ + // return "Timeout"; + //} + return $"{time}"; + } + + private void UpdateFunc(string indexId, string delay, string speed = "") + { + _updateFunc(indexId, delay, speed); } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/StatisticsHandler.cs b/v2rayN/v2rayN/Handler/StatisticsHandler.cs index 4ca0ae02..2a4d0d4b 100644 --- a/v2rayN/v2rayN/Handler/StatisticsHandler.cs +++ b/v2rayN/v2rayN/Handler/StatisticsHandler.cs @@ -3,159 +3,158 @@ using System.Net.Sockets; using v2rayN.Base; using v2rayN.Mode; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class StatisticsHandler { - internal class StatisticsHandler + private Config _config; + private ServerStatItem? _serverStatItem; + private List _lstServerStat; + private Action _updateFunc; + private StatisticsV2ray? _statisticsV2Ray; + private StatisticsSingbox? _statisticsSingbox; + + public List ServerStat => _lstServerStat; + public bool Enable { get; set; } + + public StatisticsHandler(Config config, Action update) { - private Config _config; - private ServerStatItem? _serverStatItem; - private List _lstServerStat; - private Action _updateFunc; - private StatisticsV2ray? _statisticsV2Ray; - private StatisticsSingbox? _statisticsSingbox; - - public List ServerStat => _lstServerStat; - public bool Enable { get; set; } - - public StatisticsHandler(Config config, Action update) + _config = config; + Enable = config.guiItem.enableStatistics; + if (!Enable) { - _config = config; - Enable = config.guiItem.enableStatistics; - if (!Enable) - { - return; - } - - _updateFunc = update; - - Init(); - Global.statePort = GetFreePort(); - - _statisticsV2Ray = new StatisticsV2ray(config, UpdateServerStat); - _statisticsSingbox = new StatisticsSingbox(config, UpdateServerStat); + return; } - public void Close() + _updateFunc = update; + + Init(); + Global.statePort = GetFreePort(); + + _statisticsV2Ray = new StatisticsV2ray(config, UpdateServerStat); + _statisticsSingbox = new StatisticsSingbox(config, UpdateServerStat); + } + + public void Close() + { + try { - try - { - _statisticsV2Ray?.Close(); - _statisticsSingbox?.Close(); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } + _statisticsV2Ray?.Close(); + _statisticsSingbox?.Close(); } - - public void ClearAllServerStatistics() + catch (Exception ex) { - SqliteHelper.Instance.Execute($"delete from ServerStatItem "); - _serverStatItem = null; - _lstServerStat = new(); - } - - public void SaveTo() - { - try - { - SqliteHelper.Instance.UpdateAll(_lstServerStat); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - } - - private void Init() - { - SqliteHelper.Instance.Execute($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )"); - - long ticks = DateTime.Now.Date.Ticks; - SqliteHelper.Instance.Execute($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}"); - - _lstServerStat = SqliteHelper.Instance.Table().ToList(); - } - - private void UpdateServerStat(ServerSpeedItem server) - { - GetServerStatItem(_config.indexId); - - if (server.proxyUp != 0 || server.proxyDown != 0) - { - _serverStatItem.todayUp += server.proxyUp; - _serverStatItem.todayDown += server.proxyDown; - _serverStatItem.totalUp += server.proxyUp; - _serverStatItem.totalDown += server.proxyDown; - } - if (Global.ShowInTaskbar) - { - server.indexId = _config.indexId; - server.todayUp = _serverStatItem.todayUp; - server.todayDown = _serverStatItem.todayDown; - server.totalUp = _serverStatItem.totalUp; - server.totalDown = _serverStatItem.totalDown; - _updateFunc(server); - } - } - - private void GetServerStatItem(string indexId) - { - long ticks = DateTime.Now.Date.Ticks; - if (_serverStatItem != null && _serverStatItem.indexId != indexId) - { - _serverStatItem = null; - } - - if (_serverStatItem == null) - { - _serverStatItem = _lstServerStat.FirstOrDefault(t => t.indexId == indexId); - if (_serverStatItem == null) - { - _serverStatItem = new ServerStatItem - { - indexId = indexId, - totalUp = 0, - totalDown = 0, - todayUp = 0, - todayDown = 0, - dateNow = ticks - }; - SqliteHelper.Instance.Replace(_serverStatItem); - _lstServerStat.Add(_serverStatItem); - } - } - - if (_serverStatItem.dateNow != ticks) - { - _serverStatItem.todayUp = 0; - _serverStatItem.todayDown = 0; - _serverStatItem.dateNow = ticks; - } - } - - private int GetFreePort() - { - try - { - int defaultPort = 9090; - if (!Utils.PortInUse(defaultPort)) - { - return defaultPort; - } - for (int i = 0; i < 3; i++) - { - TcpListener l = new(IPAddress.Loopback, 0); - l.Start(); - int port = ((IPEndPoint)l.LocalEndpoint).Port; - l.Stop(); - return port; - } - } - catch - { - } - return 69090; + Utils.SaveLog(ex.Message, ex); } } + + public void ClearAllServerStatistics() + { + SqliteHelper.Instance.Execute($"delete from ServerStatItem "); + _serverStatItem = null; + _lstServerStat = new(); + } + + public void SaveTo() + { + try + { + SqliteHelper.Instance.UpdateAll(_lstServerStat); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + } + + private void Init() + { + SqliteHelper.Instance.Execute($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )"); + + long ticks = DateTime.Now.Date.Ticks; + SqliteHelper.Instance.Execute($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}"); + + _lstServerStat = SqliteHelper.Instance.Table().ToList(); + } + + private void UpdateServerStat(ServerSpeedItem server) + { + GetServerStatItem(_config.indexId); + + if (server.proxyUp != 0 || server.proxyDown != 0) + { + _serverStatItem.todayUp += server.proxyUp; + _serverStatItem.todayDown += server.proxyDown; + _serverStatItem.totalUp += server.proxyUp; + _serverStatItem.totalDown += server.proxyDown; + } + if (Global.ShowInTaskbar) + { + server.indexId = _config.indexId; + server.todayUp = _serverStatItem.todayUp; + server.todayDown = _serverStatItem.todayDown; + server.totalUp = _serverStatItem.totalUp; + server.totalDown = _serverStatItem.totalDown; + _updateFunc(server); + } + } + + private void GetServerStatItem(string indexId) + { + long ticks = DateTime.Now.Date.Ticks; + if (_serverStatItem != null && _serverStatItem.indexId != indexId) + { + _serverStatItem = null; + } + + if (_serverStatItem == null) + { + _serverStatItem = _lstServerStat.FirstOrDefault(t => t.indexId == indexId); + if (_serverStatItem == null) + { + _serverStatItem = new ServerStatItem + { + indexId = indexId, + totalUp = 0, + totalDown = 0, + todayUp = 0, + todayDown = 0, + dateNow = ticks + }; + SqliteHelper.Instance.Replace(_serverStatItem); + _lstServerStat.Add(_serverStatItem); + } + } + + if (_serverStatItem.dateNow != ticks) + { + _serverStatItem.todayUp = 0; + _serverStatItem.todayDown = 0; + _serverStatItem.dateNow = ticks; + } + } + + private int GetFreePort() + { + try + { + int defaultPort = 9090; + if (!Utils.PortInUse(defaultPort)) + { + return defaultPort; + } + for (int i = 0; i < 3; i++) + { + TcpListener l = new(IPAddress.Loopback, 0); + l.Start(); + int port = ((IPEndPoint)l.LocalEndpoint).Port; + l.Stop(); + return port; + } + } + catch + { + } + return 69090; + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/StatisticsSingbox.cs b/v2rayN/v2rayN/Handler/StatisticsSingbox.cs index 449d2e11..23fedeba 100644 --- a/v2rayN/v2rayN/Handler/StatisticsSingbox.cs +++ b/v2rayN/v2rayN/Handler/StatisticsSingbox.cs @@ -2,127 +2,126 @@ using System.Text; using v2rayN.Mode; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class StatisticsSingbox { - internal class StatisticsSingbox + private Config _config; + private bool _exitFlag; + private ClientWebSocket? webSocket; + private string url = string.Empty; + private Action _updateFunc; + + public StatisticsSingbox(Config config, Action update) { - private Config _config; - private bool _exitFlag; - private ClientWebSocket? webSocket; - private string url = string.Empty; - private Action _updateFunc; + _config = config; + _updateFunc = update; + _exitFlag = false; - public StatisticsSingbox(Config config, Action update) + Task.Run(() => Run()); + } + + private async void Init() + { + await Task.Delay(5000); + + try { - _config = config; - _updateFunc = update; - _exitFlag = false; + url = $"ws://{Global.Loopback}:{Global.statePort}/traffic"; - Task.Run(() => Run()); - } - - private async void Init() - { - await Task.Delay(5000); - - try + if (webSocket == null) { - url = $"ws://{Global.Loopback}:{Global.statePort}/traffic"; - - if (webSocket == null) - { - webSocket = new ClientWebSocket(); - await webSocket.ConnectAsync(new Uri(url), CancellationToken.None); - } + webSocket = new ClientWebSocket(); + await webSocket.ConnectAsync(new Uri(url), CancellationToken.None); } - catch { } } + catch { } + } - public void Close() + public void Close() + { + try + { + _exitFlag = true; + if (webSocket != null) + { + webSocket.Abort(); + webSocket = null; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + } + + private async void Run() + { + Init(); + + while (!_exitFlag) { try { - _exitFlag = true; if (webSocket != null) { - webSocket.Abort(); - webSocket = null; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - } - - private async void Run() - { - Init(); - - while (!_exitFlag) - { - try - { - if (webSocket != null) + if (webSocket.State == WebSocketState.Aborted + || webSocket.State == WebSocketState.Closed) { - if (webSocket.State == WebSocketState.Aborted - || webSocket.State == WebSocketState.Closed) - { - webSocket.Abort(); - webSocket = null; - Init(); - continue; - } - - if (webSocket.State != WebSocketState.Open) - { - continue; - } - - var buffer = new byte[1024]; - var res = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - while (!res.CloseStatus.HasValue) - { - var result = Encoding.UTF8.GetString(buffer, 0, res.Count); - if (!string.IsNullOrEmpty(result)) - { - ParseOutput(result, out ulong up, out ulong down); - - _updateFunc(new ServerSpeedItem() - { - proxyUp = (long)(up / 1000), - proxyDown = (long)(down / 1000) - }); - } - res = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } + webSocket.Abort(); + webSocket = null; + Init(); + continue; } - } - catch - { - } - finally - { - await Task.Delay(1000); - } - } - } - private void ParseOutput(string source, out ulong up, out ulong down) - { - up = 0; down = 0; - try - { - var trafficItem = Utils.FromJson(source); - if (trafficItem != null) - { - up = trafficItem.up; - down = trafficItem.down; + if (webSocket.State != WebSocketState.Open) + { + continue; + } + + var buffer = new byte[1024]; + var res = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + while (!res.CloseStatus.HasValue) + { + var result = Encoding.UTF8.GetString(buffer, 0, res.Count); + if (!string.IsNullOrEmpty(result)) + { + ParseOutput(result, out ulong up, out ulong down); + + _updateFunc(new ServerSpeedItem() + { + proxyUp = (long)(up / 1000), + proxyDown = (long)(down / 1000) + }); + } + res = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } } } catch { } + finally + { + await Task.Delay(1000); + } + } + } + + private void ParseOutput(string source, out ulong up, out ulong down) + { + up = 0; down = 0; + try + { + var trafficItem = Utils.FromJson(source); + if (trafficItem != null) + { + up = trafficItem.up; + down = trafficItem.down; + } + } + catch + { } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/StatisticsV2ray.cs b/v2rayN/v2rayN/Handler/StatisticsV2ray.cs index ef70f02a..6c0ee9d6 100644 --- a/v2rayN/v2rayN/Handler/StatisticsV2ray.cs +++ b/v2rayN/v2rayN/Handler/StatisticsV2ray.cs @@ -3,117 +3,116 @@ using Grpc.Net.Client; using ProtosLib.Statistics; using v2rayN.Mode; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class StatisticsV2ray { - internal class StatisticsV2ray + private Mode.Config _config; + private GrpcChannel _channel; + private StatsService.StatsServiceClient _client; + private bool _exitFlag; + private Action _updateFunc; + + public StatisticsV2ray(Mode.Config config, Action update) { - private Mode.Config _config; - private GrpcChannel _channel; - private StatsService.StatsServiceClient _client; - private bool _exitFlag; - private Action _updateFunc; + _config = config; + _updateFunc = update; + _exitFlag = false; - public StatisticsV2ray(Mode.Config config, Action update) + GrpcInit(); + + Task.Run(Run); + } + + private void GrpcInit() + { + if (_channel == null) { - _config = config; - _updateFunc = update; - _exitFlag = false; - - GrpcInit(); - - Task.Run(Run); + _channel = GrpcChannel.ForAddress($"{Global.httpProtocol}{Global.Loopback}:{Global.statePort}"); + _client = new StatsService.StatsServiceClient(_channel); } + } - private void GrpcInit() + public void Close() + { + _exitFlag = true; + } + + private async void Run() + { + while (!_exitFlag) { - if (_channel == null) - { - _channel = GrpcChannel.ForAddress($"{Global.httpProtocol}{Global.Loopback}:{Global.statePort}"); - _client = new StatsService.StatsServiceClient(_channel); - } - } - - public void Close() - { - _exitFlag = true; - } - - private async void Run() - { - while (!_exitFlag) - { - try - { - if (_channel.State == ConnectivityState.Ready) - { - QueryStatsResponse? res = null; - try - { - res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true }); - } - catch - { - } - - if (res != null) - { - ParseOutput(res.Stat, out ServerSpeedItem server); - _updateFunc(server); - } - } - await Task.Delay(1000); - await _channel.ConnectAsync(); - } - catch - { - } - } - } - - private void ParseOutput(Google.Protobuf.Collections.RepeatedField source, out ServerSpeedItem server) - { - server = new(); try { - foreach (Stat stat in source) + if (_channel.State == ConnectivityState.Ready) { - string name = stat.Name; - long value = stat.Value / 1024; //KByte - string[] nStr = name.Split(">>>".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); - string type = ""; - - name = name.Trim(); - - name = nStr[1]; - type = nStr[3]; - - if (name == Global.agentTag) + QueryStatsResponse? res = null; + try { - if (type == "uplink") - { - server.proxyUp = value; - } - else if (type == "downlink") - { - server.proxyDown = value; - } + res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true }); } - else if (name == Global.directTag) + catch { - if (type == "uplink") - { - server.directUp = value; - } - else if (type == "downlink") - { - server.directDown = value; - } + } + + if (res != null) + { + ParseOutput(res.Stat, out ServerSpeedItem server); + _updateFunc(server); } } + await Task.Delay(1000); + await _channel.ConnectAsync(); } catch { } } } + + private void ParseOutput(Google.Protobuf.Collections.RepeatedField source, out ServerSpeedItem server) + { + server = new(); + try + { + foreach (Stat stat in source) + { + string name = stat.Name; + long value = stat.Value / 1024; //KByte + string[] nStr = name.Split(">>>".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + string type = ""; + + name = name.Trim(); + + name = nStr[1]; + type = nStr[3]; + + if (name == Global.agentTag) + { + if (type == "uplink") + { + server.proxyUp = value; + } + else if (type == "downlink") + { + server.proxyDown = value; + } + } + else if (name == Global.directTag) + { + if (type == "uplink") + { + server.directUp = value; + } + else if (type == "downlink") + { + server.directDown = value; + } + } + } + } + catch + { + } + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/SysProxyHandle.cs b/v2rayN/v2rayN/Handler/SysProxyHandle.cs index 44a25aec..fb68964b 100644 --- a/v2rayN/v2rayN/Handler/SysProxyHandle.cs +++ b/v2rayN/v2rayN/Handler/SysProxyHandle.cs @@ -6,217 +6,216 @@ using v2rayN.Mode; using v2rayN.Properties; using v2rayN.Tool; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +public static class SysProxyHandle { - public static class SysProxyHandle + //private const string _userWininetConfigFile = "user-wininet.json"; + + //private static string _queryStr; + + // In general, this won't change + // format: + // + // + // + // + private static SysproxyConfig? _userSettings = null; + + private enum RET_ERRORS : int { - //private const string _userWininetConfigFile = "user-wininet.json"; + RET_NO_ERROR = 0, + INVALID_FORMAT = 1, + NO_PERMISSION = 2, + SYSCALL_FAILED = 3, + NO_MEMORY = 4, + INVAILD_OPTION_COUNT = 5, + }; - //private static string _queryStr; - - // In general, this won't change - // format: - // - // - // - // - private static SysproxyConfig? _userSettings = null; - - private enum RET_ERRORS : int + static SysProxyHandle() + { + try { - RET_NO_ERROR = 0, - INVALID_FORMAT = 1, - NO_PERMISSION = 2, - SYSCALL_FAILED = 3, - NO_MEMORY = 4, - INVAILD_OPTION_COUNT = 5, - }; - - static SysProxyHandle() - { - try - { - FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"), - Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe); - } - catch (IOException ex) - { - Utils.SaveLog(ex.Message, ex); - } + FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"), + Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe); } - - public static bool UpdateSysProxy(Config config, bool forceDisable) + catch (IOException ex) { - var type = config.sysProxyType; - - if (forceDisable && type != ESysProxyType.Unchanged) - { - type = ESysProxyType.ForcedClear; - } - - try - { - int port = LazyConfig.Instance.GetLocalPort(Global.InboundHttp); - int portSocks = LazyConfig.Instance.GetLocalPort(Global.InboundSocks); - int portPac = LazyConfig.Instance.GetLocalPort(ESysProxyType.Pac.ToString()); - if (port <= 0) - { - return false; - } - if (type == ESysProxyType.ForcedChange) - { - var strExceptions = $"{config.constItem.defIEProxyExceptions};{config.systemProxyExceptions}"; - - var strProxy = string.Empty; - if (Utils.IsNullOrEmpty(config.systemProxyAdvancedProtocol)) - { - strProxy = $"{Global.Loopback}:{port}"; - } - else - { - strProxy = config.systemProxyAdvancedProtocol - .Replace("{ip}", Global.Loopback) - .Replace("{http_port}", port.ToString()) - .Replace("{socks_port}", portSocks.ToString()); - } - ProxySetting.SetProxy(strProxy, strExceptions, 2); - SetIEProxy(true, strProxy, strExceptions); - } - else if (type == ESysProxyType.ForcedClear) - { - ProxySetting.UnsetProxy(); - ResetIEProxy(); - } - else if (type == ESysProxyType.Unchanged) - { - } - else if (type == ESysProxyType.Pac) - { - PacHandler.Start(Utils.GetConfigPath(), port, portPac); - var strProxy = $"{Global.httpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}"; - ProxySetting.SetProxy(strProxy, "", 4); - SetIEProxy(false, strProxy, ""); - } - - if (type != ESysProxyType.Pac) - { - PacHandler.Stop(); - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return true; - } - - public static void ResetIEProxy4WindowsShutDown() - { - try - { - //TODO To be verified - Utils.RegWriteValue(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", "ProxyEnable", 0); - } - catch - { - } - } - - public static void SetIEProxy(bool global, string strProxy, string strExceptions) - { - string arguments = global - ? $"global {strProxy} {strExceptions}" - : $"pac {strProxy}"; - - ExecSysproxy(arguments); - } - - // set system proxy to 1 (null) (null) (null) - public static void ResetIEProxy() - { - ExecSysproxy("set 1 - - -"); - } - - private static void ExecSysproxy(string arguments) - { - // using event to avoid hanging when redirect standard output/error - // ref: https://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why - // and http://blog.csdn.net/zhangweixing0/article/details/7356841 - using AutoResetEvent outputWaitHandle = new(false); - using AutoResetEvent errorWaitHandle = new(false); - using Process process = new(); - - // Configure the process using the StartInfo properties. - process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe"); - process.StartInfo.Arguments = arguments; - process.StartInfo.WorkingDirectory = Utils.GetTempPath(); - process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardError = true; - process.StartInfo.RedirectStandardOutput = true; - - // Need to provide encoding info, or output/error strings we got will be wrong. - process.StartInfo.StandardOutputEncoding = Encoding.Unicode; - process.StartInfo.StandardErrorEncoding = Encoding.Unicode; - - process.StartInfo.CreateNoWindow = true; - - StringBuilder output = new(1024); - StringBuilder error = new(1024); - - process.OutputDataReceived += (sender, e) => - { - if (e.Data == null) - { - outputWaitHandle.Set(); - } - else - { - output.AppendLine(e.Data); - } - }; - process.ErrorDataReceived += (sender, e) => - { - if (e.Data == null) - { - errorWaitHandle.Set(); - } - else - { - error.AppendLine(e.Data); - } - }; - try - { - process.Start(); - - process.BeginErrorReadLine(); - process.BeginOutputReadLine(); - - process.WaitForExit(); - } - catch (System.ComponentModel.Win32Exception e) - { - // log the arguments - throw new Exception(process.StartInfo.Arguments); - } - string stderr = error.ToString(); - string stdout = output.ToString(); - - int exitCode = process.ExitCode; - if (exitCode != (int)RET_ERRORS.RET_NO_ERROR) - { - throw new Exception(stderr); - } - - //if (arguments == "query") - //{ - // if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty()) - // { - // throw new Exception("failed to query wininet settings"); - // } - // _queryStr = stdout; - //} + Utils.SaveLog(ex.Message, ex); } } + + public static bool UpdateSysProxy(Config config, bool forceDisable) + { + var type = config.sysProxyType; + + if (forceDisable && type != ESysProxyType.Unchanged) + { + type = ESysProxyType.ForcedClear; + } + + try + { + int port = LazyConfig.Instance.GetLocalPort(Global.InboundHttp); + int portSocks = LazyConfig.Instance.GetLocalPort(Global.InboundSocks); + int portPac = LazyConfig.Instance.GetLocalPort(ESysProxyType.Pac.ToString()); + if (port <= 0) + { + return false; + } + if (type == ESysProxyType.ForcedChange) + { + var strExceptions = $"{config.constItem.defIEProxyExceptions};{config.systemProxyExceptions}"; + + var strProxy = string.Empty; + if (Utils.IsNullOrEmpty(config.systemProxyAdvancedProtocol)) + { + strProxy = $"{Global.Loopback}:{port}"; + } + else + { + strProxy = config.systemProxyAdvancedProtocol + .Replace("{ip}", Global.Loopback) + .Replace("{http_port}", port.ToString()) + .Replace("{socks_port}", portSocks.ToString()); + } + ProxySetting.SetProxy(strProxy, strExceptions, 2); + SetIEProxy(true, strProxy, strExceptions); + } + else if (type == ESysProxyType.ForcedClear) + { + ProxySetting.UnsetProxy(); + ResetIEProxy(); + } + else if (type == ESysProxyType.Unchanged) + { + } + else if (type == ESysProxyType.Pac) + { + PacHandler.Start(Utils.GetConfigPath(), port, portPac); + var strProxy = $"{Global.httpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}"; + ProxySetting.SetProxy(strProxy, "", 4); + SetIEProxy(false, strProxy, ""); + } + + if (type != ESysProxyType.Pac) + { + PacHandler.Stop(); + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + } + return true; + } + + public static void ResetIEProxy4WindowsShutDown() + { + try + { + //TODO To be verified + Utils.RegWriteValue(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", "ProxyEnable", 0); + } + catch + { + } + } + + public static void SetIEProxy(bool global, string strProxy, string strExceptions) + { + string arguments = global + ? $"global {strProxy} {strExceptions}" + : $"pac {strProxy}"; + + ExecSysproxy(arguments); + } + + // set system proxy to 1 (null) (null) (null) + public static void ResetIEProxy() + { + ExecSysproxy("set 1 - - -"); + } + + private static void ExecSysproxy(string arguments) + { + // using event to avoid hanging when redirect standard output/error + // ref: https://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why + // and http://blog.csdn.net/zhangweixing0/article/details/7356841 + using AutoResetEvent outputWaitHandle = new(false); + using AutoResetEvent errorWaitHandle = new(false); + using Process process = new(); + + // Configure the process using the StartInfo properties. + process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe"); + process.StartInfo.Arguments = arguments; + process.StartInfo.WorkingDirectory = Utils.GetTempPath(); + process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.RedirectStandardOutput = true; + + // Need to provide encoding info, or output/error strings we got will be wrong. + process.StartInfo.StandardOutputEncoding = Encoding.Unicode; + process.StartInfo.StandardErrorEncoding = Encoding.Unicode; + + process.StartInfo.CreateNoWindow = true; + + StringBuilder output = new(1024); + StringBuilder error = new(1024); + + process.OutputDataReceived += (sender, e) => + { + if (e.Data == null) + { + outputWaitHandle.Set(); + } + else + { + output.AppendLine(e.Data); + } + }; + process.ErrorDataReceived += (sender, e) => + { + if (e.Data == null) + { + errorWaitHandle.Set(); + } + else + { + error.AppendLine(e.Data); + } + }; + try + { + process.Start(); + + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + + process.WaitForExit(); + } + catch (System.ComponentModel.Win32Exception e) + { + // log the arguments + throw new Exception(process.StartInfo.Arguments); + } + string stderr = error.ToString(); + string stdout = output.ToString(); + + int exitCode = process.ExitCode; + if (exitCode != (int)RET_ERRORS.RET_NO_ERROR) + { + throw new Exception(stderr); + } + + //if (arguments == "query") + //{ + // if (stdout.IsNullOrWhiteSpace() || stdout.IsNullOrEmpty()) + // { + // throw new Exception("failed to query wininet settings"); + // } + // _queryStr = stdout; + //} + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/UpdateHandle.cs b/v2rayN/v2rayN/Handler/UpdateHandle.cs index 24c01fd6..8d9803b8 100644 --- a/v2rayN/v2rayN/Handler/UpdateHandle.cs +++ b/v2rayN/v2rayN/Handler/UpdateHandle.cs @@ -11,636 +11,635 @@ using v2rayN.Mode; using v2rayN.Resx; using v2rayN.Tool; -namespace v2rayN.Handler +namespace v2rayN.Handler; + +internal class UpdateHandle { - internal class UpdateHandle + private Action _updateFunc; + private Config _config; + + public event EventHandler AbsoluteCompleted; + + public class ResultEventArgs : EventArgs { - private Action _updateFunc; - private Config _config; + public bool Success; + public string Msg; - public event EventHandler AbsoluteCompleted; - - public class ResultEventArgs : EventArgs + public ResultEventArgs(bool success, string msg) { - public bool Success; - public string Msg; - - public ResultEventArgs(bool success, string msg) - { - Success = success; - Msg = msg; - } + Success = success; + Msg = msg; } + } - public void CheckUpdateGuiN(Config config, Action update, bool preRelease) + public void CheckUpdateGuiN(Config config, Action update, bool preRelease) + { + _config = config; + _updateFunc = update; + var url = string.Empty; + + DownloadHandle downloadHandle = new(); + downloadHandle.UpdateCompleted += (sender2, args) => { - _config = config; - _updateFunc = update; - var url = string.Empty; - - DownloadHandle downloadHandle = new(); - downloadHandle.UpdateCompleted += (sender2, args) => + if (args.Success) { - if (args.Success) - { - _updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully); + _updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully); - try + try + { + string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url)); + fileName = Utils.UrlEncode(fileName); + Process process = new() { - string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url)); - fileName = Utils.UrlEncode(fileName); - Process process = new() + StartInfo = new ProcessStartInfo { - StartInfo = new ProcessStartInfo - { - FileName = "v2rayUpgrade.exe", - Arguments = $"\"{fileName}\"", - WorkingDirectory = Utils.StartupPath() - } - }; - process.Start(); - if (process.Id > 0) - { - _updateFunc(true, ""); + FileName = "v2rayUpgrade.exe", + Arguments = $"\"{fileName}\"", + WorkingDirectory = Utils.StartupPath() } - } - catch (Exception ex) - { - _updateFunc(false, ex.Message); - } - } - else - { - _updateFunc(false, args.Msg); - } - }; - downloadHandle.Error += (sender2, args) => - { - _updateFunc(false, args.GetException().Message); - }; - AbsoluteCompleted += (sender2, args) => - { - if (args.Success) - { - _updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, "v2rayN")); - - url = args.Msg; - _ = askToDownload(downloadHandle, url, true); - } - else - { - Locator.Current.GetService()?.Enqueue(args.Msg); - _updateFunc(false, args.Msg); - } - }; - _updateFunc(false, string.Format(ResUI.MsgStartUpdating, "v2rayN")); - CheckUpdateAsync(ECoreType.v2rayN, preRelease); - } - - public void CheckUpdateCore(ECoreType type, Config config, Action update, bool preRelease) - { - _config = config; - _updateFunc = update; - var url = string.Empty; - - DownloadHandle downloadHandle = new(); - downloadHandle.UpdateCompleted += (sender2, args) => - { - if (args.Success) - { - _updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully); - _updateFunc(false, ResUI.MsgUnpacking); - - try - { - _updateFunc(true, url); - } - catch (Exception ex) - { - _updateFunc(false, ex.Message); - } - } - else - { - _updateFunc(false, args.Msg); - } - }; - downloadHandle.Error += (sender2, args) => - { - _updateFunc(true, args.GetException().Message); - }; - - AbsoluteCompleted += (sender2, args) => - { - if (args.Success) - { - _updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, "Core")); - url = args.Msg; - _ = askToDownload(downloadHandle, url, true); - } - else - { - Locator.Current.GetService()?.Enqueue(args.Msg); - _updateFunc(false, args.Msg); - } - }; - _updateFunc(false, string.Format(ResUI.MsgStartUpdating, "Core")); - CheckUpdateAsync(type, preRelease); - } - - public void UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action update) - { - _config = config; - _updateFunc = update; - - _updateFunc(false, ResUI.MsgUpdateSubscriptionStart); - var subItem = LazyConfig.Instance.SubItems().OrderBy(t => t.sort).ToList(); - - if (subItem == null || subItem.Count <= 0) - { - _updateFunc(false, ResUI.MsgNoValidSubscription); - return; - } - - Task.Run(async () => - { - foreach (var item in subItem) - { - string id = item.id.TrimEx(); - string url = item.url.TrimEx(); - string userAgent = item.userAgent.TrimEx(); - string hashCode = $"{item.remarks}->"; - if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (!Utils.IsNullOrEmpty(subId) && item.id != subId)) - { - //_updateFunc(false, $"{hashCode}{ResUI.MsgNoValidSubscription}"); - continue; - } - if (!url.StartsWith(Global.httpsProtocol) && !url.StartsWith(Global.httpProtocol)) - { - continue; - } - if (item.enabled == false) - { - _updateFunc(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}"); - continue; - } - - var downloadHandle = new DownloadHandle(); - downloadHandle.Error += (sender2, args) => - { - _updateFunc(false, $"{hashCode}{args.GetException().Message}"); }; - - _updateFunc(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}"); - - //one url - url = Utils.GetPunycode(url); - //convert - if (!Utils.IsNullOrEmpty(item.convertTarget)) + process.Start(); + if (process.Id > 0) { - var subConvertUrl = string.IsNullOrEmpty(config.constItem.subConvertUrl) ? Global.SubConvertUrls.FirstOrDefault() : config.constItem.subConvertUrl; - url = string.Format(subConvertUrl!, Utils.UrlEncode(url)); - if (!url.Contains("target=")) - { - url += string.Format("&target={0}", item.convertTarget); - } - if (!url.Contains("config=")) - { - url += string.Format("&config={0}", Global.SubConvertConfig.FirstOrDefault()); - } - } - var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent); - if (blProxy && Utils.IsNullOrEmpty(result)) - { - result = await downloadHandle.TryDownloadString(url, false, userAgent); - } - - //more url - if (Utils.IsNullOrEmpty(item.convertTarget) && !Utils.IsNullOrEmpty(item.moreUrl.TrimEx())) - { - if (!Utils.IsNullOrEmpty(result) && Utils.IsBase64String(result)) - { - result = Utils.Base64Decode(result); - } - - var lstUrl = new List - { - item.moreUrl.TrimEx().Split(",") - }; - foreach (var it in lstUrl) - { - var url2 = Utils.GetPunycode(it); - if (Utils.IsNullOrEmpty(url2)) - { - continue; - } - - var result2 = await downloadHandle.TryDownloadString(url2, blProxy, userAgent); - if (blProxy && Utils.IsNullOrEmpty(result2)) - { - result2 = await downloadHandle.TryDownloadString(url2, false, userAgent); - } - if (!Utils.IsNullOrEmpty(result2)) - { - if (Utils.IsBase64String(result2)) - { - result += Utils.Base64Decode(result2); - } - else - { - result += result2; - } - } - } - } - - if (Utils.IsNullOrEmpty(result)) - { - _updateFunc(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}"); - } - else - { - _updateFunc(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}"); - if (result!.Length < 99) - { - _updateFunc(false, $"{hashCode}{result}"); - } - - int ret = ConfigHandler.AddBatchServers(ref config, result, id, true); - if (ret <= 0) - { - Utils.SaveLog("FailedImportSubscription"); - Utils.SaveLog(result); - } - _updateFunc(false, - ret > 0 - ? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}" - : $"{hashCode}{ResUI.MsgFailedImportSubscription}"); - } - _updateFunc(false, "-------------------------------------------------------"); - } - - _updateFunc(true, $"{ResUI.MsgUpdateSubscriptionEnd}"); - }); - } - - public void UpdateGeoFileAll(Config config, Action update) - { - Task.Run(async () => - { - await UpdateGeoFile("geosite", _config, update); - await UpdateGeoFile("geoip", _config, update); - - await UpdateGeoFile4Singbox("geosite", _config, false, update); - await UpdateGeoFile4Singbox("geoip", _config, true, update); - }); - } - - public void RunAvailabilityCheck(Action update) - { - Task.Run(async () => - { - var time = await (new DownloadHandle()).RunAvailabilityCheck(null); - - update(false, string.Format(ResUI.TestMeOutput, time)); - }); - } - - #region private - - private async void CheckUpdateAsync(ECoreType type, bool preRelease) - { - try - { - var coreInfo = LazyConfig.Instance.GetCoreInfo(type); - string url = coreInfo.coreReleaseApiUrl; - - var result = await (new DownloadHandle()).DownloadStringAsync(url, true, ""); - if (!Utils.IsNullOrEmpty(result)) - { - responseHandler(type, result, preRelease); - } - else - { - Utils.SaveLog("StatusCode error: " + url); - return; - } - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - _updateFunc(false, ex.Message); - } - } - - /// - /// 获取V2RayCore版本 - /// - private SemanticVersion getCoreVersion(ECoreType type) - { - try - { - var coreInfo = LazyConfig.Instance.GetCoreInfo(type); - string filePath = string.Empty; - foreach (string name in coreInfo.coreExes) - { - string vName = $"{name}.exe"; - vName = Utils.GetBinPath(vName, coreInfo.coreType); - if (File.Exists(vName)) - { - filePath = vName; - break; + _updateFunc(true, ""); } } - - if (!File.Exists(filePath)) + catch (Exception ex) { - string msg = string.Format(ResUI.NotFoundCore, @"", "", ""); - //ShowMsg(true, msg); - return new SemanticVersion(""); - } - - using Process p = new(); - p.StartInfo.FileName = filePath; - p.StartInfo.Arguments = coreInfo.versionArg; - p.StartInfo.WorkingDirectory = Utils.StartupPath(); - p.StartInfo.UseShellExecute = false; - p.StartInfo.RedirectStandardOutput = true; - p.StartInfo.CreateNoWindow = true; - p.StartInfo.StandardOutputEncoding = Encoding.UTF8; - p.Start(); - p.WaitForExit(5000); - string echo = p.StandardOutput.ReadToEnd(); - string version = string.Empty; - switch (type) - { - case ECoreType.v2fly: - case ECoreType.SagerNet: - case ECoreType.Xray: - case ECoreType.v2fly_v5: - version = Regex.Match(echo, $"{coreInfo.match} ([0-9.]+) \\(").Groups[1].Value; - break; - - case ECoreType.clash: - case ECoreType.clash_meta: - version = Regex.Match(echo, $"v[0-9.]+").Groups[0].Value; - break; - - case ECoreType.sing_box: - version = Regex.Match(echo, $"([0-9.]+)").Groups[1].Value; - break; - } - return new SemanticVersion(version); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - _updateFunc(false, ex.Message); - return new SemanticVersion(""); - } - } - - private void responseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease) - { - try - { - var gitHubReleases = Utils.FromJson>(gitHubReleaseApi); - SemanticVersion version; - if (preRelease) - { - version = new SemanticVersion(gitHubReleases!.First().TagName); - } - else - { - version = new SemanticVersion(gitHubReleases!.First(r => r.Prerelease == false).TagName); - } - var coreInfo = LazyConfig.Instance.GetCoreInfo(type); - - SemanticVersion curVersion; - string message; - string url; - switch (type) - { - case ECoreType.v2fly: - case ECoreType.SagerNet: - case ECoreType.Xray: - case ECoreType.v2fly_v5: - { - curVersion = getCoreVersion(type); - message = string.Format(ResUI.IsLatestCore, curVersion.ToVersionString("v")); - string osBit = "64"; - switch (RuntimeInformation.ProcessArchitecture) - { - case Architecture.Arm64: - osBit = "arm64-v8a"; - break; - - case Architecture.X86: - osBit = "32"; - break; - - default: - osBit = "64"; - break; - } - - url = string.Format(coreInfo.coreDownloadUrl64, version.ToVersionString("v"), osBit); - break; - } - case ECoreType.clash: - case ECoreType.clash_meta: - { - curVersion = getCoreVersion(type); - message = string.Format(ResUI.IsLatestCore, curVersion); - switch (RuntimeInformation.ProcessArchitecture) - { - case Architecture.Arm64: - url = coreInfo.coreDownloadUrlArm64; - break; - - case Architecture.X86: - url = coreInfo.coreDownloadUrl32; - break; - - default: - url = coreInfo.coreDownloadUrl64; - break; - } - url = string.Format(url, version.ToVersionString("v")); - break; - } - case ECoreType.sing_box: - { - curVersion = getCoreVersion(type); - message = string.Format(ResUI.IsLatestCore, curVersion.ToVersionString("v")); - switch (RuntimeInformation.ProcessArchitecture) - { - case Architecture.Arm64: - url = coreInfo.coreDownloadUrlArm64; - break; - - case Architecture.X86: - url = coreInfo.coreDownloadUrl32; - break; - - default: - url = coreInfo.coreDownloadUrl64; - break; - } - url = string.Format(url, version.ToVersionString("v"), version); - break; - } - case ECoreType.v2rayN: - { - curVersion = new SemanticVersion(FileVersionInfo.GetVersionInfo(Utils.GetExePath()).FileVersion.ToString()); - message = string.Format(ResUI.IsLatestN, curVersion); - switch (RuntimeInformation.ProcessArchitecture) - { - case Architecture.Arm64: - url = string.Format(coreInfo.coreDownloadUrlArm64, version); - break; - - case Architecture.X86: - url = string.Format(coreInfo.coreDownloadUrl32, version); - break; - - default: - url = string.Format(coreInfo.coreDownloadUrl64, version); - break; - } - break; - } - default: - throw new ArgumentException("Type"); - } - - if (curVersion >= version && version != new SemanticVersion(0, 0, 0)) - { - AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message)); - return; - } - - AbsoluteCompleted?.Invoke(this, new ResultEventArgs(true, url)); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - _updateFunc(false, ex.Message); - } - } - - private async Task askToDownload(DownloadHandle downloadHandle, string url, bool blAsk) - { - bool blDownload = false; - if (blAsk) - { - if (UI.ShowYesNo(string.Format(ResUI.DownloadYesNo, url)) == MessageBoxResult.Yes) - { - blDownload = true; + _updateFunc(false, ex.Message); } } else + { + _updateFunc(false, args.Msg); + } + }; + downloadHandle.Error += (sender2, args) => + { + _updateFunc(false, args.GetException().Message); + }; + AbsoluteCompleted += (sender2, args) => + { + if (args.Success) + { + _updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, "v2rayN")); + + url = args.Msg; + _ = askToDownload(downloadHandle, url, true); + } + else + { + Locator.Current.GetService()?.Enqueue(args.Msg); + _updateFunc(false, args.Msg); + } + }; + _updateFunc(false, string.Format(ResUI.MsgStartUpdating, "v2rayN")); + CheckUpdateAsync(ECoreType.v2rayN, preRelease); + } + + public void CheckUpdateCore(ECoreType type, Config config, Action update, bool preRelease) + { + _config = config; + _updateFunc = update; + var url = string.Empty; + + DownloadHandle downloadHandle = new(); + downloadHandle.UpdateCompleted += (sender2, args) => + { + if (args.Success) + { + _updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully); + _updateFunc(false, ResUI.MsgUnpacking); + + try + { + _updateFunc(true, url); + } + catch (Exception ex) + { + _updateFunc(false, ex.Message); + } + } + else + { + _updateFunc(false, args.Msg); + } + }; + downloadHandle.Error += (sender2, args) => + { + _updateFunc(true, args.GetException().Message); + }; + + AbsoluteCompleted += (sender2, args) => + { + if (args.Success) + { + _updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, "Core")); + url = args.Msg; + _ = askToDownload(downloadHandle, url, true); + } + else + { + Locator.Current.GetService()?.Enqueue(args.Msg); + _updateFunc(false, args.Msg); + } + }; + _updateFunc(false, string.Format(ResUI.MsgStartUpdating, "Core")); + CheckUpdateAsync(type, preRelease); + } + + public void UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action update) + { + _config = config; + _updateFunc = update; + + _updateFunc(false, ResUI.MsgUpdateSubscriptionStart); + var subItem = LazyConfig.Instance.SubItems().OrderBy(t => t.sort).ToList(); + + if (subItem == null || subItem.Count <= 0) + { + _updateFunc(false, ResUI.MsgNoValidSubscription); + return; + } + + Task.Run(async () => + { + foreach (var item in subItem) + { + string id = item.id.TrimEx(); + string url = item.url.TrimEx(); + string userAgent = item.userAgent.TrimEx(); + string hashCode = $"{item.remarks}->"; + if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (!Utils.IsNullOrEmpty(subId) && item.id != subId)) + { + //_updateFunc(false, $"{hashCode}{ResUI.MsgNoValidSubscription}"); + continue; + } + if (!url.StartsWith(Global.httpsProtocol) && !url.StartsWith(Global.httpProtocol)) + { + continue; + } + if (item.enabled == false) + { + _updateFunc(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}"); + continue; + } + + var downloadHandle = new DownloadHandle(); + downloadHandle.Error += (sender2, args) => + { + _updateFunc(false, $"{hashCode}{args.GetException().Message}"); + }; + + _updateFunc(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}"); + + //one url + url = Utils.GetPunycode(url); + //convert + if (!Utils.IsNullOrEmpty(item.convertTarget)) + { + var subConvertUrl = string.IsNullOrEmpty(config.constItem.subConvertUrl) ? Global.SubConvertUrls.FirstOrDefault() : config.constItem.subConvertUrl; + url = string.Format(subConvertUrl!, Utils.UrlEncode(url)); + if (!url.Contains("target=")) + { + url += string.Format("&target={0}", item.convertTarget); + } + if (!url.Contains("config=")) + { + url += string.Format("&config={0}", Global.SubConvertConfig.FirstOrDefault()); + } + } + var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent); + if (blProxy && Utils.IsNullOrEmpty(result)) + { + result = await downloadHandle.TryDownloadString(url, false, userAgent); + } + + //more url + if (Utils.IsNullOrEmpty(item.convertTarget) && !Utils.IsNullOrEmpty(item.moreUrl.TrimEx())) + { + if (!Utils.IsNullOrEmpty(result) && Utils.IsBase64String(result)) + { + result = Utils.Base64Decode(result); + } + + var lstUrl = new List + { + item.moreUrl.TrimEx().Split(",") + }; + foreach (var it in lstUrl) + { + var url2 = Utils.GetPunycode(it); + if (Utils.IsNullOrEmpty(url2)) + { + continue; + } + + var result2 = await downloadHandle.TryDownloadString(url2, blProxy, userAgent); + if (blProxy && Utils.IsNullOrEmpty(result2)) + { + result2 = await downloadHandle.TryDownloadString(url2, false, userAgent); + } + if (!Utils.IsNullOrEmpty(result2)) + { + if (Utils.IsBase64String(result2)) + { + result += Utils.Base64Decode(result2); + } + else + { + result += result2; + } + } + } + } + + if (Utils.IsNullOrEmpty(result)) + { + _updateFunc(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}"); + } + else + { + _updateFunc(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}"); + if (result!.Length < 99) + { + _updateFunc(false, $"{hashCode}{result}"); + } + + int ret = ConfigHandler.AddBatchServers(ref config, result, id, true); + if (ret <= 0) + { + Utils.SaveLog("FailedImportSubscription"); + Utils.SaveLog(result); + } + _updateFunc(false, + ret > 0 + ? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}" + : $"{hashCode}{ResUI.MsgFailedImportSubscription}"); + } + _updateFunc(false, "-------------------------------------------------------"); + } + + _updateFunc(true, $"{ResUI.MsgUpdateSubscriptionEnd}"); + }); + } + + public void UpdateGeoFileAll(Config config, Action update) + { + Task.Run(async () => + { + await UpdateGeoFile("geosite", _config, update); + await UpdateGeoFile("geoip", _config, update); + + await UpdateGeoFile4Singbox("geosite", _config, false, update); + await UpdateGeoFile4Singbox("geoip", _config, true, update); + }); + } + + public void RunAvailabilityCheck(Action update) + { + Task.Run(async () => + { + var time = await (new DownloadHandle()).RunAvailabilityCheck(null); + + update(false, string.Format(ResUI.TestMeOutput, time)); + }); + } + + #region private + + private async void CheckUpdateAsync(ECoreType type, bool preRelease) + { + try + { + var coreInfo = LazyConfig.Instance.GetCoreInfo(type); + string url = coreInfo.coreReleaseApiUrl; + + var result = await (new DownloadHandle()).DownloadStringAsync(url, true, ""); + if (!Utils.IsNullOrEmpty(result)) + { + responseHandler(type, result, preRelease); + } + else + { + Utils.SaveLog("StatusCode error: " + url); + return; + } + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + _updateFunc(false, ex.Message); + } + } + + /// + /// 获取V2RayCore版本 + /// + private SemanticVersion getCoreVersion(ECoreType type) + { + try + { + var coreInfo = LazyConfig.Instance.GetCoreInfo(type); + string filePath = string.Empty; + foreach (string name in coreInfo.coreExes) + { + string vName = $"{name}.exe"; + vName = Utils.GetBinPath(vName, coreInfo.coreType); + if (File.Exists(vName)) + { + filePath = vName; + break; + } + } + + if (!File.Exists(filePath)) + { + string msg = string.Format(ResUI.NotFoundCore, @"", "", ""); + //ShowMsg(true, msg); + return new SemanticVersion(""); + } + + using Process p = new(); + p.StartInfo.FileName = filePath; + p.StartInfo.Arguments = coreInfo.versionArg; + p.StartInfo.WorkingDirectory = Utils.StartupPath(); + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.CreateNoWindow = true; + p.StartInfo.StandardOutputEncoding = Encoding.UTF8; + p.Start(); + p.WaitForExit(5000); + string echo = p.StandardOutput.ReadToEnd(); + string version = string.Empty; + switch (type) + { + case ECoreType.v2fly: + case ECoreType.SagerNet: + case ECoreType.Xray: + case ECoreType.v2fly_v5: + version = Regex.Match(echo, $"{coreInfo.match} ([0-9.]+) \\(").Groups[1].Value; + break; + + case ECoreType.clash: + case ECoreType.clash_meta: + version = Regex.Match(echo, $"v[0-9.]+").Groups[0].Value; + break; + + case ECoreType.sing_box: + version = Regex.Match(echo, $"([0-9.]+)").Groups[1].Value; + break; + } + return new SemanticVersion(version); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + _updateFunc(false, ex.Message); + return new SemanticVersion(""); + } + } + + private void responseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease) + { + try + { + var gitHubReleases = Utils.FromJson>(gitHubReleaseApi); + SemanticVersion version; + if (preRelease) + { + version = new SemanticVersion(gitHubReleases!.First().TagName); + } + else + { + version = new SemanticVersion(gitHubReleases!.First(r => r.Prerelease == false).TagName); + } + var coreInfo = LazyConfig.Instance.GetCoreInfo(type); + + SemanticVersion curVersion; + string message; + string url; + switch (type) + { + case ECoreType.v2fly: + case ECoreType.SagerNet: + case ECoreType.Xray: + case ECoreType.v2fly_v5: + { + curVersion = getCoreVersion(type); + message = string.Format(ResUI.IsLatestCore, curVersion.ToVersionString("v")); + string osBit = "64"; + switch (RuntimeInformation.ProcessArchitecture) + { + case Architecture.Arm64: + osBit = "arm64-v8a"; + break; + + case Architecture.X86: + osBit = "32"; + break; + + default: + osBit = "64"; + break; + } + + url = string.Format(coreInfo.coreDownloadUrl64, version.ToVersionString("v"), osBit); + break; + } + case ECoreType.clash: + case ECoreType.clash_meta: + { + curVersion = getCoreVersion(type); + message = string.Format(ResUI.IsLatestCore, curVersion); + switch (RuntimeInformation.ProcessArchitecture) + { + case Architecture.Arm64: + url = coreInfo.coreDownloadUrlArm64; + break; + + case Architecture.X86: + url = coreInfo.coreDownloadUrl32; + break; + + default: + url = coreInfo.coreDownloadUrl64; + break; + } + url = string.Format(url, version.ToVersionString("v")); + break; + } + case ECoreType.sing_box: + { + curVersion = getCoreVersion(type); + message = string.Format(ResUI.IsLatestCore, curVersion.ToVersionString("v")); + switch (RuntimeInformation.ProcessArchitecture) + { + case Architecture.Arm64: + url = coreInfo.coreDownloadUrlArm64; + break; + + case Architecture.X86: + url = coreInfo.coreDownloadUrl32; + break; + + default: + url = coreInfo.coreDownloadUrl64; + break; + } + url = string.Format(url, version.ToVersionString("v"), version); + break; + } + case ECoreType.v2rayN: + { + curVersion = new SemanticVersion(FileVersionInfo.GetVersionInfo(Utils.GetExePath()).FileVersion.ToString()); + message = string.Format(ResUI.IsLatestN, curVersion); + switch (RuntimeInformation.ProcessArchitecture) + { + case Architecture.Arm64: + url = string.Format(coreInfo.coreDownloadUrlArm64, version); + break; + + case Architecture.X86: + url = string.Format(coreInfo.coreDownloadUrl32, version); + break; + + default: + url = string.Format(coreInfo.coreDownloadUrl64, version); + break; + } + break; + } + default: + throw new ArgumentException("Type"); + } + + if (curVersion >= version && version != new SemanticVersion(0, 0, 0)) + { + AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message)); + return; + } + + AbsoluteCompleted?.Invoke(this, new ResultEventArgs(true, url)); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + _updateFunc(false, ex.Message); + } + } + + private async Task askToDownload(DownloadHandle downloadHandle, string url, bool blAsk) + { + bool blDownload = false; + if (blAsk) + { + if (UI.ShowYesNo(string.Format(ResUI.DownloadYesNo, url)) == MessageBoxResult.Yes) { blDownload = true; } - if (blDownload) - { - await downloadHandle.DownloadFileAsync(url, true, 600); - } } - - private async Task UpdateGeoFile(string geoName, Config config, Action update) + else { - _config = config; - _updateFunc = update; - var url = string.Format(Global.geoUrl, geoName); - - DownloadHandle downloadHandle = new(); - downloadHandle.UpdateCompleted += (sender2, args) => - { - if (args.Success) - { - _updateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, geoName)); - - try - { - string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url)); - if (File.Exists(fileName)) - { - //Global.coreTypes.ForEach(it => - //{ - // string targetPath = Utils.GetBinPath($"{geoName}.dat", (ECoreType)Enum.Parse(typeof(ECoreType), it)); - // File.Copy(fileName, targetPath, true); - //}); - string targetPath = Utils.GetBinPath($"{geoName}.dat"); - File.Copy(fileName, targetPath, true); - - File.Delete(fileName); - //_updateFunc(true, ""); - } - } - catch (Exception ex) - { - _updateFunc(false, ex.Message); - } - } - else - { - _updateFunc(false, args.Msg); - } - }; - downloadHandle.Error += (sender2, args) => - { - _updateFunc(false, args.GetException().Message); - }; - await askToDownload(downloadHandle, url, false); + blDownload = true; } - - private async Task UpdateGeoFile4Singbox(string geoName, Config config, bool needStop, Action update) + if (blDownload) { - _config = config; - _updateFunc = update; - var url = string.Format(Global.singboxGeoUrl, geoName); - - DownloadHandle downloadHandle = new(); - downloadHandle.UpdateCompleted += (sender2, args) => - { - if (args.Success) - { - _updateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, geoName)); - var coreHandler = Locator.Current.GetService(); - - try - { - if (needStop) coreHandler?.CoreStop(); - Task.Delay(1000); - string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url)); - if (File.Exists(fileName)) - { - string targetPath = Utils.GetConfigPath($"{geoName}.db"); - File.Copy(fileName, targetPath, true); - - File.Delete(fileName); - } - if (needStop) coreHandler?.LoadCore(); - } - catch (Exception ex) - { - _updateFunc(false, ex.Message); - } - } - else - { - _updateFunc(false, args.Msg); - } - }; - downloadHandle.Error += (sender2, args) => - { - _updateFunc(false, args.GetException().Message); - }; - await askToDownload(downloadHandle, url, false); + await downloadHandle.DownloadFileAsync(url, true, 600); } - - #endregion private } + + private async Task UpdateGeoFile(string geoName, Config config, Action update) + { + _config = config; + _updateFunc = update; + var url = string.Format(Global.geoUrl, geoName); + + DownloadHandle downloadHandle = new(); + downloadHandle.UpdateCompleted += (sender2, args) => + { + if (args.Success) + { + _updateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, geoName)); + + try + { + string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url)); + if (File.Exists(fileName)) + { + //Global.coreTypes.ForEach(it => + //{ + // string targetPath = Utils.GetBinPath($"{geoName}.dat", (ECoreType)Enum.Parse(typeof(ECoreType), it)); + // File.Copy(fileName, targetPath, true); + //}); + string targetPath = Utils.GetBinPath($"{geoName}.dat"); + File.Copy(fileName, targetPath, true); + + File.Delete(fileName); + //_updateFunc(true, ""); + } + } + catch (Exception ex) + { + _updateFunc(false, ex.Message); + } + } + else + { + _updateFunc(false, args.Msg); + } + }; + downloadHandle.Error += (sender2, args) => + { + _updateFunc(false, args.GetException().Message); + }; + await askToDownload(downloadHandle, url, false); + } + + private async Task UpdateGeoFile4Singbox(string geoName, Config config, bool needStop, Action update) + { + _config = config; + _updateFunc = update; + var url = string.Format(Global.singboxGeoUrl, geoName); + + DownloadHandle downloadHandle = new(); + downloadHandle.UpdateCompleted += (sender2, args) => + { + if (args.Success) + { + _updateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, geoName)); + var coreHandler = Locator.Current.GetService(); + + try + { + if (needStop) coreHandler?.CoreStop(); + Task.Delay(1000); + string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url)); + if (File.Exists(fileName)) + { + string targetPath = Utils.GetConfigPath($"{geoName}.db"); + File.Copy(fileName, targetPath, true); + + File.Delete(fileName); + } + if (needStop) coreHandler?.LoadCore(); + } + catch (Exception ex) + { + _updateFunc(false, ex.Message); + } + } + else + { + _updateFunc(false, args.Msg); + } + }; + downloadHandle.Error += (sender2, args) => + { + _updateFunc(false, args.GetException().Message); + }; + await askToDownload(downloadHandle, url, false); + } + + #endregion private } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ComboItem.cs b/v2rayN/v2rayN/Mode/ComboItem.cs index b3d77cb0..614696ca 100644 --- a/v2rayN/v2rayN/Mode/ComboItem.cs +++ b/v2rayN/v2rayN/Mode/ComboItem.cs @@ -1,15 +1,14 @@ -namespace v2rayN.Mode -{ - public class ComboItem - { - public string ID - { - get; set; - } +namespace v2rayN.Mode; - public string Text - { - get; set; - } +public class ComboItem +{ + public string ID + { + get; set; + } + + public string Text + { + get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/Config.cs b/v2rayN/v2rayN/Mode/Config.cs index f6639b93..39bbef96 100644 --- a/v2rayN/v2rayN/Mode/Config.cs +++ b/v2rayN/v2rayN/Mode/Config.cs @@ -1,37 +1,36 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +/// +/// 本软件配置文件实体类 +/// +[Serializable] +public class Config { - /// - /// 本软件配置文件实体类 - /// - [Serializable] - public class Config - { - #region property + #region property - public string indexId { get; set; } - public string subIndexId { get; set; } - public ESysProxyType sysProxyType { get; set; } - public string systemProxyExceptions { get; set; } - public string systemProxyAdvancedProtocol { get; set; } + public string indexId { get; set; } + public string subIndexId { get; set; } + public ESysProxyType sysProxyType { get; set; } + public string systemProxyExceptions { get; set; } + public string systemProxyAdvancedProtocol { get; set; } - #endregion property + #endregion property - #region other entities + #region other entities - public CoreBasicItem coreBasicItem { get; set; } - public TunModeItem tunModeItem { get; set; } - public KcpItem kcpItem { get; set; } - public GrpcItem grpcItem { get; set; } - public RoutingBasicItem routingBasicItem { get; set; } - public GUIItem guiItem { get; set; } - public UIItem uiItem { get; set; } - public ConstItem constItem { get; set; } - public SpeedTestItem speedTestItem { get; set; } - public Mux4Sbox mux4Sbox { get; set; } - public List inbound { get; set; } - public List globalHotkeys { get; set; } - public List coreTypeItem { get; set; } + public CoreBasicItem coreBasicItem { get; set; } + public TunModeItem tunModeItem { get; set; } + public KcpItem kcpItem { get; set; } + public GrpcItem grpcItem { get; set; } + public RoutingBasicItem routingBasicItem { get; set; } + public GUIItem guiItem { get; set; } + public UIItem uiItem { get; set; } + public ConstItem constItem { get; set; } + public SpeedTestItem speedTestItem { get; set; } + public Mux4Sbox mux4Sbox { get; set; } + public List inbound { get; set; } + public List globalHotkeys { get; set; } + public List coreTypeItem { get; set; } - #endregion other entities - } + #endregion other entities } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ConfigItems.cs b/v2rayN/v2rayN/Mode/ConfigItems.cs index 799ed299..1d3823fb 100644 --- a/v2rayN/v2rayN/Mode/ConfigItems.cs +++ b/v2rayN/v2rayN/Mode/ConfigItems.cs @@ -1,209 +1,208 @@ using System.Windows.Input; -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class CoreBasicItem { - [Serializable] - public class CoreBasicItem - { - /// - /// 允许日志 - /// - public bool logEnabled { get; set; } + /// + /// 允许日志 + /// + public bool logEnabled { get; set; } - /// - /// 日志等级 - /// - public string loglevel { get; set; } + /// + /// 日志等级 + /// + public string loglevel { get; set; } - /// - /// 允许Mux多路复用 - /// - public bool muxEnabled { get; set; } + /// + /// 允许Mux多路复用 + /// + public bool muxEnabled { get; set; } - /// - /// 是否允许不安全连接 - /// - public bool defAllowInsecure { get; set; } + /// + /// 是否允许不安全连接 + /// + public bool defAllowInsecure { get; set; } - public string defFingerprint { get; set; } + public string defFingerprint { get; set; } - /// - /// 默认用户代理 - /// - public string defUserAgent { get; set; } - } + /// + /// 默认用户代理 + /// + public string defUserAgent { get; set; } +} - [Serializable] - public class InItem - { - public int localPort { get; set; } +[Serializable] +public class InItem +{ + public int localPort { get; set; } - public string protocol { get; set; } + public string protocol { get; set; } - public bool udpEnabled { get; set; } + public bool udpEnabled { get; set; } - public bool sniffingEnabled { get; set; } = true; - public bool routeOnly { get; set; } + public bool sniffingEnabled { get; set; } = true; + public bool routeOnly { get; set; } - public bool allowLANConn { get; set; } + public bool allowLANConn { get; set; } - public bool newPort4LAN { get; set; } + public bool newPort4LAN { get; set; } - public string user { get; set; } + public string user { get; set; } - public string pass { get; set; } - } + public string pass { get; set; } +} - [Serializable] - public class KcpItem - { - public int mtu { get; set; } +[Serializable] +public class KcpItem +{ + public int mtu { get; set; } - public int tti { get; set; } + public int tti { get; set; } - public int uplinkCapacity { get; set; } + public int uplinkCapacity { get; set; } - public int downlinkCapacity { get; set; } + public int downlinkCapacity { get; set; } - public bool congestion { get; set; } + public bool congestion { get; set; } - public int readBufferSize { get; set; } + public int readBufferSize { get; set; } - public int writeBufferSize { get; set; } - } + public int writeBufferSize { get; set; } +} - [Serializable] - public class GrpcItem - { - public int idle_timeout { get; set; } - public int health_check_timeout { get; set; } - public bool permit_without_stream { get; set; } - public int initial_windows_size { get; set; } - } +[Serializable] +public class GrpcItem +{ + public int idle_timeout { get; set; } + public int health_check_timeout { get; set; } + public bool permit_without_stream { get; set; } + public int initial_windows_size { get; set; } +} - [Serializable] - public class GUIItem - { - public bool autoRun { get; set; } +[Serializable] +public class GUIItem +{ + public bool autoRun { get; set; } - public bool enableStatistics { get; set; } + public bool enableStatistics { get; set; } - public bool keepOlderDedupl { get; set; } + public bool keepOlderDedupl { get; set; } - public bool ignoreGeoUpdateCore { get; set; } = true; + public bool ignoreGeoUpdateCore { get; set; } = true; - public int autoUpdateInterval { get; set; } = 10; + public int autoUpdateInterval { get; set; } = 10; - public bool checkPreReleaseUpdate { get; set; } = false; + public bool checkPreReleaseUpdate { get; set; } = false; - public bool enableSecurityProtocolTls13 { get; set; } + public bool enableSecurityProtocolTls13 { get; set; } - public int trayMenuServersLimit { get; set; } = 20; + public int trayMenuServersLimit { get; set; } = 20; - public bool enableHWA { get; set; } = false; + public bool enableHWA { get; set; } = false; - public bool enableLog { get; set; } = true; - } + public bool enableLog { get; set; } = true; +} - [Serializable] - public class UIItem - { - public bool enableAutoAdjustMainLvColWidth { get; set; } - public double mainWidth { get; set; } - public double mainHeight { get; set; } - public double mainGirdHeight1 { get; set; } - public double mainGirdHeight2 { get; set; } - public bool colorModeDark { get; set; } - public bool followSystemTheme { get; set; } - public string? colorPrimaryName { get; set; } - public string currentLanguage { get; set; } - public string currentFontFamily { get; set; } - public int currentFontSize { get; set; } - public bool enableDragDropSort { get; set; } - public bool doubleClick2Activate { get; set; } - public bool autoHideStartup { get; set; } = true; - public string mainMsgFilter { get; set; } - public bool showTrayTip { get; set; } - public List mainColumnItem { get; set; } - } +[Serializable] +public class UIItem +{ + public bool enableAutoAdjustMainLvColWidth { get; set; } + public double mainWidth { get; set; } + public double mainHeight { get; set; } + public double mainGirdHeight1 { get; set; } + public double mainGirdHeight2 { get; set; } + public bool colorModeDark { get; set; } + public bool followSystemTheme { get; set; } + public string? colorPrimaryName { get; set; } + public string currentLanguage { get; set; } + public string currentFontFamily { get; set; } + public int currentFontSize { get; set; } + public bool enableDragDropSort { get; set; } + public bool doubleClick2Activate { get; set; } + public bool autoHideStartup { get; set; } = true; + public string mainMsgFilter { get; set; } + public bool showTrayTip { get; set; } + public List mainColumnItem { get; set; } +} - [Serializable] - public class ConstItem - { - public string defIEProxyExceptions { get; set; } - public string subConvertUrl { get; set; } = string.Empty; - } +[Serializable] +public class ConstItem +{ + public string defIEProxyExceptions { get; set; } + public string subConvertUrl { get; set; } = string.Empty; +} - [Serializable] - public class KeyEventItem - { - public EGlobalHotkey eGlobalHotkey { get; set; } +[Serializable] +public class KeyEventItem +{ + public EGlobalHotkey eGlobalHotkey { get; set; } - public bool Alt { get; set; } + public bool Alt { get; set; } - public bool Control { get; set; } + public bool Control { get; set; } - public bool Shift { get; set; } + public bool Shift { get; set; } - public Key? KeyCode { get; set; } - } + public Key? KeyCode { get; set; } +} - [Serializable] - public class CoreTypeItem - { - public EConfigType configType { get; set; } +[Serializable] +public class CoreTypeItem +{ + public EConfigType configType { get; set; } - public ECoreType coreType { get; set; } - } + public ECoreType coreType { get; set; } +} - [Serializable] - public class TunModeItem - { - public bool enableTun { get; set; } - public bool strictRoute { get; set; } = true; - public string stack { get; set; } - public int mtu { get; set; } - public bool enableExInbound { get; set; } - } +[Serializable] +public class TunModeItem +{ + public bool enableTun { get; set; } + public bool strictRoute { get; set; } = true; + public string stack { get; set; } + public int mtu { get; set; } + public bool enableExInbound { get; set; } +} - [Serializable] - public class SpeedTestItem - { - public int speedTestTimeout { get; set; } - public string speedTestUrl { get; set; } - public string speedPingTestUrl { get; set; } - } +[Serializable] +public class SpeedTestItem +{ + public int speedTestTimeout { get; set; } + public string speedTestUrl { get; set; } + public string speedPingTestUrl { get; set; } +} - [Serializable] - public class RoutingBasicItem - { - /// - /// 域名解析策略 - /// - public string domainStrategy { get; set; } +[Serializable] +public class RoutingBasicItem +{ + /// + /// 域名解析策略 + /// + public string domainStrategy { get; set; } - public string domainStrategy4Singbox { get; set; } + public string domainStrategy4Singbox { get; set; } - public string domainMatcher { get; set; } - public string routingIndexId { get; set; } - public bool enableRoutingAdvanced { get; set; } - } + public string domainMatcher { get; set; } + public string routingIndexId { get; set; } + public bool enableRoutingAdvanced { get; set; } +} - [Serializable] - public class ColumnItem - { - public string Name { get; set; } - public int Width { get; set; } - public int Index { get; set; } - } +[Serializable] +public class ColumnItem +{ + public string Name { get; set; } + public int Width { get; set; } + public int Index { get; set; } +} - [Serializable] - public class Mux4Sbox - { - public string protocol { get; set; } - public int max_connections { get; set; } - public int min_streams { get; set; } - public int max_streams { get; set; } - public bool padding { get; set; } - } +[Serializable] +public class Mux4Sbox +{ + public string protocol { get; set; } + public int max_connections { get; set; } + public int min_streams { get; set; } + public int max_streams { get; set; } + public bool padding { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ConfigOld.cs b/v2rayN/v2rayN/Mode/ConfigOld.cs index 72cbf898..e4f08521 100644 --- a/v2rayN/v2rayN/Mode/ConfigOld.cs +++ b/v2rayN/v2rayN/Mode/ConfigOld.cs @@ -1,440 +1,439 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class ConfigOld { - [Serializable] - public class ConfigOld + #region property + + /// + /// 允许日志 + /// + public bool logEnabled { - #region property - - /// - /// 允许日志 - /// - public bool logEnabled - { - get; set; - } - - /// - /// 日志等级 - /// - public string loglevel - { - get; set; - } - - public string indexId - { - get; set; - } - - /// - /// 允许Mux多路复用 - /// - public bool muxEnabled - { - get; set; - } - - /// - /// - /// - public ESysProxyType sysProxyType - { - get; set; - } - - /// - /// 启用实时网速和流量统计 - /// - public bool enableStatistics - { - get; set; - } - - /// - /// 去重时优先保留较旧(顶部)节点 - /// - public bool keepOlderDedupl - { - get; set; - } - - /// - /// 视图刷新率 - /// - public int statisticsFreshRate - { - get; set; - } - - /// - /// 自定义远程DNS - /// - public string remoteDNS - { - get; set; - } - - /// - /// Outbound Freedom domainStrategy - /// - public string domainStrategy4Freedom - { - get; set; - } - - /// - /// 是否允许不安全连接 - /// - public bool defAllowInsecure - { - get; set; - } - - /// - /// 域名解析策略 - /// - public string domainStrategy - { - get; set; - } - - public string domainMatcher - { - get; set; - } - - public int routingIndex - { - get; set; - } - - public bool enableRoutingAdvanced - { - get; set; - } - - public bool ignoreGeoUpdateCore - { - get; set; - } - - /// - /// systemProxyExceptions - /// - public string systemProxyExceptions - { - get; set; - } - - public string systemProxyAdvancedProtocol { get; set; } - - public int autoUpdateInterval { get; set; } = 0; - - public int autoUpdateSubInterval { get; set; } = 0; - - public bool checkPreReleaseUpdate { get; set; } = false; - - public bool enableSecurityProtocolTls13 - { - get; set; - } - - public int trayMenuServersLimit { get; set; } - - #endregion property - - #region other entities - - /// - /// 本地监听 - /// - public List inbound - { - get; set; - } - - /// - /// vmess服务器信息 - /// - public List vmess - { - get; set; - } - - /// - /// KcpItem - /// - public KcpItem kcpItem - { - get; set; - } - - /// - /// 订阅 - /// - public List subItem - { - get; set; - } - - /// - /// UI - /// - public UIItem uiItem - { - get; set; - } - - public List routings - { - get; set; - } - - public ConstItem constItem - { - get; set; - } - - public List globalHotkeys - { - get; set; - } - - public List coreTypeItem - { - get; set; - } - - #endregion other entities + get; set; } - [Serializable] - public class VmessItem + /// + /// 日志等级 + /// + public string loglevel { - public VmessItem() - { - indexId = string.Empty; - configType = EConfigType.VMess; - configVersion = 2; - sort = 0; - address = string.Empty; - port = 0; - id = string.Empty; - alterId = 0; - security = string.Empty; - network = string.Empty; - remarks = string.Empty; - headerType = string.Empty; - requestHost = string.Empty; - path = string.Empty; - streamSecurity = string.Empty; - allowInsecure = string.Empty; - testResult = string.Empty; - subid = string.Empty; - flow = string.Empty; - groupId = string.Empty; - } - - public string indexId - { - get; set; - } - - /// - /// config type(1=normal,2=custom) - /// - public EConfigType configType - { - get; set; - } - - /// - /// 版本(现在=2) - /// - public int configVersion - { - get; set; - } - - public int sort - { - get; set; - } - - /// - /// 远程服务器地址 - /// - public string address - { - get; set; - } - - /// - /// 远程服务器端口 - /// - public int port - { - get; set; - } - - /// - /// 远程服务器ID - /// - public string id - { - get; set; - } - - /// - /// 远程服务器额外ID - /// - public int alterId - { - get; set; - } - - /// - /// 本地安全策略 - /// - public string security - { - get; set; - } - - /// - /// tcp,kcp,ws,h2,quic - /// - public string network - { - get; set; - } - - /// - /// - /// - public string remarks - { - get; set; - } - - /// - /// 伪装类型 - /// - public string headerType - { - get; set; - } - - /// - /// 伪装的域名 - /// - public string requestHost - { - get; set; - } - - /// - /// ws h2 path - /// - public string path - { - get; set; - } - - /// - /// 传输层安全 - /// - public string streamSecurity - { - get; set; - } - - /// - /// 是否允许不安全连接(用于客户端) - /// - public string allowInsecure - { - get; set; - } - - /// - /// - /// - public string testResult - { - get; set; - } - - /// - /// SubItem id - /// - public string subid - { - get; set; - } - - /// - /// VLESS flow - /// - public string flow - { - get; set; - } - - /// - /// tls sni - /// - public string sni - { - get; set; - } - - public string groupId - { - get; set; - } = string.Empty; - - public ECoreType? coreType - { - get; set; - } - - public int preSocksPort - { - get; set; - } - - public string fingerprint { get; set; } + get; set; } - [Serializable] - public class RoutingItemOld + public string indexId { - public string remarks - { - get; set; - } + get; set; + } - public string url - { - get; set; - } + /// + /// 允许Mux多路复用 + /// + public bool muxEnabled + { + get; set; + } - public List rules - { - get; set; - } + /// + /// + /// + public ESysProxyType sysProxyType + { + get; set; + } - public bool enabled { get; set; } = true; + /// + /// 启用实时网速和流量统计 + /// + public bool enableStatistics + { + get; set; + } - public bool locked - { - get; set; - } + /// + /// 去重时优先保留较旧(顶部)节点 + /// + public bool keepOlderDedupl + { + get; set; + } - public string customIcon - { - get; set; - } + /// + /// 视图刷新率 + /// + public int statisticsFreshRate + { + get; set; + } + + /// + /// 自定义远程DNS + /// + public string remoteDNS + { + get; set; + } + + /// + /// Outbound Freedom domainStrategy + /// + public string domainStrategy4Freedom + { + get; set; + } + + /// + /// 是否允许不安全连接 + /// + public bool defAllowInsecure + { + get; set; + } + + /// + /// 域名解析策略 + /// + public string domainStrategy + { + get; set; + } + + public string domainMatcher + { + get; set; + } + + public int routingIndex + { + get; set; + } + + public bool enableRoutingAdvanced + { + get; set; + } + + public bool ignoreGeoUpdateCore + { + get; set; + } + + /// + /// systemProxyExceptions + /// + public string systemProxyExceptions + { + get; set; + } + + public string systemProxyAdvancedProtocol { get; set; } + + public int autoUpdateInterval { get; set; } = 0; + + public int autoUpdateSubInterval { get; set; } = 0; + + public bool checkPreReleaseUpdate { get; set; } = false; + + public bool enableSecurityProtocolTls13 + { + get; set; + } + + public int trayMenuServersLimit { get; set; } + + #endregion property + + #region other entities + + /// + /// 本地监听 + /// + public List inbound + { + get; set; + } + + /// + /// vmess服务器信息 + /// + public List vmess + { + get; set; + } + + /// + /// KcpItem + /// + public KcpItem kcpItem + { + get; set; + } + + /// + /// 订阅 + /// + public List subItem + { + get; set; + } + + /// + /// UI + /// + public UIItem uiItem + { + get; set; + } + + public List routings + { + get; set; + } + + public ConstItem constItem + { + get; set; + } + + public List globalHotkeys + { + get; set; + } + + public List coreTypeItem + { + get; set; + } + + #endregion other entities +} + +[Serializable] +public class VmessItem +{ + public VmessItem() + { + indexId = string.Empty; + configType = EConfigType.VMess; + configVersion = 2; + sort = 0; + address = string.Empty; + port = 0; + id = string.Empty; + alterId = 0; + security = string.Empty; + network = string.Empty; + remarks = string.Empty; + headerType = string.Empty; + requestHost = string.Empty; + path = string.Empty; + streamSecurity = string.Empty; + allowInsecure = string.Empty; + testResult = string.Empty; + subid = string.Empty; + flow = string.Empty; + groupId = string.Empty; + } + + public string indexId + { + get; set; + } + + /// + /// config type(1=normal,2=custom) + /// + public EConfigType configType + { + get; set; + } + + /// + /// 版本(现在=2) + /// + public int configVersion + { + get; set; + } + + public int sort + { + get; set; + } + + /// + /// 远程服务器地址 + /// + public string address + { + get; set; + } + + /// + /// 远程服务器端口 + /// + public int port + { + get; set; + } + + /// + /// 远程服务器ID + /// + public string id + { + get; set; + } + + /// + /// 远程服务器额外ID + /// + public int alterId + { + get; set; + } + + /// + /// 本地安全策略 + /// + public string security + { + get; set; + } + + /// + /// tcp,kcp,ws,h2,quic + /// + public string network + { + get; set; + } + + /// + /// + /// + public string remarks + { + get; set; + } + + /// + /// 伪装类型 + /// + public string headerType + { + get; set; + } + + /// + /// 伪装的域名 + /// + public string requestHost + { + get; set; + } + + /// + /// ws h2 path + /// + public string path + { + get; set; + } + + /// + /// 传输层安全 + /// + public string streamSecurity + { + get; set; + } + + /// + /// 是否允许不安全连接(用于客户端) + /// + public string allowInsecure + { + get; set; + } + + /// + /// + /// + public string testResult + { + get; set; + } + + /// + /// SubItem id + /// + public string subid + { + get; set; + } + + /// + /// VLESS flow + /// + public string flow + { + get; set; + } + + /// + /// tls sni + /// + public string sni + { + get; set; + } + + public string groupId + { + get; set; + } = string.Empty; + + public ECoreType? coreType + { + get; set; + } + + public int preSocksPort + { + get; set; + } + + public string fingerprint { get; set; } +} + +[Serializable] +public class RoutingItemOld +{ + public string remarks + { + get; set; + } + + public string url + { + get; set; + } + + public List rules + { + get; set; + } + + public bool enabled { get; set; } = true; + + public bool locked + { + get; set; + } + + public string customIcon + { + get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/CoreInfo.cs b/v2rayN/v2rayN/Mode/CoreInfo.cs index 63538776..b2de419f 100644 --- a/v2rayN/v2rayN/Mode/CoreInfo.cs +++ b/v2rayN/v2rayN/Mode/CoreInfo.cs @@ -1,27 +1,26 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class CoreInfo { - [Serializable] - public class CoreInfo - { - public ECoreType coreType { get; set; } + public ECoreType coreType { get; set; } - public List coreExes { get; set; } + public List coreExes { get; set; } - public string arguments { get; set; } + public string arguments { get; set; } - public string coreUrl { get; set; } + public string coreUrl { get; set; } - public string coreReleaseApiUrl { get; set; } + public string coreReleaseApiUrl { get; set; } - public string coreDownloadUrl32 { get; set; } + public string coreDownloadUrl32 { get; set; } - public string coreDownloadUrl64 { get; set; } + public string coreDownloadUrl64 { get; set; } - public string coreDownloadUrlArm64 { get; set; } + public string coreDownloadUrlArm64 { get; set; } - public string match { get; set; } - public string versionArg { get; set; } + public string match { get; set; } + public string versionArg { get; set; } - public bool redirectInfo { get; set; } - } + public bool redirectInfo { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/DNSItem.cs b/v2rayN/v2rayN/Mode/DNSItem.cs index fef779ec..50e39932 100644 --- a/v2rayN/v2rayN/Mode/DNSItem.cs +++ b/v2rayN/v2rayN/Mode/DNSItem.cs @@ -1,18 +1,17 @@ using SQLite; -namespace v2rayN.Mode -{ - [Serializable] - public class DNSItem - { - [PrimaryKey] - public string id { get; set; } +namespace v2rayN.Mode; - public string remarks { get; set; } - public bool enabled { get; set; } = true; - public ECoreType coreType { get; set; } - public string? normalDNS { get; set; } - public string? tunDNS { get; set; } - public string? domainStrategy4Freedom { get; set; } - } +[Serializable] +public class DNSItem +{ + [PrimaryKey] + public string id { get; set; } + + public string remarks { get; set; } + public bool enabled { get; set; } = true; + public ECoreType coreType { get; set; } + public string? normalDNS { get; set; } + public string? tunDNS { get; set; } + public string? domainStrategy4Freedom { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/EConfigType.cs b/v2rayN/v2rayN/Mode/EConfigType.cs index e92b3ebc..51073203 100644 --- a/v2rayN/v2rayN/Mode/EConfigType.cs +++ b/v2rayN/v2rayN/Mode/EConfigType.cs @@ -1,12 +1,11 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public enum EConfigType { - public enum EConfigType - { - VMess = 1, - Custom = 2, - Shadowsocks = 3, - Socks = 4, - VLESS = 5, - Trojan = 6 - } + VMess = 1, + Custom = 2, + Shadowsocks = 3, + Socks = 4, + VLESS = 5, + Trojan = 6 } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ECoreType.cs b/v2rayN/v2rayN/Mode/ECoreType.cs index 2f20ed0b..c182f22e 100644 --- a/v2rayN/v2rayN/Mode/ECoreType.cs +++ b/v2rayN/v2rayN/Mode/ECoreType.cs @@ -1,18 +1,17 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public enum ECoreType { - public enum ECoreType - { - v2fly = 1, - Xray = 2, - SagerNet = 3, - v2fly_v5 = 4, - clash = 11, - clash_meta = 12, - hysteria = 21, - naiveproxy = 22, - tuic = 23, - sing_box = 24, - juicity = 25, - v2rayN = 99 - } + v2fly = 1, + Xray = 2, + SagerNet = 3, + v2fly_v5 = 4, + clash = 11, + clash_meta = 12, + hysteria = 21, + naiveproxy = 22, + tuic = 23, + sing_box = 24, + juicity = 25, + v2rayN = 99 } diff --git a/v2rayN/v2rayN/Mode/EGlobalHotkey.cs b/v2rayN/v2rayN/Mode/EGlobalHotkey.cs index 8110fdf9..c80e762c 100644 --- a/v2rayN/v2rayN/Mode/EGlobalHotkey.cs +++ b/v2rayN/v2rayN/Mode/EGlobalHotkey.cs @@ -1,11 +1,10 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public enum EGlobalHotkey { - public enum EGlobalHotkey - { - ShowForm = 0, - SystemProxyClear = 1, - SystemProxySet = 2, - SystemProxyUnchanged = 3, - SystemProxyPac = 4, - } + ShowForm = 0, + SystemProxyClear = 1, + SystemProxySet = 2, + SystemProxyUnchanged = 3, + SystemProxyPac = 4, } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/EMove.cs b/v2rayN/v2rayN/Mode/EMove.cs index 0dae4135..9419aa55 100644 --- a/v2rayN/v2rayN/Mode/EMove.cs +++ b/v2rayN/v2rayN/Mode/EMove.cs @@ -1,11 +1,10 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public enum EMove { - public enum EMove - { - Top = 1, - Up = 2, - Down = 3, - Bottom = 4, - Position = 5 - } + Top = 1, + Up = 2, + Down = 3, + Bottom = 4, + Position = 5 } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/EServerColName.cs b/v2rayN/v2rayN/Mode/EServerColName.cs index be49c2cb..117ea2ce 100644 --- a/v2rayN/v2rayN/Mode/EServerColName.cs +++ b/v2rayN/v2rayN/Mode/EServerColName.cs @@ -1,22 +1,21 @@ -namespace v2rayN.Mode -{ - public enum EServerColName - { - def = 0, - configType, - remarks, - address, - port, - security, - network, - streamSecurity, - subRemarks, - delayVal, - speedVal, +namespace v2rayN.Mode; - todayDown, - todayUp, - totalDown, - totalUp - } +public enum EServerColName +{ + def = 0, + configType, + remarks, + address, + port, + security, + network, + streamSecurity, + subRemarks, + delayVal, + speedVal, + + todayDown, + todayUp, + totalDown, + totalUp } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ESpeedActionType.cs b/v2rayN/v2rayN/Mode/ESpeedActionType.cs index 4864061b..ee5b2ea0 100644 --- a/v2rayN/v2rayN/Mode/ESpeedActionType.cs +++ b/v2rayN/v2rayN/Mode/ESpeedActionType.cs @@ -1,11 +1,10 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public enum ESpeedActionType { - public enum ESpeedActionType - { - Ping, - Tcping, - Realping, - Speedtest, - Mixedtest - } + Ping, + Tcping, + Realping, + Speedtest, + Mixedtest } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ESysProxyType.cs b/v2rayN/v2rayN/Mode/ESysProxyType.cs index e52887da..d944b6c8 100644 --- a/v2rayN/v2rayN/Mode/ESysProxyType.cs +++ b/v2rayN/v2rayN/Mode/ESysProxyType.cs @@ -1,10 +1,9 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public enum ESysProxyType { - public enum ESysProxyType - { - ForcedClear = 0, - ForcedChange = 1, - Unchanged = 2, - Pac = 3 - } + ForcedClear = 0, + ForcedChange = 1, + Unchanged = 2, + Pac = 3 } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/EViewAction.cs b/v2rayN/v2rayN/Mode/EViewAction.cs index d1f2c626..2ca20d45 100644 --- a/v2rayN/v2rayN/Mode/EViewAction.cs +++ b/v2rayN/v2rayN/Mode/EViewAction.cs @@ -1,8 +1,7 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public enum EViewAction { - public enum EViewAction - { - AdjustMainLvColWidth, - ProfilesFocus - } + AdjustMainLvColWidth, + ProfilesFocus } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/GitHubRelease.cs b/v2rayN/v2rayN/Mode/GitHubRelease.cs index e509b10f..911f5c1b 100644 --- a/v2rayN/v2rayN/Mode/GitHubRelease.cs +++ b/v2rayN/v2rayN/Mode/GitHubRelease.cs @@ -1,68 +1,67 @@ using Newtonsoft.Json; -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public class GitHubReleaseAsset { - public class GitHubReleaseAsset - { - [JsonProperty("url")] public string Url { get; set; } + [JsonProperty("url")] public string Url { get; set; } - [JsonProperty("id")] public int Id { get; set; } + [JsonProperty("id")] public int Id { get; set; } - [JsonProperty("node_id")] public string NodeId { get; set; } + [JsonProperty("node_id")] public string NodeId { get; set; } - [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("name")] public string Name { get; set; } - [JsonProperty("label")] public object Label { get; set; } + [JsonProperty("label")] public object Label { get; set; } - [JsonProperty("content_type")] public string ContentType { get; set; } + [JsonProperty("content_type")] public string ContentType { get; set; } - [JsonProperty("state")] public string State { get; set; } + [JsonProperty("state")] public string State { get; set; } - [JsonProperty("size")] public int Size { get; set; } + [JsonProperty("size")] public int Size { get; set; } - [JsonProperty("download_count")] public int DownloadCount { get; set; } + [JsonProperty("download_count")] public int DownloadCount { get; set; } - [JsonProperty("created_at")] public DateTime CreatedAt { get; set; } + [JsonProperty("created_at")] public DateTime CreatedAt { get; set; } - [JsonProperty("updated_at")] public DateTime UpdatedAt { get; set; } + [JsonProperty("updated_at")] public DateTime UpdatedAt { get; set; } - [JsonProperty("browser_download_url")] public string BrowserDownloadUrl { get; set; } - } + [JsonProperty("browser_download_url")] public string BrowserDownloadUrl { get; set; } +} - public class GitHubRelease - { - [JsonProperty("url")] public string Url { get; set; } +public class GitHubRelease +{ + [JsonProperty("url")] public string Url { get; set; } - [JsonProperty("assets_url")] public string AssetsUrl { get; set; } + [JsonProperty("assets_url")] public string AssetsUrl { get; set; } - [JsonProperty("upload_url")] public string UploadUrl { get; set; } + [JsonProperty("upload_url")] public string UploadUrl { get; set; } - [JsonProperty("html_url")] public string HtmlUrl { get; set; } + [JsonProperty("html_url")] public string HtmlUrl { get; set; } - [JsonProperty("id")] public int Id { get; set; } + [JsonProperty("id")] public int Id { get; set; } - [JsonProperty("node_id")] public string NodeId { get; set; } + [JsonProperty("node_id")] public string NodeId { get; set; } - [JsonProperty("tag_name")] public string TagName { get; set; } + [JsonProperty("tag_name")] public string TagName { get; set; } - [JsonProperty("target_commitish")] public string TargetCommitish { get; set; } + [JsonProperty("target_commitish")] public string TargetCommitish { get; set; } - [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("name")] public string Name { get; set; } - [JsonProperty("draft")] public bool Draft { get; set; } + [JsonProperty("draft")] public bool Draft { get; set; } - [JsonProperty("prerelease")] public bool Prerelease { get; set; } + [JsonProperty("prerelease")] public bool Prerelease { get; set; } - [JsonProperty("created_at")] public DateTime CreatedAt { get; set; } + [JsonProperty("created_at")] public DateTime CreatedAt { get; set; } - [JsonProperty("published_at")] public DateTime PublishedAt { get; set; } + [JsonProperty("published_at")] public DateTime PublishedAt { get; set; } - [JsonProperty("assets")] public List Assets { get; set; } + [JsonProperty("assets")] public List Assets { get; set; } - [JsonProperty("tarball_url")] public string TarballUrl { get; set; } + [JsonProperty("tarball_url")] public string TarballUrl { get; set; } - [JsonProperty("zipball_url")] public string ZipballUrl { get; set; } + [JsonProperty("zipball_url")] public string ZipballUrl { get; set; } - [JsonProperty("body")] public string Body { get; set; } - } + [JsonProperty("body")] public string Body { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ProfileExItem.cs b/v2rayN/v2rayN/Mode/ProfileExItem.cs index cbe09223..e83ab0c8 100644 --- a/v2rayN/v2rayN/Mode/ProfileExItem.cs +++ b/v2rayN/v2rayN/Mode/ProfileExItem.cs @@ -1,15 +1,14 @@ using SQLite; -namespace v2rayN.Mode -{ - [Serializable] - public class ProfileExItem - { - [PrimaryKey] - public string indexId { get; set; } +namespace v2rayN.Mode; - public int delay { get; set; } - public decimal speed { get; set; } - public int sort { get; set; } - } +[Serializable] +public class ProfileExItem +{ + [PrimaryKey] + public string indexId { get; set; } + + public int delay { get; set; } + public decimal speed { get; set; } + public int sort { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ProfileItem.cs b/v2rayN/v2rayN/Mode/ProfileItem.cs index b1990e10..dd1fa49d 100644 --- a/v2rayN/v2rayN/Mode/ProfileItem.cs +++ b/v2rayN/v2rayN/Mode/ProfileItem.cs @@ -1,195 +1,194 @@ using SQLite; using v2rayN.Base; -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class ProfileItem { - [Serializable] - public class ProfileItem + public ProfileItem() { - public ProfileItem() - { - indexId = string.Empty; - configType = EConfigType.VMess; - configVersion = 2; - address = string.Empty; - port = 0; - id = string.Empty; - alterId = 0; - security = string.Empty; - network = string.Empty; - remarks = string.Empty; - headerType = string.Empty; - requestHost = string.Empty; - path = string.Empty; - streamSecurity = string.Empty; - allowInsecure = string.Empty; - subid = string.Empty; - flow = string.Empty; - } - - #region function - - public string GetSummary() - { - string summary = string.Format("[{0}] ", (configType).ToString()); - string[] arrAddr = address.Split('.'); - string addr; - if (arrAddr.Length > 2) - { - addr = $"{arrAddr[0]}***{arrAddr[arrAddr.Length - 1]}"; - } - else if (arrAddr.Length > 1) - { - addr = $"***{arrAddr[arrAddr.Length - 1]}"; - } - else - { - addr = address; - } - switch (configType) - { - case EConfigType.VMess: - case EConfigType.Shadowsocks: - case EConfigType.Socks: - case EConfigType.VLESS: - case EConfigType.Trojan: - summary += string.Format("{0}({1}:{2})", remarks, addr, port); - break; - - default: - summary += string.Format("{0}", remarks); - break; - } - return summary; - } - - public List GetAlpn() - { - if (Utils.IsNullOrEmpty(alpn)) - { - return null; - } - else - { - return Utils.String2List(alpn); - } - } - - public string GetNetwork() - { - if (Utils.IsNullOrEmpty(network) || !Global.networks.Contains(network)) - { - return Global.DefaultNetwork; - } - return network.TrimEx(); - } - - #endregion function - - [PrimaryKey] - public string indexId { get; set; } - - /// - /// config type(1=normal,2=custom) - /// - public EConfigType configType { get; set; } - - /// - /// 版本(现在=2) - /// - public int configVersion { get; set; } - - /// - /// 远程服务器地址 - /// - public string address { get; set; } - - /// - /// 远程服务器端口 - /// - public int port { get; set; } - - /// - /// 远程服务器ID - /// - public string id { get; set; } - - /// - /// 远程服务器额外ID - /// - public int alterId { get; set; } - - /// - /// 本地安全策略 - /// - public string security { get; set; } - - /// - /// tcp,kcp,ws,h2,quic - /// - public string network { get; set; } - - /// - /// - /// - public string remarks { get; set; } - - /// - /// 伪装类型 - /// - public string headerType { get; set; } - - /// - /// 伪装的域名 - /// - public string requestHost { get; set; } - - /// - /// ws h2 path - /// - public string path { get; set; } - - /// - /// 传输层安全 - /// - public string streamSecurity { get; set; } - - /// - /// 是否允许不安全连接(用于客户端) - /// - public string allowInsecure { get; set; } - - /// - /// SubItem id - /// - public string subid { get; set; } - - public bool isSub { get; set; } = true; - - /// - /// VLESS flow - /// - public string flow { get; set; } - - /// - /// tls sni - /// - public string sni { get; set; } - - /// - /// tls alpn - /// - public string alpn { get; set; } = string.Empty; - - public ECoreType? coreType { get; set; } - - public int preSocksPort { get; set; } - - public string fingerprint { get; set; } - - public bool displayLog { get; set; } = true; - public string publicKey { get; set; } - public string shortId { get; set; } - public string spiderX { get; set; } + indexId = string.Empty; + configType = EConfigType.VMess; + configVersion = 2; + address = string.Empty; + port = 0; + id = string.Empty; + alterId = 0; + security = string.Empty; + network = string.Empty; + remarks = string.Empty; + headerType = string.Empty; + requestHost = string.Empty; + path = string.Empty; + streamSecurity = string.Empty; + allowInsecure = string.Empty; + subid = string.Empty; + flow = string.Empty; } + + #region function + + public string GetSummary() + { + string summary = string.Format("[{0}] ", (configType).ToString()); + string[] arrAddr = address.Split('.'); + string addr; + if (arrAddr.Length > 2) + { + addr = $"{arrAddr[0]}***{arrAddr[arrAddr.Length - 1]}"; + } + else if (arrAddr.Length > 1) + { + addr = $"***{arrAddr[arrAddr.Length - 1]}"; + } + else + { + addr = address; + } + switch (configType) + { + case EConfigType.VMess: + case EConfigType.Shadowsocks: + case EConfigType.Socks: + case EConfigType.VLESS: + case EConfigType.Trojan: + summary += string.Format("{0}({1}:{2})", remarks, addr, port); + break; + + default: + summary += string.Format("{0}", remarks); + break; + } + return summary; + } + + public List GetAlpn() + { + if (Utils.IsNullOrEmpty(alpn)) + { + return null; + } + else + { + return Utils.String2List(alpn); + } + } + + public string GetNetwork() + { + if (Utils.IsNullOrEmpty(network) || !Global.networks.Contains(network)) + { + return Global.DefaultNetwork; + } + return network.TrimEx(); + } + + #endregion function + + [PrimaryKey] + public string indexId { get; set; } + + /// + /// config type(1=normal,2=custom) + /// + public EConfigType configType { get; set; } + + /// + /// 版本(现在=2) + /// + public int configVersion { get; set; } + + /// + /// 远程服务器地址 + /// + public string address { get; set; } + + /// + /// 远程服务器端口 + /// + public int port { get; set; } + + /// + /// 远程服务器ID + /// + public string id { get; set; } + + /// + /// 远程服务器额外ID + /// + public int alterId { get; set; } + + /// + /// 本地安全策略 + /// + public string security { get; set; } + + /// + /// tcp,kcp,ws,h2,quic + /// + public string network { get; set; } + + /// + /// + /// + public string remarks { get; set; } + + /// + /// 伪装类型 + /// + public string headerType { get; set; } + + /// + /// 伪装的域名 + /// + public string requestHost { get; set; } + + /// + /// ws h2 path + /// + public string path { get; set; } + + /// + /// 传输层安全 + /// + public string streamSecurity { get; set; } + + /// + /// 是否允许不安全连接(用于客户端) + /// + public string allowInsecure { get; set; } + + /// + /// SubItem id + /// + public string subid { get; set; } + + public bool isSub { get; set; } = true; + + /// + /// VLESS flow + /// + public string flow { get; set; } + + /// + /// tls sni + /// + public string sni { get; set; } + + /// + /// tls alpn + /// + public string alpn { get; set; } = string.Empty; + + public ECoreType? coreType { get; set; } + + public int preSocksPort { get; set; } + + public string fingerprint { get; set; } + + public bool displayLog { get; set; } = true; + public string publicKey { get; set; } + public string shortId { get; set; } + public string spiderX { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ProfileItemModel.cs b/v2rayN/v2rayN/Mode/ProfileItemModel.cs index e4b761e6..05ba1bbb 100644 --- a/v2rayN/v2rayN/Mode/ProfileItemModel.cs +++ b/v2rayN/v2rayN/Mode/ProfileItemModel.cs @@ -1,18 +1,17 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class ProfileItemModel : ProfileItem { - [Serializable] - public class ProfileItemModel : ProfileItem - { - public bool isActive { get; set; } - public string subRemarks { get; set; } - public int delay { get; set; } - public decimal speed { get; set; } - public int sort { get; set; } - public string delayVal { get; set; } - public string speedVal { get; set; } - public string todayUp { get; set; } - public string todayDown { get; set; } - public string totalUp { get; set; } - public string totalDown { get; set; } - } + public bool isActive { get; set; } + public string subRemarks { get; set; } + public int delay { get; set; } + public decimal speed { get; set; } + public int sort { get; set; } + public string delayVal { get; set; } + public string speedVal { get; set; } + public string todayUp { get; set; } + public string todayDown { get; set; } + public string totalUp { get; set; } + public string totalDown { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/RoutingItem.cs b/v2rayN/v2rayN/Mode/RoutingItem.cs index 25c85606..771638cb 100644 --- a/v2rayN/v2rayN/Mode/RoutingItem.cs +++ b/v2rayN/v2rayN/Mode/RoutingItem.cs @@ -1,22 +1,21 @@ using SQLite; -namespace v2rayN.Mode -{ - [Serializable] - public class RoutingItem - { - [PrimaryKey] - public string id { get; set; } +namespace v2rayN.Mode; - public string remarks { get; set; } - public string url { get; set; } - public string ruleSet { get; set; } - public int ruleNum { get; set; } - public bool enabled { get; set; } = true; - public bool locked { get; set; } - public string customIcon { get; set; } - public string domainStrategy { get; set; } - public string domainStrategy4Singbox { get; set; } - public int sort { get; set; } - } +[Serializable] +public class RoutingItem +{ + [PrimaryKey] + public string id { get; set; } + + public string remarks { get; set; } + public string url { get; set; } + public string ruleSet { get; set; } + public int ruleNum { get; set; } + public bool enabled { get; set; } = true; + public bool locked { get; set; } + public string customIcon { get; set; } + public string domainStrategy { get; set; } + public string domainStrategy4Singbox { get; set; } + public int sort { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/RoutingItemModel.cs b/v2rayN/v2rayN/Mode/RoutingItemModel.cs index bc6d08be..cc9ff84e 100644 --- a/v2rayN/v2rayN/Mode/RoutingItemModel.cs +++ b/v2rayN/v2rayN/Mode/RoutingItemModel.cs @@ -1,8 +1,7 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class RoutingItemModel : RoutingItem { - [Serializable] - public class RoutingItemModel : RoutingItem - { - public bool isActive { get; set; } - } + public bool isActive { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/RulesItem.cs b/v2rayN/v2rayN/Mode/RulesItem.cs index 1985c258..c4c72e1e 100644 --- a/v2rayN/v2rayN/Mode/RulesItem.cs +++ b/v2rayN/v2rayN/Mode/RulesItem.cs @@ -1,25 +1,24 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class RulesItem { - [Serializable] - public class RulesItem - { - public string id { get; set; } - public string type { get; set; } + public string id { get; set; } + public string type { get; set; } - public string port { get; set; } + public string port { get; set; } - public List inboundTag { get; set; } + public List inboundTag { get; set; } - public string outboundTag { get; set; } + public string outboundTag { get; set; } - public List ip { get; set; } + public List ip { get; set; } - public List domain { get; set; } + public List domain { get; set; } - public List protocol { get; set; } + public List protocol { get; set; } - public List process { get; set; } + public List process { get; set; } - public bool enabled { get; set; } = true; - } + public bool enabled { get; set; } = true; } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/RulesItemModel.cs b/v2rayN/v2rayN/Mode/RulesItemModel.cs index 65f0d84f..311cbad8 100644 --- a/v2rayN/v2rayN/Mode/RulesItemModel.cs +++ b/v2rayN/v2rayN/Mode/RulesItemModel.cs @@ -1,14 +1,13 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class RulesItemModel : RulesItem { - [Serializable] - public class RulesItemModel : RulesItem - { - public string inboundTags { get; set; } + public string inboundTags { get; set; } - public string ips { get; set; } + public string ips { get; set; } - public string domains { get; set; } + public string domains { get; set; } - public string protocols { get; set; } - } + public string protocols { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ServerSpeedItem.cs b/v2rayN/v2rayN/Mode/ServerSpeedItem.cs index c51f6d11..c3a303fb 100644 --- a/v2rayN/v2rayN/Mode/ServerSpeedItem.cs +++ b/v2rayN/v2rayN/Mode/ServerSpeedItem.cs @@ -1,40 +1,39 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +internal class ServerSpeedItem : ServerStatItem { - [Serializable] - internal class ServerSpeedItem : ServerStatItem + public long proxyUp { - public long proxyUp - { - get; set; - } - - public long proxyDown - { - get; set; - } - - public long directUp - { - get; set; - } - - public long directDown - { - get; set; - } + get; set; } - [Serializable] - public class TrafficItem + public long proxyDown { - public ulong up - { - get; set; - } + get; set; + } - public ulong down - { - get; set; - } + public long directUp + { + get; set; + } + + public long directDown + { + get; set; + } +} + +[Serializable] +public class TrafficItem +{ + public ulong up + { + get; set; + } + + public ulong down + { + get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ServerStatItem.cs b/v2rayN/v2rayN/Mode/ServerStatItem.cs index 9664fa89..24b7818d 100644 --- a/v2rayN/v2rayN/Mode/ServerStatItem.cs +++ b/v2rayN/v2rayN/Mode/ServerStatItem.cs @@ -1,39 +1,38 @@ using SQLite; -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class ServerStatItem { - [Serializable] - public class ServerStatItem + [PrimaryKey] + public string indexId { - [PrimaryKey] - public string indexId - { - get; set; - } + get; set; + } - public long totalUp - { - get; set; - } + public long totalUp + { + get; set; + } - public long totalDown - { - get; set; - } + public long totalDown + { + get; set; + } - public long todayUp - { - get; set; - } + public long todayUp + { + get; set; + } - public long todayDown - { - get; set; - } + public long todayDown + { + get; set; + } - public long dateNow - { - get; set; - } + public long dateNow + { + get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/ServerTestItem.cs b/v2rayN/v2rayN/Mode/ServerTestItem.cs index bb210935..acf25057 100644 --- a/v2rayN/v2rayN/Mode/ServerTestItem.cs +++ b/v2rayN/v2rayN/Mode/ServerTestItem.cs @@ -1,13 +1,12 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +internal class ServerTestItem { - [Serializable] - internal class ServerTestItem - { - public string indexId { get; set; } - public string address { get; set; } - public int port { get; set; } - public EConfigType configType { get; set; } - public bool allowTest { get; set; } - public int delay { get; set; } - } + public string indexId { get; set; } + public string address { get; set; } + public int port { get; set; } + public EConfigType configType { get; set; } + public bool allowTest { get; set; } + public int delay { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/SingboxConfig.cs b/v2rayN/v2rayN/Mode/SingboxConfig.cs index ffc1f01e..cee00442 100644 --- a/v2rayN/v2rayN/Mode/SingboxConfig.cs +++ b/v2rayN/v2rayN/Mode/SingboxConfig.cs @@ -1,212 +1,211 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +public class SingboxConfig { - public class SingboxConfig - { - public Log4Sbox log { get; set; } - public object dns { get; set; } - public List inbounds { get; set; } - public List outbounds { get; set; } - public Route4Sbox route { get; set; } - public Experimental4Sbox experimental { get; set; } - } + public Log4Sbox log { get; set; } + public object dns { get; set; } + public List inbounds { get; set; } + public List outbounds { get; set; } + public Route4Sbox route { get; set; } + public Experimental4Sbox experimental { get; set; } +} - public class Log4Sbox - { - public bool? disabled { get; set; } - public string level { get; set; } - public string output { get; set; } - public bool timestamp { get; set; } - } +public class Log4Sbox +{ + public bool? disabled { get; set; } + public string level { get; set; } + public string output { get; set; } + public bool timestamp { get; set; } +} - public class Dns4Sbox - { - public List servers { get; set; } - public List rules { get; set; } - public string? final { get; set; } - public string? strategy { get; set; } - public bool? disable_cache { get; set; } - public bool? disable_expire { get; set; } - public bool? independent_cache { get; set; } - public bool? reverse_mapping { get; set; } - public Fakeip4Sbox? fakeip { get; set; } - } +public class Dns4Sbox +{ + public List servers { get; set; } + public List rules { get; set; } + public string? final { get; set; } + public string? strategy { get; set; } + public bool? disable_cache { get; set; } + public bool? disable_expire { get; set; } + public bool? independent_cache { get; set; } + public bool? reverse_mapping { get; set; } + public Fakeip4Sbox? fakeip { get; set; } +} - public class Route4Sbox - { - public bool? auto_detect_interface { get; set; } - public List rules { get; set; } - } +public class Route4Sbox +{ + public bool? auto_detect_interface { get; set; } + public List rules { get; set; } +} - [Serializable] - public class Rule4Sbox - { - public string outbound { get; set; } - public string server { get; set; } - public bool? disable_cache { get; set; } - public List? inbound { get; set; } - public List? protocol { get; set; } - public string type { get; set; } - public string mode { get; set; } - public string network { get; set; } - public List? port { get; set; } - public List? port_range { get; set; } - public List? geosite { get; set; } - public List? domain { get; set; } - public List? domain_suffix { get; set; } - public List? domain_keyword { get; set; } - public List? domain_regex { get; set; } - public List? geoip { get; set; } - public List? ip_cidr { get; set; } - public List? source_ip_cidr { get; set; } +[Serializable] +public class Rule4Sbox +{ + public string outbound { get; set; } + public string server { get; set; } + public bool? disable_cache { get; set; } + public List? inbound { get; set; } + public List? protocol { get; set; } + public string type { get; set; } + public string mode { get; set; } + public string network { get; set; } + public List? port { get; set; } + public List? port_range { get; set; } + public List? geosite { get; set; } + public List? domain { get; set; } + public List? domain_suffix { get; set; } + public List? domain_keyword { get; set; } + public List? domain_regex { get; set; } + public List? geoip { get; set; } + public List? ip_cidr { get; set; } + public List? source_ip_cidr { get; set; } - public List? process_name { get; set; } - } + public List? process_name { get; set; } +} - [Serializable] - public class Inbound4Sbox - { - public string type { get; set; } - public string tag { get; set; } - public string listen { get; set; } - public int? listen_port { get; set; } - public string? domain_strategy { get; set; } - public string interface_name { get; set; } - public string inet4_address { get; set; } - public string inet6_address { get; set; } - public int? mtu { get; set; } - public bool? auto_route { get; set; } - public bool? strict_route { get; set; } - public bool? endpoint_independent_nat { get; set; } - public string? stack { get; set; } - public bool? sniff { get; set; } - public bool? sniff_override_destination { get; set; } - public List users { get; set; } - } +[Serializable] +public class Inbound4Sbox +{ + public string type { get; set; } + public string tag { get; set; } + public string listen { get; set; } + public int? listen_port { get; set; } + public string? domain_strategy { get; set; } + public string interface_name { get; set; } + public string inet4_address { get; set; } + public string inet6_address { get; set; } + public int? mtu { get; set; } + public bool? auto_route { get; set; } + public bool? strict_route { get; set; } + public bool? endpoint_independent_nat { get; set; } + public string? stack { get; set; } + public bool? sniff { get; set; } + public bool? sniff_override_destination { get; set; } + public List users { get; set; } +} - public class User4Sbox - { - public string username { get; set; } - public string password { get; set; } - } +public class User4Sbox +{ + public string username { get; set; } + public string password { get; set; } +} - public class Outbound4Sbox - { - public string type { get; set; } - public string tag { get; set; } - public string server { get; set; } - public int? server_port { get; set; } - public string uuid { get; set; } - public string security { get; set; } - public int? alter_id { get; set; } - public string flow { get; set; } - public int? up_mbps { get; set; } - public int? down_mbps { get; set; } - public string auth_str { get; set; } - public int? recv_window_conn { get; set; } - public int? recv_window { get; set; } - public bool? disable_mtu_discovery { get; set; } - public string detour { get; set; } - public string method { get; set; } - public string username { get; set; } - public string password { get; set; } - public string? version { get; set; } - public string? network { get; set; } - public string packet_encoding { get; set; } - public Tls4Sbox tls { get; set; } - public Multiplex4Sbox multiplex { get; set; } - public Transport4Sbox transport { get; set; } - } +public class Outbound4Sbox +{ + public string type { get; set; } + public string tag { get; set; } + public string server { get; set; } + public int? server_port { get; set; } + public string uuid { get; set; } + public string security { get; set; } + public int? alter_id { get; set; } + public string flow { get; set; } + public int? up_mbps { get; set; } + public int? down_mbps { get; set; } + public string auth_str { get; set; } + public int? recv_window_conn { get; set; } + public int? recv_window { get; set; } + public bool? disable_mtu_discovery { get; set; } + public string detour { get; set; } + public string method { get; set; } + public string username { get; set; } + public string password { get; set; } + public string? version { get; set; } + public string? network { get; set; } + public string packet_encoding { get; set; } + public Tls4Sbox tls { get; set; } + public Multiplex4Sbox multiplex { get; set; } + public Transport4Sbox transport { get; set; } +} - public class Tls4Sbox - { - public bool enabled { get; set; } - public string server_name { get; set; } - public bool? insecure { get; set; } - public List alpn { get; set; } - public Utls4Sbox utls { get; set; } - public Reality4Sbox reality { get; set; } - } +public class Tls4Sbox +{ + public bool enabled { get; set; } + public string server_name { get; set; } + public bool? insecure { get; set; } + public List alpn { get; set; } + public Utls4Sbox utls { get; set; } + public Reality4Sbox reality { get; set; } +} - public class Multiplex4Sbox - { - public bool enabled { get; set; } - public string protocol { get; set; } - public int max_connections { get; set; } - public int min_streams { get; set; } - public int max_streams { get; set; } - public bool padding { get; set; } - } +public class Multiplex4Sbox +{ + public bool enabled { get; set; } + public string protocol { get; set; } + public int max_connections { get; set; } + public int min_streams { get; set; } + public int max_streams { get; set; } + public bool padding { get; set; } +} - public class Utls4Sbox - { - public bool enabled { get; set; } - public string fingerprint { get; set; } - } +public class Utls4Sbox +{ + public bool enabled { get; set; } + public string fingerprint { get; set; } +} - public class Reality4Sbox - { - public bool enabled { get; set; } - public string public_key { get; set; } - public string short_id { get; set; } - } +public class Reality4Sbox +{ + public bool enabled { get; set; } + public string public_key { get; set; } + public string short_id { get; set; } +} - public class Transport4Sbox - { - public string type { get; set; } - public List? host { get; set; } - public string? path { get; set; } - public Headers4Sbox? headers { get; set; } +public class Transport4Sbox +{ + public string type { get; set; } + public List? host { get; set; } + public string? path { get; set; } + public Headers4Sbox? headers { get; set; } - public string service_name { get; set; } - public string idle_timeout { get; set; } - public string ping_timeout { get; set; } - public bool? permit_without_stream { get; set; } - } + public string service_name { get; set; } + public string idle_timeout { get; set; } + public string ping_timeout { get; set; } + public bool? permit_without_stream { get; set; } +} - public class Headers4Sbox - { - public string? Host { get; set; } - } +public class Headers4Sbox +{ + public string? Host { get; set; } +} - public class Server4Sbox - { - public string tag { get; set; } - public string address { get; set; } - public string address_resolver { get; set; } - public string strategy { get; set; } - public string detour { get; set; } - } +public class Server4Sbox +{ + public string tag { get; set; } + public string address { get; set; } + public string address_resolver { get; set; } + public string strategy { get; set; } + public string detour { get; set; } +} - public class Experimental4Sbox - { - public V2ray_Api4Sbox v2ray_api { get; set; } - public Clash_Api4Sbox clash_api { get; set; } - } +public class Experimental4Sbox +{ + public V2ray_Api4Sbox v2ray_api { get; set; } + public Clash_Api4Sbox clash_api { get; set; } +} - public class V2ray_Api4Sbox - { - public string listen { get; set; } - public Stats4Sbox stats { get; set; } - } +public class V2ray_Api4Sbox +{ + public string listen { get; set; } + public Stats4Sbox stats { get; set; } +} - public class Clash_Api4Sbox - { - public string external_controller { get; set; } - public bool store_selected { get; set; } - } +public class Clash_Api4Sbox +{ + public string external_controller { get; set; } + public bool store_selected { get; set; } +} - public class Stats4Sbox - { - public bool enabled { get; set; } - public List? inbounds { get; set; } - public List? outbounds { get; set; } - public List? users { get; set; } - } +public class Stats4Sbox +{ + public bool enabled { get; set; } + public List? inbounds { get; set; } + public List? outbounds { get; set; } + public List? users { get; set; } +} - public class Fakeip4Sbox - { - public bool enabled { get; set; } - public string inet4_range { get; set; } - public string inet6_range { get; set; } - } +public class Fakeip4Sbox +{ + public bool enabled { get; set; } + public string inet4_range { get; set; } + public string inet6_range { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/SsSIP008.cs b/v2rayN/v2rayN/Mode/SsSIP008.cs index 08471474..27b69fe5 100644 --- a/v2rayN/v2rayN/Mode/SsSIP008.cs +++ b/v2rayN/v2rayN/Mode/SsSIP008.cs @@ -1,18 +1,17 @@ -namespace v2rayN.Mode -{ - public class SsSIP008 - { - public List servers { get; set; } - } +namespace v2rayN.Mode; - [Serializable] - public class SsServer - { - public string remarks { get; set; } - public string server { get; set; } - public string server_port { get; set; } - public string method { get; set; } - public string password { get; set; } - public string plugin { get; set; } - } +public class SsSIP008 +{ + public List servers { get; set; } +} + +[Serializable] +public class SsServer +{ + public string remarks { get; set; } + public string server { get; set; } + public string server_port { get; set; } + public string method { get; set; } + public string password { get; set; } + public string plugin { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/SubItem.cs b/v2rayN/v2rayN/Mode/SubItem.cs index 3221e914..5514c556 100644 --- a/v2rayN/v2rayN/Mode/SubItem.cs +++ b/v2rayN/v2rayN/Mode/SubItem.cs @@ -1,31 +1,30 @@ using SQLite; -namespace v2rayN.Mode +namespace v2rayN.Mode; + +[Serializable] +public class SubItem { - [Serializable] - public class SubItem - { - [PrimaryKey] - public string id { get; set; } + [PrimaryKey] + public string id { get; set; } - public string remarks { get; set; } + public string remarks { get; set; } - public string url { get; set; } + public string url { get; set; } - public string moreUrl { get; set; } + public string moreUrl { get; set; } - public bool enabled { get; set; } = true; + public bool enabled { get; set; } = true; - public string userAgent { get; set; } = string.Empty; + public string userAgent { get; set; } = string.Empty; - public int sort { get; set; } + public int sort { get; set; } - public string? filter { get; set; } + public string? filter { get; set; } - public int autoUpdateInterval { get; set; } + public int autoUpdateInterval { get; set; } - public long updateTime { get; set; } + public long updateTime { get; set; } - public string? convertTarget { get; set; } - } + public string? convertTarget { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/SysproxyConfig.cs b/v2rayN/v2rayN/Mode/SysproxyConfig.cs index f40715aa..f50ad929 100644 --- a/v2rayN/v2rayN/Mode/SysproxyConfig.cs +++ b/v2rayN/v2rayN/Mode/SysproxyConfig.cs @@ -1,20 +1,19 @@ -namespace v2rayN.Mode -{ - internal class SysproxyConfig - { - public bool UserSettingsRecorded; - public string Flags; - public string ProxyServer; - public string BypassList; - public string PacUrl; +namespace v2rayN.Mode; - public SysproxyConfig() - { - UserSettingsRecorded = false; - Flags = "1"; - ProxyServer = ""; - BypassList = ""; - PacUrl = ""; - } +internal class SysproxyConfig +{ + public bool UserSettingsRecorded; + public string Flags; + public string ProxyServer; + public string BypassList; + public string PacUrl; + + public SysproxyConfig() + { + UserSettingsRecorded = false; + Flags = "1"; + ProxyServer = ""; + BypassList = ""; + PacUrl = ""; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/V2rayConfig.cs b/v2rayN/v2rayN/Mode/V2rayConfig.cs index 14c5dee0..8ff483b7 100644 --- a/v2rayN/v2rayN/Mode/V2rayConfig.cs +++ b/v2rayN/v2rayN/Mode/V2rayConfig.cs @@ -1,635 +1,634 @@ using Newtonsoft.Json; -namespace v2rayN.Mode +namespace v2rayN.Mode; + +/// +/// v2ray配置文件实体类 +/// 例子SampleConfig.txt +/// +public class V2rayConfig { /// - /// v2ray配置文件实体类 - /// 例子SampleConfig.txt + /// 日志配置 /// - public class V2rayConfig - { - /// - /// 日志配置 - /// - public Log4Ray log { get; set; } - - /// - /// 传入连接配置 - /// - public List inbounds { get; set; } - - /// - /// 传出连接配置 - /// - public List outbounds { get; set; } - - /// - /// 统计需要, 空对象 - /// - public Stats4Ray stats { get; set; } - - /// - public API4Ray api { get; set; } - - /// - public Policy4Ray policy; - - /// - /// DNS 配置 - /// - public object dns { get; set; } - - /// - /// 路由配置 - /// - public Routing4Ray routing { get; set; } - } - - public class Stats4Ray - { }; - - public class API4Ray - { - public string tag { get; set; } - public List services { get; set; } - } - - public class Policy4Ray - { - public SystemPolicy4Ray system; - } - - public class SystemPolicy4Ray - { - public bool statsOutboundUplink; - public bool statsOutboundDownlink; - } - - public class Log4Ray - { - /// - /// - /// - public string access { get; set; } - - /// - /// - /// - public string error { get; set; } - - /// - /// - /// - public string loglevel { get; set; } - } - - public class Inbounds4Ray - { - public string tag { get; set; } - - /// - /// - /// - public int port { get; set; } - - /// - /// - /// - public string listen { get; set; } - - /// - /// - /// - public string protocol { get; set; } - - /// - /// - /// - public Sniffing4Ray sniffing { get; set; } - - /// - /// - /// - public Inboundsettings4Ray settings { get; set; } - - /// - /// - /// - public StreamSettings4Ray streamSettings { get; set; } - } - - public class Inboundsettings4Ray - { - /// - /// - /// - public string auth { get; set; } - - /// - /// - /// - public bool udp { get; set; } - - /// - /// - /// - public string ip { get; set; } - - /// - /// api 使用 - /// - public string address { get; set; } - - /// - /// - /// - public List clients { get; set; } - - /// - /// VLESS - /// - public string decryption { get; set; } - - public bool allowTransparent { get; set; } - - public List accounts { get; set; } - } - - public class UsersItem4Ray - { - /// - /// - /// - public string id { get; set; } - - /// - /// - /// - public int alterId { get; set; } - - /// - /// - /// - public string email { get; set; } - - /// - /// - /// - public string security { get; set; } - - /// - /// VLESS - /// - public string encryption { get; set; } - - /// - /// VLESS - /// - public string? flow { get; set; } - } - - public class Sniffing4Ray - { - public bool enabled { get; set; } - public List destOverride { get; set; } - public bool routeOnly { get; set; } - } - - public class Outbounds4Ray - { - /// - /// 默认值agentout - /// - public string tag { get; set; } - - /// - /// - /// - public string protocol { get; set; } - - /// - /// - /// - public Outboundsettings4Ray settings { get; set; } - - /// - /// - /// - public StreamSettings4Ray streamSettings { get; set; } - - /// - /// - /// - public Mux4Ray mux { get; set; } - } - - public class Outboundsettings4Ray - { - /// - /// - /// - public List vnext { get; set; } - - /// - /// - /// - public List servers { get; set; } - - /// - /// - /// - public Response4Ray response { get; set; } - - /// - /// - /// - public string domainStrategy { get; set; } - - /// - /// - /// - public int? userLevel { get; set; } - } - - public class VnextItem4Ray - { - /// - /// - /// - public string address { get; set; } - - /// - /// - /// - public int port { get; set; } - - /// - /// - /// - public List users { get; set; } - } - - public class ServersItem4Ray - { - /// - /// - /// - public string email { get; set; } - - /// - /// - /// - public string address { get; set; } - - /// - /// - /// - public string method { get; set; } - - /// - /// - /// - public bool ota { get; set; } - - /// - /// - /// - public string password { get; set; } - - /// - /// - /// - public int port { get; set; } - - /// - /// - /// - public int level { get; set; } - - /// - /// trojan - /// - public string flow { get; set; } - - /// - /// - /// - public List users { get; set; } - } - - public class SocksUsersItem4Ray - { - /// - /// - /// - public string user { get; set; } - - /// - /// - /// - public string pass { get; set; } - - /// - /// - /// - public int level { get; set; } - } - - public class Mux4Ray - { - /// - /// - /// - public bool enabled { get; set; } - - /// - /// - /// - public int concurrency { get; set; } - } - - public class Response4Ray - { - /// - /// - /// - public string type { get; set; } - } - - public class Dns4Ray - { - /// - /// - /// - public List servers { get; set; } - } - - public class Routing4Ray - { - /// - /// - /// - public string domainStrategy { get; set; } - - /// - /// - /// - public string domainMatcher { get; set; } - - /// - /// - /// - public List rules { get; set; } - } - - [Serializable] - public class RulesItem4Ray - { - public string type { get; set; } - - public string port { get; set; } - - public List inboundTag { get; set; } - - public string outboundTag { get; set; } - - public List ip { get; set; } - - public List domain { get; set; } - - public List protocol { get; set; } - } - - public class StreamSettings4Ray - { - /// - /// - /// - public string network { get; set; } - - /// - /// - /// - public string security { get; set; } - - /// - /// - /// - public TlsSettings4Ray tlsSettings { get; set; } - - /// - /// Tcp传输额外设置 - /// - public TcpSettings4Ray tcpSettings { get; set; } - - /// - /// Kcp传输额外设置 - /// - public KcpSettings4Ray kcpSettings { get; set; } - - /// - /// ws传输额外设置 - /// - public WsSettings4Ray wsSettings { get; set; } - - /// - /// h2传输额外设置 - /// - public HttpSettings4Ray httpSettings { get; set; } - - /// - /// QUIC - /// - public QuicSettings4Ray quicSettings { get; set; } - - /// - /// VLESS only - /// - public TlsSettings4Ray realitySettings { get; set; } - - /// - /// grpc - /// - public GrpcSettings4Ray grpcSettings { get; set; } - } - - public class TlsSettings4Ray - { - /// - /// 是否允许不安全连接(用于客户端) - /// - public bool? allowInsecure { get; set; } - - /// - /// - /// - public string? serverName { get; set; } - - /// - /// - /// - public List? alpn { get; set; } - - public string? fingerprint { get; set; } - - public bool? show { get; set; } = false; - public string? publicKey { get; set; } - public string? shortId { get; set; } - public string? spiderX { get; set; } - } - - public class TcpSettings4Ray - { - /// - /// 数据包头部伪装设置 - /// - public Header4Ray header { get; set; } - } - - public class Header4Ray - { - /// - /// 伪装 - /// - public string type { get; set; } - - /// - /// 结构复杂,直接存起来 - /// - public object request { get; set; } - - /// - /// 结构复杂,直接存起来 - /// - public object response { get; set; } - } - - public class KcpSettings4Ray - { - /// - /// - /// - public int mtu { get; set; } - - /// - /// - /// - public int tti { get; set; } - - /// - /// - /// - public int uplinkCapacity { get; set; } - - /// - /// - /// - public int downlinkCapacity { get; set; } - - /// - /// - /// - public bool congestion { get; set; } - - /// - /// - /// - public int readBufferSize { get; set; } - - /// - /// - /// - public int writeBufferSize { get; set; } - - /// - /// - /// - public Header4Ray header { get; set; } - - /// - /// - /// - public string seed { get; set; } - } - - public class WsSettings4Ray - { - /// - /// - /// - public string path { get; set; } - - /// - /// - /// - public Headers4Ray headers { get; set; } - } - - public class Headers4Ray - { - /// - /// - /// - public string Host { get; set; } - - /// - /// 用户代理 - /// - [JsonProperty("User-Agent")] - public string UserAgent { get; set; } - } - - public class HttpSettings4Ray - { - /// - /// - /// - public string path { get; set; } - - /// - /// - /// - public List host { get; set; } - } - - public class QuicSettings4Ray - { - /// - /// - /// - public string security { get; set; } - - /// - /// - /// - public string key { get; set; } - - /// - /// - /// - public Header4Ray header { get; set; } - } - - public class GrpcSettings4Ray - { - public string serviceName { get; set; } - public bool multiMode { get; set; } - public int idle_timeout { get; set; } - public int health_check_timeout { get; set; } - public bool permit_without_stream { get; set; } - public int initial_windows_size { get; set; } - } - - public class AccountsItem4Ray - { - /// - /// - /// - public string user { get; set; } - - /// - /// - /// - public string pass { get; set; } - } + public Log4Ray log { get; set; } + + /// + /// 传入连接配置 + /// + public List inbounds { get; set; } + + /// + /// 传出连接配置 + /// + public List outbounds { get; set; } + + /// + /// 统计需要, 空对象 + /// + public Stats4Ray stats { get; set; } + + /// + public API4Ray api { get; set; } + + /// + public Policy4Ray policy; + + /// + /// DNS 配置 + /// + public object dns { get; set; } + + /// + /// 路由配置 + /// + public Routing4Ray routing { get; set; } +} + +public class Stats4Ray +{ }; + +public class API4Ray +{ + public string tag { get; set; } + public List services { get; set; } +} + +public class Policy4Ray +{ + public SystemPolicy4Ray system; +} + +public class SystemPolicy4Ray +{ + public bool statsOutboundUplink; + public bool statsOutboundDownlink; +} + +public class Log4Ray +{ + /// + /// + /// + public string access { get; set; } + + /// + /// + /// + public string error { get; set; } + + /// + /// + /// + public string loglevel { get; set; } +} + +public class Inbounds4Ray +{ + public string tag { get; set; } + + /// + /// + /// + public int port { get; set; } + + /// + /// + /// + public string listen { get; set; } + + /// + /// + /// + public string protocol { get; set; } + + /// + /// + /// + public Sniffing4Ray sniffing { get; set; } + + /// + /// + /// + public Inboundsettings4Ray settings { get; set; } + + /// + /// + /// + public StreamSettings4Ray streamSettings { get; set; } +} + +public class Inboundsettings4Ray +{ + /// + /// + /// + public string auth { get; set; } + + /// + /// + /// + public bool udp { get; set; } + + /// + /// + /// + public string ip { get; set; } + + /// + /// api 使用 + /// + public string address { get; set; } + + /// + /// + /// + public List clients { get; set; } + + /// + /// VLESS + /// + public string decryption { get; set; } + + public bool allowTransparent { get; set; } + + public List accounts { get; set; } +} + +public class UsersItem4Ray +{ + /// + /// + /// + public string id { get; set; } + + /// + /// + /// + public int alterId { get; set; } + + /// + /// + /// + public string email { get; set; } + + /// + /// + /// + public string security { get; set; } + + /// + /// VLESS + /// + public string encryption { get; set; } + + /// + /// VLESS + /// + public string? flow { get; set; } +} + +public class Sniffing4Ray +{ + public bool enabled { get; set; } + public List destOverride { get; set; } + public bool routeOnly { get; set; } +} + +public class Outbounds4Ray +{ + /// + /// 默认值agentout + /// + public string tag { get; set; } + + /// + /// + /// + public string protocol { get; set; } + + /// + /// + /// + public Outboundsettings4Ray settings { get; set; } + + /// + /// + /// + public StreamSettings4Ray streamSettings { get; set; } + + /// + /// + /// + public Mux4Ray mux { get; set; } +} + +public class Outboundsettings4Ray +{ + /// + /// + /// + public List vnext { get; set; } + + /// + /// + /// + public List servers { get; set; } + + /// + /// + /// + public Response4Ray response { get; set; } + + /// + /// + /// + public string domainStrategy { get; set; } + + /// + /// + /// + public int? userLevel { get; set; } +} + +public class VnextItem4Ray +{ + /// + /// + /// + public string address { get; set; } + + /// + /// + /// + public int port { get; set; } + + /// + /// + /// + public List users { get; set; } +} + +public class ServersItem4Ray +{ + /// + /// + /// + public string email { get; set; } + + /// + /// + /// + public string address { get; set; } + + /// + /// + /// + public string method { get; set; } + + /// + /// + /// + public bool ota { get; set; } + + /// + /// + /// + public string password { get; set; } + + /// + /// + /// + public int port { get; set; } + + /// + /// + /// + public int level { get; set; } + + /// + /// trojan + /// + public string flow { get; set; } + + /// + /// + /// + public List users { get; set; } +} + +public class SocksUsersItem4Ray +{ + /// + /// + /// + public string user { get; set; } + + /// + /// + /// + public string pass { get; set; } + + /// + /// + /// + public int level { get; set; } +} + +public class Mux4Ray +{ + /// + /// + /// + public bool enabled { get; set; } + + /// + /// + /// + public int concurrency { get; set; } +} + +public class Response4Ray +{ + /// + /// + /// + public string type { get; set; } +} + +public class Dns4Ray +{ + /// + /// + /// + public List servers { get; set; } +} + +public class Routing4Ray +{ + /// + /// + /// + public string domainStrategy { get; set; } + + /// + /// + /// + public string domainMatcher { get; set; } + + /// + /// + /// + public List rules { get; set; } +} + +[Serializable] +public class RulesItem4Ray +{ + public string type { get; set; } + + public string port { get; set; } + + public List inboundTag { get; set; } + + public string outboundTag { get; set; } + + public List ip { get; set; } + + public List domain { get; set; } + + public List protocol { get; set; } +} + +public class StreamSettings4Ray +{ + /// + /// + /// + public string network { get; set; } + + /// + /// + /// + public string security { get; set; } + + /// + /// + /// + public TlsSettings4Ray tlsSettings { get; set; } + + /// + /// Tcp传输额外设置 + /// + public TcpSettings4Ray tcpSettings { get; set; } + + /// + /// Kcp传输额外设置 + /// + public KcpSettings4Ray kcpSettings { get; set; } + + /// + /// ws传输额外设置 + /// + public WsSettings4Ray wsSettings { get; set; } + + /// + /// h2传输额外设置 + /// + public HttpSettings4Ray httpSettings { get; set; } + + /// + /// QUIC + /// + public QuicSettings4Ray quicSettings { get; set; } + + /// + /// VLESS only + /// + public TlsSettings4Ray realitySettings { get; set; } + + /// + /// grpc + /// + public GrpcSettings4Ray grpcSettings { get; set; } +} + +public class TlsSettings4Ray +{ + /// + /// 是否允许不安全连接(用于客户端) + /// + public bool? allowInsecure { get; set; } + + /// + /// + /// + public string? serverName { get; set; } + + /// + /// + /// + public List? alpn { get; set; } + + public string? fingerprint { get; set; } + + public bool? show { get; set; } = false; + public string? publicKey { get; set; } + public string? shortId { get; set; } + public string? spiderX { get; set; } +} + +public class TcpSettings4Ray +{ + /// + /// 数据包头部伪装设置 + /// + public Header4Ray header { get; set; } +} + +public class Header4Ray +{ + /// + /// 伪装 + /// + public string type { get; set; } + + /// + /// 结构复杂,直接存起来 + /// + public object request { get; set; } + + /// + /// 结构复杂,直接存起来 + /// + public object response { get; set; } +} + +public class KcpSettings4Ray +{ + /// + /// + /// + public int mtu { get; set; } + + /// + /// + /// + public int tti { get; set; } + + /// + /// + /// + public int uplinkCapacity { get; set; } + + /// + /// + /// + public int downlinkCapacity { get; set; } + + /// + /// + /// + public bool congestion { get; set; } + + /// + /// + /// + public int readBufferSize { get; set; } + + /// + /// + /// + public int writeBufferSize { get; set; } + + /// + /// + /// + public Header4Ray header { get; set; } + + /// + /// + /// + public string seed { get; set; } +} + +public class WsSettings4Ray +{ + /// + /// + /// + public string path { get; set; } + + /// + /// + /// + public Headers4Ray headers { get; set; } +} + +public class Headers4Ray +{ + /// + /// + /// + public string Host { get; set; } + + /// + /// 用户代理 + /// + [JsonProperty("User-Agent")] + public string UserAgent { get; set; } +} + +public class HttpSettings4Ray +{ + /// + /// + /// + public string path { get; set; } + + /// + /// + /// + public List host { get; set; } +} + +public class QuicSettings4Ray +{ + /// + /// + /// + public string security { get; set; } + + /// + /// + /// + public string key { get; set; } + + /// + /// + /// + public Header4Ray header { get; set; } +} + +public class GrpcSettings4Ray +{ + public string serviceName { get; set; } + public bool multiMode { get; set; } + public int idle_timeout { get; set; } + public int health_check_timeout { get; set; } + public bool permit_without_stream { get; set; } + public int initial_windows_size { get; set; } +} + +public class AccountsItem4Ray +{ + /// + /// + /// + public string user { get; set; } + + /// + /// + /// + public string pass { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/V2rayTcpRequest.cs b/v2rayN/v2rayN/Mode/V2rayTcpRequest.cs index 79d7fa05..50fba470 100644 --- a/v2rayN/v2rayN/Mode/V2rayTcpRequest.cs +++ b/v2rayN/v2rayN/Mode/V2rayTcpRequest.cs @@ -1,21 +1,20 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +/// +/// Tcp伪装http的Request,只要Host +/// +public class V2rayTcpRequest { /// - /// Tcp伪装http的Request,只要Host + /// /// - public class V2rayTcpRequest - { - /// - /// - /// - public RequestHeaders headers { get; set; } - } + public RequestHeaders headers { get; set; } +} - public class RequestHeaders - { - /// - /// - /// - public List Host { get; set; } - } +public class RequestHeaders +{ + /// + /// + /// + public List Host { get; set; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Mode/VmessQRCode.cs b/v2rayN/v2rayN/Mode/VmessQRCode.cs index fec376d9..d91dac5f 100644 --- a/v2rayN/v2rayN/Mode/VmessQRCode.cs +++ b/v2rayN/v2rayN/Mode/VmessQRCode.cs @@ -1,84 +1,83 @@ -namespace v2rayN.Mode +namespace v2rayN.Mode; + +/// +/// https://github.com/2dust/v2rayN/wiki/ +/// +[Serializable] +internal class VmessQRCode { /// - /// https://github.com/2dust/v2rayN/wiki/ + /// /// - [Serializable] - internal class VmessQRCode - { - /// - /// - /// - public string v { get; set; } = string.Empty; + public string v { get; set; } = string.Empty; - /// - /// - /// - public string ps { get; set; } = string.Empty; + /// + /// + /// + public string ps { get; set; } = string.Empty; - /// - /// - /// - public string add { get; set; } = string.Empty; + /// + /// + /// + public string add { get; set; } = string.Empty; - /// - /// - /// - public string port { get; set; } = string.Empty; + /// + /// + /// + public string port { get; set; } = string.Empty; - /// - /// - /// - public string id { get; set; } = string.Empty; + /// + /// + /// + public string id { get; set; } = string.Empty; - /// - /// - /// - public string aid { get; set; } = string.Empty; + /// + /// + /// + public string aid { get; set; } = string.Empty; - /// - /// - /// - public string scy { get; set; } = string.Empty; + /// + /// + /// + public string scy { get; set; } = string.Empty; - /// - /// - /// - public string net { get; set; } = string.Empty; + /// + /// + /// + public string net { get; set; } = string.Empty; - /// - /// - /// - public string type { get; set; } = string.Empty; + /// + /// + /// + public string type { get; set; } = string.Empty; - /// - /// - /// - public string host { get; set; } = string.Empty; + /// + /// + /// + public string host { get; set; } = string.Empty; - /// - /// - /// - public string path { get; set; } = string.Empty; + /// + /// + /// + public string path { get; set; } = string.Empty; - /// - /// TLS - /// - public string tls { get; set; } = string.Empty; + /// + /// TLS + /// + public string tls { get; set; } = string.Empty; - /// - /// TLS SNI - /// - public string sni { get; set; } = string.Empty; + /// + /// TLS SNI + /// + public string sni { get; set; } = string.Empty; - /// - /// TLS alpn - /// - public string alpn { get; set; } = string.Empty; + /// + /// TLS alpn + /// + public string alpn { get; set; } = string.Empty; - /// - /// TLS fingerprint - /// - public string fp { get; set; } = string.Empty; - } + /// + /// TLS fingerprint + /// + public string fp { get; set; } = string.Empty; } \ No newline at end of file diff --git a/v2rayN/v2rayN/Tool/FileManager.cs b/v2rayN/v2rayN/Tool/FileManager.cs index fa17fde5..a10f92d7 100644 --- a/v2rayN/v2rayN/Tool/FileManager.cs +++ b/v2rayN/v2rayN/Tool/FileManager.cs @@ -2,89 +2,88 @@ using System.IO.Compression; using System.Text; -namespace v2rayN.Tool +namespace v2rayN.Tool; + +public static class FileManager { - public static class FileManager + public static bool ByteArrayToFile(string fileName, byte[] content) { - public static bool ByteArrayToFile(string fileName, byte[] content) + try { - try - { - File.WriteAllBytes(fileName, content); - return true; - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - return false; + File.WriteAllBytes(fileName, content); + return true; } - - public static void UncompressFile(string fileName, byte[] content) + catch (Exception ex) { - try - { - using FileStream fs = File.Create(fileName); - using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false); - input.CopyTo(fs); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } + Utils.SaveLog(ex.Message, ex); } + return false; + } - public static string NonExclusiveReadAllText(string path) + public static void UncompressFile(string fileName, byte[] content) + { + try { - return NonExclusiveReadAllText(path, Encoding.Default); + using FileStream fs = File.Create(fileName); + using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false); + input.CopyTo(fs); } - - public static string NonExclusiveReadAllText(string path, Encoding encoding) + catch (Exception ex) { - try - { - using FileStream fs = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - using StreamReader sr = new(fs, encoding); - return sr.ReadToEnd(); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - throw; - } + Utils.SaveLog(ex.Message, ex); } + } - public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName) + public static string NonExclusiveReadAllText(string path) + { + return NonExclusiveReadAllText(path, Encoding.Default); + } + + public static string NonExclusiveReadAllText(string path, Encoding encoding) + { + try { - try + using FileStream fs = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using StreamReader sr = new(fs, encoding); + return sr.ReadToEnd(); + } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + throw; + } + } + + public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName) + { + try + { + using ZipArchive archive = ZipFile.OpenRead(fileName); + foreach (ZipArchiveEntry entry in archive.Entries) { - using ZipArchive archive = ZipFile.OpenRead(fileName); - foreach (ZipArchiveEntry entry in archive.Entries) + if (entry.Length == 0) { - if (entry.Length == 0) + continue; + } + try + { + if (!Utils.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName)) { continue; } - try - { - if (!Utils.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName)) - { - continue; - } - entry.ExtractToFile(Path.Combine(toPath, entry.Name), true); - } - catch (IOException ex) - { - Utils.SaveLog(ex.Message, ex); - } + entry.ExtractToFile(Path.Combine(toPath, entry.Name), true); + } + catch (IOException ex) + { + Utils.SaveLog(ex.Message, ex); } } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - return false; - } - return true; } + catch (Exception ex) + { + Utils.SaveLog(ex.Message, ex); + return false; + } + return true; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Tool/Job.cs b/v2rayN/v2rayN/Tool/Job.cs index 78b1a56e..466c20cf 100644 --- a/v2rayN/v2rayN/Tool/Job.cs +++ b/v2rayN/v2rayN/Tool/Job.cs @@ -1,176 +1,175 @@ using System.Diagnostics; using System.Runtime.InteropServices; -namespace v2rayN +namespace v2rayN; + +/* + * See: + * http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net + */ + +public class Job : IDisposable { - /* - * See: - * http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net - */ + private IntPtr handle = IntPtr.Zero; - public class Job : IDisposable + public Job() { - private IntPtr handle = IntPtr.Zero; - - public Job() + handle = CreateJobObject(IntPtr.Zero, null); + IntPtr extendedInfoPtr = IntPtr.Zero; + JOBOBJECT_BASIC_LIMIT_INFORMATION info = new() { - handle = CreateJobObject(IntPtr.Zero, null); - IntPtr extendedInfoPtr = IntPtr.Zero; - JOBOBJECT_BASIC_LIMIT_INFORMATION info = new() - { - LimitFlags = 0x2000 - }; + LimitFlags = 0x2000 + }; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new() - { - BasicLimitInformation = info - }; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new() + { + BasicLimitInformation = info + }; - try - { - int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); - extendedInfoPtr = Marshal.AllocHGlobal(length); - Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); + try + { + int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); + extendedInfoPtr = Marshal.AllocHGlobal(length); + Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); - if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, - (uint)length)) - throw new Exception(string.Format("Unable to set information. Error: {0}", - Marshal.GetLastWin32Error())); - } - finally + if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, + (uint)length)) + throw new Exception(string.Format("Unable to set information. Error: {0}", + Marshal.GetLastWin32Error())); + } + finally + { + if (extendedInfoPtr != IntPtr.Zero) { - if (extendedInfoPtr != IntPtr.Zero) - { - Marshal.FreeHGlobal(extendedInfoPtr); - } + Marshal.FreeHGlobal(extendedInfoPtr); } } + } - public bool AddProcess(IntPtr processHandle) + public bool AddProcess(IntPtr processHandle) + { + bool succ = AssignProcessToJobObject(handle, processHandle); + + if (!succ) { - bool succ = AssignProcessToJobObject(handle, processHandle); - - if (!succ) - { - Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error()); - } - - return succ; + Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error()); } - public bool AddProcess(int processId) + return succ; + } + + public bool AddProcess(int processId) + { + return AddProcess(Process.GetProcessById(processId).Handle); + } + + #region IDisposable + + private bool disposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) return; + disposed = true; + + if (disposing) { - return AddProcess(Process.GetProcessById(processId).Handle); + // no managed objects to free } - #region IDisposable - - private bool disposed; - - public void Dispose() + if (handle != IntPtr.Zero) { - Dispose(true); - GC.SuppressFinalize(this); + CloseHandle(handle); + handle = IntPtr.Zero; } - - protected virtual void Dispose(bool disposing) - { - if (disposed) return; - disposed = true; - - if (disposing) - { - // no managed objects to free - } - - if (handle != IntPtr.Zero) - { - CloseHandle(handle); - handle = IntPtr.Zero; - } - } - - ~Job() - { - Dispose(false); - } - - #endregion IDisposable - - #region Interop - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - private static extern IntPtr CreateJobObject(IntPtr a, string? lpName); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool CloseHandle(IntPtr hObject); - - #endregion Interop } - #region Helper classes - - [StructLayout(LayoutKind.Sequential)] - internal struct IO_COUNTERS + ~Job() { - public UInt64 ReadOperationCount; - public UInt64 WriteOperationCount; - public UInt64 OtherOperationCount; - public UInt64 ReadTransferCount; - public UInt64 WriteTransferCount; - public UInt64 OtherTransferCount; + Dispose(false); } - [StructLayout(LayoutKind.Sequential)] - internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION - { - public Int64 PerProcessUserTimeLimit; - public Int64 PerJobUserTimeLimit; - public UInt32 LimitFlags; - public UIntPtr MinimumWorkingSetSize; - public UIntPtr MaximumWorkingSetSize; - public UInt32 ActiveProcessLimit; - public UIntPtr Affinity; - public UInt32 PriorityClass; - public UInt32 SchedulingClass; - } + #endregion IDisposable - [StructLayout(LayoutKind.Sequential)] - public struct SECURITY_ATTRIBUTES - { - public UInt32 nLength; - public IntPtr lpSecurityDescriptor; - public Int32 bInheritHandle; - } + #region Interop - [StructLayout(LayoutKind.Sequential)] - internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION - { - public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; - public IO_COUNTERS IoInfo; - public UIntPtr ProcessMemoryLimit; - public UIntPtr JobMemoryLimit; - public UIntPtr PeakProcessMemoryUsed; - public UIntPtr PeakJobMemoryUsed; - } + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern IntPtr CreateJobObject(IntPtr a, string? lpName); - public enum JobObjectInfoType - { - AssociateCompletionPortInformation = 7, - BasicLimitInformation = 2, - BasicUIRestrictions = 4, - EndOfJobTimeInformation = 6, - ExtendedLimitInformation = 9, - SecurityLimitInformation = 5, - GroupInformation = 11 - } + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength); - #endregion Helper classes -} \ No newline at end of file + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool CloseHandle(IntPtr hObject); + + #endregion Interop +} + +#region Helper classes + +[StructLayout(LayoutKind.Sequential)] +internal struct IO_COUNTERS +{ + public UInt64 ReadOperationCount; + public UInt64 WriteOperationCount; + public UInt64 OtherOperationCount; + public UInt64 ReadTransferCount; + public UInt64 WriteTransferCount; + public UInt64 OtherTransferCount; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION +{ + public Int64 PerProcessUserTimeLimit; + public Int64 PerJobUserTimeLimit; + public UInt32 LimitFlags; + public UIntPtr MinimumWorkingSetSize; + public UIntPtr MaximumWorkingSetSize; + public UInt32 ActiveProcessLimit; + public UIntPtr Affinity; + public UInt32 PriorityClass; + public UInt32 SchedulingClass; +} + +[StructLayout(LayoutKind.Sequential)] +public struct SECURITY_ATTRIBUTES +{ + public UInt32 nLength; + public IntPtr lpSecurityDescriptor; + public Int32 bInheritHandle; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION +{ + public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; + public IO_COUNTERS IoInfo; + public UIntPtr ProcessMemoryLimit; + public UIntPtr JobMemoryLimit; + public UIntPtr PeakProcessMemoryUsed; + public UIntPtr PeakJobMemoryUsed; +} + +public enum JobObjectInfoType +{ + AssociateCompletionPortInformation = 7, + BasicLimitInformation = 2, + BasicUIRestrictions = 4, + EndOfJobTimeInformation = 6, + ExtendedLimitInformation = 9, + SecurityLimitInformation = 5, + GroupInformation = 11 +} + +#endregion Helper classes diff --git a/v2rayN/v2rayN/Tool/Logging.cs b/v2rayN/v2rayN/Tool/Logging.cs index b390fc99..5baace53 100644 --- a/v2rayN/v2rayN/Tool/Logging.cs +++ b/v2rayN/v2rayN/Tool/Logging.cs @@ -3,53 +3,52 @@ using NLog.Config; using NLog.Targets; using System.IO; -namespace v2rayN.Tool +namespace v2rayN.Tool; + +public class Logging { - public class Logging + public static void Setup() { - public static void Setup() - { - LoggingConfiguration config = new(); - FileTarget fileTarget = new(); - config.AddTarget("file", fileTarget); - fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}"; - fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt"); - config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget)); - LogManager.Configuration = config; - } + LoggingConfiguration config = new(); + FileTarget fileTarget = new(); + config.AddTarget("file", fileTarget); + fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}"; + fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt"); + config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget)); + LogManager.Configuration = config; + } - public static void LoggingEnabled(bool enable) + public static void LoggingEnabled(bool enable) + { + if (!enable) { - if (!enable) - { - LogManager.SuspendLogging(); - } - } - - public static void ClearLogs() - { - Task.Run(() => - { - try - { - var now = DateTime.Now.AddMonths(-1); - var dir = Utils.GetLogPath(); - var files = Directory.GetFiles(dir, "*.txt"); - foreach (var filePath in files) - { - var file = new FileInfo(filePath); - if (file.CreationTime < now) - { - try - { - file.Delete(); - } - catch { } - } - } - } - catch { } - }); + LogManager.SuspendLogging(); } } + + public static void ClearLogs() + { + Task.Run(() => + { + try + { + var now = DateTime.Now.AddMonths(-1); + var dir = Utils.GetLogPath(); + var files = Directory.GetFiles(dir, "*.txt"); + foreach (var filePath in files) + { + var file = new FileInfo(filePath); + if (file.CreationTime < now) + { + try + { + file.Delete(); + } + catch { } + } + } + } + catch { } + }); + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Tool/QueryableExtension.cs b/v2rayN/v2rayN/Tool/QueryableExtension.cs index 218f7273..77d4fc67 100644 --- a/v2rayN/v2rayN/Tool/QueryableExtension.cs +++ b/v2rayN/v2rayN/Tool/QueryableExtension.cs @@ -1,50 +1,49 @@ using System.Linq.Expressions; using System.Reflection; -namespace v2rayN.Tool +namespace v2rayN.Tool; + +public static class QueryableExtension { - public static class QueryableExtension + public static IOrderedQueryable OrderBy(this IQueryable query, string propertyName) { - public static IOrderedQueryable OrderBy(this IQueryable query, string propertyName) - { - return _OrderBy(query, propertyName, false); - } + return _OrderBy(query, propertyName, false); + } - public static IOrderedQueryable OrderByDescending(this IQueryable query, string propertyName) - { - return _OrderBy(query, propertyName, true); - } + public static IOrderedQueryable OrderByDescending(this IQueryable query, string propertyName) + { + return _OrderBy(query, propertyName, true); + } - private static IOrderedQueryable _OrderBy(IQueryable query, string propertyName, bool isDesc) - { - string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal"; + private static IOrderedQueryable _OrderBy(IQueryable query, string propertyName, bool isDesc) + { + string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal"; - var memberProp = typeof(T).GetProperty(propertyName); + var memberProp = typeof(T).GetProperty(propertyName); - var method = typeof(QueryableExtension).GetMethod(methodname) - .MakeGenericMethod(typeof(T), memberProp.PropertyType); + var method = typeof(QueryableExtension).GetMethod(methodname) + .MakeGenericMethod(typeof(T), memberProp.PropertyType); - return (IOrderedQueryable)method.Invoke(null, new object[] { query, memberProp }); - } + return (IOrderedQueryable)method.Invoke(null, new object[] { query, memberProp }); + } - public static IOrderedQueryable OrderByInternal(IQueryable query, PropertyInfo memberProperty) - {//public - return query.OrderBy(_GetLamba(memberProperty)); - } + public static IOrderedQueryable OrderByInternal(IQueryable query, PropertyInfo memberProperty) + {//public + return query.OrderBy(_GetLamba(memberProperty)); + } - public static IOrderedQueryable OrderByDescendingInternal(IQueryable query, PropertyInfo memberProperty) - {//public - return query.OrderByDescending(_GetLamba(memberProperty)); - } + public static IOrderedQueryable OrderByDescendingInternal(IQueryable query, PropertyInfo memberProperty) + {//public + return query.OrderByDescending(_GetLamba(memberProperty)); + } - private static Expression> _GetLamba(PropertyInfo memberProperty) - { - if (memberProperty.PropertyType != typeof(TProp)) throw new Exception(); + private static Expression> _GetLamba(PropertyInfo memberProperty) + { + if (memberProperty.PropertyType != typeof(TProp)) throw new Exception(); - var thisArg = Expression.Parameter(typeof(T)); - var lamba = Expression.Lambda>(Expression.Property(thisArg, memberProperty), thisArg); + var thisArg = Expression.Parameter(typeof(T)); + var lamba = Expression.Lambda>(Expression.Property(thisArg, memberProperty), thisArg); - return lamba; - } + return lamba; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Tool/SemanticVersion.cs b/v2rayN/v2rayN/Tool/SemanticVersion.cs index 5b01242e..5de0cee8 100644 --- a/v2rayN/v2rayN/Tool/SemanticVersion.cs +++ b/v2rayN/v2rayN/Tool/SemanticVersion.cs @@ -1,182 +1,181 @@ using v2rayN.Base; -namespace v2rayN.Tool +namespace v2rayN.Tool; + +public class SemanticVersion { - public class SemanticVersion + private int major; + private int minor; + private int patch; + private string version; + + public SemanticVersion(int major, int minor, int patch) { - private int major; - private int minor; - private int patch; - private string version; + this.major = major; + this.minor = minor; + this.patch = patch; + this.version = $"{major}.{minor}.{patch}"; + } - public SemanticVersion(int major, int minor, int patch) + public SemanticVersion(string version) + { + this.version = version.RemovePrefix('v'); + try { - this.major = major; - this.minor = minor; - this.patch = patch; - this.version = $"{major}.{minor}.{patch}"; - } - - public SemanticVersion(string version) - { - this.version = version.RemovePrefix('v'); - try + string[] parts = this.version.Split('.'); + if (parts.Length == 2) { - string[] parts = this.version.Split('.'); - if (parts.Length == 2) - { - this.major = int.Parse(parts[0]); - this.minor = int.Parse(parts[1]); - this.patch = 0; - } - else if (parts.Length == 3) - { - this.major = int.Parse(parts[0]); - this.minor = int.Parse(parts[1]); - this.patch = int.Parse(parts[2]); - } - else - { - throw new ArgumentException("Invalid version string"); - } - } - catch - { - this.major = 0; - this.minor = 0; + this.major = int.Parse(parts[0]); + this.minor = int.Parse(parts[1]); this.patch = 0; - //this.version = "0.0.0"; } - } - - public override bool Equals(object? obj) - { - if (obj is SemanticVersion other) + else if (parts.Length == 3) { - return this.major == other.major && this.minor == other.minor && this.patch == other.patch; + this.major = int.Parse(parts[0]); + this.minor = int.Parse(parts[1]); + this.patch = int.Parse(parts[2]); } else { - return false; + throw new ArgumentException("Invalid version string"); } } - - public override int GetHashCode() + catch { - return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode(); + this.major = 0; + this.minor = 0; + this.patch = 0; + //this.version = "0.0.0"; } + } - /// - /// Use ToVersionString(string? prefix) instead if possible. - /// - /// major.minor.patch - public override string ToString() + public override bool Equals(object? obj) + { + if (obj is SemanticVersion other) + { + return this.major == other.major && this.minor == other.minor && this.patch == other.patch; + } + else + { + return false; + } + } + + public override int GetHashCode() + { + return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode(); + } + + /// + /// Use ToVersionString(string? prefix) instead if possible. + /// + /// major.minor.patch + public override string ToString() + { + return this.version; + } + + public string ToVersionString(string? prefix = null) + { + if (prefix == null) { return this.version; } - - public string ToVersionString(string? prefix = null) + else { - if (prefix == null) - { - return this.version; - } - else - { - return $"{prefix}{this.version}"; - } + return $"{prefix}{this.version}"; } - - public static bool operator ==(SemanticVersion v1, SemanticVersion v2) - { return v1.Equals(v2); } - - public static bool operator !=(SemanticVersion v1, SemanticVersion v2) - { return !v1.Equals(v2); } - - public static bool operator >=(SemanticVersion v1, SemanticVersion v2) - { return v1.GreaterEquals(v2); } - - public static bool operator <=(SemanticVersion v1, SemanticVersion v2) - { return v1.LessEquals(v2); } - - #region Private - - private bool GreaterEquals(SemanticVersion other) - { - if (this.major < other.major) - { - return false; - } - else if (this.major > other.major) - { - return true; - } - else - { - if (this.minor < other.minor) - { - return false; - } - else if (this.minor > other.minor) - { - return true; - } - else - { - if (this.patch < other.patch) - { - return false; - } - else if (this.patch > other.patch) - { - return true; - } - else - { - return true; - } - } - } - } - - private bool LessEquals(SemanticVersion other) - { - if (this.major < other.major) - { - return true; - } - else if (this.major > other.major) - { - return false; - } - else - { - if (this.minor < other.minor) - { - return true; - } - else if (this.minor > other.minor) - { - return false; - } - else - { - if (this.patch < other.patch) - { - return true; - } - else if (this.patch > other.patch) - { - return false; - } - else - { - return true; - } - } - } - } - - #endregion Private } + + public static bool operator ==(SemanticVersion v1, SemanticVersion v2) + { return v1.Equals(v2); } + + public static bool operator !=(SemanticVersion v1, SemanticVersion v2) + { return !v1.Equals(v2); } + + public static bool operator >=(SemanticVersion v1, SemanticVersion v2) + { return v1.GreaterEquals(v2); } + + public static bool operator <=(SemanticVersion v1, SemanticVersion v2) + { return v1.LessEquals(v2); } + + #region Private + + private bool GreaterEquals(SemanticVersion other) + { + if (this.major < other.major) + { + return false; + } + else if (this.major > other.major) + { + return true; + } + else + { + if (this.minor < other.minor) + { + return false; + } + else if (this.minor > other.minor) + { + return true; + } + else + { + if (this.patch < other.patch) + { + return false; + } + else if (this.patch > other.patch) + { + return true; + } + else + { + return true; + } + } + } + } + + private bool LessEquals(SemanticVersion other) + { + if (this.major < other.major) + { + return true; + } + else if (this.major > other.major) + { + return false; + } + else + { + if (this.minor < other.minor) + { + return true; + } + else if (this.minor > other.minor) + { + return false; + } + else + { + if (this.patch < other.patch) + { + return true; + } + else if (this.patch > other.patch) + { + return false; + } + else + { + return true; + } + } + } + } + + #endregion Private } \ No newline at end of file diff --git a/v2rayN/v2rayN/Tool/UI.cs b/v2rayN/v2rayN/Tool/UI.cs index dccbeee1..24510f92 100644 --- a/v2rayN/v2rayN/Tool/UI.cs +++ b/v2rayN/v2rayN/Tool/UI.cs @@ -1,24 +1,23 @@ using System.Windows; -namespace v2rayN +namespace v2rayN; + +internal class UI { - internal class UI + private static readonly string caption = "v2rayN"; + + public static void Show(string msg) { - private static readonly string caption = "v2rayN"; + MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK); + } - public static void Show(string msg) - { - MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK); - } + public static void ShowWarning(string msg) + { + MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK); + } - public static void ShowWarning(string msg) - { - MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK); - } - - public static MessageBoxResult ShowYesNo(string msg) - { - return MessageBox.Show(msg, caption, MessageBoxButton.YesNo, MessageBoxImage.Question); - } + public static MessageBoxResult ShowYesNo(string msg) + { + return MessageBox.Show(msg, caption, MessageBoxButton.YesNo, MessageBoxImage.Question); } } \ No newline at end of file diff --git a/v2rayN/v2rayN/Tool/Utils.cs b/v2rayN/v2rayN/Tool/Utils.cs index d65bebae..0173aa2f 100644 --- a/v2rayN/v2rayN/Tool/Utils.cs +++ b/v2rayN/v2rayN/Tool/Utils.cs @@ -22,6 +22,7 @@ using System.Windows; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; +using System.Xml.Serialization; using v2rayN.Base; using v2rayN.Mode; using ZXing; @@ -29,1249 +30,1248 @@ using ZXing.Common; using ZXing.QrCode; using ZXing.Windows.Compatibility; -namespace v2rayN +namespace v2rayN; + +internal class Utils { - internal class Utils + #region 资源Json操作 + + /// + /// 获取嵌入文本资源 + /// + /// + /// + public static string GetEmbedText(string res) { - #region 资源Json操作 + string result = string.Empty; - /// - /// 获取嵌入文本资源 - /// - /// - /// - public static string GetEmbedText(string res) + try { - string result = string.Empty; - - try - { - Assembly assembly = Assembly.GetExecutingAssembly(); - using Stream? stream = assembly.GetManifestResourceStream(res); - ArgumentNullException.ThrowIfNull(stream); - using StreamReader reader = new(stream); - result = reader.ReadToEnd(); - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - return result; + Assembly assembly = Assembly.GetExecutingAssembly(); + using Stream? stream = assembly.GetManifestResourceStream(res); + ArgumentNullException.ThrowIfNull(stream); + using StreamReader reader = new(stream); + result = reader.ReadToEnd(); } - - /// - /// 取得存储资源 - /// - /// - public static string? LoadResource(string res) + catch (Exception ex) { - try - { - if (!File.Exists(res)) return null; - return File.ReadAllText(res); - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - return null; + SaveLog(ex.Message, ex); } + return result; + } - /// - /// 反序列化成对象 - /// - /// - /// - /// - public static T? FromJson(string? strJson) + /// + /// 取得存储资源 + /// + /// + public static string? LoadResource(string res) + { + try { - try - { - if (string.IsNullOrEmpty(strJson)) - { - return default; - } - return JsonConvert.DeserializeObject(strJson); - } - catch + if (!File.Exists(res)) return null; + return File.ReadAllText(res); + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + } + return null; + } + + /// + /// 反序列化成对象 + /// + /// + /// + /// + public static T? FromJson(string? strJson) + { + try + { + if (string.IsNullOrEmpty(strJson)) { return default; } + return JsonConvert.DeserializeObject(strJson); } - - /// - /// 序列化成Json - /// - /// - /// - public static string ToJson(object? obj, bool indented = true) + catch { - string result = string.Empty; - try - { - if (obj == null) - { - return result; - } - if (indented) - { - result = JsonConvert.SerializeObject(obj, - Formatting.Indented, - new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); - } - else - { - result = JsonConvert.SerializeObject(obj, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); - } - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - return result; + return default; } + } - /// - /// 保存成json文件 - /// - /// - /// - /// - public static int ToJsonFile(object? obj, string filePath, bool nullValue = true) + /// + /// 序列化成Json + /// + /// + /// + public static string ToJson(object? obj, bool indented = true) + { + string result = string.Empty; + try { - int result; - try + if (obj == null) { - using StreamWriter file = File.CreateText(filePath); - JsonSerializer serializer; - if (nullValue) - { - serializer = new JsonSerializer() { Formatting = Formatting.Indented }; - } - else - { - serializer = new JsonSerializer() { Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore }; - } - - serializer.Serialize(file, obj); - result = 0; + return result; } - catch (Exception ex) + if (indented) { - SaveLog(ex.Message, ex); - result = -1; + result = JsonConvert.SerializeObject(obj, + Formatting.Indented, + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); } - return result; - } - - public static JObject? ParseJson(string strJson) - { - try + else { - return JObject.Parse(strJson); - } - catch (Exception ex) - { - //SaveLog(ex.Message, ex); - return null; + result = JsonConvert.SerializeObject(obj, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); } } - - #endregion 资源Json操作 - - #region 转换函数 - - /// - /// List转逗号分隔的字符串 - /// - /// - /// - public static string List2String(List lst, bool wrap = false) + catch (Exception ex) { - try + SaveLog(ex.Message, ex); + } + return result; + } + + /// + /// 保存成json文件 + /// + /// + /// + /// + public static int ToJsonFile(object? obj, string filePath, bool nullValue = true) + { + int result; + try + { + using StreamWriter file = File.CreateText(filePath); + JsonSerializer serializer; + if (nullValue) { - if (lst == null) - { - return string.Empty; - } - if (wrap) - { - return string.Join("," + Environment.NewLine, lst); - } - else - { - return string.Join(",", lst); - } + serializer = new JsonSerializer() { Formatting = Formatting.Indented }; } - catch (Exception ex) + else + { + serializer = new JsonSerializer() { Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore }; + } + + serializer.Serialize(file, obj); + result = 0; + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + result = -1; + } + return result; + } + + public static JObject? ParseJson(string strJson) + { + try + { + return JObject.Parse(strJson); + } + catch (Exception ex) + { + //SaveLog(ex.Message, ex); + return null; + } + } + + #endregion 资源Json操作 + + #region 转换函数 + + /// + /// List转逗号分隔的字符串 + /// + /// + /// + public static string List2String(List lst, bool wrap = false) + { + try + { + if (lst == null) { - SaveLog(ex.Message, ex); return string.Empty; } - } - - /// - /// 逗号分隔的字符串,转List - /// - /// - /// - public static List String2List(string str) - { - try + if (wrap) { - str = str.Replace(Environment.NewLine, ""); - return new List(str.Split(',', StringSplitOptions.RemoveEmptyEntries)); + return string.Join("," + Environment.NewLine, lst); } - catch (Exception ex) + else { - SaveLog(ex.Message, ex); - return new List(); + return string.Join(",", lst); } } - - /// - /// 逗号分隔的字符串,先排序后转List - /// - /// - /// - public static List String2ListSorted(string str) + catch (Exception ex) { - try - { - str = str.Replace(Environment.NewLine, ""); - List list = new(str.Split(',', StringSplitOptions.RemoveEmptyEntries)); - list.Sort(); - return list; - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - return new List(); - } + SaveLog(ex.Message, ex); + return string.Empty; } + } - /// - /// Base64编码 - /// - /// - /// - public static string Base64Encode(string plainText) + /// + /// 逗号分隔的字符串,转List + /// + /// + /// + public static List String2List(string str) + { + try { - try - { - byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); - return Convert.ToBase64String(plainTextBytes); - } - catch (Exception ex) - { - SaveLog("Base64Encode", ex); - return string.Empty; - } + str = str.Replace(Environment.NewLine, ""); + return new List(str.Split(',', StringSplitOptions.RemoveEmptyEntries)); } - - /// - /// Base64解码 - /// - /// - /// - public static string Base64Decode(string plainText) + catch (Exception ex) { - try - { - plainText = plainText.TrimEx() - .Replace(Environment.NewLine, "") - .Replace("\n", "") - .Replace("\r", "") - .Replace('_', '/') - .Replace('-', '+') - .Replace(" ", ""); + SaveLog(ex.Message, ex); + return new List(); + } + } - if (plainText.Length % 4 > 0) + /// + /// 逗号分隔的字符串,先排序后转List + /// + /// + /// + public static List String2ListSorted(string str) + { + try + { + str = str.Replace(Environment.NewLine, ""); + List list = new(str.Split(',', StringSplitOptions.RemoveEmptyEntries)); + list.Sort(); + return list; + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + return new List(); + } + } + + /// + /// Base64编码 + /// + /// + /// + public static string Base64Encode(string plainText) + { + try + { + byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); + return Convert.ToBase64String(plainTextBytes); + } + catch (Exception ex) + { + SaveLog("Base64Encode", ex); + return string.Empty; + } + } + + /// + /// Base64解码 + /// + /// + /// + public static string Base64Decode(string plainText) + { + try + { + plainText = plainText.TrimEx() + .Replace(Environment.NewLine, "") + .Replace("\n", "") + .Replace("\r", "") + .Replace('_', '/') + .Replace('-', '+') + .Replace(" ", ""); + + if (plainText.Length % 4 > 0) + { + plainText = plainText.PadRight(plainText.Length + 4 - plainText.Length % 4, '='); + } + + byte[] data = Convert.FromBase64String(plainText); + return Encoding.UTF8.GetString(data); + } + catch (Exception ex) + { + SaveLog("Base64Decode", ex); + return string.Empty; + } + } + + /// + /// 转Int + /// + /// + /// + public static int ToInt(object obj) + { + try + { + return Convert.ToInt32(obj); + } + catch (Exception ex) + { + //SaveLog(ex.Message, ex); + return 0; + } + } + + public static bool ToBool(object obj) + { + try + { + return Convert.ToBoolean(obj); + } + catch (Exception ex) + { + //SaveLog(ex.Message, ex); + return false; + } + } + + public static string ToString(object obj) + { + try + { + return obj?.ToString() ?? string.Empty; + } + catch (Exception ex) + { + //SaveLog(ex.Message, ex); + return string.Empty; + } + } + + /// + /// byte 转成 有两位小数点的 方便阅读的数据 + /// 比如 2.50 MB + /// + /// bytes + /// 转换之后的数据 + /// 单位 + public static void ToHumanReadable(long amount, out double result, out string unit) + { + uint factor = 1024u; + //long KBs = amount / factor; + long KBs = amount; + if (KBs > 0) + { + // multi KB + long MBs = KBs / factor; + if (MBs > 0) + { + // multi MB + long GBs = MBs / factor; + if (GBs > 0) { - plainText = plainText.PadRight(plainText.Length + 4 - plainText.Length % 4, '='); - } - - byte[] data = Convert.FromBase64String(plainText); - return Encoding.UTF8.GetString(data); - } - catch (Exception ex) - { - SaveLog("Base64Decode", ex); - return string.Empty; - } - } - - /// - /// 转Int - /// - /// - /// - public static int ToInt(object obj) - { - try - { - return Convert.ToInt32(obj); - } - catch (Exception ex) - { - //SaveLog(ex.Message, ex); - return 0; - } - } - - public static bool ToBool(object obj) - { - try - { - return Convert.ToBoolean(obj); - } - catch (Exception ex) - { - //SaveLog(ex.Message, ex); - return false; - } - } - - public static string ToString(object obj) - { - try - { - return obj?.ToString() ?? string.Empty; - } - catch (Exception ex) - { - //SaveLog(ex.Message, ex); - return string.Empty; - } - } - - /// - /// byte 转成 有两位小数点的 方便阅读的数据 - /// 比如 2.50 MB - /// - /// bytes - /// 转换之后的数据 - /// 单位 - public static void ToHumanReadable(long amount, out double result, out string unit) - { - uint factor = 1024u; - //long KBs = amount / factor; - long KBs = amount; - if (KBs > 0) - { - // multi KB - long MBs = KBs / factor; - if (MBs > 0) - { - // multi MB - long GBs = MBs / factor; - if (GBs > 0) + // multi GB + long TBs = GBs / factor; + if (TBs > 0) { - // multi GB - long TBs = GBs / factor; - if (TBs > 0) - { - result = TBs + ((GBs % factor) / (factor + 0.0)); - unit = "TB"; - return; - } - result = GBs + ((MBs % factor) / (factor + 0.0)); - unit = "GB"; + result = TBs + ((GBs % factor) / (factor + 0.0)); + unit = "TB"; return; } - result = MBs + ((KBs % factor) / (factor + 0.0)); - unit = "MB"; + result = GBs + ((MBs % factor) / (factor + 0.0)); + unit = "GB"; return; } - result = KBs + ((amount % factor) / (factor + 0.0)); - unit = "KB"; + result = MBs + ((KBs % factor) / (factor + 0.0)); + unit = "MB"; return; } + result = KBs + ((amount % factor) / (factor + 0.0)); + unit = "KB"; + return; + } + else + { + result = amount; + unit = "B"; + } + } + + public static string HumanFy(long amount) + { + ToHumanReadable(amount, out double result, out string unit); + return $"{string.Format("{0:f1}", result)} {unit}"; + } + + public static string UrlEncode(string url) + { + return Uri.EscapeDataString(url); + //return HttpUtility.UrlEncode(url); + } + + public static string UrlDecode(string url) + { + return HttpUtility.UrlDecode(url); + } + + public static string GetMD5(string str) + { + byte[] byteOld = Encoding.UTF8.GetBytes(str); + byte[] byteNew = MD5.HashData(byteOld); + StringBuilder sb = new(32); + foreach (byte b in byteNew) + { + sb.Append(b.ToString("x2")); + } + return sb.ToString(); + } + + public static ImageSource IconToImageSource(Icon icon) + { + return Imaging.CreateBitmapSourceFromHIcon( + icon.Handle, + new System.Windows.Int32Rect(0, 0, icon.Width, icon.Height), + BitmapSizeOptions.FromEmptyOptions()); + } + + /// + /// idn to idc + /// + /// + /// + public static string GetPunycode(string url) + { + if (string.IsNullOrWhiteSpace(url)) + { + return url; + } + try + { + Uri uri = new(url); + if (uri.Host == uri.IdnHost) + { + return url; + } else { - result = amount; - unit = "B"; + return url.Replace(uri.Host, uri.IdnHost); } } - - public static string HumanFy(long amount) + catch { - ToHumanReadable(amount, out double result, out string unit); - return $"{string.Format("{0:f1}", result)} {unit}"; + return url; } + } - public static string UrlEncode(string url) + public static bool IsBase64String(string plainText) + { + var buffer = new Span(new byte[plainText.Length]); + return Convert.TryFromBase64String(plainText, buffer, out int _); + } + + public static string Convert2Comma(string text) + { + if (string.IsNullOrWhiteSpace(text)) { - return Uri.EscapeDataString(url); - //return HttpUtility.UrlEncode(url); + return text; } + return text.Replace(",", ",").Replace(Environment.NewLine, ","); + } - public static string UrlDecode(string url) + #endregion 转换函数 + + #region 数据检查 + + /// + /// 判断输入的是否是数字 + /// + /// + /// + public static bool IsNumberic(string oText) + { + try { - return HttpUtility.UrlDecode(url); + int var1 = ToInt(oText); + return true; } - - public static string GetMD5(string str) + catch (Exception ex) { - byte[] byteOld = Encoding.UTF8.GetBytes(str); - byte[] byteNew = MD5.HashData(byteOld); - StringBuilder sb = new(32); - foreach (byte b in byteNew) - { - sb.Append(b.ToString("x2")); - } - return sb.ToString(); + SaveLog(ex.Message, ex); + return false; } + } - public static ImageSource IconToImageSource(Icon icon) + /// + /// 文本 + /// + /// + /// + public static bool IsNullOrEmpty(string? text) + { + if (string.IsNullOrEmpty(text)) { - return Imaging.CreateBitmapSourceFromHIcon( - icon.Handle, - new System.Windows.Int32Rect(0, 0, icon.Width, icon.Height), - BitmapSizeOptions.FromEmptyOptions()); + return true; } - - /// - /// idn to idc - /// - /// - /// - public static string GetPunycode(string url) + if (text == "null") { - if (string.IsNullOrWhiteSpace(url)) - { - return url; - } - try - { - Uri uri = new(url); - if (uri.Host == uri.IdnHost) - { - return url; - } - else - { - return url.Replace(uri.Host, uri.IdnHost); - } - } - catch - { - return url; - } + return true; } + return false; + } - public static bool IsBase64String(string plainText) + /// + /// 验证IP地址是否合法 + /// + /// + public static bool IsIP(string ip) + { + //如果为空 + if (IsNullOrEmpty(ip)) { - var buffer = new Span(new byte[plainText.Length]); - return Convert.TryFromBase64String(plainText, buffer, out int _); - } - - public static string Convert2Comma(string text) - { - if (string.IsNullOrWhiteSpace(text)) - { - return text; - } - return text.Replace(",", ",").Replace(Environment.NewLine, ","); - } - - #endregion 转换函数 - - #region 数据检查 - - /// - /// 判断输入的是否是数字 - /// - /// - /// - public static bool IsNumberic(string oText) - { - try - { - int var1 = ToInt(oText); - return true; - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - return false; - } - } - - /// - /// 文本 - /// - /// - /// - public static bool IsNullOrEmpty(string? text) - { - if (string.IsNullOrEmpty(text)) - { - return true; - } - if (text == "null") - { - return true; - } return false; } - /// - /// 验证IP地址是否合法 - /// - /// - public static bool IsIP(string ip) + //清除要验证字符串中的空格 + //ip = ip.TrimEx(); + //可能是CIDR + if (ip.IndexOf(@"/") > 0) { - //如果为空 - if (IsNullOrEmpty(ip)) + string[] cidr = ip.Split('/'); + if (cidr.Length == 2) { - return false; - } - - //清除要验证字符串中的空格 - //ip = ip.TrimEx(); - //可能是CIDR - if (ip.IndexOf(@"/") > 0) - { - string[] cidr = ip.Split('/'); - if (cidr.Length == 2) + if (!IsNumberic(cidr[0])) { - if (!IsNumberic(cidr[0])) - { - return false; - } - ip = cidr[0]; + return false; } + ip = cidr[0]; } - - //模式字符串 - string pattern = @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"; - - //验证 - return IsMatch(ip, pattern); } - /// - /// 验证Domain地址是否合法 - /// - /// - public static bool IsDomain(string domain) - { - //如果为空 - if (IsNullOrEmpty(domain)) - { - return false; - } + //模式字符串 + string pattern = @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"; - return Uri.CheckHostName(domain) == UriHostNameType.Dns; - } + //验证 + return IsMatch(ip, pattern); + } - /// - /// 验证输入字符串是否与模式字符串匹配,匹配返回true - /// - /// 输入字符串 - /// 模式字符串 - public static bool IsMatch(string input, string pattern) + /// + /// 验证Domain地址是否合法 + /// + /// + public static bool IsDomain(string domain) + { + //如果为空 + if (IsNullOrEmpty(domain)) { - return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase); - } - - public static bool IsIpv6(string ip) - { - if (IPAddress.TryParse(ip, out IPAddress? address)) - { - return address.AddressFamily switch - { - AddressFamily.InterNetwork => false, - AddressFamily.InterNetworkV6 => true, - _ => false, - }; - } return false; } - #endregion 数据检查 + return Uri.CheckHostName(domain) == UriHostNameType.Dns; + } - #region 开机自动启动 + /// + /// 验证输入字符串是否与模式字符串匹配,匹配返回true + /// + /// 输入字符串 + /// 模式字符串 + public static bool IsMatch(string input, string pattern) + { + return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase); + } - /// - /// 开机自动启动 - /// - /// - /// - public static void SetAutoRun(bool run) + public static bool IsIpv6(string ip) + { + if (IPAddress.TryParse(ip, out IPAddress? address)) { - try + return address.AddressFamily switch { - var autoRunName = $"{Global.AutoRunName}_{GetMD5(StartupPath())}"; + AddressFamily.InterNetwork => false, + AddressFamily.InterNetworkV6 => true, + _ => false, + }; + } + return false; + } - //delete first - RegWriteValue(Global.AutoRunRegPath, autoRunName, ""); + #endregion 数据检查 + + #region 开机自动启动 + + /// + /// 开机自动启动 + /// + /// + /// + public static void SetAutoRun(bool run) + { + try + { + var autoRunName = $"{Global.AutoRunName}_{GetMD5(StartupPath())}"; + + //delete first + RegWriteValue(Global.AutoRunRegPath, autoRunName, ""); + if (IsAdministrator()) + { + AutoStart(autoRunName, "", ""); + } + + if (run) + { + string exePath = $"\"{GetExePath()}\""; if (IsAdministrator()) { - AutoStart(autoRunName, "", ""); - } - - if (run) - { - string exePath = $"\"{GetExePath()}\""; - if (IsAdministrator()) - { - AutoStart(autoRunName, exePath, ""); - } - else - { - RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath); - } - } - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - } - - /// - /// 是否已经设置开机自动启动 - /// - /// - public static bool IsAutoRun() - { - try - { - //clear - if (!RegReadValue(Global.AutoRunRegPath, Global.AutoRunName, "").IsNullOrEmpty()) - { - RegWriteValue(Global.AutoRunRegPath, Global.AutoRunName, ""); - } - - string value = RegReadValue(Global.AutoRunRegPath, Global.AutoRunName, ""); - string exePath = GetExePath(); - if (value == exePath || value == $"\"{exePath}\"") - { - return true; - } - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - return false; - } - - /// - /// 获取启动了应用程序的可执行文件的路径 - /// - /// - public static string GetPath(string fileName) - { - string startupPath = StartupPath(); - if (IsNullOrEmpty(fileName)) - { - return startupPath; - } - return Path.Combine(startupPath, fileName); - } - - /// - /// 获取启动了应用程序的可执行文件的路径及文件名 - /// - /// - public static string GetExePath() - { - return Environment.ProcessPath; - } - - public static string StartupPath() - { - return AppDomain.CurrentDomain.BaseDirectory; - } - - public static string? RegReadValue(string path, string name, string def) - { - RegistryKey? regKey = null; - try - { - regKey = Registry.CurrentUser.OpenSubKey(path, false); - string? value = regKey?.GetValue(name) as string; - if (IsNullOrEmpty(value)) - { - return def; + AutoStart(autoRunName, exePath, ""); } else { - return value; + RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath); } } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - finally - { - regKey?.Close(); - } - return def; } - - public static void RegWriteValue(string path, string name, object value) + catch (Exception ex) { - RegistryKey? regKey = null; - try + SaveLog(ex.Message, ex); + } + } + + /// + /// 是否已经设置开机自动启动 + /// + /// + public static bool IsAutoRun() + { + try + { + //clear + if (!RegReadValue(Global.AutoRunRegPath, Global.AutoRunName, "").IsNullOrEmpty()) { - regKey = Registry.CurrentUser.CreateSubKey(path); - if (IsNullOrEmpty(value.ToString())) - { - regKey?.DeleteValue(name, false); - } - else - { - regKey?.SetValue(name, value); - } + RegWriteValue(Global.AutoRunRegPath, Global.AutoRunName, ""); } - catch (Exception ex) + + string value = RegReadValue(Global.AutoRunRegPath, Global.AutoRunName, ""); + string exePath = GetExePath(); + if (value == exePath || value == $"\"{exePath}\"") { - SaveLog(ex.Message, ex); - } - finally - { - regKey?.Close(); + return true; } } - - /// - /// 判断.Net Framework的Release是否符合 - /// (.Net Framework 版本在4.0及以上) - /// - /// 需要的版本4.6.2=394802;4.8=528040 - /// - public static bool CheckForDotNetVersion(int release = 528040) + catch (Exception ex) { - const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\"; - using RegistryKey? ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey); - if (ndpKey?.GetValue("Release") != null) - { - return (int)ndpKey.GetValue("Release") >= release; - } - return false; + SaveLog(ex.Message, ex); } + return false; + } - /// - /// Auto Start via TaskService - /// - /// - /// - /// - /// - public static void AutoStart(string taskName, string fileName, string description) + /// + /// 获取启动了应用程序的可执行文件的路径 + /// + /// + public static string GetPath(string fileName) + { + string startupPath = StartupPath(); + if (IsNullOrEmpty(fileName)) { - if (string.IsNullOrEmpty(taskName)) - { - return; - } - string TaskName = taskName; - var logonUser = WindowsIdentity.GetCurrent().Name; - string taskDescription = description; - string deamonFileName = fileName; - - using var taskService = new TaskService(); - var tasks = taskService.RootFolder.GetTasks(new Regex(TaskName)); - foreach (var t in tasks) - { - taskService.RootFolder.DeleteTask(t.Name); - } - if (string.IsNullOrEmpty(fileName)) - { - return; - } - - var task = taskService.NewTask(); - task.RegistrationInfo.Description = taskDescription; - task.Settings.DisallowStartIfOnBatteries = false; - task.Settings.StopIfGoingOnBatteries = false; - task.Settings.RunOnlyIfIdle = false; - task.Settings.IdleSettings.StopOnIdleEnd = false; - task.Settings.ExecutionTimeLimit = TimeSpan.Zero; - task.Triggers.Add(new LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) }); - task.Principal.RunLevel = TaskRunLevel.Highest; - task.Actions.Add(new ExecAction(deamonFileName)); - - taskService.RootFolder.RegisterTaskDefinition(TaskName, task); + return startupPath; } + return Path.Combine(startupPath, fileName); + } - #endregion 开机自动启动 + /// + /// 获取启动了应用程序的可执行文件的路径及文件名 + /// + /// + public static string GetExePath() + { + return Environment.ProcessPath; + } - #region 测速 + public static string StartupPath() + { + return AppDomain.CurrentDomain.BaseDirectory; + } - /// - /// 取得本机 IP Address - /// - /// - //public static List GetHostIPAddress() - //{ - // List lstIPAddress = new List(); - // try - // { - // IPHostEntry IpEntry = Dns.GetHostEntry(Dns.GetHostName()); - // foreach (IPAddress ipa in IpEntry.AddressList) - // { - // if (ipa.AddressFamily == AddressFamily.InterNetwork) - // lstIPAddress.Add(ipa.ToString()); - // } - // } - // catch (Exception ex) - // { - // SaveLog(ex.Message, ex); - // } - // return lstIPAddress; - //} - - public static void SetSecurityProtocol(bool enableSecurityProtocolTls13) + public static string? RegReadValue(string path, string name, string def) + { + RegistryKey? regKey = null; + try { - if (enableSecurityProtocolTls13) + regKey = Registry.CurrentUser.OpenSubKey(path, false); + string? value = regKey?.GetValue(name) as string; + if (IsNullOrEmpty(value)) { - ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13; + return def; } else { - ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; - } - ServicePointManager.DefaultConnectionLimit = 256; - } - - public static bool PortInUse(int port) - { - bool inUse = false; - try - { - IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); - IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners(); - - var lstIpEndPoints = new List(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); - - foreach (IPEndPoint endPoint in ipEndPoints) - { - if (endPoint.Port == port) - { - inUse = true; - break; - } - } - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - return inUse; - } - - #endregion 测速 - - #region 杂项 - - /// - /// 取得版本 - /// - /// - public static string GetVersion(bool blFull = true) - { - try - { - string location = GetExePath(); - if (blFull) - { - return string.Format("v2rayN - V{0} - {1}", - FileVersionInfo.GetVersionInfo(location).FileVersion.ToString(), - File.GetLastWriteTime(location).ToString("yyyy/MM/dd")); - } - else - { - return string.Format("v2rayN/{0}", - FileVersionInfo.GetVersionInfo(location).FileVersion.ToString()); - } - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - return string.Empty; + return value; } } - - /// - /// 深度拷贝 - /// - /// - /// - /// - public static T DeepCopy(T obj) + catch (Exception ex) { - object retval; - MemoryStream ms = new MemoryStream(); - BinaryFormatter bf = new BinaryFormatter(); - //序列化成流 - bf.Serialize(ms, obj); - ms.Seek(0, SeekOrigin.Begin); - //反序列化成对象 - retval = bf.Deserialize(ms); - return (T)retval; + SaveLog(ex.Message, ex); + } + finally + { + regKey?.Close(); + } + return def; + } + + public static void RegWriteValue(string path, string name, object value) + { + RegistryKey? regKey = null; + try + { + regKey = Registry.CurrentUser.CreateSubKey(path); + if (IsNullOrEmpty(value.ToString())) + { + regKey?.DeleteValue(name, false); + } + else + { + regKey?.SetValue(name, value); + } + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + } + finally + { + regKey?.Close(); + } + } + + /// + /// 判断.Net Framework的Release是否符合 + /// (.Net Framework 版本在4.0及以上) + /// + /// 需要的版本4.6.2=394802;4.8=528040 + /// + public static bool CheckForDotNetVersion(int release = 528040) + { + const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\"; + using RegistryKey? ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey); + if (ndpKey?.GetValue("Release") != null) + { + return (int)ndpKey.GetValue("Release") >= release; + } + return false; + } + + /// + /// Auto Start via TaskService + /// + /// + /// + /// + /// + public static void AutoStart(string taskName, string fileName, string description) + { + if (string.IsNullOrEmpty(taskName)) + { + return; + } + string TaskName = taskName; + var logonUser = WindowsIdentity.GetCurrent().Name; + string taskDescription = description; + string deamonFileName = fileName; + + using var taskService = new TaskService(); + var tasks = taskService.RootFolder.GetTasks(new Regex(TaskName)); + foreach (var t in tasks) + { + taskService.RootFolder.DeleteTask(t.Name); + } + if (string.IsNullOrEmpty(fileName)) + { + return; } - /// - /// 获取剪贴板数 - /// - /// - public static string? GetClipboardData() + var task = taskService.NewTask(); + task.RegistrationInfo.Description = taskDescription; + task.Settings.DisallowStartIfOnBatteries = false; + task.Settings.StopIfGoingOnBatteries = false; + task.Settings.RunOnlyIfIdle = false; + task.Settings.IdleSettings.StopOnIdleEnd = false; + task.Settings.ExecutionTimeLimit = TimeSpan.Zero; + task.Triggers.Add(new LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) }); + task.Principal.RunLevel = TaskRunLevel.Highest; + task.Actions.Add(new ExecAction(deamonFileName)); + + taskService.RootFolder.RegisterTaskDefinition(TaskName, task); + } + + #endregion 开机自动启动 + + #region 测速 + + /// + /// 取得本机 IP Address + /// + /// + //public static List GetHostIPAddress() + //{ + // List lstIPAddress = new List(); + // try + // { + // IPHostEntry IpEntry = Dns.GetHostEntry(Dns.GetHostName()); + // foreach (IPAddress ipa in IpEntry.AddressList) + // { + // if (ipa.AddressFamily == AddressFamily.InterNetwork) + // lstIPAddress.Add(ipa.ToString()); + // } + // } + // catch (Exception ex) + // { + // SaveLog(ex.Message, ex); + // } + // return lstIPAddress; + //} + + public static void SetSecurityProtocol(bool enableSecurityProtocolTls13) + { + if (enableSecurityProtocolTls13) { - string? strData = string.Empty; - try + ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13; + } + else + { + ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; + } + ServicePointManager.DefaultConnectionLimit = 256; + } + + public static bool PortInUse(int port) + { + bool inUse = false; + try + { + IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); + IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners(); + + var lstIpEndPoints = new List(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()); + + foreach (IPEndPoint endPoint in ipEndPoints) { - IDataObject data = Clipboard.GetDataObject(); - if (data.GetDataPresent(DataFormats.UnicodeText)) + if (endPoint.Port == port) { - strData = data.GetData(DataFormats.UnicodeText)?.ToString(); + inUse = true; + break; } - return strData; } - catch (Exception ex) + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + } + return inUse; + } + + #endregion 测速 + + #region 杂项 + + /// + /// 取得版本 + /// + /// + public static string GetVersion(bool blFull = true) + { + try + { + string location = GetExePath(); + if (blFull) { - SaveLog(ex.Message, ex); + return string.Format("v2rayN - V{0} - {1}", + FileVersionInfo.GetVersionInfo(location).FileVersion.ToString(), + File.GetLastWriteTime(location).ToString("yyyy/MM/dd")); + } + else + { + return string.Format("v2rayN/{0}", + FileVersionInfo.GetVersionInfo(location).FileVersion.ToString()); + } + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + return string.Empty; + } + } + + /// + /// 深度拷贝 + /// + /// + /// + /// + public static T DeepCopy(T obj) + { + object retval; + MemoryStream ms = new MemoryStream(); + XmlSerializer bf = new XmlSerializer(obj.GetType()); + //序列化成流 + bf.Serialize(ms, obj); + ms.Seek(0, SeekOrigin.Begin); + //反序列化成对象 + retval = bf.Deserialize(ms); + return (T)retval; + } + + /// + /// 获取剪贴板数 + /// + /// + public static string? GetClipboardData() + { + string? strData = string.Empty; + try + { + IDataObject data = Clipboard.GetDataObject(); + if (data.GetDataPresent(DataFormats.UnicodeText)) + { + strData = data.GetData(DataFormats.UnicodeText)?.ToString(); } return strData; } - - /// - /// 拷贝至剪贴板 - /// - /// - public static void SetClipboardData(string strData) + catch (Exception ex) { - try - { - Clipboard.SetText(strData); - } - catch - { - } + SaveLog(ex.Message, ex); } + return strData; + } - /// - /// 取得GUID - /// - /// - public static string GetGUID(bool full = true) + /// + /// 拷贝至剪贴板 + /// + /// + public static void SetClipboardData(string strData) + { + try { - try - { - if (full) - { - return Guid.NewGuid().ToString("D"); - } - else - { - return BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0).ToString(); - } - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - return string.Empty; + Clipboard.SetText(strData); } - - /// - /// IsAdministrator - /// - /// - public static bool IsAdministrator() + catch { - try + } + } + + /// + /// 取得GUID + /// + /// + public static string GetGUID(bool full = true) + { + try + { + if (full) { - WindowsIdentity current = WindowsIdentity.GetCurrent(); - WindowsPrincipal windowsPrincipal = new WindowsPrincipal(current); - //WindowsBuiltInRole可以枚举出很多权限,例如系统用户、User、Guest等等 - return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator); - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - return false; - } - } - - public static string GetDownloadFileName(string url) - { - var fileName = Path.GetFileName(url); - fileName += "_temp"; - - return fileName; - } - - public static IPAddress? GetDefaultGateway() - { - return NetworkInterface - .GetAllNetworkInterfaces() - .Where(n => n.OperationalStatus == OperationalStatus.Up) - .Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback) - .SelectMany(n => n.GetIPProperties()?.GatewayAddresses) - .Select(g => g?.Address) - .Where(a => a != null) - // .Where(a => a.AddressFamily == AddressFamily.InterNetwork) - // .Where(a => Array.FindIndex(a.GetAddressBytes(), b => b != 0) >= 0) - .FirstOrDefault(); - } - - public static bool IsGuidByParse(string strSrc) - { - return Guid.TryParse(strSrc, out Guid g); - } - - public static void ProcessStart(string fileName) - { - try - { - Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true }); - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - } - - public static void SetDarkBorder(System.Windows.Window window, bool dark) - { - // Make sure the handle is created before the window is shown - IntPtr hWnd = new System.Windows.Interop.WindowInteropHelper(window).EnsureHandle(); - int attribute = dark ? 1 : 0; - uint attributeSize = (uint)Marshal.SizeOf(attribute); - DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, ref attribute, attributeSize); - DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize); - } - - public static bool IsLightTheme() - { - using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"); - var value = key?.GetValue("AppsUseLightTheme"); - return value is int i && i > 0; - } - - #endregion 杂项 - - #region TempPath - - // return path to store temporary files - public static string GetTempPath(string filename = "") - { - string _tempPath = Path.Combine(StartupPath(), "guiTemps"); - if (!Directory.Exists(_tempPath)) - { - Directory.CreateDirectory(_tempPath); - } - if (string.IsNullOrEmpty(filename)) - { - return _tempPath; + return Guid.NewGuid().ToString("D"); } else { - return Path.Combine(_tempPath, filename); + return BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0).ToString(); } } - - public static string UnGzip(byte[] buf) + catch (Exception ex) { - using MemoryStream sb = new(); - using GZipStream input = new(new MemoryStream(buf), CompressionMode.Decompress, false); - input.CopyTo(sb); - sb.Position = 0; - return new StreamReader(sb, Encoding.UTF8).ReadToEnd(); + SaveLog(ex.Message, ex); } + return string.Empty; + } - public static string GetBackupPath(string filename) + /// + /// IsAdministrator + /// + /// + public static bool IsAdministrator() + { + try + { + WindowsIdentity current = WindowsIdentity.GetCurrent(); + WindowsPrincipal windowsPrincipal = new WindowsPrincipal(current); + //WindowsBuiltInRole可以枚举出很多权限,例如系统用户、User、Guest等等 + return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator); + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + return false; + } + } + + public static string GetDownloadFileName(string url) + { + var fileName = Path.GetFileName(url); + fileName += "_temp"; + + return fileName; + } + + public static IPAddress? GetDefaultGateway() + { + return NetworkInterface + .GetAllNetworkInterfaces() + .Where(n => n.OperationalStatus == OperationalStatus.Up) + .Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback) + .SelectMany(n => n.GetIPProperties()?.GatewayAddresses) + .Select(g => g?.Address) + .Where(a => a != null) + // .Where(a => a.AddressFamily == AddressFamily.InterNetwork) + // .Where(a => Array.FindIndex(a.GetAddressBytes(), b => b != 0) >= 0) + .FirstOrDefault(); + } + + public static bool IsGuidByParse(string strSrc) + { + return Guid.TryParse(strSrc, out Guid g); + } + + public static void ProcessStart(string fileName) + { + try + { + Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true }); + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + } + } + + public static void SetDarkBorder(System.Windows.Window window, bool dark) + { + // Make sure the handle is created before the window is shown + IntPtr hWnd = new System.Windows.Interop.WindowInteropHelper(window).EnsureHandle(); + int attribute = dark ? 1 : 0; + uint attributeSize = (uint)Marshal.SizeOf(attribute); + DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, ref attribute, attributeSize); + DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize); + } + + public static bool IsLightTheme() + { + using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"); + var value = key?.GetValue("AppsUseLightTheme"); + return value is int i && i > 0; + } + + #endregion 杂项 + + #region TempPath + + // return path to store temporary files + public static string GetTempPath(string filename = "") + { + string _tempPath = Path.Combine(StartupPath(), "guiTemps"); + if (!Directory.Exists(_tempPath)) + { + Directory.CreateDirectory(_tempPath); + } + if (string.IsNullOrEmpty(filename)) + { + return _tempPath; + } + else { - string _tempPath = Path.Combine(StartupPath(), "guiBackups"); - if (!Directory.Exists(_tempPath)) - { - Directory.CreateDirectory(_tempPath); - } return Path.Combine(_tempPath, filename); } - - public static string GetConfigPath(string filename = "") - { - string _tempPath = Path.Combine(StartupPath(), "guiConfigs"); - if (!Directory.Exists(_tempPath)) - { - Directory.CreateDirectory(_tempPath); - } - if (string.IsNullOrEmpty(filename)) - { - return _tempPath; - } - else - { - return Path.Combine(_tempPath, filename); - } - } - - public static string GetBinPath(string filename, ECoreType? coreType = null) - { - string _tempPath = Path.Combine(StartupPath(), "bin"); - if (!Directory.Exists(_tempPath)) - { - Directory.CreateDirectory(_tempPath); - } - if (coreType != null) - { - _tempPath = Path.Combine(_tempPath, coreType.ToString()!); - if (!Directory.Exists(_tempPath)) - { - Directory.CreateDirectory(_tempPath); - } - } - if (string.IsNullOrEmpty(filename)) - { - return _tempPath; - } - else - { - return Path.Combine(_tempPath, filename); - } - } - - public static string GetLogPath(string filename = "") - { - string _tempPath = Path.Combine(StartupPath(), "guiLogs"); - if (!Directory.Exists(_tempPath)) - { - Directory.CreateDirectory(_tempPath); - } - if (string.IsNullOrEmpty(filename)) - { - return _tempPath; - } - else - { - return Path.Combine(_tempPath, filename); - } - } - - public static string GetFontsPath(string filename = "") - { - string _tempPath = Path.Combine(StartupPath(), "guiFonts"); - if (!Directory.Exists(_tempPath)) - { - Directory.CreateDirectory(_tempPath); - } - if (string.IsNullOrEmpty(filename)) - { - return _tempPath; - } - else - { - return Path.Combine(_tempPath, filename); - } - } - - #endregion TempPath - - #region Log - - public static void SaveLog(string strContent) - { - if (LogManager.IsLoggingEnabled()) - { - var logger = LogManager.GetLogger("Log1"); - logger.Info(strContent); - } - } - - public static void SaveLog(string strTitle, Exception ex) - { - if (LogManager.IsLoggingEnabled()) - { - var logger = LogManager.GetLogger("Log2"); - logger.Debug($"{strTitle},{ex.Message}"); - logger.Debug(ex.StackTrace); - if (ex?.InnerException != null) - { - logger.Error(ex.InnerException); - } - } - } - - #endregion Log - - #region scan screen - - public static string ScanScreen(float dpiX, float dpiY) - { - try - { - var left = (int)(SystemParameters.WorkArea.Left); - var top = (int)(SystemParameters.WorkArea.Top); - var width = (int)(SystemParameters.WorkArea.Width / dpiX); - var height = (int)(SystemParameters.WorkArea.Height / dpiY); - - using Bitmap fullImage = new Bitmap(width, height); - using (Graphics g = Graphics.FromImage(fullImage)) - { - g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy); - } - int maxTry = 10; - for (int i = 0; i < maxTry; i++) - { - int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry); - int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry); - Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2); - Bitmap target = new(width, height); - - double imageScale = (double)width / (double)cropRect.Width; - using (Graphics g = Graphics.FromImage(target)) - { - g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height), - cropRect, - GraphicsUnit.Pixel); - } - - BitmapLuminanceSource source = new(target); - BinaryBitmap bitmap = new(new HybridBinarizer(source)); - QRCodeReader reader = new(); - Result result = reader.decode(bitmap); - if (result != null) - { - string ret = result.Text; - return ret; - } - } - } - catch (Exception ex) - { - SaveLog(ex.Message, ex); - } - return string.Empty; - } - - public static Tuple GetDpiXY(Window window) - { - IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle(); - Graphics g = Graphics.FromHwnd(hWnd); - - return new(96 / g.DpiX, 96 / g.DpiY); - } - - #endregion scan screen - - #region Windows API - - [Flags] - public enum DWMWINDOWATTRIBUTE : uint - { - DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, - DWMWA_USE_IMMERSIVE_DARK_MODE = 20, - } - - [DllImport("dwmapi.dll")] - public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize); - - #endregion Windows API } + + public static string UnGzip(byte[] buf) + { + using MemoryStream sb = new(); + using GZipStream input = new(new MemoryStream(buf), CompressionMode.Decompress, false); + input.CopyTo(sb); + sb.Position = 0; + return new StreamReader(sb, Encoding.UTF8).ReadToEnd(); + } + + public static string GetBackupPath(string filename) + { + string _tempPath = Path.Combine(StartupPath(), "guiBackups"); + if (!Directory.Exists(_tempPath)) + { + Directory.CreateDirectory(_tempPath); + } + return Path.Combine(_tempPath, filename); + } + + public static string GetConfigPath(string filename = "") + { + string _tempPath = Path.Combine(StartupPath(), "guiConfigs"); + if (!Directory.Exists(_tempPath)) + { + Directory.CreateDirectory(_tempPath); + } + if (string.IsNullOrEmpty(filename)) + { + return _tempPath; + } + else + { + return Path.Combine(_tempPath, filename); + } + } + + public static string GetBinPath(string filename, ECoreType? coreType = null) + { + string _tempPath = Path.Combine(StartupPath(), "bin"); + if (!Directory.Exists(_tempPath)) + { + Directory.CreateDirectory(_tempPath); + } + if (coreType != null) + { + _tempPath = Path.Combine(_tempPath, coreType.ToString()!); + if (!Directory.Exists(_tempPath)) + { + Directory.CreateDirectory(_tempPath); + } + } + if (string.IsNullOrEmpty(filename)) + { + return _tempPath; + } + else + { + return Path.Combine(_tempPath, filename); + } + } + + public static string GetLogPath(string filename = "") + { + string _tempPath = Path.Combine(StartupPath(), "guiLogs"); + if (!Directory.Exists(_tempPath)) + { + Directory.CreateDirectory(_tempPath); + } + if (string.IsNullOrEmpty(filename)) + { + return _tempPath; + } + else + { + return Path.Combine(_tempPath, filename); + } + } + + public static string GetFontsPath(string filename = "") + { + string _tempPath = Path.Combine(StartupPath(), "guiFonts"); + if (!Directory.Exists(_tempPath)) + { + Directory.CreateDirectory(_tempPath); + } + if (string.IsNullOrEmpty(filename)) + { + return _tempPath; + } + else + { + return Path.Combine(_tempPath, filename); + } + } + + #endregion TempPath + + #region Log + + public static void SaveLog(string strContent) + { + if (LogManager.IsLoggingEnabled()) + { + var logger = LogManager.GetLogger("Log1"); + logger.Info(strContent); + } + } + + public static void SaveLog(string strTitle, Exception ex) + { + if (LogManager.IsLoggingEnabled()) + { + var logger = LogManager.GetLogger("Log2"); + logger.Debug($"{strTitle},{ex.Message}"); + logger.Debug(ex.StackTrace); + if (ex?.InnerException != null) + { + logger.Error(ex.InnerException); + } + } + } + + #endregion Log + + #region scan screen + + public static string ScanScreen(float dpiX, float dpiY) + { + try + { + var left = (int)(SystemParameters.WorkArea.Left); + var top = (int)(SystemParameters.WorkArea.Top); + var width = (int)(SystemParameters.WorkArea.Width / dpiX); + var height = (int)(SystemParameters.WorkArea.Height / dpiY); + + using Bitmap fullImage = new Bitmap(width, height); + using (Graphics g = Graphics.FromImage(fullImage)) + { + g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy); + } + int maxTry = 10; + for (int i = 0; i < maxTry; i++) + { + int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry); + int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry); + Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2); + Bitmap target = new(width, height); + + double imageScale = (double)width / (double)cropRect.Width; + using (Graphics g = Graphics.FromImage(target)) + { + g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height), + cropRect, + GraphicsUnit.Pixel); + } + + BitmapLuminanceSource source = new(target); + BinaryBitmap bitmap = new(new HybridBinarizer(source)); + QRCodeReader reader = new(); + Result result = reader.decode(bitmap); + if (result != null) + { + string ret = result.Text; + return ret; + } + } + } + catch (Exception ex) + { + SaveLog(ex.Message, ex); + } + return string.Empty; + } + + public static Tuple GetDpiXY(Window window) + { + IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle(); + Graphics g = Graphics.FromHwnd(hWnd); + + return new(96 / g.DpiX, 96 / g.DpiY); + } + + #endregion scan screen + + #region Windows API + + [Flags] + public enum DWMWINDOWATTRIBUTE : uint + { + DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, + DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + } + + [DllImport("dwmapi.dll")] + public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize); + + #endregion Windows API } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/AddServer2ViewModel.cs b/v2rayN/v2rayN/ViewModels/AddServer2ViewModel.cs index 53934b1f..b179fcde 100644 --- a/v2rayN/v2rayN/ViewModels/AddServer2ViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/AddServer2ViewModel.cs @@ -11,150 +11,149 @@ using v2rayN.Handler; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class AddServer2ViewModel : ReactiveValidationObject { - public class AddServer2ViewModel : ReactiveValidationObject + private static Config _config; + private NoticeHandler? _noticeHandler; + private Window _view; + + [Reactive] + public ProfileItem SelectedSource { get; set; } + + public ReactiveCommand BrowseServerCmd { get; } + public ReactiveCommand EditServerCmd { get; } + public ReactiveCommand SaveServerCmd { get; } + public bool IsModified { get; set; } + + public AddServer2ViewModel(ProfileItem profileItem, Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; - private Window _view; + _noticeHandler = Locator.Current.GetService(); + _config = LazyConfig.Instance.GetConfig(); - [Reactive] - public ProfileItem SelectedSource { get; set; } - - public ReactiveCommand BrowseServerCmd { get; } - public ReactiveCommand EditServerCmd { get; } - public ReactiveCommand SaveServerCmd { get; } - public bool IsModified { get; set; } - - public AddServer2ViewModel(ProfileItem profileItem, Window view) + if (profileItem.indexId.IsNullOrEmpty()) { - _noticeHandler = Locator.Current.GetService(); - _config = LazyConfig.Instance.GetConfig(); - - if (profileItem.indexId.IsNullOrEmpty()) - { - SelectedSource = profileItem; - } - else - { - SelectedSource = Utils.DeepCopy(profileItem); - } - - _view = view; - - BrowseServerCmd = ReactiveCommand.Create(() => - { - BrowseServer(); - }); - - EditServerCmd = ReactiveCommand.Create(() => - { - EditServer(); - }); - - SaveServerCmd = ReactiveCommand.Create(() => - { - SaveServer(); - }); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + SelectedSource = profileItem; + } + else + { + SelectedSource = Utils.DeepCopy(profileItem); } - private void SaveServer() + _view = view; + + BrowseServerCmd = ReactiveCommand.Create(() => { - string remarks = SelectedSource.remarks; - if (Utils.IsNullOrEmpty(remarks)) - { - UI.Show(ResUI.PleaseFillRemarks); - return; - } + BrowseServer(); + }); - if (Utils.IsNullOrEmpty(SelectedSource.address)) - { - UI.Show(ResUI.FillServerAddressCustom); - return; - } + EditServerCmd = ReactiveCommand.Create(() => + { + EditServer(); + }); - var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId); - if (item is null) - { - item = SelectedSource; - } - else - { - item.remarks = SelectedSource.remarks; - item.address = SelectedSource.address; - item.coreType = SelectedSource.coreType; - item.displayLog = SelectedSource.displayLog; - item.preSocksPort = SelectedSource.preSocksPort; - } + SaveServerCmd = ReactiveCommand.Create(() => + { + SaveServer(); + }); - if (ConfigHandler.EditCustomServer(ref _config, item) == 0) - { - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - _view.DialogResult = true; - } - else - { - UI.Show(ResUI.OperationFailed); - } + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } + + private void SaveServer() + { + string remarks = SelectedSource.remarks; + if (Utils.IsNullOrEmpty(remarks)) + { + UI.Show(ResUI.PleaseFillRemarks); + return; } - private void BrowseServer() + if (Utils.IsNullOrEmpty(SelectedSource.address)) { - UI.Show(ResUI.CustomServerTips); - - OpenFileDialog fileDialog = new() - { - Multiselect = false, - Filter = "Config|*.json|YAML|*.yaml;*.yml|All|*.*" - }; - if (fileDialog.ShowDialog() != true) - { - return; - } - string fileName = fileDialog.FileName; - if (Utils.IsNullOrEmpty(fileName)) - { - return; - } - var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId); - item ??= SelectedSource; - item.address = fileName; - if (ConfigHandler.AddCustomServer(ref _config, item, false) == 0) - { - _noticeHandler?.Enqueue(ResUI.SuccessfullyImportedCustomServer); - if (!Utils.IsNullOrEmpty(item.indexId)) - { - SelectedSource = Utils.DeepCopy(item); - } - IsModified = true; - } - else - { - UI.ShowWarning(ResUI.FailedImportedCustomServer); - } + UI.Show(ResUI.FillServerAddressCustom); + return; } - private void EditServer() + var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId); + if (item is null) { - var address = SelectedSource.address; - if (Utils.IsNullOrEmpty(address)) - { - UI.Show(ResUI.FillServerAddressCustom); - return; - } + item = SelectedSource; + } + else + { + item.remarks = SelectedSource.remarks; + item.address = SelectedSource.address; + item.coreType = SelectedSource.coreType; + item.displayLog = SelectedSource.displayLog; + item.preSocksPort = SelectedSource.preSocksPort; + } - address = Utils.GetConfigPath(address); - if (File.Exists(address)) + if (ConfigHandler.EditCustomServer(ref _config, item) == 0) + { + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + _view.DialogResult = true; + } + else + { + UI.Show(ResUI.OperationFailed); + } + } + + private void BrowseServer() + { + UI.Show(ResUI.CustomServerTips); + + OpenFileDialog fileDialog = new() + { + Multiselect = false, + Filter = "Config|*.json|YAML|*.yaml;*.yml|All|*.*" + }; + if (fileDialog.ShowDialog() != true) + { + return; + } + string fileName = fileDialog.FileName; + if (Utils.IsNullOrEmpty(fileName)) + { + return; + } + var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId); + item ??= SelectedSource; + item.address = fileName; + if (ConfigHandler.AddCustomServer(ref _config, item, false) == 0) + { + _noticeHandler?.Enqueue(ResUI.SuccessfullyImportedCustomServer); + if (!Utils.IsNullOrEmpty(item.indexId)) { - Utils.ProcessStart(address); - } - else - { - _noticeHandler?.Enqueue(ResUI.FailedReadConfiguration); + SelectedSource = Utils.DeepCopy(item); } + IsModified = true; + } + else + { + UI.ShowWarning(ResUI.FailedImportedCustomServer); + } + } + + private void EditServer() + { + var address = SelectedSource.address; + if (Utils.IsNullOrEmpty(address)) + { + UI.Show(ResUI.FillServerAddressCustom); + return; + } + + address = Utils.GetConfigPath(address); + if (File.Exists(address)) + { + Utils.ProcessStart(address); + } + else + { + _noticeHandler?.Enqueue(ResUI.FailedReadConfiguration); } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/AddServerViewModel.cs b/v2rayN/v2rayN/ViewModels/AddServerViewModel.cs index f99d4a01..90424c95 100644 --- a/v2rayN/v2rayN/ViewModels/AddServerViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/AddServerViewModel.cs @@ -8,155 +8,154 @@ using v2rayN.Handler; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class AddServerViewModel : ReactiveObject { - public class AddServerViewModel : ReactiveObject + private static Config _config; + private NoticeHandler? _noticeHandler; + private Window _view; + + [Reactive] + public ProfileItem SelectedSource { get; set; } + + public ReactiveCommand SaveCmd { get; } + + public AddServerViewModel(ProfileItem profileItem, Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; - private Window _view; + _config = LazyConfig.Instance.GetConfig(); + _noticeHandler = Locator.Current.GetService(); + _view = view; - [Reactive] - public ProfileItem SelectedSource { get; set; } - - public ReactiveCommand SaveCmd { get; } - - public AddServerViewModel(ProfileItem profileItem, Window view) + if (profileItem.id.IsNullOrEmpty()) { - _config = LazyConfig.Instance.GetConfig(); - _noticeHandler = Locator.Current.GetService(); - _view = view; - - if (profileItem.id.IsNullOrEmpty()) - { - profileItem.network = Global.DefaultNetwork; - profileItem.headerType = Global.None; - profileItem.requestHost = ""; - profileItem.streamSecurity = ""; - SelectedSource = profileItem; - } - else - { - SelectedSource = Utils.DeepCopy(profileItem); - } - - SaveCmd = ReactiveCommand.Create(() => - { - SaveServer(); - }); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + profileItem.network = Global.DefaultNetwork; + profileItem.headerType = Global.None; + profileItem.requestHost = ""; + profileItem.streamSecurity = ""; + SelectedSource = profileItem; + } + else + { + SelectedSource = Utils.DeepCopy(profileItem); } - private void SaveServer() + SaveCmd = ReactiveCommand.Create(() => { - if (Utils.IsNullOrEmpty(SelectedSource.remarks)) + SaveServer(); + }); + + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } + + private void SaveServer() + { + if (Utils.IsNullOrEmpty(SelectedSource.remarks)) + { + UI.Show(ResUI.PleaseFillRemarks); + return; + } + + if (Utils.IsNullOrEmpty(SelectedSource.address)) + { + UI.Show(ResUI.FillServerAddress); + return; + } + var port = SelectedSource.port.ToString(); + if (Utils.IsNullOrEmpty(port) || !Utils.IsNumberic(port) + || SelectedSource.port <= 0 || SelectedSource.port >= Global.MaxPort) + { + UI.Show(ResUI.FillCorrectServerPort); + return; + } + if (SelectedSource.configType == EConfigType.Shadowsocks) + { + if (Utils.IsNullOrEmpty(SelectedSource.id)) { - UI.Show(ResUI.PleaseFillRemarks); + UI.Show(ResUI.FillPassword); return; } - - if (Utils.IsNullOrEmpty(SelectedSource.address)) + if (Utils.IsNullOrEmpty(SelectedSource.security)) { - UI.Show(ResUI.FillServerAddress); + UI.Show(ResUI.PleaseSelectEncryption); return; } - var port = SelectedSource.port.ToString(); - if (Utils.IsNullOrEmpty(port) || !Utils.IsNumberic(port) - || SelectedSource.port <= 0 || SelectedSource.port >= Global.MaxPort) + } + if (SelectedSource.configType != EConfigType.Socks) + { + if (Utils.IsNullOrEmpty(SelectedSource.id)) { - UI.Show(ResUI.FillCorrectServerPort); + UI.Show(ResUI.FillUUID); return; } - if (SelectedSource.configType == EConfigType.Shadowsocks) - { - if (Utils.IsNullOrEmpty(SelectedSource.id)) - { - UI.Show(ResUI.FillPassword); - return; - } - if (Utils.IsNullOrEmpty(SelectedSource.security)) - { - UI.Show(ResUI.PleaseSelectEncryption); - return; - } - } - if (SelectedSource.configType != EConfigType.Socks) - { - if (Utils.IsNullOrEmpty(SelectedSource.id)) - { - UI.Show(ResUI.FillUUID); - return; - } - } + } - var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId); - if (item is null) - { - item = SelectedSource; - } - else - { - item.coreType = SelectedSource.coreType; - item.remarks = SelectedSource.remarks; - item.address = SelectedSource.address; - item.port = SelectedSource.port; + var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId); + if (item is null) + { + item = SelectedSource; + } + else + { + item.coreType = SelectedSource.coreType; + item.remarks = SelectedSource.remarks; + item.address = SelectedSource.address; + item.port = SelectedSource.port; - item.id = SelectedSource.id; - item.alterId = SelectedSource.alterId; - item.security = SelectedSource.security; - item.flow = SelectedSource.flow; + item.id = SelectedSource.id; + item.alterId = SelectedSource.alterId; + item.security = SelectedSource.security; + item.flow = SelectedSource.flow; - item.network = SelectedSource.network; - item.headerType = SelectedSource.headerType; - item.requestHost = SelectedSource.requestHost; - item.path = SelectedSource.path; + item.network = SelectedSource.network; + item.headerType = SelectedSource.headerType; + item.requestHost = SelectedSource.requestHost; + item.path = SelectedSource.path; - item.streamSecurity = SelectedSource.streamSecurity; - item.sni = SelectedSource.sni; - item.allowInsecure = SelectedSource.allowInsecure; - item.fingerprint = SelectedSource.fingerprint; - item.alpn = SelectedSource.alpn; + item.streamSecurity = SelectedSource.streamSecurity; + item.sni = SelectedSource.sni; + item.allowInsecure = SelectedSource.allowInsecure; + item.fingerprint = SelectedSource.fingerprint; + item.alpn = SelectedSource.alpn; - item.publicKey = SelectedSource.publicKey; - item.shortId = SelectedSource.shortId; - item.spiderX = SelectedSource.spiderX; - } + item.publicKey = SelectedSource.publicKey; + item.shortId = SelectedSource.shortId; + item.spiderX = SelectedSource.spiderX; + } - int ret = -1; - switch (item.configType) - { - case EConfigType.VMess: - ret = ConfigHandler.AddServer(ref _config, item); - break; + int ret = -1; + switch (item.configType) + { + case EConfigType.VMess: + ret = ConfigHandler.AddServer(ref _config, item); + break; - case EConfigType.Shadowsocks: - ret = ConfigHandler.AddShadowsocksServer(ref _config, item); - break; + case EConfigType.Shadowsocks: + ret = ConfigHandler.AddShadowsocksServer(ref _config, item); + break; - case EConfigType.Socks: - ret = ConfigHandler.AddSocksServer(ref _config, item); - break; + case EConfigType.Socks: + ret = ConfigHandler.AddSocksServer(ref _config, item); + break; - case EConfigType.VLESS: - ret = ConfigHandler.AddVlessServer(ref _config, item); - break; + case EConfigType.VLESS: + ret = ConfigHandler.AddVlessServer(ref _config, item); + break; - case EConfigType.Trojan: - ret = ConfigHandler.AddTrojanServer(ref _config, item); - break; - } + case EConfigType.Trojan: + ret = ConfigHandler.AddTrojanServer(ref _config, item); + break; + } - if (ret == 0) - { - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - _view.DialogResult = true; - //_view?.Close(); - } - else - { - UI.Show(ResUI.OperationFailed); - } + if (ret == 0) + { + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + _view.DialogResult = true; + //_view?.Close(); + } + else + { + UI.Show(ResUI.OperationFailed); } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/DNSSettingViewModel.cs b/v2rayN/v2rayN/ViewModels/DNSSettingViewModel.cs index 3ead0dcf..dd7fa2b9 100644 --- a/v2rayN/v2rayN/ViewModels/DNSSettingViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/DNSSettingViewModel.cs @@ -7,104 +7,103 @@ using v2rayN.Handler; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class DNSSettingViewModel : ReactiveObject { - public class DNSSettingViewModel : ReactiveObject + private static Config _config; + private NoticeHandler? _noticeHandler; + private Window _view; + + [Reactive] public string domainStrategy4Freedom { get; set; } + [Reactive] public string normalDNS { get; set; } + [Reactive] public string normalDNS2 { get; set; } + [Reactive] public string tunDNS2 { get; set; } + + public ReactiveCommand SaveCmd { get; } + public ReactiveCommand ImportDefConfig4V2rayCmd { get; } + public ReactiveCommand ImportDefConfig4SingboxCmd { get; } + + public DNSSettingViewModel(Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; - private Window _view; + _config = LazyConfig.Instance.GetConfig(); + _noticeHandler = Locator.Current.GetService(); + _view = view; - [Reactive] public string domainStrategy4Freedom { get; set; } - [Reactive] public string normalDNS { get; set; } - [Reactive] public string normalDNS2 { get; set; } - [Reactive] public string tunDNS2 { get; set; } + var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray); + domainStrategy4Freedom = item?.domainStrategy4Freedom!; + normalDNS = item?.normalDNS!; - public ReactiveCommand SaveCmd { get; } - public ReactiveCommand ImportDefConfig4V2rayCmd { get; } - public ReactiveCommand ImportDefConfig4SingboxCmd { get; } + var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box); + normalDNS2 = item2?.normalDNS!; + tunDNS2 = item2?.tunDNS!; - public DNSSettingViewModel(Window view) + SaveCmd = ReactiveCommand.Create(() => { - _config = LazyConfig.Instance.GetConfig(); - _noticeHandler = Locator.Current.GetService(); - _view = view; + SaveSetting(); + }); - var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray); - domainStrategy4Freedom = item?.domainStrategy4Freedom!; - normalDNS = item?.normalDNS!; - - var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box); - normalDNS2 = item2?.normalDNS!; - tunDNS2 = item2?.tunDNS!; - - SaveCmd = ReactiveCommand.Create(() => - { - SaveSetting(); - }); - - ImportDefConfig4V2rayCmd = ReactiveCommand.Create(() => - { - normalDNS = Utils.GetEmbedText(Global.DNSV2rayNormalFileName); - }); - - ImportDefConfig4SingboxCmd = ReactiveCommand.Create(() => - { - normalDNS2 = Utils.GetEmbedText(Global.DNSSingboxNormalFileName); - tunDNS2 = Utils.GetEmbedText(Global.TunSingboxDNSFileName); - }); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); - } - - private void SaveSetting() + ImportDefConfig4V2rayCmd = ReactiveCommand.Create(() => { - if (!Utils.IsNullOrEmpty(normalDNS)) + normalDNS = Utils.GetEmbedText(Global.DNSV2rayNormalFileName); + }); + + ImportDefConfig4SingboxCmd = ReactiveCommand.Create(() => + { + normalDNS2 = Utils.GetEmbedText(Global.DNSSingboxNormalFileName); + tunDNS2 = Utils.GetEmbedText(Global.TunSingboxDNSFileName); + }); + + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } + + private void SaveSetting() + { + if (!Utils.IsNullOrEmpty(normalDNS)) + { + var obj = Utils.ParseJson(normalDNS); + if (obj != null && obj.ContainsKey("servers") == true) { - var obj = Utils.ParseJson(normalDNS); - if (obj != null && obj.ContainsKey("servers") == true) - { - } - else - { - if (normalDNS.Contains("{") || normalDNS.Contains("}")) - { - UI.Show(ResUI.FillCorrectDNSText); - return; - } - } } - if (!Utils.IsNullOrEmpty(normalDNS2)) + else { - var obj2 = Utils.FromJson(normalDNS2); - if (obj2 == null) + if (normalDNS.Contains("{") || normalDNS.Contains("}")) { UI.Show(ResUI.FillCorrectDNSText); return; } } - if (!Utils.IsNullOrEmpty(tunDNS2)) - { - var obj2 = Utils.FromJson(tunDNS2); - if (obj2 == null) - { - UI.Show(ResUI.FillCorrectDNSText); - return; - } - } - - var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray); - item.domainStrategy4Freedom = domainStrategy4Freedom; - item.normalDNS = normalDNS; - ConfigHandler.SaveDNSItems(_config, item); - - var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box); - item2.normalDNS = Utils.ToJson(Utils.ParseJson(normalDNS2)); - item2.tunDNS = Utils.ToJson(Utils.ParseJson(tunDNS2)); - ConfigHandler.SaveDNSItems(_config, item2); - - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - _view.DialogResult = true; } + if (!Utils.IsNullOrEmpty(normalDNS2)) + { + var obj2 = Utils.FromJson(normalDNS2); + if (obj2 == null) + { + UI.Show(ResUI.FillCorrectDNSText); + return; + } + } + if (!Utils.IsNullOrEmpty(tunDNS2)) + { + var obj2 = Utils.FromJson(tunDNS2); + if (obj2 == null) + { + UI.Show(ResUI.FillCorrectDNSText); + return; + } + } + + var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray); + item.domainStrategy4Freedom = domainStrategy4Freedom; + item.normalDNS = normalDNS; + ConfigHandler.SaveDNSItems(_config, item); + + var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box); + item2.normalDNS = Utils.ToJson(Utils.ParseJson(normalDNS2)); + item2.tunDNS = Utils.ToJson(Utils.ParseJson(tunDNS2)); + ConfigHandler.SaveDNSItems(_config, item2); + + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + _view.DialogResult = true; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs b/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs index 84204192..8f328ca4 100644 --- a/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs @@ -22,1825 +22,1824 @@ using v2rayN.Resx; using v2rayN.Tool; using v2rayN.Views; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class MainWindowViewModel : ReactiveObject { - public class MainWindowViewModel : ReactiveObject + #region private prop + + private CoreHandler _coreHandler; + private StatisticsHandler _statistics; + private List _lstProfile; + private string _subId = string.Empty; + private string _serverFilter = string.Empty; + private static Config _config; + private NoticeHandler? _noticeHandler; + private readonly PaletteHelper _paletteHelper = new(); + private Dictionary _dicHeaderSort = new(); + private Action _updateView; + + #endregion private prop + + #region ObservableCollection + + private IObservableCollection _profileItems = new ObservableCollectionExtended(); + public IObservableCollection ProfileItems => _profileItems; + + private IObservableCollection _subItems = new ObservableCollectionExtended(); + public IObservableCollection SubItems => _subItems; + + private IObservableCollection _routingItems = new ObservableCollectionExtended(); + public IObservableCollection RoutingItems => _routingItems; + + private IObservableCollection _servers = new ObservableCollectionExtended(); + public IObservableCollection Servers => _servers; + + [Reactive] + public ProfileItemModel SelectedProfile { get; set; } + + public IList SelectedProfiles { get; set; } + + [Reactive] + public SubItem SelectedSub { get; set; } + + [Reactive] + public SubItem SelectedMoveToGroup { get; set; } + + [Reactive] + public RoutingItem SelectedRouting { get; set; } + + [Reactive] + public ComboItem SelectedServer { get; set; } + + [Reactive] + public string ServerFilter { get; set; } + + [Reactive] + public bool BlServers { get; set; } + + #endregion ObservableCollection + + #region Menu + + //servers + public ReactiveCommand AddVmessServerCmd { get; } + + public ReactiveCommand AddVlessServerCmd { get; } + public ReactiveCommand AddShadowsocksServerCmd { get; } + public ReactiveCommand AddSocksServerCmd { get; } + public ReactiveCommand AddTrojanServerCmd { get; } + public ReactiveCommand AddCustomServerCmd { get; } + public ReactiveCommand AddServerViaClipboardCmd { get; } + public ReactiveCommand AddServerViaScanCmd { get; } + + //servers delete + public ReactiveCommand EditServerCmd { get; } + + public ReactiveCommand RemoveServerCmd { get; } + public ReactiveCommand RemoveDuplicateServerCmd { get; } + public ReactiveCommand CopyServerCmd { get; } + public ReactiveCommand SetDefaultServerCmd { get; } + public ReactiveCommand ShareServerCmd { get; } + + //servers move + public ReactiveCommand MoveTopCmd { get; } + + public ReactiveCommand MoveUpCmd { get; } + public ReactiveCommand MoveDownCmd { get; } + public ReactiveCommand MoveBottomCmd { get; } + + //servers ping + public ReactiveCommand MixedTestServerCmd { get; } + + public ReactiveCommand PingServerCmd { get; } + public ReactiveCommand TcpingServerCmd { get; } + public ReactiveCommand RealPingServerCmd { get; } + public ReactiveCommand SpeedServerCmd { get; } + public ReactiveCommand SortServerResultCmd { get; } + + //servers export + public ReactiveCommand Export2ClientConfigCmd { get; } + + public ReactiveCommand Export2ShareUrlCmd { get; } + public ReactiveCommand Export2SubContentCmd { get; } + + //Subscription + public ReactiveCommand SubSettingCmd { get; } + + public ReactiveCommand AddSubCmd { get; } + public ReactiveCommand SubUpdateCmd { get; } + public ReactiveCommand SubUpdateViaProxyCmd { get; } + public ReactiveCommand SubGroupUpdateCmd { get; } + public ReactiveCommand SubGroupUpdateViaProxyCmd { get; } + + //Setting + public ReactiveCommand OptionSettingCmd { get; } + + public ReactiveCommand RoutingSettingCmd { get; } + public ReactiveCommand DNSSettingCmd { get; } + public ReactiveCommand GlobalHotkeySettingCmd { get; } + public ReactiveCommand RebootAsAdminCmd { get; } + public ReactiveCommand ClearServerStatisticsCmd { get; } + public ReactiveCommand ImportOldGuiConfigCmd { get; } + + //CheckUpdate + public ReactiveCommand CheckUpdateNCmd { get; } + + public ReactiveCommand CheckUpdateV2flyCoreCmd { get; } + public ReactiveCommand CheckUpdateSagerNetCoreCmd { get; } + public ReactiveCommand CheckUpdateXrayCoreCmd { get; } + public ReactiveCommand CheckUpdateClashCoreCmd { get; } + public ReactiveCommand CheckUpdateClashMetaCoreCmd { get; } + public ReactiveCommand CheckUpdateSingBoxCoreCmd { get; } + public ReactiveCommand CheckUpdateGeoCmd { get; } + + public ReactiveCommand ReloadCmd { get; } + + [Reactive] + public bool BlReloadEnabled { get; set; } + + public ReactiveCommand NotifyLeftClickCmd { get; } + + [Reactive] + public Icon NotifyIcon { get; set; } + + [Reactive] + public ImageSource AppIcon { get; set; } + + //[Reactive] + //public bool BlShowTrayTip { get; set; } + + #endregion Menu + + #region System Proxy + + [Reactive] + public bool BlSystemProxyClear { get; set; } + + [Reactive] + public bool BlSystemProxySet { get; set; } + + [Reactive] + public bool BlSystemProxyNothing { get; set; } + + [Reactive] + public bool BlSystemProxyPac { get; set; } + + public ReactiveCommand SystemProxyClearCmd { get; } + public ReactiveCommand SystemProxySetCmd { get; } + public ReactiveCommand SystemProxyNothingCmd { get; } + public ReactiveCommand SystemProxyPacCmd { get; } + + [Reactive] + public bool BlRouting { get; set; } + + [Reactive] + public int SystemProxySelected { get; set; } + + #endregion System Proxy + + #region UI + + [Reactive] + public string InboundDisplay { get; set; } + + [Reactive] + public string InboundLanDisplay { get; set; } + + [Reactive] + public string RunningServerDisplay { get; set; } + + //[Reactive] + //public string RunningServerToolTipText { get; set; } + + [Reactive] + public string RunningInfoDisplay { get; set; } + + [Reactive] + public string SpeedProxyDisplay { get; set; } + + [Reactive] + public string SpeedDirectDisplay { get; set; } + + [Reactive] + public bool EnableTun { get; set; } + + [Reactive] + public bool ColorModeDark { get; set; } + + private IObservableCollection _swatches = new ObservableCollectionExtended(); + public IObservableCollection Swatches => _swatches; + + [Reactive] + public Swatch SelectedSwatch { get; set; } + + [Reactive] + public int CurrentFontSize { get; set; } + + [Reactive] + public bool FollowSystemTheme { get; set; } + + [Reactive] + public string CurrentLanguage { get; set; } + + #endregion UI + + #region Init + + public MainWindowViewModel(ISnackbarMessageQueue snackbarMessageQueue, Action updateView) { - #region private prop + _updateView = updateView; + ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false); - private CoreHandler _coreHandler; - private StatisticsHandler _statistics; - private List _lstProfile; - private string _subId = string.Empty; - private string _serverFilter = string.Empty; - private static Config _config; - private NoticeHandler? _noticeHandler; - private readonly PaletteHelper _paletteHelper = new(); - private Dictionary _dicHeaderSort = new(); - private Action _updateView; + Locator.CurrentMutable.RegisterLazySingleton(() => new NoticeHandler(snackbarMessageQueue), typeof(NoticeHandler)); + _noticeHandler = Locator.Current.GetService(); + _config = LazyConfig.Instance.GetConfig(); + //ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false); + Init(); - #endregion private prop + SelectedProfile = new(); + SelectedSub = new(); + SelectedMoveToGroup = new(); + SelectedRouting = new(); + SelectedServer = new(); + if (_config.tunModeItem.enableTun && Utils.IsAdministrator()) + { + EnableTun = true; + } + _subId = _config.subIndexId; - #region ObservableCollection + InitSubscriptionView(); + RefreshRoutingsMenu(); + RefreshServers(); - private IObservableCollection _profileItems = new ObservableCollectionExtended(); - public IObservableCollection ProfileItems => _profileItems; + var canEditRemove = this.WhenAnyValue( + x => x.SelectedProfile, + selectedSource => selectedSource != null && !selectedSource.indexId.IsNullOrEmpty()); - private IObservableCollection _subItems = new ObservableCollectionExtended(); - public IObservableCollection SubItems => _subItems; + this.WhenAnyValue( + x => x.SelectedSub, + y => y != null && !y.remarks.IsNullOrEmpty() && _subId != y.id) + .Subscribe(c => SubSelectedChanged(c)); + this.WhenAnyValue( + x => x.SelectedMoveToGroup, + y => y != null && !y.remarks.IsNullOrEmpty()) + .Subscribe(c => MoveToGroup(c)); - private IObservableCollection _routingItems = new ObservableCollectionExtended(); - public IObservableCollection RoutingItems => _routingItems; + this.WhenAnyValue( + x => x.SelectedRouting, + y => y != null && !y.remarks.IsNullOrEmpty()) + .Subscribe(c => RoutingSelectedChanged(c)); - private IObservableCollection _servers = new ObservableCollectionExtended(); - public IObservableCollection Servers => _servers; + this.WhenAnyValue( + x => x.SelectedServer, + y => y != null && !y.Text.IsNullOrEmpty()) + .Subscribe(c => ServerSelectedChanged(c)); - [Reactive] - public ProfileItemModel SelectedProfile { get; set; } + this.WhenAnyValue( + x => x.ServerFilter, + y => y != null && _serverFilter != y) + .Subscribe(c => ServerFilterChanged(c)); - public IList SelectedProfiles { get; set; } + SystemProxySelected = (int)_config.sysProxyType; + this.WhenAnyValue( + x => x.SystemProxySelected, + y => y >= 0) + .Subscribe(c => DoSystemProxySelected(c)); - [Reactive] - public SubItem SelectedSub { get; set; } + this.WhenAnyValue( + x => x.EnableTun, + y => y == true) + .Subscribe(c => DoEnableTun(c)); - [Reactive] - public SubItem SelectedMoveToGroup { get; set; } - - [Reactive] - public RoutingItem SelectedRouting { get; set; } - - [Reactive] - public ComboItem SelectedServer { get; set; } - - [Reactive] - public string ServerFilter { get; set; } - - [Reactive] - public bool BlServers { get; set; } - - #endregion ObservableCollection - - #region Menu + BindingUI(); + RestoreUI(); + AutoHideStartup(); //servers - public ReactiveCommand AddVmessServerCmd { get; } - - public ReactiveCommand AddVlessServerCmd { get; } - public ReactiveCommand AddShadowsocksServerCmd { get; } - public ReactiveCommand AddSocksServerCmd { get; } - public ReactiveCommand AddTrojanServerCmd { get; } - public ReactiveCommand AddCustomServerCmd { get; } - public ReactiveCommand AddServerViaClipboardCmd { get; } - public ReactiveCommand AddServerViaScanCmd { get; } - + AddVmessServerCmd = ReactiveCommand.Create(() => + { + EditServer(true, EConfigType.VMess); + }); + AddVlessServerCmd = ReactiveCommand.Create(() => + { + EditServer(true, EConfigType.VLESS); + }); + AddShadowsocksServerCmd = ReactiveCommand.Create(() => + { + EditServer(true, EConfigType.Shadowsocks); + }); + AddSocksServerCmd = ReactiveCommand.Create(() => + { + EditServer(true, EConfigType.Socks); + }); + AddTrojanServerCmd = ReactiveCommand.Create(() => + { + EditServer(true, EConfigType.Trojan); + }); + AddCustomServerCmd = ReactiveCommand.Create(() => + { + EditServer(true, EConfigType.Custom); + }); + AddServerViaClipboardCmd = ReactiveCommand.Create(() => + { + AddServerViaClipboard(); + }); + AddServerViaScanCmd = ReactiveCommand.CreateFromTask(() => + { + return ScanScreenTaskAsync(); + }); //servers delete - public ReactiveCommand EditServerCmd { get; } - - public ReactiveCommand RemoveServerCmd { get; } - public ReactiveCommand RemoveDuplicateServerCmd { get; } - public ReactiveCommand CopyServerCmd { get; } - public ReactiveCommand SetDefaultServerCmd { get; } - public ReactiveCommand ShareServerCmd { get; } - + EditServerCmd = ReactiveCommand.Create(() => + { + EditServer(false, EConfigType.Custom); + }, canEditRemove); + RemoveServerCmd = ReactiveCommand.Create(() => + { + RemoveServer(); + }, canEditRemove); + RemoveDuplicateServerCmd = ReactiveCommand.Create(() => + { + RemoveDuplicateServer(); + }); + CopyServerCmd = ReactiveCommand.Create(() => + { + CopyServer(); + }, canEditRemove); + SetDefaultServerCmd = ReactiveCommand.Create(() => + { + SetDefaultServer(); + }, canEditRemove); + ShareServerCmd = ReactiveCommand.Create(() => + { + ShareServer(); + }, canEditRemove); //servers move - public ReactiveCommand MoveTopCmd { get; } - - public ReactiveCommand MoveUpCmd { get; } - public ReactiveCommand MoveDownCmd { get; } - public ReactiveCommand MoveBottomCmd { get; } + MoveTopCmd = ReactiveCommand.Create(() => + { + MoveServer(EMove.Top); + }, canEditRemove); + MoveUpCmd = ReactiveCommand.Create(() => + { + MoveServer(EMove.Up); + }, canEditRemove); + MoveDownCmd = ReactiveCommand.Create(() => + { + MoveServer(EMove.Down); + }, canEditRemove); + MoveBottomCmd = ReactiveCommand.Create(() => + { + MoveServer(EMove.Bottom); + }, canEditRemove); //servers ping - public ReactiveCommand MixedTestServerCmd { get; } - - public ReactiveCommand PingServerCmd { get; } - public ReactiveCommand TcpingServerCmd { get; } - public ReactiveCommand RealPingServerCmd { get; } - public ReactiveCommand SpeedServerCmd { get; } - public ReactiveCommand SortServerResultCmd { get; } - + MixedTestServerCmd = ReactiveCommand.Create(() => + { + ServerSpeedtest(ESpeedActionType.Mixedtest); + }); + PingServerCmd = ReactiveCommand.Create(() => + { + ServerSpeedtest(ESpeedActionType.Ping); + }, canEditRemove); + TcpingServerCmd = ReactiveCommand.Create(() => + { + ServerSpeedtest(ESpeedActionType.Tcping); + }, canEditRemove); + RealPingServerCmd = ReactiveCommand.Create(() => + { + ServerSpeedtest(ESpeedActionType.Realping); + }, canEditRemove); + SpeedServerCmd = ReactiveCommand.Create(() => + { + ServerSpeedtest(ESpeedActionType.Speedtest); + }, canEditRemove); + SortServerResultCmd = ReactiveCommand.Create(() => + { + SortServer(EServerColName.delayVal.ToString()); + }); //servers export - public ReactiveCommand Export2ClientConfigCmd { get; } - - public ReactiveCommand Export2ShareUrlCmd { get; } - public ReactiveCommand Export2SubContentCmd { get; } + Export2ClientConfigCmd = ReactiveCommand.Create(() => + { + Export2ClientConfig(); + }, canEditRemove); + Export2ShareUrlCmd = ReactiveCommand.Create(() => + { + Export2ShareUrl(); + }, canEditRemove); + Export2SubContentCmd = ReactiveCommand.Create(() => + { + Export2SubContent(); + }, canEditRemove); //Subscription - public ReactiveCommand SubSettingCmd { get; } - - public ReactiveCommand AddSubCmd { get; } - public ReactiveCommand SubUpdateCmd { get; } - public ReactiveCommand SubUpdateViaProxyCmd { get; } - public ReactiveCommand SubGroupUpdateCmd { get; } - public ReactiveCommand SubGroupUpdateViaProxyCmd { get; } + SubSettingCmd = ReactiveCommand.Create(() => + { + SubSetting(); + }); + AddSubCmd = ReactiveCommand.Create(() => + { + AddSub(); + }); + SubUpdateCmd = ReactiveCommand.Create(() => + { + UpdateSubscriptionProcess("", false); + }); + SubUpdateViaProxyCmd = ReactiveCommand.Create(() => + { + UpdateSubscriptionProcess("", true); + }); + SubGroupUpdateCmd = ReactiveCommand.Create(() => + { + UpdateSubscriptionProcess(_subId, false); + }); + SubGroupUpdateViaProxyCmd = ReactiveCommand.Create(() => + { + UpdateSubscriptionProcess(_subId, true); + }); //Setting - public ReactiveCommand OptionSettingCmd { get; } - - public ReactiveCommand RoutingSettingCmd { get; } - public ReactiveCommand DNSSettingCmd { get; } - public ReactiveCommand GlobalHotkeySettingCmd { get; } - public ReactiveCommand RebootAsAdminCmd { get; } - public ReactiveCommand ClearServerStatisticsCmd { get; } - public ReactiveCommand ImportOldGuiConfigCmd { get; } + OptionSettingCmd = ReactiveCommand.Create(() => + { + OptionSetting(); + }); + RoutingSettingCmd = ReactiveCommand.Create(() => + { + RoutingSetting(); + }); + DNSSettingCmd = ReactiveCommand.Create(() => + { + DNSSetting(); + }); + GlobalHotkeySettingCmd = ReactiveCommand.Create(() => + { + if ((new GlobalHotkeySettingWindow()).ShowDialog() == true) + { + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + } + }); + RebootAsAdminCmd = ReactiveCommand.Create(() => + { + RebootAsAdmin(); + }); + ClearServerStatisticsCmd = ReactiveCommand.Create(() => + { + _statistics?.ClearAllServerStatistics(); + RefreshServers(); + }); + ImportOldGuiConfigCmd = ReactiveCommand.Create(() => + { + ImportOldGuiConfig(); + }); //CheckUpdate - public ReactiveCommand CheckUpdateNCmd { get; } - - public ReactiveCommand CheckUpdateV2flyCoreCmd { get; } - public ReactiveCommand CheckUpdateSagerNetCoreCmd { get; } - public ReactiveCommand CheckUpdateXrayCoreCmd { get; } - public ReactiveCommand CheckUpdateClashCoreCmd { get; } - public ReactiveCommand CheckUpdateClashMetaCoreCmd { get; } - public ReactiveCommand CheckUpdateSingBoxCoreCmd { get; } - public ReactiveCommand CheckUpdateGeoCmd { get; } - - public ReactiveCommand ReloadCmd { get; } - - [Reactive] - public bool BlReloadEnabled { get; set; } - - public ReactiveCommand NotifyLeftClickCmd { get; } - - [Reactive] - public Icon NotifyIcon { get; set; } - - [Reactive] - public ImageSource AppIcon { get; set; } - - //[Reactive] - //public bool BlShowTrayTip { get; set; } - - #endregion Menu - - #region System Proxy - - [Reactive] - public bool BlSystemProxyClear { get; set; } - - [Reactive] - public bool BlSystemProxySet { get; set; } - - [Reactive] - public bool BlSystemProxyNothing { get; set; } - - [Reactive] - public bool BlSystemProxyPac { get; set; } - - public ReactiveCommand SystemProxyClearCmd { get; } - public ReactiveCommand SystemProxySetCmd { get; } - public ReactiveCommand SystemProxyNothingCmd { get; } - public ReactiveCommand SystemProxyPacCmd { get; } - - [Reactive] - public bool BlRouting { get; set; } - - [Reactive] - public int SystemProxySelected { get; set; } - - #endregion System Proxy - - #region UI - - [Reactive] - public string InboundDisplay { get; set; } - - [Reactive] - public string InboundLanDisplay { get; set; } - - [Reactive] - public string RunningServerDisplay { get; set; } - - //[Reactive] - //public string RunningServerToolTipText { get; set; } - - [Reactive] - public string RunningInfoDisplay { get; set; } - - [Reactive] - public string SpeedProxyDisplay { get; set; } - - [Reactive] - public string SpeedDirectDisplay { get; set; } - - [Reactive] - public bool EnableTun { get; set; } - - [Reactive] - public bool ColorModeDark { get; set; } - - private IObservableCollection _swatches = new ObservableCollectionExtended(); - public IObservableCollection Swatches => _swatches; - - [Reactive] - public Swatch SelectedSwatch { get; set; } - - [Reactive] - public int CurrentFontSize { get; set; } - - [Reactive] - public bool FollowSystemTheme { get; set; } - - [Reactive] - public string CurrentLanguage { get; set; } - - #endregion UI - - #region Init - - public MainWindowViewModel(ISnackbarMessageQueue snackbarMessageQueue, Action updateView) + CheckUpdateNCmd = ReactiveCommand.Create(() => { - _updateView = updateView; - ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false); + CheckUpdateN(); + }); + CheckUpdateV2flyCoreCmd = ReactiveCommand.Create(() => + { + CheckUpdateCore(ECoreType.v2fly_v5); + }); + CheckUpdateSagerNetCoreCmd = ReactiveCommand.Create(() => + { + CheckUpdateCore(ECoreType.SagerNet); + }); + CheckUpdateXrayCoreCmd = ReactiveCommand.Create(() => + { + CheckUpdateCore(ECoreType.Xray); + }); + CheckUpdateClashCoreCmd = ReactiveCommand.Create(() => + { + CheckUpdateCore(ECoreType.clash); + }); + CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() => + { + CheckUpdateCore(ECoreType.clash_meta); + }); + CheckUpdateSingBoxCoreCmd = ReactiveCommand.Create(() => + { + CheckUpdateCore(ECoreType.sing_box); + }); + CheckUpdateGeoCmd = ReactiveCommand.Create(() => + { + CheckUpdateGeo(); + }); - Locator.CurrentMutable.RegisterLazySingleton(() => new NoticeHandler(snackbarMessageQueue), typeof(NoticeHandler)); - _noticeHandler = Locator.Current.GetService(); - _config = LazyConfig.Instance.GetConfig(); - //ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false); - Init(); + ReloadCmd = ReactiveCommand.Create(() => + { + Reload(); + }); - SelectedProfile = new(); - SelectedSub = new(); - SelectedMoveToGroup = new(); - SelectedRouting = new(); - SelectedServer = new(); - if (_config.tunModeItem.enableTun && Utils.IsAdministrator()) - { - EnableTun = true; - } - _subId = _config.subIndexId; + NotifyLeftClickCmd = ReactiveCommand.Create(() => + { + ShowHideWindow(null); + }); - InitSubscriptionView(); - RefreshRoutingsMenu(); + //System proxy + SystemProxyClearCmd = ReactiveCommand.Create(() => + { + SetListenerType(ESysProxyType.ForcedClear); + }); + SystemProxySetCmd = ReactiveCommand.Create(() => + { + SetListenerType(ESysProxyType.ForcedChange); + }); + SystemProxyNothingCmd = ReactiveCommand.Create(() => + { + SetListenerType(ESysProxyType.Unchanged); + }); + SystemProxyPacCmd = ReactiveCommand.Create(() => + { + SetListenerType(ESysProxyType.Pac); + }); + + Global.ShowInTaskbar = true; + } + + private void Init() + { + ConfigHandler.InitBuiltinRouting(ref _config); + ConfigHandler.InitBuiltinDNS(_config); + _coreHandler = new CoreHandler(_config, UpdateHandler); + Locator.CurrentMutable.RegisterLazySingleton(() => _coreHandler, typeof(CoreHandler)); + + if (_config.guiItem.enableStatistics) + { + _statistics = new StatisticsHandler(_config, UpdateStatisticsHandler); + } + + MainFormHandler.Instance.UpdateTask(_config, UpdateTaskHandler); + MainFormHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, UpdateTaskHandler); + + Reload(); + ChangeSystemProxyStatus(_config.sysProxyType, true); + } + + private void OnProgramStarted(object state, bool timeout) + { + Application.Current.Dispatcher.Invoke((Action)(() => + { + ShowHideWindow(true); + })); + } + + #endregion Init + + #region Actions + + private void UpdateHandler(bool notify, string msg) + { + _noticeHandler?.SendMessage(msg); + } + + private void UpdateTaskHandler(bool success, string msg) + { + _noticeHandler?.SendMessage(msg); + if (success) + { + var indexIdOld = _config.indexId; RefreshServers(); - - var canEditRemove = this.WhenAnyValue( - x => x.SelectedProfile, - selectedSource => selectedSource != null && !selectedSource.indexId.IsNullOrEmpty()); - - this.WhenAnyValue( - x => x.SelectedSub, - y => y != null && !y.remarks.IsNullOrEmpty() && _subId != y.id) - .Subscribe(c => SubSelectedChanged(c)); - this.WhenAnyValue( - x => x.SelectedMoveToGroup, - y => y != null && !y.remarks.IsNullOrEmpty()) - .Subscribe(c => MoveToGroup(c)); - - this.WhenAnyValue( - x => x.SelectedRouting, - y => y != null && !y.remarks.IsNullOrEmpty()) - .Subscribe(c => RoutingSelectedChanged(c)); - - this.WhenAnyValue( - x => x.SelectedServer, - y => y != null && !y.Text.IsNullOrEmpty()) - .Subscribe(c => ServerSelectedChanged(c)); - - this.WhenAnyValue( - x => x.ServerFilter, - y => y != null && _serverFilter != y) - .Subscribe(c => ServerFilterChanged(c)); - - SystemProxySelected = (int)_config.sysProxyType; - this.WhenAnyValue( - x => x.SystemProxySelected, - y => y >= 0) - .Subscribe(c => DoSystemProxySelected(c)); - - this.WhenAnyValue( - x => x.EnableTun, - y => y == true) - .Subscribe(c => DoEnableTun(c)); - - BindingUI(); - RestoreUI(); - AutoHideStartup(); - - //servers - AddVmessServerCmd = ReactiveCommand.Create(() => - { - EditServer(true, EConfigType.VMess); - }); - AddVlessServerCmd = ReactiveCommand.Create(() => - { - EditServer(true, EConfigType.VLESS); - }); - AddShadowsocksServerCmd = ReactiveCommand.Create(() => - { - EditServer(true, EConfigType.Shadowsocks); - }); - AddSocksServerCmd = ReactiveCommand.Create(() => - { - EditServer(true, EConfigType.Socks); - }); - AddTrojanServerCmd = ReactiveCommand.Create(() => - { - EditServer(true, EConfigType.Trojan); - }); - AddCustomServerCmd = ReactiveCommand.Create(() => - { - EditServer(true, EConfigType.Custom); - }); - AddServerViaClipboardCmd = ReactiveCommand.Create(() => - { - AddServerViaClipboard(); - }); - AddServerViaScanCmd = ReactiveCommand.CreateFromTask(() => - { - return ScanScreenTaskAsync(); - }); - //servers delete - EditServerCmd = ReactiveCommand.Create(() => - { - EditServer(false, EConfigType.Custom); - }, canEditRemove); - RemoveServerCmd = ReactiveCommand.Create(() => - { - RemoveServer(); - }, canEditRemove); - RemoveDuplicateServerCmd = ReactiveCommand.Create(() => - { - RemoveDuplicateServer(); - }); - CopyServerCmd = ReactiveCommand.Create(() => - { - CopyServer(); - }, canEditRemove); - SetDefaultServerCmd = ReactiveCommand.Create(() => - { - SetDefaultServer(); - }, canEditRemove); - ShareServerCmd = ReactiveCommand.Create(() => - { - ShareServer(); - }, canEditRemove); - //servers move - MoveTopCmd = ReactiveCommand.Create(() => - { - MoveServer(EMove.Top); - }, canEditRemove); - MoveUpCmd = ReactiveCommand.Create(() => - { - MoveServer(EMove.Up); - }, canEditRemove); - MoveDownCmd = ReactiveCommand.Create(() => - { - MoveServer(EMove.Down); - }, canEditRemove); - MoveBottomCmd = ReactiveCommand.Create(() => - { - MoveServer(EMove.Bottom); - }, canEditRemove); - - //servers ping - MixedTestServerCmd = ReactiveCommand.Create(() => - { - ServerSpeedtest(ESpeedActionType.Mixedtest); - }); - PingServerCmd = ReactiveCommand.Create(() => - { - ServerSpeedtest(ESpeedActionType.Ping); - }, canEditRemove); - TcpingServerCmd = ReactiveCommand.Create(() => - { - ServerSpeedtest(ESpeedActionType.Tcping); - }, canEditRemove); - RealPingServerCmd = ReactiveCommand.Create(() => - { - ServerSpeedtest(ESpeedActionType.Realping); - }, canEditRemove); - SpeedServerCmd = ReactiveCommand.Create(() => - { - ServerSpeedtest(ESpeedActionType.Speedtest); - }, canEditRemove); - SortServerResultCmd = ReactiveCommand.Create(() => - { - SortServer(EServerColName.delayVal.ToString()); - }); - //servers export - Export2ClientConfigCmd = ReactiveCommand.Create(() => - { - Export2ClientConfig(); - }, canEditRemove); - Export2ShareUrlCmd = ReactiveCommand.Create(() => - { - Export2ShareUrl(); - }, canEditRemove); - Export2SubContentCmd = ReactiveCommand.Create(() => - { - Export2SubContent(); - }, canEditRemove); - - //Subscription - SubSettingCmd = ReactiveCommand.Create(() => - { - SubSetting(); - }); - AddSubCmd = ReactiveCommand.Create(() => - { - AddSub(); - }); - SubUpdateCmd = ReactiveCommand.Create(() => - { - UpdateSubscriptionProcess("", false); - }); - SubUpdateViaProxyCmd = ReactiveCommand.Create(() => - { - UpdateSubscriptionProcess("", true); - }); - SubGroupUpdateCmd = ReactiveCommand.Create(() => - { - UpdateSubscriptionProcess(_subId, false); - }); - SubGroupUpdateViaProxyCmd = ReactiveCommand.Create(() => - { - UpdateSubscriptionProcess(_subId, true); - }); - - //Setting - OptionSettingCmd = ReactiveCommand.Create(() => - { - OptionSetting(); - }); - RoutingSettingCmd = ReactiveCommand.Create(() => - { - RoutingSetting(); - }); - DNSSettingCmd = ReactiveCommand.Create(() => - { - DNSSetting(); - }); - GlobalHotkeySettingCmd = ReactiveCommand.Create(() => - { - if ((new GlobalHotkeySettingWindow()).ShowDialog() == true) - { - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - } - }); - RebootAsAdminCmd = ReactiveCommand.Create(() => - { - RebootAsAdmin(); - }); - ClearServerStatisticsCmd = ReactiveCommand.Create(() => - { - _statistics?.ClearAllServerStatistics(); - RefreshServers(); - }); - ImportOldGuiConfigCmd = ReactiveCommand.Create(() => - { - ImportOldGuiConfig(); - }); - - //CheckUpdate - CheckUpdateNCmd = ReactiveCommand.Create(() => - { - CheckUpdateN(); - }); - CheckUpdateV2flyCoreCmd = ReactiveCommand.Create(() => - { - CheckUpdateCore(ECoreType.v2fly_v5); - }); - CheckUpdateSagerNetCoreCmd = ReactiveCommand.Create(() => - { - CheckUpdateCore(ECoreType.SagerNet); - }); - CheckUpdateXrayCoreCmd = ReactiveCommand.Create(() => - { - CheckUpdateCore(ECoreType.Xray); - }); - CheckUpdateClashCoreCmd = ReactiveCommand.Create(() => - { - CheckUpdateCore(ECoreType.clash); - }); - CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() => - { - CheckUpdateCore(ECoreType.clash_meta); - }); - CheckUpdateSingBoxCoreCmd = ReactiveCommand.Create(() => - { - CheckUpdateCore(ECoreType.sing_box); - }); - CheckUpdateGeoCmd = ReactiveCommand.Create(() => - { - CheckUpdateGeo(); - }); - - ReloadCmd = ReactiveCommand.Create(() => + if (indexIdOld != _config.indexId) { Reload(); - }); - - NotifyLeftClickCmd = ReactiveCommand.Create(() => - { - ShowHideWindow(null); - }); - - //System proxy - SystemProxyClearCmd = ReactiveCommand.Create(() => - { - SetListenerType(ESysProxyType.ForcedClear); - }); - SystemProxySetCmd = ReactiveCommand.Create(() => - { - SetListenerType(ESysProxyType.ForcedChange); - }); - SystemProxyNothingCmd = ReactiveCommand.Create(() => - { - SetListenerType(ESysProxyType.Unchanged); - }); - SystemProxyPacCmd = ReactiveCommand.Create(() => - { - SetListenerType(ESysProxyType.Pac); - }); - - Global.ShowInTaskbar = true; - } - - private void Init() - { - ConfigHandler.InitBuiltinRouting(ref _config); - ConfigHandler.InitBuiltinDNS(_config); - _coreHandler = new CoreHandler(_config, UpdateHandler); - Locator.CurrentMutable.RegisterLazySingleton(() => _coreHandler, typeof(CoreHandler)); - - if (_config.guiItem.enableStatistics) - { - _statistics = new StatisticsHandler(_config, UpdateStatisticsHandler); } - - MainFormHandler.Instance.UpdateTask(_config, UpdateTaskHandler); - MainFormHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, UpdateTaskHandler); - - Reload(); - ChangeSystemProxyStatus(_config.sysProxyType, true); + if (_config.uiItem.enableAutoAdjustMainLvColWidth) + { + _updateView(EViewAction.AdjustMainLvColWidth); + } } + } - private void OnProgramStarted(object state, bool timeout) + private void UpdateStatisticsHandler(ServerSpeedItem update) + { + try { Application.Current.Dispatcher.Invoke((Action)(() => { - ShowHideWindow(true); - })); - } - - #endregion Init - - #region Actions - - private void UpdateHandler(bool notify, string msg) - { - _noticeHandler?.SendMessage(msg); - } - - private void UpdateTaskHandler(bool success, string msg) - { - _noticeHandler?.SendMessage(msg); - if (success) - { - var indexIdOld = _config.indexId; - RefreshServers(); - if (indexIdOld != _config.indexId) + if (!Global.ShowInTaskbar) { - Reload(); + return; } - if (_config.uiItem.enableAutoAdjustMainLvColWidth) + + SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.agentTag, Utils.HumanFy(update.proxyUp), Utils.HumanFy(update.proxyDown)); + SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.directTag, Utils.HumanFy(update.directUp), Utils.HumanFy(update.directDown)); + + if (update.proxyUp + update.proxyDown > 0) { - _updateView(EViewAction.AdjustMainLvColWidth); - } - } - } - - private void UpdateStatisticsHandler(ServerSpeedItem update) - { - try - { - Application.Current.Dispatcher.Invoke((Action)(() => - { - if (!Global.ShowInTaskbar) + var second = DateTime.Now.Second; + if (second % 3 == 0) { - return; - } - - SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.agentTag, Utils.HumanFy(update.proxyUp), Utils.HumanFy(update.proxyDown)); - SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.directTag, Utils.HumanFy(update.directUp), Utils.HumanFy(update.directDown)); - - if (update.proxyUp + update.proxyDown > 0) - { - var second = DateTime.Now.Second; - if (second % 3 == 0) + var item = _profileItems.Where(it => it.indexId == update.indexId).FirstOrDefault(); + if (item != null) { - var item = _profileItems.Where(it => it.indexId == update.indexId).FirstOrDefault(); - if (item != null) - { - item.todayDown = Utils.HumanFy(update.todayDown); - item.todayUp = Utils.HumanFy(update.todayUp); - item.totalDown = Utils.HumanFy(update.totalDown); - item.totalUp = Utils.HumanFy(update.totalUp); + item.todayDown = Utils.HumanFy(update.todayDown); + item.todayUp = Utils.HumanFy(update.todayUp); + item.totalDown = Utils.HumanFy(update.totalDown); + item.totalUp = Utils.HumanFy(update.totalUp); - if (SelectedProfile?.indexId == item.indexId) - { - var temp = Utils.DeepCopy(item); - _profileItems.Replace(item, temp); - SelectedProfile = temp; - } - else - { - _profileItems.Replace(item, Utils.DeepCopy(item)); - } + if (SelectedProfile?.indexId == item.indexId) + { + var temp = Utils.DeepCopy(item); + _profileItems.Replace(item, temp); + SelectedProfile = temp; + } + else + { + _profileItems.Replace(item, Utils.DeepCopy(item)); } } } - })); - } - catch (Exception ex) - { - Utils.SaveLog(ex.Message, ex); - } - } - - private void UpdateSpeedtestHandler(string indexId, string delay, string speed) - { - Application.Current.Dispatcher.Invoke((Action)(() => - { - SetTestResult(indexId, delay, speed); - })); - } - - private void SetTestResult(string indexId, string delay, string speed) - { - if (Utils.IsNullOrEmpty(indexId)) - { - _noticeHandler?.SendMessage(delay, true); - _noticeHandler?.Enqueue(delay); - return; - } - var item = _profileItems.Where(it => it.indexId == indexId).FirstOrDefault(); - if (item != null) - { - if (!Utils.IsNullOrEmpty(delay)) - { - int.TryParse(delay, out int temp); - item.delay = temp; - item.delayVal = $"{delay} {Global.DelayUnit}"; - } - if (!Utils.IsNullOrEmpty(speed)) - { - item.speedVal = $"{speed} {Global.SpeedUnit}"; - } - _profileItems.Replace(item, Utils.DeepCopy(item)); - } - } - - private void OnHotkeyHandler(EGlobalHotkey e) - { - switch (e) - { - case EGlobalHotkey.ShowForm: - ShowHideWindow(null); - break; - - case EGlobalHotkey.SystemProxyClear: - SetListenerType(ESysProxyType.ForcedClear); - break; - - case EGlobalHotkey.SystemProxySet: - SetListenerType(ESysProxyType.ForcedChange); - break; - - case EGlobalHotkey.SystemProxyUnchanged: - SetListenerType(ESysProxyType.Unchanged); - break; - - case EGlobalHotkey.SystemProxyPac: - SetListenerType(ESysProxyType.Pac); - break; - } - } - - public void MyAppExit(bool blWindowsShutDown) - { - try - { - Utils.SaveLog("MyAppExit Begin"); - - StorageUI(); - ConfigHandler.SaveConfig(ref _config); - - //HttpProxyHandle.CloseHttpAgent(config); - if (blWindowsShutDown) - { - SysProxyHandle.ResetIEProxy4WindowsShutDown(); - } - else - { - SysProxyHandle.UpdateSysProxy(_config, true); - } - - ProfileExHandler.Instance.SaveTo(); - - _statistics?.SaveTo(); - _statistics?.Close(); - - _coreHandler.CoreStop(); - Utils.SaveLog("MyAppExit End"); - } - catch { } - finally - { - Application.Current.Shutdown(); - Environment.Exit(0); - } - } - - #endregion Actions - - #region Servers && Groups - - private void SubSelectedChanged(bool c) - { - if (!c) - { - return; - } - _subId = SelectedSub?.id; - _config.subIndexId = _subId; - - RefreshServers(); - - _updateView(EViewAction.ProfilesFocus); - } - - private void ServerFilterChanged(bool c) - { - if (!c) - { - return; - } - _serverFilter = ServerFilter; - if (Utils.IsNullOrEmpty(_serverFilter)) - { - RefreshServers(); - } - } - - public void RefreshServers() - { - List lstModel = LazyConfig.Instance.ProfileItems(_subId, _serverFilter); - - ConfigHandler.SetDefaultServer(_config, lstModel); - - List lstServerStat = new(); - if (_statistics != null && _statistics.Enable) - { - lstServerStat = _statistics.ServerStat; - } - var lstProfileExs = ProfileExHandler.Instance.ProfileExs; - lstModel = (from t in lstModel - join t2 in lstServerStat on t.indexId equals t2.indexId into t2b - from t22 in t2b.DefaultIfEmpty() - join t3 in lstProfileExs on t.indexId equals t3.indexId into t3b - from t33 in t3b.DefaultIfEmpty() - select new ProfileItemModel - { - indexId = t.indexId, - configType = t.configType, - remarks = t.remarks, - address = t.address, - port = t.port, - security = t.security, - network = t.network, - streamSecurity = t.streamSecurity, - subid = t.subid, - subRemarks = t.subRemarks, - isActive = t.indexId == _config.indexId, - sort = t33 == null ? 0 : t33.sort, - delay = t33 == null ? 0 : t33.delay, - delayVal = t33?.delay != 0 ? $"{t33?.delay} {Global.DelayUnit}" : string.Empty, - speedVal = t33?.speed != 0 ? $"{t33?.speed} {Global.SpeedUnit}" : string.Empty, - todayDown = t22 == null ? "" : Utils.HumanFy(t22.todayDown), - todayUp = t22 == null ? "" : Utils.HumanFy(t22.todayUp), - totalDown = t22 == null ? "" : Utils.HumanFy(t22.totalDown), - totalUp = t22 == null ? "" : Utils.HumanFy(t22.totalUp) - }).OrderBy(t => t.sort).ToList(); - _lstProfile = Utils.FromJson>(Utils.ToJson(lstModel)); - - Application.Current.Dispatcher.Invoke((Action)(() => - { - _profileItems.Clear(); - _profileItems.AddRange(lstModel); - if (lstModel.Count > 0) - { - var selected = lstModel.FirstOrDefault(t => t.indexId == _config.indexId); - if (selected != null) - { - SelectedProfile = selected; - } - else - { - SelectedProfile = lstModel[0]; - } - } - - RefreshServersMenu(); - - //display running server - var running = ConfigHandler.GetDefaultServer(ref _config); - if (running != null) - { - var runningSummary = running.GetSummary(); - RunningServerDisplay = $"{ResUI.menuServers}:{runningSummary}"; - //RunningServerToolTipText = runningSummary; - } - else - { - RunningServerDisplay = ResUI.CheckServerSettings; - //RunningServerToolTipText = ResUI.CheckServerSettings; } })); } - - private void RefreshServersMenu() + catch (Exception ex) { - _servers.Clear(); - if (_lstProfile.Count > _config.guiItem.trayMenuServersLimit) - { - BlServers = false; - return; - } - - BlServers = true; - for (int k = 0; k < _lstProfile.Count; k++) - { - ProfileItem it = _lstProfile[k]; - string name = it.GetSummary(); - - var item = new ComboItem() { ID = it.indexId, Text = name }; - _servers.Add(item); - if (_config.indexId == it.indexId) - { - SelectedServer = item; - } - } + Utils.SaveLog(ex.Message, ex); } + } - private void InitSubscriptionView() + private void UpdateSpeedtestHandler(string indexId, string delay, string speed) + { + Application.Current.Dispatcher.Invoke((Action)(() => { - _subItems.Clear(); + SetTestResult(indexId, delay, speed); + })); + } - _subItems.Add(new SubItem { remarks = ResUI.AllGroupServers }); - foreach (var item in LazyConfig.Instance.SubItems().OrderBy(t => t.sort)) + private void SetTestResult(string indexId, string delay, string speed) + { + if (Utils.IsNullOrEmpty(indexId)) + { + _noticeHandler?.SendMessage(delay, true); + _noticeHandler?.Enqueue(delay); + return; + } + var item = _profileItems.Where(it => it.indexId == indexId).FirstOrDefault(); + if (item != null) + { + if (!Utils.IsNullOrEmpty(delay)) { - _subItems.Add(item); + int.TryParse(delay, out int temp); + item.delay = temp; + item.delayVal = $"{delay} {Global.DelayUnit}"; } - if (_subId != null && _subItems.FirstOrDefault(t => t.id == _subId) != null) + if (!Utils.IsNullOrEmpty(speed)) { - SelectedSub = _subItems.FirstOrDefault(t => t.id == _subId); + item.speedVal = $"{speed} {Global.SpeedUnit}"; + } + _profileItems.Replace(item, Utils.DeepCopy(item)); + } + } + + private void OnHotkeyHandler(EGlobalHotkey e) + { + switch (e) + { + case EGlobalHotkey.ShowForm: + ShowHideWindow(null); + break; + + case EGlobalHotkey.SystemProxyClear: + SetListenerType(ESysProxyType.ForcedClear); + break; + + case EGlobalHotkey.SystemProxySet: + SetListenerType(ESysProxyType.ForcedChange); + break; + + case EGlobalHotkey.SystemProxyUnchanged: + SetListenerType(ESysProxyType.Unchanged); + break; + + case EGlobalHotkey.SystemProxyPac: + SetListenerType(ESysProxyType.Pac); + break; + } + } + + public void MyAppExit(bool blWindowsShutDown) + { + try + { + Utils.SaveLog("MyAppExit Begin"); + + StorageUI(); + ConfigHandler.SaveConfig(ref _config); + + //HttpProxyHandle.CloseHttpAgent(config); + if (blWindowsShutDown) + { + SysProxyHandle.ResetIEProxy4WindowsShutDown(); } else { - SelectedSub = _subItems[0]; + SysProxyHandle.UpdateSysProxy(_config, true); } + + ProfileExHandler.Instance.SaveTo(); + + _statistics?.SaveTo(); + _statistics?.Close(); + + _coreHandler.CoreStop(); + Utils.SaveLog("MyAppExit End"); } - - #endregion Servers && Groups - - #region Add Servers - - private int GetProfileItems(out List lstSelecteds, bool latest) + catch { } + finally { - lstSelecteds = new List(); - if (SelectedProfiles == null || SelectedProfiles.Count <= 0) - { - return -1; - } + Application.Current.Shutdown(); + Environment.Exit(0); + } + } - var orderProfiles = SelectedProfiles?.OrderBy(t => t.sort); - if (latest) - { - foreach (var profile in orderProfiles) - { - var item = LazyConfig.Instance.GetProfileItem(profile.indexId); - if (item is not null) + #endregion Actions + + #region Servers && Groups + + private void SubSelectedChanged(bool c) + { + if (!c) + { + return; + } + _subId = SelectedSub?.id; + _config.subIndexId = _subId; + + RefreshServers(); + + _updateView(EViewAction.ProfilesFocus); + } + + private void ServerFilterChanged(bool c) + { + if (!c) + { + return; + } + _serverFilter = ServerFilter; + if (Utils.IsNullOrEmpty(_serverFilter)) + { + RefreshServers(); + } + } + + public void RefreshServers() + { + List lstModel = LazyConfig.Instance.ProfileItems(_subId, _serverFilter); + + ConfigHandler.SetDefaultServer(_config, lstModel); + + List lstServerStat = new(); + if (_statistics != null && _statistics.Enable) + { + lstServerStat = _statistics.ServerStat; + } + var lstProfileExs = ProfileExHandler.Instance.ProfileExs; + lstModel = (from t in lstModel + join t2 in lstServerStat on t.indexId equals t2.indexId into t2b + from t22 in t2b.DefaultIfEmpty() + join t3 in lstProfileExs on t.indexId equals t3.indexId into t3b + from t33 in t3b.DefaultIfEmpty() + select new ProfileItemModel { - lstSelecteds.Add(item); - } + indexId = t.indexId, + configType = t.configType, + remarks = t.remarks, + address = t.address, + port = t.port, + security = t.security, + network = t.network, + streamSecurity = t.streamSecurity, + subid = t.subid, + subRemarks = t.subRemarks, + isActive = t.indexId == _config.indexId, + sort = t33 == null ? 0 : t33.sort, + delay = t33 == null ? 0 : t33.delay, + delayVal = t33?.delay != 0 ? $"{t33?.delay} {Global.DelayUnit}" : string.Empty, + speedVal = t33?.speed != 0 ? $"{t33?.speed} {Global.SpeedUnit}" : string.Empty, + todayDown = t22 == null ? "" : Utils.HumanFy(t22.todayDown), + todayUp = t22 == null ? "" : Utils.HumanFy(t22.todayUp), + totalDown = t22 == null ? "" : Utils.HumanFy(t22.totalDown), + totalUp = t22 == null ? "" : Utils.HumanFy(t22.totalUp) + }).OrderBy(t => t.sort).ToList(); + _lstProfile = Utils.FromJson>(Utils.ToJson(lstModel)); + + Application.Current.Dispatcher.Invoke((Action)(() => + { + _profileItems.Clear(); + _profileItems.AddRange(lstModel); + if (lstModel.Count > 0) + { + var selected = lstModel.FirstOrDefault(t => t.indexId == _config.indexId); + if (selected != null) + { + SelectedProfile = selected; } + else + { + SelectedProfile = lstModel[0]; + } + } + + RefreshServersMenu(); + + //display running server + var running = ConfigHandler.GetDefaultServer(ref _config); + if (running != null) + { + var runningSummary = running.GetSummary(); + RunningServerDisplay = $"{ResUI.menuServers}:{runningSummary}"; + //RunningServerToolTipText = runningSummary; } else { - lstSelecteds = Utils.FromJson>(Utils.ToJson(orderProfiles)); + RunningServerDisplay = ResUI.CheckServerSettings; + //RunningServerToolTipText = ResUI.CheckServerSettings; } + })); + } - return 0; + private void RefreshServersMenu() + { + _servers.Clear(); + if (_lstProfile.Count > _config.guiItem.trayMenuServersLimit) + { + BlServers = false; + return; } - public void EditServer(bool blNew, EConfigType eConfigType) + BlServers = true; + for (int k = 0; k < _lstProfile.Count; k++) { - ProfileItem item; - if (blNew) + ProfileItem it = _lstProfile[k]; + string name = it.GetSummary(); + + var item = new ComboItem() { ID = it.indexId, Text = name }; + _servers.Add(item); + if (_config.indexId == it.indexId) { - item = new() - { - subid = _subId, - configType = eConfigType, - isSub = false, - }; + SelectedServer = item; } - else + } + } + + private void InitSubscriptionView() + { + _subItems.Clear(); + + _subItems.Add(new SubItem { remarks = ResUI.AllGroupServers }); + foreach (var item in LazyConfig.Instance.SubItems().OrderBy(t => t.sort)) + { + _subItems.Add(item); + } + if (_subId != null && _subItems.FirstOrDefault(t => t.id == _subId) != null) + { + SelectedSub = _subItems.FirstOrDefault(t => t.id == _subId); + } + else + { + SelectedSub = _subItems[0]; + } + } + + #endregion Servers && Groups + + #region Add Servers + + private int GetProfileItems(out List lstSelecteds, bool latest) + { + lstSelecteds = new List(); + if (SelectedProfiles == null || SelectedProfiles.Count <= 0) + { + return -1; + } + + var orderProfiles = SelectedProfiles?.OrderBy(t => t.sort); + if (latest) + { + foreach (var profile in orderProfiles) { - if (Utils.IsNullOrEmpty(SelectedProfile?.indexId)) + var item = LazyConfig.Instance.GetProfileItem(profile.indexId); + if (item is not null) { - return; - } - item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId); - if (item is null) - { - _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); - return; - } - eConfigType = item.configType; - } - bool? ret = false; - if (eConfigType == EConfigType.Custom) - { - ret = (new AddServer2Window(item)).ShowDialog(); - } - else - { - ret = (new AddServerWindow(item)).ShowDialog(); - } - if (ret == true) - { - RefreshServers(); - if (item.indexId == _config.indexId) - { - Reload(); + lstSelecteds.Add(item); } } } - - public void AddServerViaClipboard() + else { - var clipboardData = Utils.GetClipboardData(); - int ret = ConfigHandler.AddBatchServers(ref _config, clipboardData!, _subId, false); - if (ret > 0) - { - InitSubscriptionView(); - RefreshServers(); - _noticeHandler?.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret)); - } + lstSelecteds = Utils.FromJson>(Utils.ToJson(orderProfiles)); } - public async Task ScanScreenTaskAsync() + return 0; + } + + public void EditServer(bool blNew, EConfigType eConfigType) + { + ProfileItem item; + if (blNew) { - ShowHideWindow(false); - - var dpiXY = Utils.GetDpiXY(Application.Current.MainWindow); - string result = await Task.Run(() => + item = new() { - return Utils.ScanScreen(dpiXY.Item1, dpiXY.Item2); - }); - - ShowHideWindow(true); - - if (Utils.IsNullOrEmpty(result)) - { - _noticeHandler?.Enqueue(ResUI.NoValidQRcodeFound); - } - else - { - int ret = ConfigHandler.AddBatchServers(ref _config, result, _subId, false); - if (ret > 0) - { - InitSubscriptionView(); - RefreshServers(); - _noticeHandler?.Enqueue(ResUI.SuccessfullyImportedServerViaScan); - } - } + subid = _subId, + configType = eConfigType, + isSub = false, + }; } - - public void RemoveServer() - { - if (GetProfileItems(out List lstSelecteds, true) < 0) - { - return; - } - - if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No) - { - return; - } - var exists = lstSelecteds.Exists(t => t.indexId == _config.indexId); - - ConfigHandler.RemoveServer(_config, lstSelecteds); - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - - RefreshServers(); - if (exists) - { - Reload(); - } - } - - private void RemoveDuplicateServer() - { - var tuple = ConfigHandler.DedupServerList(_config, _subId); - RefreshServers(); - Reload(); - _noticeHandler?.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2)); - } - - private void CopyServer() - { - if (GetProfileItems(out List lstSelecteds, false) < 0) - { - return; - } - if (ConfigHandler.CopyServer(ref _config, lstSelecteds) == 0) - { - RefreshServers(); - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - } - } - - public void SetDefaultServer() + else { if (Utils.IsNullOrEmpty(SelectedProfile?.indexId)) { return; } - SetDefaultServer(SelectedProfile.indexId); - } - - private void SetDefaultServer(string indexId) - { - if (Utils.IsNullOrEmpty(indexId)) - { - return; - } - if (indexId == _config.indexId) - { - return; - } - var item = LazyConfig.Instance.GetProfileItem(indexId); + item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId); if (item is null) { _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); return; } - - if (ConfigHandler.SetDefaultServerIndex(ref _config, indexId) == 0) + eConfigType = item.configType; + } + bool? ret = false; + if (eConfigType == EConfigType.Custom) + { + ret = (new AddServer2Window(item)).ShowDialog(); + } + else + { + ret = (new AddServerWindow(item)).ShowDialog(); + } + if (ret == true) + { + RefreshServers(); + if (item.indexId == _config.indexId) { - RefreshServers(); Reload(); } } + } - private void ServerSelectedChanged(bool c) + public void AddServerViaClipboard() + { + var clipboardData = Utils.GetClipboardData(); + int ret = ConfigHandler.AddBatchServers(ref _config, clipboardData!, _subId, false); + if (ret > 0) { - if (!c) + InitSubscriptionView(); + RefreshServers(); + _noticeHandler?.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret)); + } + } + + public async Task ScanScreenTaskAsync() + { + ShowHideWindow(false); + + var dpiXY = Utils.GetDpiXY(Application.Current.MainWindow); + string result = await Task.Run(() => + { + return Utils.ScanScreen(dpiXY.Item1, dpiXY.Item2); + }); + + ShowHideWindow(true); + + if (Utils.IsNullOrEmpty(result)) + { + _noticeHandler?.Enqueue(ResUI.NoValidQRcodeFound); + } + else + { + int ret = ConfigHandler.AddBatchServers(ref _config, result, _subId, false); + if (ret > 0) { - return; + InitSubscriptionView(); + RefreshServers(); + _noticeHandler?.Enqueue(ResUI.SuccessfullyImportedServerViaScan); } - if (SelectedServer == null) - { - return; - } - if (Utils.IsNullOrEmpty(SelectedServer.ID)) - { - return; - } - SetDefaultServer(SelectedServer.ID); + } + } + + public void RemoveServer() + { + if (GetProfileItems(out List lstSelecteds, true) < 0) + { + return; } - public async void ShareServer() + if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No) { - var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId); - if (item is null) + return; + } + var exists = lstSelecteds.Exists(t => t.indexId == _config.indexId); + + ConfigHandler.RemoveServer(_config, lstSelecteds); + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + + RefreshServers(); + if (exists) + { + Reload(); + } + } + + private void RemoveDuplicateServer() + { + var tuple = ConfigHandler.DedupServerList(_config, _subId); + RefreshServers(); + Reload(); + _noticeHandler?.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2)); + } + + private void CopyServer() + { + if (GetProfileItems(out List lstSelecteds, false) < 0) + { + return; + } + if (ConfigHandler.CopyServer(ref _config, lstSelecteds) == 0) + { + RefreshServers(); + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + } + } + + public void SetDefaultServer() + { + if (Utils.IsNullOrEmpty(SelectedProfile?.indexId)) + { + return; + } + SetDefaultServer(SelectedProfile.indexId); + } + + private void SetDefaultServer(string indexId) + { + if (Utils.IsNullOrEmpty(indexId)) + { + return; + } + if (indexId == _config.indexId) + { + return; + } + var item = LazyConfig.Instance.GetProfileItem(indexId); + if (item is null) + { + _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); + return; + } + + if (ConfigHandler.SetDefaultServerIndex(ref _config, indexId) == 0) + { + RefreshServers(); + Reload(); + } + } + + private void ServerSelectedChanged(bool c) + { + if (!c) + { + return; + } + if (SelectedServer == null) + { + return; + } + if (Utils.IsNullOrEmpty(SelectedServer.ID)) + { + return; + } + SetDefaultServer(SelectedServer.ID); + } + + public async void ShareServer() + { + var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId); + if (item is null) + { + _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); + return; + } + string url = ShareHandler.GetShareUrl(item); + if (Utils.IsNullOrEmpty(url)) + { + return; + } + var img = QRCodeHelper.GetQRCode(url); + var dialog = new QrcodeView() + { + imgQrcode = { Source = img }, + txtContent = { Text = url }, + }; + + await DialogHost.Show(dialog, "RootDialog"); + } + + public void SortServer(string colName) + { + if (Utils.IsNullOrEmpty(colName)) + { + return; + } + + _dicHeaderSort.TryAdd(colName, true); + _dicHeaderSort.TryGetValue(colName, out bool asc); + if (ConfigHandler.SortServers(ref _config, _subId, colName, asc) != 0) + { + return; + } + _dicHeaderSort[colName] = !asc; + RefreshServers(); + } + + public void TestServerAvailability() + { + var item = ConfigHandler.GetDefaultServer(ref _config); + if (item == null || item.configType == EConfigType.Custom) + { + return; + } + (new UpdateHandle()).RunAvailabilityCheck((bool success, string msg) => + { + _noticeHandler?.SendMessage(msg, true); + Application.Current.Dispatcher.Invoke((Action)(() => { - _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); - return; + if (!Global.ShowInTaskbar) + { + return; + } + RunningInfoDisplay = msg; + })); + }); + } + + //move server + private void MoveToGroup(bool c) + { + if (!c) + { + return; + } + + if (GetProfileItems(out List lstSelecteds, true) < 0) + { + return; + } + + ConfigHandler.MoveToGroup(_config, lstSelecteds, SelectedMoveToGroup.id); + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + + RefreshServers(); + SelectedMoveToGroup = new(); + //Reload(); + } + + public void MoveServer(EMove eMove) + { + var item = _lstProfile.FirstOrDefault(t => t.indexId == SelectedProfile.indexId); + if (item is null) + { + _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); + return; + } + + int index = _lstProfile.IndexOf(item); + if (index < 0) + { + return; + } + if (ConfigHandler.MoveServer(ref _config, ref _lstProfile, index, eMove) == 0) + { + RefreshServers(); + } + } + + public void MoveServerTo(int startIndex, ProfileItemModel targetItem) + { + var targetIndex = _profileItems.IndexOf(targetItem); + if (startIndex >= 0 && targetIndex >= 0 && startIndex != targetIndex) + { + if (ConfigHandler.MoveServer(ref _config, ref _lstProfile, startIndex, EMove.Position, targetIndex) == 0) + { + RefreshServers(); } - string url = ShareHandler.GetShareUrl(item); + } + } + + public void ServerSpeedtest(ESpeedActionType actionType) + { + if (actionType == ESpeedActionType.Mixedtest) + { + SelectedProfiles = _profileItems; + } + if (GetProfileItems(out List lstSelecteds, false) < 0) + { + return; + } + //ClearTestResult(); + new SpeedtestHandler(_config, _coreHandler, lstSelecteds, actionType, UpdateSpeedtestHandler); + } + + private void Export2ClientConfig() + { + var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId); + if (item is null) + { + _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); + return; + } + MainFormHandler.Instance.Export2ClientConfig(item, _config); + } + + public void Export2ShareUrl() + { + if (GetProfileItems(out List lstSelecteds, true) < 0) + { + return; + } + + StringBuilder sb = new(); + foreach (var it in lstSelecteds) + { + string url = ShareHandler.GetShareUrl(it); if (Utils.IsNullOrEmpty(url)) { - return; + continue; } - var img = QRCodeHelper.GetQRCode(url); - var dialog = new QrcodeView() - { - imgQrcode = { Source = img }, - txtContent = { Text = url }, - }; + sb.Append(url); + sb.AppendLine(); + } + if (sb.Length > 0) + { + Utils.SetClipboardData(sb.ToString()); + _noticeHandler?.SendMessage(ResUI.BatchExportURLSuccessfully); + } + } - await DialogHost.Show(dialog, "RootDialog"); + private void Export2SubContent() + { + if (GetProfileItems(out List lstSelecteds, true) < 0) + { + return; } - public void SortServer(string colName) + StringBuilder sb = new(); + foreach (var it in lstSelecteds) { - if (Utils.IsNullOrEmpty(colName)) + string? url = ShareHandler.GetShareUrl(it); + if (Utils.IsNullOrEmpty(url)) { - return; + continue; } + sb.Append(url); + sb.AppendLine(); + } + if (sb.Length > 0) + { + Utils.SetClipboardData(Utils.Base64Encode(sb.ToString())); + _noticeHandler?.SendMessage(ResUI.BatchExportSubscriptionSuccessfully); + } + } - _dicHeaderSort.TryAdd(colName, true); - _dicHeaderSort.TryGetValue(colName, out bool asc); - if (ConfigHandler.SortServers(ref _config, _subId, colName, asc) != 0) - { - return; - } - _dicHeaderSort[colName] = !asc; + #endregion Add Servers + + #region Subscription + + private void SubSetting() + { + if ((new SubSettingWindow()).ShowDialog() == true) + { + InitSubscriptionView(); + SubSelectedChanged(true); + } + } + + private void AddSub() + { + SubItem item = new(); + var ret = (new SubEditWindow(item)).ShowDialog(); + if (ret == true) + { + InitSubscriptionView(); + SubSelectedChanged(true); + } + } + + private void UpdateSubscriptionProcess(string subId, bool blProxy) + { + (new UpdateHandle()).UpdateSubscriptionProcess(_config, subId, blProxy, UpdateTaskHandler); + } + + #endregion Subscription + + #region Setting + + private void OptionSetting() + { + var ret = (new OptionSettingWindow()).ShowDialog(); + if (ret == true) + { + //RefreshServers(); + Reload(); + } + } + + private void RoutingSetting() + { + var ret = (new RoutingSettingWindow()).ShowDialog(); + if (ret == true) + { + ConfigHandler.InitBuiltinRouting(ref _config); + RefreshRoutingsMenu(); + //RefreshServers(); + Reload(); + } + } + + private void DNSSetting() + { + var ret = (new DNSSettingWindow()).ShowDialog(); + if (ret == true) + { + Reload(); + } + } + + private void RebootAsAdmin() + { + ProcessStartInfo startInfo = new() + { + UseShellExecute = true, + Arguments = Global.RebootAs, + WorkingDirectory = Utils.StartupPath(), + FileName = Utils.GetExePath(), + Verb = "runas", + }; + try + { + Process.Start(startInfo); + MyAppExit(false); + } + catch { } + } + + private void ImportOldGuiConfig() + { + OpenFileDialog fileDialog = new() + { + Multiselect = false, + Filter = "guiNConfig|*.json|All|*.*" + }; + if (fileDialog.ShowDialog() != true) + { + return; + } + string fileName = fileDialog.FileName; + if (Utils.IsNullOrEmpty(fileName)) + { + return; + } + + var ret = ConfigHandler.ImportOldGuiConfig(ref _config, fileName); + if (ret == 0) + { + RefreshRoutingsMenu(); + InitSubscriptionView(); RefreshServers(); + Reload(); + UI.Show(ResUI.OperationSuccess); } - - public void TestServerAvailability() + else { - var item = ConfigHandler.GetDefaultServer(ref _config); - if (item == null || item.configType == EConfigType.Custom) - { - return; - } - (new UpdateHandle()).RunAvailabilityCheck((bool success, string msg) => - { - _noticeHandler?.SendMessage(msg, true); - Application.Current.Dispatcher.Invoke((Action)(() => - { - if (!Global.ShowInTaskbar) - { - return; - } - RunningInfoDisplay = msg; - })); - }); + _noticeHandler.Enqueue(ResUI.OperationFailed); } + } - //move server - private void MoveToGroup(bool c) + #endregion Setting + + #region CheckUpdate + + private void CheckUpdateN() + { + void _updateUI(bool success, string msg) { - if (!c) + _noticeHandler?.SendMessage(msg); + if (success) { - return; - } - - if (GetProfileItems(out List lstSelecteds, true) < 0) - { - return; - } - - ConfigHandler.MoveToGroup(_config, lstSelecteds, SelectedMoveToGroup.id); - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - - RefreshServers(); - SelectedMoveToGroup = new(); - //Reload(); - } - - public void MoveServer(EMove eMove) - { - var item = _lstProfile.FirstOrDefault(t => t.indexId == SelectedProfile.indexId); - if (item is null) - { - _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); - return; - } - - int index = _lstProfile.IndexOf(item); - if (index < 0) - { - return; - } - if (ConfigHandler.MoveServer(ref _config, ref _lstProfile, index, eMove) == 0) - { - RefreshServers(); - } - } - - public void MoveServerTo(int startIndex, ProfileItemModel targetItem) - { - var targetIndex = _profileItems.IndexOf(targetItem); - if (startIndex >= 0 && targetIndex >= 0 && startIndex != targetIndex) - { - if (ConfigHandler.MoveServer(ref _config, ref _lstProfile, startIndex, EMove.Position, targetIndex) == 0) - { - RefreshServers(); - } - } - } - - public void ServerSpeedtest(ESpeedActionType actionType) - { - if (actionType == ESpeedActionType.Mixedtest) - { - SelectedProfiles = _profileItems; - } - if (GetProfileItems(out List lstSelecteds, false) < 0) - { - return; - } - //ClearTestResult(); - new SpeedtestHandler(_config, _coreHandler, lstSelecteds, actionType, UpdateSpeedtestHandler); - } - - private void Export2ClientConfig() - { - var item = LazyConfig.Instance.GetProfileItem(SelectedProfile.indexId); - if (item is null) - { - _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); - return; - } - MainFormHandler.Instance.Export2ClientConfig(item, _config); - } - - public void Export2ShareUrl() - { - if (GetProfileItems(out List lstSelecteds, true) < 0) - { - return; - } - - StringBuilder sb = new(); - foreach (var it in lstSelecteds) - { - string url = ShareHandler.GetShareUrl(it); - if (Utils.IsNullOrEmpty(url)) - { - continue; - } - sb.Append(url); - sb.AppendLine(); - } - if (sb.Length > 0) - { - Utils.SetClipboardData(sb.ToString()); - _noticeHandler?.SendMessage(ResUI.BatchExportURLSuccessfully); - } - } - - private void Export2SubContent() - { - if (GetProfileItems(out List lstSelecteds, true) < 0) - { - return; - } - - StringBuilder sb = new(); - foreach (var it in lstSelecteds) - { - string? url = ShareHandler.GetShareUrl(it); - if (Utils.IsNullOrEmpty(url)) - { - continue; - } - sb.Append(url); - sb.AppendLine(); - } - if (sb.Length > 0) - { - Utils.SetClipboardData(Utils.Base64Encode(sb.ToString())); - _noticeHandler?.SendMessage(ResUI.BatchExportSubscriptionSuccessfully); - } - } - - #endregion Add Servers - - #region Subscription - - private void SubSetting() - { - if ((new SubSettingWindow()).ShowDialog() == true) - { - InitSubscriptionView(); - SubSelectedChanged(true); - } - } - - private void AddSub() - { - SubItem item = new(); - var ret = (new SubEditWindow(item)).ShowDialog(); - if (ret == true) - { - InitSubscriptionView(); - SubSelectedChanged(true); - } - } - - private void UpdateSubscriptionProcess(string subId, bool blProxy) - { - (new UpdateHandle()).UpdateSubscriptionProcess(_config, subId, blProxy, UpdateTaskHandler); - } - - #endregion Subscription - - #region Setting - - private void OptionSetting() - { - var ret = (new OptionSettingWindow()).ShowDialog(); - if (ret == true) - { - //RefreshServers(); - Reload(); - } - } - - private void RoutingSetting() - { - var ret = (new RoutingSettingWindow()).ShowDialog(); - if (ret == true) - { - ConfigHandler.InitBuiltinRouting(ref _config); - RefreshRoutingsMenu(); - //RefreshServers(); - Reload(); - } - } - - private void DNSSetting() - { - var ret = (new DNSSettingWindow()).ShowDialog(); - if (ret == true) - { - Reload(); - } - } - - private void RebootAsAdmin() - { - ProcessStartInfo startInfo = new() - { - UseShellExecute = true, - Arguments = Global.RebootAs, - WorkingDirectory = Utils.StartupPath(), - FileName = Utils.GetExePath(), - Verb = "runas", - }; - try - { - Process.Start(startInfo); MyAppExit(false); } - catch { } } + (new UpdateHandle()).CheckUpdateGuiN(_config, _updateUI, _config.guiItem.checkPreReleaseUpdate); + } - private void ImportOldGuiConfig() + private void CheckUpdateCore(ECoreType type) + { + void _updateUI(bool success, string msg) { - OpenFileDialog fileDialog = new() + _noticeHandler?.SendMessage(msg); + if (success) { - Multiselect = false, - Filter = "guiNConfig|*.json|All|*.*" - }; - if (fileDialog.ShowDialog() != true) - { - return; - } - string fileName = fileDialog.FileName; - if (Utils.IsNullOrEmpty(fileName)) - { - return; - } + CloseV2ray(); + + string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(msg)); + string toPath = Utils.GetBinPath("", type); + + FileManager.ZipExtractToFile(fileName, toPath, _config.guiItem.ignoreGeoUpdateCore ? "geo" : ""); + + _noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfullyMore); - var ret = ConfigHandler.ImportOldGuiConfig(ref _config, fileName); - if (ret == 0) - { - RefreshRoutingsMenu(); - InitSubscriptionView(); - RefreshServers(); Reload(); - UI.Show(ResUI.OperationSuccess); - } - else - { - _noticeHandler.Enqueue(ResUI.OperationFailed); - } - } - #endregion Setting + _noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfully); - #region CheckUpdate - - private void CheckUpdateN() - { - void _updateUI(bool success, string msg) - { - _noticeHandler?.SendMessage(msg); - if (success) + if (File.Exists(fileName)) { - MyAppExit(false); - } - } - (new UpdateHandle()).CheckUpdateGuiN(_config, _updateUI, _config.guiItem.checkPreReleaseUpdate); - } - - private void CheckUpdateCore(ECoreType type) - { - void _updateUI(bool success, string msg) - { - _noticeHandler?.SendMessage(msg); - if (success) - { - CloseV2ray(); - - string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(msg)); - string toPath = Utils.GetBinPath("", type); - - FileManager.ZipExtractToFile(fileName, toPath, _config.guiItem.ignoreGeoUpdateCore ? "geo" : ""); - - _noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfullyMore); - - Reload(); - - _noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfully); - - if (File.Exists(fileName)) - { - File.Delete(fileName); - } - } - } - (new UpdateHandle()).CheckUpdateCore(type, _config, _updateUI, _config.guiItem.checkPreReleaseUpdate); - } - - private void CheckUpdateGeo() - { - (new UpdateHandle()).UpdateGeoFileAll(_config, UpdateTaskHandler); - } - - #endregion CheckUpdate - - #region v2ray job - - public void Reload() - { - _ = LoadV2ray(); - } - - private async Task LoadV2ray() - { - Application.Current.Dispatcher.Invoke((Action)(() => - { - BlReloadEnabled = false; - })); - - await Task.Run(() => - { - _coreHandler.LoadCore(); - - //ConfigHandler.SaveConfig(ref _config, false); - - ChangeSystemProxyStatus(_config.sysProxyType, false); - }); - - TestServerAvailability(); - - Application.Current.Dispatcher.Invoke((Action)(() => - { - BlReloadEnabled = true; - })); - } - - private void CloseV2ray() - { - ConfigHandler.SaveConfig(ref _config, false); - - ChangeSystemProxyStatus(ESysProxyType.ForcedClear, false); - - _coreHandler.CoreStop(); - } - - #endregion v2ray job - - #region System proxy and Routings - - public void SetListenerType(ESysProxyType type) - { - if (_config.sysProxyType == type) - { - return; - } - _config.sysProxyType = type; - ChangeSystemProxyStatus(type, true); - - SystemProxySelected = (int)_config.sysProxyType; - ConfigHandler.SaveConfig(ref _config, false); - } - - private void ChangeSystemProxyStatus(ESysProxyType type, bool blChange) - { - SysProxyHandle.UpdateSysProxy(_config, _config.tunModeItem.enableTun ? true : false); - _noticeHandler?.SendMessage(ResUI.TipChangeSystemProxy + _config.sysProxyType.ToString(), true); - - Application.Current.Dispatcher.Invoke((Action)(() => - { - BlSystemProxyClear = (type == ESysProxyType.ForcedClear); - BlSystemProxySet = (type == ESysProxyType.ForcedChange); - BlSystemProxyNothing = (type == ESysProxyType.Unchanged); - BlSystemProxyPac = (type == ESysProxyType.Pac); - - InboundDisplayStaus(); - - if (blChange) - { - NotifyIcon = MainFormHandler.Instance.GetNotifyIcon(_config); - AppIcon = MainFormHandler.Instance.GetAppIcon(_config); - } - })); - } - - private void RefreshRoutingsMenu() - { - _routingItems.Clear(); - if (!_config.routingBasicItem.enableRoutingAdvanced) - { - BlRouting = false; - return; - } - - BlRouting = true; - var routings = LazyConfig.Instance.RoutingItems(); - foreach (var item in routings) - { - _routingItems.Add(item); - if (item.id == _config.routingBasicItem.routingIndexId) - { - SelectedRouting = item; + File.Delete(fileName); } } } + (new UpdateHandle()).CheckUpdateCore(type, _config, _updateUI, _config.guiItem.checkPreReleaseUpdate); + } - private void RoutingSelectedChanged(bool c) + private void CheckUpdateGeo() + { + (new UpdateHandle()).UpdateGeoFileAll(_config, UpdateTaskHandler); + } + + #endregion CheckUpdate + + #region v2ray job + + public void Reload() + { + _ = LoadV2ray(); + } + + private async Task LoadV2ray() + { + Application.Current.Dispatcher.Invoke((Action)(() => { - if (!c) - { - return; - } + BlReloadEnabled = false; + })); - if (SelectedRouting == null) - { - return; - } + await Task.Run(() => + { + _coreHandler.LoadCore(); - var item = LazyConfig.Instance.GetRoutingItem(SelectedRouting?.id); - if (item is null) - { - return; - } - if (_config.routingBasicItem.routingIndexId == item.id) - { - return; - } + //ConfigHandler.SaveConfig(ref _config, false); - if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0) + ChangeSystemProxyStatus(_config.sysProxyType, false); + }); + + TestServerAvailability(); + + Application.Current.Dispatcher.Invoke((Action)(() => + { + BlReloadEnabled = true; + })); + } + + private void CloseV2ray() + { + ConfigHandler.SaveConfig(ref _config, false); + + ChangeSystemProxyStatus(ESysProxyType.ForcedClear, false); + + _coreHandler.CoreStop(); + } + + #endregion v2ray job + + #region System proxy and Routings + + public void SetListenerType(ESysProxyType type) + { + if (_config.sysProxyType == type) + { + return; + } + _config.sysProxyType = type; + ChangeSystemProxyStatus(type, true); + + SystemProxySelected = (int)_config.sysProxyType; + ConfigHandler.SaveConfig(ref _config, false); + } + + private void ChangeSystemProxyStatus(ESysProxyType type, bool blChange) + { + SysProxyHandle.UpdateSysProxy(_config, _config.tunModeItem.enableTun ? true : false); + _noticeHandler?.SendMessage(ResUI.TipChangeSystemProxy + _config.sysProxyType.ToString(), true); + + Application.Current.Dispatcher.Invoke((Action)(() => + { + BlSystemProxyClear = (type == ESysProxyType.ForcedClear); + BlSystemProxySet = (type == ESysProxyType.ForcedChange); + BlSystemProxyNothing = (type == ESysProxyType.Unchanged); + BlSystemProxyPac = (type == ESysProxyType.Pac); + + InboundDisplayStaus(); + + if (blChange) { - _noticeHandler?.SendMessage(ResUI.TipChangeRouting, true); - Reload(); + NotifyIcon = MainFormHandler.Instance.GetNotifyIcon(_config); + AppIcon = MainFormHandler.Instance.GetAppIcon(_config); } + })); + } + + private void RefreshRoutingsMenu() + { + _routingItems.Clear(); + if (!_config.routingBasicItem.enableRoutingAdvanced) + { + BlRouting = false; + return; } - private void DoSystemProxySelected(bool c) + BlRouting = true; + var routings = LazyConfig.Instance.RoutingItems(); + foreach (var item in routings) { - if (!c) + _routingItems.Add(item); + if (item.id == _config.routingBasicItem.routingIndexId) { - return; - } - if (_config.sysProxyType == (ESysProxyType)SystemProxySelected) - { - return; - } - SetListenerType((ESysProxyType)SystemProxySelected); - } - - private void DoEnableTun(bool c) - { - if (_config.tunModeItem.enableTun != EnableTun) - { - _config.tunModeItem.enableTun = EnableTun; - Reload(); + SelectedRouting = item; } } + } - #endregion System proxy and Routings - - #region UI - - public void ShowHideWindow(bool? blShow) + private void RoutingSelectedChanged(bool c) + { + if (!c) { - var bl = blShow ?? !Global.ShowInTaskbar; - if (bl) - { - //Application.Current.MainWindow.ShowInTaskbar = true; - Application.Current.MainWindow.Show(); - if (Application.Current.MainWindow.WindowState == WindowState.Minimized) - { - Application.Current.MainWindow.WindowState = WindowState.Normal; - } - Application.Current.MainWindow.Activate(); - Application.Current.MainWindow.Focus(); - } - else - { - Application.Current.MainWindow.Hide(); - //Application.Current.MainWindow.ShowInTaskbar = false; - //IntPtr windowHandle = new WindowInteropHelper(Application.Current.MainWindow).Handle; - //Utils.RegWriteValue(Global.MyRegPath, Utils.WindowHwndKey, Convert.ToString((long)windowHandle)); - } - Global.ShowInTaskbar = bl; + return; } - private void RestoreUI() + if (SelectedRouting == null) { - if (FollowSystemTheme) - { - ModifyTheme(!Utils.IsLightTheme()); - } - else - { - ModifyTheme(_config.uiItem.colorModeDark); - } - - if (!_config.uiItem.colorPrimaryName.IsNullOrEmpty()) - { - var swatch = new SwatchesProvider().Swatches.FirstOrDefault(t => t.Name == _config.uiItem.colorPrimaryName); - if (swatch != null - && swatch.ExemplarHue != null - && swatch.ExemplarHue?.Color != null) - { - ChangePrimaryColor(swatch.ExemplarHue.Color); - } - } + return; } - private void StorageUI() + var item = LazyConfig.Instance.GetRoutingItem(SelectedRouting?.id); + if (item is null) { + return; + } + if (_config.routingBasicItem.routingIndexId == item.id) + { + return; } - private void BindingUI() + if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0) { - ColorModeDark = _config.uiItem.colorModeDark; - FollowSystemTheme = _config.uiItem.followSystemTheme; - _swatches.AddRange(new SwatchesProvider().Swatches); - if (!_config.uiItem.colorPrimaryName.IsNullOrEmpty()) + _noticeHandler?.SendMessage(ResUI.TipChangeRouting, true); + Reload(); + } + } + + private void DoSystemProxySelected(bool c) + { + if (!c) + { + return; + } + if (_config.sysProxyType == (ESysProxyType)SystemProxySelected) + { + return; + } + SetListenerType((ESysProxyType)SystemProxySelected); + } + + private void DoEnableTun(bool c) + { + if (_config.tunModeItem.enableTun != EnableTun) + { + _config.tunModeItem.enableTun = EnableTun; + Reload(); + } + } + + #endregion System proxy and Routings + + #region UI + + public void ShowHideWindow(bool? blShow) + { + var bl = blShow ?? !Global.ShowInTaskbar; + if (bl) + { + //Application.Current.MainWindow.ShowInTaskbar = true; + Application.Current.MainWindow.Show(); + if (Application.Current.MainWindow.WindowState == WindowState.Minimized) { - SelectedSwatch = _swatches.FirstOrDefault(t => t.Name == _config.uiItem.colorPrimaryName); + Application.Current.MainWindow.WindowState = WindowState.Normal; } - CurrentFontSize = _config.uiItem.currentFontSize; - CurrentLanguage = _config.uiItem.currentLanguage; - //BlShowTrayTip = _config.uiItem.showTrayTip; + Application.Current.MainWindow.Activate(); + Application.Current.MainWindow.Focus(); + } + else + { + Application.Current.MainWindow.Hide(); + //Application.Current.MainWindow.ShowInTaskbar = false; + //IntPtr windowHandle = new WindowInteropHelper(Application.Current.MainWindow).Handle; + //Utils.RegWriteValue(Global.MyRegPath, Utils.WindowHwndKey, Convert.ToString((long)windowHandle)); + } + Global.ShowInTaskbar = bl; + } - this.WhenAnyValue( - x => x.ColorModeDark, - y => y == true) - .Subscribe(c => - { - if (_config.uiItem.colorModeDark != ColorModeDark) - { - _config.uiItem.colorModeDark = ColorModeDark; - ModifyTheme(ColorModeDark); - ConfigHandler.SaveConfig(ref _config); - } - }); + private void RestoreUI() + { + if (FollowSystemTheme) + { + ModifyTheme(!Utils.IsLightTheme()); + } + else + { + ModifyTheme(_config.uiItem.colorModeDark); + } - this.WhenAnyValue(x => x.FollowSystemTheme, - y => y == true) - .Subscribe(c => - { - if (_config.uiItem.followSystemTheme != FollowSystemTheme) - { - _config.uiItem.followSystemTheme = FollowSystemTheme; - ConfigHandler.SaveConfig(ref _config); - if (FollowSystemTheme) - { - ModifyTheme(!Utils.IsLightTheme()); - } - } - }); + if (!_config.uiItem.colorPrimaryName.IsNullOrEmpty()) + { + var swatch = new SwatchesProvider().Swatches.FirstOrDefault(t => t.Name == _config.uiItem.colorPrimaryName); + if (swatch != null + && swatch.ExemplarHue != null + && swatch.ExemplarHue?.Color != null) + { + ChangePrimaryColor(swatch.ExemplarHue.Color); + } + } + } - this.WhenAnyValue( - x => x.SelectedSwatch, - y => y != null && !y.Name.IsNullOrEmpty()) - .Subscribe(c => - { - if (SelectedSwatch == null - || SelectedSwatch.Name.IsNullOrEmpty() - || SelectedSwatch.ExemplarHue == null - || SelectedSwatch.ExemplarHue?.Color == null) - { - return; - } - if (_config.uiItem.colorPrimaryName != SelectedSwatch?.Name) - { - _config.uiItem.colorPrimaryName = SelectedSwatch?.Name; - ChangePrimaryColor(SelectedSwatch.ExemplarHue.Color); - ConfigHandler.SaveConfig(ref _config); - } - }); + private void StorageUI() + { + } - this.WhenAnyValue( - x => x.CurrentFontSize, - y => y > 0) + private void BindingUI() + { + ColorModeDark = _config.uiItem.colorModeDark; + FollowSystemTheme = _config.uiItem.followSystemTheme; + _swatches.AddRange(new SwatchesProvider().Swatches); + if (!_config.uiItem.colorPrimaryName.IsNullOrEmpty()) + { + SelectedSwatch = _swatches.FirstOrDefault(t => t.Name == _config.uiItem.colorPrimaryName); + } + CurrentFontSize = _config.uiItem.currentFontSize; + CurrentLanguage = _config.uiItem.currentLanguage; + //BlShowTrayTip = _config.uiItem.showTrayTip; + + this.WhenAnyValue( + x => x.ColorModeDark, + y => y == true) .Subscribe(c => { - if (CurrentFontSize >= Global.MinFontSize) + if (_config.uiItem.colorModeDark != ColorModeDark) { - _config.uiItem.currentFontSize = CurrentFontSize; - double size = (long)CurrentFontSize; - Application.Current.Resources["StdFontSize"] = size; - Application.Current.Resources["StdFontSize1"] = size + 1; - Application.Current.Resources["StdFontSize2"] = size + 2; - Application.Current.Resources["StdFontSizeMsg"] = size - 1; - + _config.uiItem.colorModeDark = ColorModeDark; + ModifyTheme(ColorModeDark); ConfigHandler.SaveConfig(ref _config); } }); - this.WhenAnyValue( - x => x.CurrentLanguage, - y => y != null && !y.IsNullOrEmpty()) + this.WhenAnyValue(x => x.FollowSystemTheme, + y => y == true) .Subscribe(c => { - if (!Utils.IsNullOrEmpty(CurrentLanguage)) + if (_config.uiItem.followSystemTheme != FollowSystemTheme) { - _config.uiItem.currentLanguage = CurrentLanguage; - Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage); + _config.uiItem.followSystemTheme = FollowSystemTheme; ConfigHandler.SaveConfig(ref _config); + if (FollowSystemTheme) + { + ModifyTheme(!Utils.IsLightTheme()); + } } }); - } - public void InboundDisplayStaus() - { - StringBuilder sb = new(); - sb.Append($"[{Global.InboundSocks}:{LazyConfig.Instance.GetLocalPort(Global.InboundSocks)}]"); - sb.Append(" | "); - //if (_config.sysProxyType == ESysProxyType.ForcedChange) - //{ - // sb.Append($"[{Global.InboundHttp}({ResUI.SystemProxy}):{LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}]"); - //} - //else - //{ - sb.Append($"[{Global.InboundHttp}:{LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}]"); - //} - InboundDisplay = $"{ResUI.LabLocal}:{sb}"; + this.WhenAnyValue( + x => x.SelectedSwatch, + y => y != null && !y.Name.IsNullOrEmpty()) + .Subscribe(c => + { + if (SelectedSwatch == null + || SelectedSwatch.Name.IsNullOrEmpty() + || SelectedSwatch.ExemplarHue == null + || SelectedSwatch.ExemplarHue?.Color == null) + { + return; + } + if (_config.uiItem.colorPrimaryName != SelectedSwatch?.Name) + { + _config.uiItem.colorPrimaryName = SelectedSwatch?.Name; + ChangePrimaryColor(SelectedSwatch.ExemplarHue.Color); + ConfigHandler.SaveConfig(ref _config); + } + }); - if (_config.inbound[0].allowLANConn) + this.WhenAnyValue( + x => x.CurrentFontSize, + y => y > 0) + .Subscribe(c => + { + if (CurrentFontSize >= Global.MinFontSize) + { + _config.uiItem.currentFontSize = CurrentFontSize; + double size = (long)CurrentFontSize; + Application.Current.Resources["StdFontSize"] = size; + Application.Current.Resources["StdFontSize1"] = size + 1; + Application.Current.Resources["StdFontSize2"] = size + 2; + Application.Current.Resources["StdFontSizeMsg"] = size - 1; + + ConfigHandler.SaveConfig(ref _config); + } + }); + + this.WhenAnyValue( + x => x.CurrentLanguage, + y => y != null && !y.IsNullOrEmpty()) + .Subscribe(c => { - if (_config.inbound[0].newPort4LAN) + if (!Utils.IsNullOrEmpty(CurrentLanguage)) { - StringBuilder sb2 = new(); - sb2.Append($"[{Global.InboundSocks}:{LazyConfig.Instance.GetLocalPort(Global.InboundSocks2)}]"); - sb2.Append(" | "); - sb2.Append($"[{Global.InboundHttp}:{LazyConfig.Instance.GetLocalPort(Global.InboundHttp2)}]"); - InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}"; - } - else - { - InboundLanDisplay = $"{ResUI.LabLAN}:{sb}"; + _config.uiItem.currentLanguage = CurrentLanguage; + Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage); + ConfigHandler.SaveConfig(ref _config); } + }); + } + + public void InboundDisplayStaus() + { + StringBuilder sb = new(); + sb.Append($"[{Global.InboundSocks}:{LazyConfig.Instance.GetLocalPort(Global.InboundSocks)}]"); + sb.Append(" | "); + //if (_config.sysProxyType == ESysProxyType.ForcedChange) + //{ + // sb.Append($"[{Global.InboundHttp}({ResUI.SystemProxy}):{LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}]"); + //} + //else + //{ + sb.Append($"[{Global.InboundHttp}:{LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}]"); + //} + InboundDisplay = $"{ResUI.LabLocal}:{sb}"; + + if (_config.inbound[0].allowLANConn) + { + if (_config.inbound[0].newPort4LAN) + { + StringBuilder sb2 = new(); + sb2.Append($"[{Global.InboundSocks}:{LazyConfig.Instance.GetLocalPort(Global.InboundSocks2)}]"); + sb2.Append(" | "); + sb2.Append($"[{Global.InboundHttp}:{LazyConfig.Instance.GetLocalPort(Global.InboundHttp2)}]"); + InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}"; } else { - InboundLanDisplay = $"{ResUI.LabLAN}:None"; + InboundLanDisplay = $"{ResUI.LabLAN}:{sb}"; } } - - public void ModifyTheme(bool isDarkTheme) + else { - var theme = _paletteHelper.GetTheme(); - - theme.SetBaseTheme(isDarkTheme ? Theme.Dark : Theme.Light); - _paletteHelper.SetTheme(theme); - - Utils.SetDarkBorder(Application.Current.MainWindow, isDarkTheme); + InboundLanDisplay = $"{ResUI.LabLAN}:None"; } - - public void ChangePrimaryColor(System.Windows.Media.Color color) - { - var theme = _paletteHelper.GetTheme(); - - theme.PrimaryLight = new ColorPair(color.Lighten()); - theme.PrimaryMid = new ColorPair(color); - theme.PrimaryDark = new ColorPair(color.Darken()); - - _paletteHelper.SetTheme(theme); - } - - private void AutoHideStartup() - { - if (_config.uiItem.autoHideStartup) - { - Observable.Range(1, 1) - .Delay(TimeSpan.FromSeconds(2)) - .Subscribe(x => - { - Application.Current.Dispatcher.Invoke(() => - { - ShowHideWindow(false); - }); - }); - } - } - - #endregion UI } + + public void ModifyTheme(bool isDarkTheme) + { + var theme = _paletteHelper.GetTheme(); + + theme.SetBaseTheme(isDarkTheme ? Theme.Dark : Theme.Light); + _paletteHelper.SetTheme(theme); + + Utils.SetDarkBorder(Application.Current.MainWindow, isDarkTheme); + } + + public void ChangePrimaryColor(System.Windows.Media.Color color) + { + var theme = _paletteHelper.GetTheme(); + + theme.PrimaryLight = new ColorPair(color.Lighten()); + theme.PrimaryMid = new ColorPair(color); + theme.PrimaryDark = new ColorPair(color.Darken()); + + _paletteHelper.SetTheme(theme); + } + + private void AutoHideStartup() + { + if (_config.uiItem.autoHideStartup) + { + Observable.Range(1, 1) + .Delay(TimeSpan.FromSeconds(2)) + .Subscribe(x => + { + Application.Current.Dispatcher.Invoke(() => + { + ShowHideWindow(false); + }); + }); + } + } + + #endregion UI } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs b/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs index e64d3aa2..5ec68353 100644 --- a/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs @@ -7,362 +7,361 @@ using v2rayN.Handler; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class OptionSettingViewModel : ReactiveObject { - public class OptionSettingViewModel : ReactiveObject + private static Config _config; + private NoticeHandler? _noticeHandler; + private Window _view; + + #region Core + + [Reactive] public int localPort { get; set; } + [Reactive] public bool udpEnabled { get; set; } + [Reactive] public bool sniffingEnabled { get; set; } + [Reactive] public bool routeOnly { get; set; } + [Reactive] public bool allowLANConn { get; set; } + [Reactive] public bool newPort4LAN { get; set; } + [Reactive] public string user { get; set; } + [Reactive] public string pass { get; set; } + [Reactive] public bool muxEnabled { get; set; } + [Reactive] public bool logEnabled { get; set; } + [Reactive] public string loglevel { get; set; } + [Reactive] public bool defAllowInsecure { get; set; } + [Reactive] public string defFingerprint { get; set; } + [Reactive] public string defUserAgent { get; set; } + [Reactive] public string mux4SboxProtocol { get; set; } + + #endregion Core + + #region Core KCP + + //[Reactive] public int Kcpmtu { get; set; } + //[Reactive] public int Kcptti { get; set; } + //[Reactive] public int KcpuplinkCapacity { get; set; } + //[Reactive] public int KcpdownlinkCapacity { get; set; } + //[Reactive] public int KcpreadBufferSize { get; set; } + //[Reactive] public int KcpwriteBufferSize { get; set; } + //[Reactive] public bool Kcpcongestion { get; set; } + + #endregion Core KCP + + #region UI + + [Reactive] public bool AutoRun { get; set; } + [Reactive] public bool EnableStatistics { get; set; } + [Reactive] public bool KeepOlderDedupl { get; set; } + [Reactive] public bool IgnoreGeoUpdateCore { get; set; } + [Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; } + [Reactive] public bool EnableSecurityProtocolTls13 { get; set; } + [Reactive] public bool AutoHideStartup { get; set; } + [Reactive] public bool EnableCheckPreReleaseUpdate { get; set; } + [Reactive] public bool EnableDragDropSort { get; set; } + [Reactive] public bool DoubleClick2Activate { get; set; } + [Reactive] public int autoUpdateInterval { get; set; } + [Reactive] public int trayMenuServersLimit { get; set; } + [Reactive] public string currentFontFamily { get; set; } + [Reactive] public int SpeedTestTimeout { get; set; } + [Reactive] public string SpeedTestUrl { get; set; } + [Reactive] public bool EnableHWA { get; set; } + [Reactive] public string SubConvertUrl { get; set; } + + #endregion UI + + #region System proxy + + [Reactive] public string systemProxyAdvancedProtocol { get; set; } + [Reactive] public string systemProxyExceptions { get; set; } + + #endregion System proxy + + #region Tun mode + + [Reactive] public bool TunStrictRoute { get; set; } + [Reactive] public string TunStack { get; set; } + [Reactive] public int TunMtu { get; set; } + + #endregion Tun mode + + #region CoreType + + [Reactive] public string CoreType1 { get; set; } + [Reactive] public string CoreType2 { get; set; } + [Reactive] public string CoreType3 { get; set; } + [Reactive] public string CoreType4 { get; set; } + [Reactive] public string CoreType5 { get; set; } + [Reactive] public string CoreType6 { get; set; } + + #endregion CoreType + + public ReactiveCommand SaveCmd { get; } + + public OptionSettingViewModel(Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; - private Window _view; + _config = LazyConfig.Instance.GetConfig(); + _noticeHandler = Locator.Current.GetService(); + _view = view; #region Core - [Reactive] public int localPort { get; set; } - [Reactive] public bool udpEnabled { get; set; } - [Reactive] public bool sniffingEnabled { get; set; } - [Reactive] public bool routeOnly { get; set; } - [Reactive] public bool allowLANConn { get; set; } - [Reactive] public bool newPort4LAN { get; set; } - [Reactive] public string user { get; set; } - [Reactive] public string pass { get; set; } - [Reactive] public bool muxEnabled { get; set; } - [Reactive] public bool logEnabled { get; set; } - [Reactive] public string loglevel { get; set; } - [Reactive] public bool defAllowInsecure { get; set; } - [Reactive] public string defFingerprint { get; set; } - [Reactive] public string defUserAgent { get; set; } - [Reactive] public string mux4SboxProtocol { get; set; } + var inbound = _config.inbound[0]; + localPort = inbound.localPort; + udpEnabled = inbound.udpEnabled; + sniffingEnabled = inbound.sniffingEnabled; + routeOnly = inbound.routeOnly; + allowLANConn = inbound.allowLANConn; + newPort4LAN = inbound.newPort4LAN; + user = inbound.user; + pass = inbound.pass; + muxEnabled = _config.coreBasicItem.muxEnabled; + logEnabled = _config.coreBasicItem.logEnabled; + loglevel = _config.coreBasicItem.loglevel; + defAllowInsecure = _config.coreBasicItem.defAllowInsecure; + defFingerprint = _config.coreBasicItem.defFingerprint; + defUserAgent = _config.coreBasicItem.defUserAgent; + mux4SboxProtocol = _config.mux4Sbox.protocol; #endregion Core #region Core KCP - //[Reactive] public int Kcpmtu { get; set; } - //[Reactive] public int Kcptti { get; set; } - //[Reactive] public int KcpuplinkCapacity { get; set; } - //[Reactive] public int KcpdownlinkCapacity { get; set; } - //[Reactive] public int KcpreadBufferSize { get; set; } - //[Reactive] public int KcpwriteBufferSize { get; set; } - //[Reactive] public bool Kcpcongestion { get; set; } + //Kcpmtu = _config.kcpItem.mtu; + //Kcptti = _config.kcpItem.tti; + //KcpuplinkCapacity = _config.kcpItem.uplinkCapacity; + //KcpdownlinkCapacity = _config.kcpItem.downlinkCapacity; + //KcpreadBufferSize = _config.kcpItem.readBufferSize; + //KcpwriteBufferSize = _config.kcpItem.writeBufferSize; + //Kcpcongestion = _config.kcpItem.congestion; #endregion Core KCP #region UI - [Reactive] public bool AutoRun { get; set; } - [Reactive] public bool EnableStatistics { get; set; } - [Reactive] public bool KeepOlderDedupl { get; set; } - [Reactive] public bool IgnoreGeoUpdateCore { get; set; } - [Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; } - [Reactive] public bool EnableSecurityProtocolTls13 { get; set; } - [Reactive] public bool AutoHideStartup { get; set; } - [Reactive] public bool EnableCheckPreReleaseUpdate { get; set; } - [Reactive] public bool EnableDragDropSort { get; set; } - [Reactive] public bool DoubleClick2Activate { get; set; } - [Reactive] public int autoUpdateInterval { get; set; } - [Reactive] public int trayMenuServersLimit { get; set; } - [Reactive] public string currentFontFamily { get; set; } - [Reactive] public int SpeedTestTimeout { get; set; } - [Reactive] public string SpeedTestUrl { get; set; } - [Reactive] public bool EnableHWA { get; set; } - [Reactive] public string SubConvertUrl { get; set; } + AutoRun = _config.guiItem.autoRun; + EnableStatistics = _config.guiItem.enableStatistics; + KeepOlderDedupl = _config.guiItem.keepOlderDedupl; + IgnoreGeoUpdateCore = _config.guiItem.ignoreGeoUpdateCore; + EnableAutoAdjustMainLvColWidth = _config.uiItem.enableAutoAdjustMainLvColWidth; + EnableSecurityProtocolTls13 = _config.guiItem.enableSecurityProtocolTls13; + AutoHideStartup = _config.uiItem.autoHideStartup; + EnableCheckPreReleaseUpdate = _config.guiItem.checkPreReleaseUpdate; + EnableDragDropSort = _config.uiItem.enableDragDropSort; + DoubleClick2Activate = _config.uiItem.doubleClick2Activate; + autoUpdateInterval = _config.guiItem.autoUpdateInterval; + trayMenuServersLimit = _config.guiItem.trayMenuServersLimit; + currentFontFamily = _config.uiItem.currentFontFamily; + SpeedTestTimeout = _config.speedTestItem.speedTestTimeout; + SpeedTestUrl = _config.speedTestItem.speedTestUrl; + EnableHWA = _config.guiItem.enableHWA; + SubConvertUrl = _config.constItem.subConvertUrl; #endregion UI #region System proxy - [Reactive] public string systemProxyAdvancedProtocol { get; set; } - [Reactive] public string systemProxyExceptions { get; set; } + systemProxyAdvancedProtocol = _config.systemProxyAdvancedProtocol; + systemProxyExceptions = _config.systemProxyExceptions; #endregion System proxy #region Tun mode - [Reactive] public bool TunStrictRoute { get; set; } - [Reactive] public string TunStack { get; set; } - [Reactive] public int TunMtu { get; set; } + TunStrictRoute = _config.tunModeItem.strictRoute; + TunStack = _config.tunModeItem.stack; + TunMtu = _config.tunModeItem.mtu; #endregion Tun mode - #region CoreType + InitCoreType(); - [Reactive] public string CoreType1 { get; set; } - [Reactive] public string CoreType2 { get; set; } - [Reactive] public string CoreType3 { get; set; } - [Reactive] public string CoreType4 { get; set; } - [Reactive] public string CoreType5 { get; set; } - [Reactive] public string CoreType6 { get; set; } - - #endregion CoreType - - public ReactiveCommand SaveCmd { get; } - - public OptionSettingViewModel(Window view) + SaveCmd = ReactiveCommand.Create(() => { - _config = LazyConfig.Instance.GetConfig(); - _noticeHandler = Locator.Current.GetService(); - _view = view; + SaveSetting(); + }); - #region Core + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } - var inbound = _config.inbound[0]; - localPort = inbound.localPort; - udpEnabled = inbound.udpEnabled; - sniffingEnabled = inbound.sniffingEnabled; - routeOnly = inbound.routeOnly; - allowLANConn = inbound.allowLANConn; - newPort4LAN = inbound.newPort4LAN; - user = inbound.user; - pass = inbound.pass; - muxEnabled = _config.coreBasicItem.muxEnabled; - logEnabled = _config.coreBasicItem.logEnabled; - loglevel = _config.coreBasicItem.loglevel; - defAllowInsecure = _config.coreBasicItem.defAllowInsecure; - defFingerprint = _config.coreBasicItem.defFingerprint; - defUserAgent = _config.coreBasicItem.defUserAgent; - mux4SboxProtocol = _config.mux4Sbox.protocol; - - #endregion Core - - #region Core KCP - - //Kcpmtu = _config.kcpItem.mtu; - //Kcptti = _config.kcpItem.tti; - //KcpuplinkCapacity = _config.kcpItem.uplinkCapacity; - //KcpdownlinkCapacity = _config.kcpItem.downlinkCapacity; - //KcpreadBufferSize = _config.kcpItem.readBufferSize; - //KcpwriteBufferSize = _config.kcpItem.writeBufferSize; - //Kcpcongestion = _config.kcpItem.congestion; - - #endregion Core KCP - - #region UI - - AutoRun = _config.guiItem.autoRun; - EnableStatistics = _config.guiItem.enableStatistics; - KeepOlderDedupl = _config.guiItem.keepOlderDedupl; - IgnoreGeoUpdateCore = _config.guiItem.ignoreGeoUpdateCore; - EnableAutoAdjustMainLvColWidth = _config.uiItem.enableAutoAdjustMainLvColWidth; - EnableSecurityProtocolTls13 = _config.guiItem.enableSecurityProtocolTls13; - AutoHideStartup = _config.uiItem.autoHideStartup; - EnableCheckPreReleaseUpdate = _config.guiItem.checkPreReleaseUpdate; - EnableDragDropSort = _config.uiItem.enableDragDropSort; - DoubleClick2Activate = _config.uiItem.doubleClick2Activate; - autoUpdateInterval = _config.guiItem.autoUpdateInterval; - trayMenuServersLimit = _config.guiItem.trayMenuServersLimit; - currentFontFamily = _config.uiItem.currentFontFamily; - SpeedTestTimeout = _config.speedTestItem.speedTestTimeout; - SpeedTestUrl = _config.speedTestItem.speedTestUrl; - EnableHWA = _config.guiItem.enableHWA; - SubConvertUrl = _config.constItem.subConvertUrl; - - #endregion UI - - #region System proxy - - systemProxyAdvancedProtocol = _config.systemProxyAdvancedProtocol; - systemProxyExceptions = _config.systemProxyExceptions; - - #endregion System proxy - - #region Tun mode - - TunStrictRoute = _config.tunModeItem.strictRoute; - TunStack = _config.tunModeItem.stack; - TunMtu = _config.tunModeItem.mtu; - - #endregion Tun mode - - InitCoreType(); - - SaveCmd = ReactiveCommand.Create(() => - { - SaveSetting(); - }); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + private void InitCoreType() + { + if (_config.coreTypeItem == null) + { + _config.coreTypeItem = new List(); } - private void InitCoreType() + foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) { - if (_config.coreTypeItem == null) + if (_config.coreTypeItem.FindIndex(t => t.configType == it) >= 0) { - _config.coreTypeItem = new List(); + continue; } - foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) + _config.coreTypeItem.Add(new CoreTypeItem() { - if (_config.coreTypeItem.FindIndex(t => t.configType == it) >= 0) - { - continue; - } - - _config.coreTypeItem.Add(new CoreTypeItem() - { - configType = it, - coreType = ECoreType.Xray - }); - } - _config.coreTypeItem.ForEach(it => - { - var type = it.coreType.ToString(); - switch ((int)it.configType) - { - case 1: - CoreType1 = type; - break; - - case 2: - CoreType2 = type; - break; - - case 3: - CoreType3 = type; - break; - - case 4: - CoreType4 = type; - break; - - case 5: - CoreType5 = type; - break; - - case 6: - CoreType6 = type; - break; - } + configType = it, + coreType = ECoreType.Xray }); } - - private void SaveSetting() + _config.coreTypeItem.ForEach(it => { - if (Utils.IsNullOrEmpty(localPort.ToString()) || !Utils.IsNumberic(localPort.ToString()) - || localPort <= 0 || localPort >= Global.MaxPort) + var type = it.coreType.ToString(); + switch ((int)it.configType) { - UI.Show(ResUI.FillLocalListeningPort); - return; + case 1: + CoreType1 = type; + break; + + case 2: + CoreType2 = type; + break; + + case 3: + CoreType3 = type; + break; + + case 4: + CoreType4 = type; + break; + + case 5: + CoreType5 = type; + break; + + case 6: + CoreType6 = type; + break; } + }); + } - //if (Utils.IsNullOrEmpty(Kcpmtu.ToString()) || !Utils.IsNumberic(Kcpmtu.ToString()) - // || Utils.IsNullOrEmpty(Kcptti.ToString()) || !Utils.IsNumberic(Kcptti.ToString()) - // || Utils.IsNullOrEmpty(KcpuplinkCapacity.ToString()) || !Utils.IsNumberic(KcpuplinkCapacity.ToString()) - // || Utils.IsNullOrEmpty(KcpdownlinkCapacity.ToString()) || !Utils.IsNumberic(KcpdownlinkCapacity.ToString()) - // || Utils.IsNullOrEmpty(KcpreadBufferSize.ToString()) || !Utils.IsNumberic(KcpreadBufferSize.ToString()) - // || Utils.IsNullOrEmpty(KcpwriteBufferSize.ToString()) || !Utils.IsNumberic(KcpwriteBufferSize.ToString())) - //{ - // UI.Show(ResUI.FillKcpParameters); - // return; - //} - - //Core - _config.inbound[0].localPort = localPort; - _config.inbound[0].udpEnabled = udpEnabled; - _config.inbound[0].sniffingEnabled = sniffingEnabled; - _config.inbound[0].routeOnly = routeOnly; - _config.inbound[0].allowLANConn = allowLANConn; - _config.inbound[0].newPort4LAN = newPort4LAN; - _config.inbound[0].user = user; - _config.inbound[0].pass = pass; - if (_config.inbound.Count > 1) - { - _config.inbound.RemoveAt(1); - } - _config.coreBasicItem.logEnabled = logEnabled; - _config.coreBasicItem.loglevel = loglevel; - _config.coreBasicItem.muxEnabled = muxEnabled; - _config.coreBasicItem.defAllowInsecure = defAllowInsecure; - _config.coreBasicItem.defFingerprint = defFingerprint; - _config.coreBasicItem.defUserAgent = defUserAgent; - _config.mux4Sbox.protocol = mux4SboxProtocol; - - //Kcp - //_config.kcpItem.mtu = Kcpmtu; - //_config.kcpItem.tti = Kcptti; - //_config.kcpItem.uplinkCapacity = KcpuplinkCapacity; - //_config.kcpItem.downlinkCapacity = KcpdownlinkCapacity; - //_config.kcpItem.readBufferSize = KcpreadBufferSize; - //_config.kcpItem.writeBufferSize = KcpwriteBufferSize; - //_config.kcpItem.congestion = Kcpcongestion; - - //UI - Utils.SetAutoRun(AutoRun); - _config.guiItem.autoRun = AutoRun; - _config.guiItem.enableStatistics = EnableStatistics; - _config.guiItem.keepOlderDedupl = KeepOlderDedupl; - _config.guiItem.ignoreGeoUpdateCore = IgnoreGeoUpdateCore; - _config.uiItem.enableAutoAdjustMainLvColWidth = EnableAutoAdjustMainLvColWidth; - _config.guiItem.enableSecurityProtocolTls13 = EnableSecurityProtocolTls13; - _config.uiItem.autoHideStartup = AutoHideStartup; - _config.guiItem.autoUpdateInterval = autoUpdateInterval; - _config.guiItem.checkPreReleaseUpdate = EnableCheckPreReleaseUpdate; - _config.uiItem.enableDragDropSort = EnableDragDropSort; - _config.uiItem.doubleClick2Activate = DoubleClick2Activate; - _config.guiItem.trayMenuServersLimit = trayMenuServersLimit; - _config.uiItem.currentFontFamily = currentFontFamily; - _config.speedTestItem.speedTestTimeout = SpeedTestTimeout; - _config.speedTestItem.speedTestUrl = SpeedTestUrl; - _config.guiItem.enableHWA = EnableHWA; - _config.constItem.subConvertUrl = SubConvertUrl; - - //systemProxy - _config.systemProxyExceptions = systemProxyExceptions; - _config.systemProxyAdvancedProtocol = systemProxyAdvancedProtocol; - - //tun mode - _config.tunModeItem.strictRoute = TunStrictRoute; - _config.tunModeItem.stack = TunStack; - _config.tunModeItem.mtu = TunMtu; - - //coreType - SaveCoreType(); - - if (ConfigHandler.SaveConfig(ref _config) == 0) - { - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - _view.DialogResult = true; - } - else - { - UI.ShowWarning(ResUI.OperationFailed); - } + private void SaveSetting() + { + if (Utils.IsNullOrEmpty(localPort.ToString()) || !Utils.IsNumberic(localPort.ToString()) + || localPort <= 0 || localPort >= Global.MaxPort) + { + UI.Show(ResUI.FillLocalListeningPort); + return; } - private int SaveCoreType() + //if (Utils.IsNullOrEmpty(Kcpmtu.ToString()) || !Utils.IsNumberic(Kcpmtu.ToString()) + // || Utils.IsNullOrEmpty(Kcptti.ToString()) || !Utils.IsNumberic(Kcptti.ToString()) + // || Utils.IsNullOrEmpty(KcpuplinkCapacity.ToString()) || !Utils.IsNumberic(KcpuplinkCapacity.ToString()) + // || Utils.IsNullOrEmpty(KcpdownlinkCapacity.ToString()) || !Utils.IsNumberic(KcpdownlinkCapacity.ToString()) + // || Utils.IsNullOrEmpty(KcpreadBufferSize.ToString()) || !Utils.IsNumberic(KcpreadBufferSize.ToString()) + // || Utils.IsNullOrEmpty(KcpwriteBufferSize.ToString()) || !Utils.IsNumberic(KcpwriteBufferSize.ToString())) + //{ + // UI.Show(ResUI.FillKcpParameters); + // return; + //} + + //Core + _config.inbound[0].localPort = localPort; + _config.inbound[0].udpEnabled = udpEnabled; + _config.inbound[0].sniffingEnabled = sniffingEnabled; + _config.inbound[0].routeOnly = routeOnly; + _config.inbound[0].allowLANConn = allowLANConn; + _config.inbound[0].newPort4LAN = newPort4LAN; + _config.inbound[0].user = user; + _config.inbound[0].pass = pass; + if (_config.inbound.Count > 1) { - for (int k = 1; k <= _config.coreTypeItem.Count; k++) - { - var item = _config.coreTypeItem[k - 1]; - var type = string.Empty; - switch ((int)item.configType) - { - case 1: - type = CoreType1; - break; + _config.inbound.RemoveAt(1); + } + _config.coreBasicItem.logEnabled = logEnabled; + _config.coreBasicItem.loglevel = loglevel; + _config.coreBasicItem.muxEnabled = muxEnabled; + _config.coreBasicItem.defAllowInsecure = defAllowInsecure; + _config.coreBasicItem.defFingerprint = defFingerprint; + _config.coreBasicItem.defUserAgent = defUserAgent; + _config.mux4Sbox.protocol = mux4SboxProtocol; - case 2: - type = CoreType2; - break; + //Kcp + //_config.kcpItem.mtu = Kcpmtu; + //_config.kcpItem.tti = Kcptti; + //_config.kcpItem.uplinkCapacity = KcpuplinkCapacity; + //_config.kcpItem.downlinkCapacity = KcpdownlinkCapacity; + //_config.kcpItem.readBufferSize = KcpreadBufferSize; + //_config.kcpItem.writeBufferSize = KcpwriteBufferSize; + //_config.kcpItem.congestion = Kcpcongestion; - case 3: - type = CoreType3; - break; + //UI + Utils.SetAutoRun(AutoRun); + _config.guiItem.autoRun = AutoRun; + _config.guiItem.enableStatistics = EnableStatistics; + _config.guiItem.keepOlderDedupl = KeepOlderDedupl; + _config.guiItem.ignoreGeoUpdateCore = IgnoreGeoUpdateCore; + _config.uiItem.enableAutoAdjustMainLvColWidth = EnableAutoAdjustMainLvColWidth; + _config.guiItem.enableSecurityProtocolTls13 = EnableSecurityProtocolTls13; + _config.uiItem.autoHideStartup = AutoHideStartup; + _config.guiItem.autoUpdateInterval = autoUpdateInterval; + _config.guiItem.checkPreReleaseUpdate = EnableCheckPreReleaseUpdate; + _config.uiItem.enableDragDropSort = EnableDragDropSort; + _config.uiItem.doubleClick2Activate = DoubleClick2Activate; + _config.guiItem.trayMenuServersLimit = trayMenuServersLimit; + _config.uiItem.currentFontFamily = currentFontFamily; + _config.speedTestItem.speedTestTimeout = SpeedTestTimeout; + _config.speedTestItem.speedTestUrl = SpeedTestUrl; + _config.guiItem.enableHWA = EnableHWA; + _config.constItem.subConvertUrl = SubConvertUrl; - case 4: - type = CoreType4; - break; + //systemProxy + _config.systemProxyExceptions = systemProxyExceptions; + _config.systemProxyAdvancedProtocol = systemProxyAdvancedProtocol; - case 5: - type = CoreType5; - break; + //tun mode + _config.tunModeItem.strictRoute = TunStrictRoute; + _config.tunModeItem.stack = TunStack; + _config.tunModeItem.mtu = TunMtu; - case 6: - type = CoreType6; - break; - } - item.coreType = (ECoreType)Enum.Parse(typeof(ECoreType), type); - } - return 0; + //coreType + SaveCoreType(); + + if (ConfigHandler.SaveConfig(ref _config) == 0) + { + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + _view.DialogResult = true; + } + else + { + UI.ShowWarning(ResUI.OperationFailed); } } + + private int SaveCoreType() + { + for (int k = 1; k <= _config.coreTypeItem.Count; k++) + { + var item = _config.coreTypeItem[k - 1]; + var type = string.Empty; + switch ((int)item.configType) + { + case 1: + type = CoreType1; + break; + + case 2: + type = CoreType2; + break; + + case 3: + type = CoreType3; + break; + + case 4: + type = CoreType4; + break; + + case 5: + type = CoreType5; + break; + + case 6: + type = CoreType6; + break; + } + item.coreType = (ECoreType)Enum.Parse(typeof(ECoreType), type); + } + return 0; + } } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/RoutingRuleDetailsViewModel.cs b/v2rayN/v2rayN/ViewModels/RoutingRuleDetailsViewModel.cs index 4cae5da2..03f11ee5 100644 --- a/v2rayN/v2rayN/ViewModels/RoutingRuleDetailsViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/RoutingRuleDetailsViewModel.cs @@ -8,98 +8,97 @@ using v2rayN.Handler; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class RoutingRuleDetailsViewModel : ReactiveObject { - public class RoutingRuleDetailsViewModel : ReactiveObject + private static Config _config; + private NoticeHandler? _noticeHandler; + private Window _view; + + public IList ProtocolItems { get; set; } + public IList InboundTagItems { get; set; } + + [Reactive] + public RulesItem SelectedSource { get; set; } + + [Reactive] + public string Domain { get; set; } + + [Reactive] + public string IP { get; set; } + + [Reactive] + public string Process { get; set; } + + [Reactive] + public bool AutoSort { get; set; } + + public ReactiveCommand SaveCmd { get; } + + public RoutingRuleDetailsViewModel(RulesItem rulesItem, Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; - private Window _view; + _config = LazyConfig.Instance.GetConfig(); + _noticeHandler = Locator.Current.GetService(); + _view = view; - public IList ProtocolItems { get; set; } - public IList InboundTagItems { get; set; } - - [Reactive] - public RulesItem SelectedSource { get; set; } - - [Reactive] - public string Domain { get; set; } - - [Reactive] - public string IP { get; set; } - - [Reactive] - public string Process { get; set; } - - [Reactive] - public bool AutoSort { get; set; } - - public ReactiveCommand SaveCmd { get; } - - public RoutingRuleDetailsViewModel(RulesItem rulesItem, Window view) + if (rulesItem.id.IsNullOrEmpty()) { - _config = LazyConfig.Instance.GetConfig(); - _noticeHandler = Locator.Current.GetService(); - _view = view; - - if (rulesItem.id.IsNullOrEmpty()) - { - rulesItem.id = Utils.GetGUID(false); - rulesItem.outboundTag = Global.agentTag; - rulesItem.enabled = true; - SelectedSource = rulesItem; - } - else - { - SelectedSource = rulesItem; - } - - Domain = Utils.List2String(SelectedSource.domain, true); - IP = Utils.List2String(SelectedSource.ip, true); - Process = Utils.List2String(SelectedSource.process, true); - - SaveCmd = ReactiveCommand.Create(() => - { - SaveRules(); - }); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + rulesItem.id = Utils.GetGUID(false); + rulesItem.outboundTag = Global.agentTag; + rulesItem.enabled = true; + SelectedSource = rulesItem; + } + else + { + SelectedSource = rulesItem; } - private void SaveRules() + Domain = Utils.List2String(SelectedSource.domain, true); + IP = Utils.List2String(SelectedSource.ip, true); + Process = Utils.List2String(SelectedSource.process, true); + + SaveCmd = ReactiveCommand.Create(() => { - Domain = Utils.Convert2Comma(Domain); - IP = Utils.Convert2Comma(IP); - Process = Utils.Convert2Comma(Process); + SaveRules(); + }); - if (AutoSort) - { - SelectedSource.domain = Utils.String2ListSorted(Domain); - SelectedSource.ip = Utils.String2ListSorted(IP); - SelectedSource.process = Utils.String2ListSorted(Process); - } - else - { - SelectedSource.domain = Utils.String2List(Domain); - SelectedSource.ip = Utils.String2List(IP); - SelectedSource.process = Utils.String2List(Process); - } - SelectedSource.protocol = ProtocolItems?.ToList(); - SelectedSource.inboundTag = InboundTagItems?.ToList(); + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } - bool hasRule = SelectedSource.domain?.Count > 0 - || SelectedSource.ip?.Count > 0 - || SelectedSource.protocol?.Count > 0 - || SelectedSource.process?.Count > 0 - || !Utils.IsNullOrEmpty(SelectedSource.port); + private void SaveRules() + { + Domain = Utils.Convert2Comma(Domain); + IP = Utils.Convert2Comma(IP); + Process = Utils.Convert2Comma(Process); - if (!hasRule) - { - UI.ShowWarning(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Port/Protocol/Domain/IP/Process")); - return; - } - //_noticeHandler?.Enqueue(ResUI.OperationSuccess); - _view.DialogResult = true; + if (AutoSort) + { + SelectedSource.domain = Utils.String2ListSorted(Domain); + SelectedSource.ip = Utils.String2ListSorted(IP); + SelectedSource.process = Utils.String2ListSorted(Process); } + else + { + SelectedSource.domain = Utils.String2List(Domain); + SelectedSource.ip = Utils.String2List(IP); + SelectedSource.process = Utils.String2List(Process); + } + SelectedSource.protocol = ProtocolItems?.ToList(); + SelectedSource.inboundTag = InboundTagItems?.ToList(); + + bool hasRule = SelectedSource.domain?.Count > 0 + || SelectedSource.ip?.Count > 0 + || SelectedSource.protocol?.Count > 0 + || SelectedSource.process?.Count > 0 + || !Utils.IsNullOrEmpty(SelectedSource.port); + + if (!hasRule) + { + UI.ShowWarning(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Port/Protocol/Domain/IP/Process")); + return; + } + //_noticeHandler?.Enqueue(ResUI.OperationSuccess); + _view.DialogResult = true; } } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs b/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs index 8c7c4273..7a3687b6 100644 --- a/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs @@ -12,349 +12,348 @@ using v2rayN.Resx; using v2rayN.Views; using Application = System.Windows.Application; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class RoutingRuleSettingViewModel : ReactiveObject { - public class RoutingRuleSettingViewModel : ReactiveObject + private static Config _config; + private NoticeHandler? _noticeHandler; + private Window _view; + private List _rules; + + [Reactive] + public RoutingItem SelectedRouting { get; set; } + + private IObservableCollection _rulesItems = new ObservableCollectionExtended(); + public IObservableCollection RulesItems => _rulesItems; + + [Reactive] + public RulesItemModel SelectedSource { get; set; } + + public IList SelectedSources { get; set; } + + public ReactiveCommand RuleAddCmd { get; } + public ReactiveCommand ImportRulesFromFileCmd { get; } + public ReactiveCommand ImportRulesFromClipboardCmd { get; } + public ReactiveCommand ImportRulesFromUrlCmd { get; } + public ReactiveCommand RuleRemoveCmd { get; } + public ReactiveCommand RuleExportSelectedCmd { get; } + public ReactiveCommand MoveTopCmd { get; } + public ReactiveCommand MoveUpCmd { get; } + public ReactiveCommand MoveDownCmd { get; } + public ReactiveCommand MoveBottomCmd { get; } + + public ReactiveCommand SaveCmd { get; } + + public RoutingRuleSettingViewModel(RoutingItem routingItem, Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; - private Window _view; - private List _rules; + _config = LazyConfig.Instance.GetConfig(); + _noticeHandler = Locator.Current.GetService(); + _view = view; + SelectedSource = new(); - [Reactive] - public RoutingItem SelectedRouting { get; set; } - - private IObservableCollection _rulesItems = new ObservableCollectionExtended(); - public IObservableCollection RulesItems => _rulesItems; - - [Reactive] - public RulesItemModel SelectedSource { get; set; } - - public IList SelectedSources { get; set; } - - public ReactiveCommand RuleAddCmd { get; } - public ReactiveCommand ImportRulesFromFileCmd { get; } - public ReactiveCommand ImportRulesFromClipboardCmd { get; } - public ReactiveCommand ImportRulesFromUrlCmd { get; } - public ReactiveCommand RuleRemoveCmd { get; } - public ReactiveCommand RuleExportSelectedCmd { get; } - public ReactiveCommand MoveTopCmd { get; } - public ReactiveCommand MoveUpCmd { get; } - public ReactiveCommand MoveDownCmd { get; } - public ReactiveCommand MoveBottomCmd { get; } - - public ReactiveCommand SaveCmd { get; } - - public RoutingRuleSettingViewModel(RoutingItem routingItem, Window view) + if (routingItem.id.IsNullOrEmpty()) { - _config = LazyConfig.Instance.GetConfig(); - _noticeHandler = Locator.Current.GetService(); - _view = view; - SelectedSource = new(); - - if (routingItem.id.IsNullOrEmpty()) - { - SelectedRouting = routingItem; - _rules = new(); - } - else - { - SelectedRouting = routingItem; - _rules = Utils.FromJson>(SelectedRouting.ruleSet); - } - - RefreshRulesItems(); - - var canEditRemove = this.WhenAnyValue( - x => x.SelectedSource, - selectedSource => selectedSource != null && !selectedSource.outboundTag.IsNullOrEmpty()); - - RuleAddCmd = ReactiveCommand.Create(() => - { - RuleEdit(true); - }); - ImportRulesFromFileCmd = ReactiveCommand.Create(() => - { - ImportRulesFromFile(); - }); - ImportRulesFromClipboardCmd = ReactiveCommand.Create(() => - { - ImportRulesFromClipboard(); - }); - ImportRulesFromUrlCmd = ReactiveCommand.Create(() => - { - ImportRulesFromUrl(); - }); - - RuleRemoveCmd = ReactiveCommand.Create(() => - { - RuleRemove(); - }, canEditRemove); - RuleExportSelectedCmd = ReactiveCommand.Create(() => - { - RuleExportSelected(); - }, canEditRemove); - - MoveTopCmd = ReactiveCommand.Create(() => - { - MoveRule(EMove.Top); - }, canEditRemove); - MoveUpCmd = ReactiveCommand.Create(() => - { - MoveRule(EMove.Up); - }, canEditRemove); - MoveDownCmd = ReactiveCommand.Create(() => - { - MoveRule(EMove.Down); - }, canEditRemove); - MoveBottomCmd = ReactiveCommand.Create(() => - { - MoveRule(EMove.Bottom); - }, canEditRemove); - - SaveCmd = ReactiveCommand.Create(() => - { - SaveRouting(); - }); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + SelectedRouting = routingItem; + _rules = new(); + } + else + { + SelectedRouting = routingItem; + _rules = Utils.FromJson>(SelectedRouting.ruleSet); } - public void RefreshRulesItems() - { - _rulesItems.Clear(); + RefreshRulesItems(); - foreach (var item in _rules) + var canEditRemove = this.WhenAnyValue( + x => x.SelectedSource, + selectedSource => selectedSource != null && !selectedSource.outboundTag.IsNullOrEmpty()); + + RuleAddCmd = ReactiveCommand.Create(() => + { + RuleEdit(true); + }); + ImportRulesFromFileCmd = ReactiveCommand.Create(() => + { + ImportRulesFromFile(); + }); + ImportRulesFromClipboardCmd = ReactiveCommand.Create(() => + { + ImportRulesFromClipboard(); + }); + ImportRulesFromUrlCmd = ReactiveCommand.Create(() => + { + ImportRulesFromUrl(); + }); + + RuleRemoveCmd = ReactiveCommand.Create(() => + { + RuleRemove(); + }, canEditRemove); + RuleExportSelectedCmd = ReactiveCommand.Create(() => + { + RuleExportSelected(); + }, canEditRemove); + + MoveTopCmd = ReactiveCommand.Create(() => + { + MoveRule(EMove.Top); + }, canEditRemove); + MoveUpCmd = ReactiveCommand.Create(() => + { + MoveRule(EMove.Up); + }, canEditRemove); + MoveDownCmd = ReactiveCommand.Create(() => + { + MoveRule(EMove.Down); + }, canEditRemove); + MoveBottomCmd = ReactiveCommand.Create(() => + { + MoveRule(EMove.Bottom); + }, canEditRemove); + + SaveCmd = ReactiveCommand.Create(() => + { + SaveRouting(); + }); + + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } + + public void RefreshRulesItems() + { + _rulesItems.Clear(); + + foreach (var item in _rules) + { + var it = new RulesItemModel() { - var it = new RulesItemModel() - { - id = item.id, - outboundTag = item.outboundTag, - port = item.port, - protocols = Utils.List2String(item.protocol), - inboundTags = Utils.List2String(item.inboundTag), - domains = Utils.List2String(item.domain), - ips = Utils.List2String(item.ip), - enabled = item.enabled, - }; - _rulesItems.Add(it); + id = item.id, + outboundTag = item.outboundTag, + port = item.port, + protocols = Utils.List2String(item.protocol), + inboundTags = Utils.List2String(item.inboundTag), + domains = Utils.List2String(item.domain), + ips = Utils.List2String(item.ip), + enabled = item.enabled, + }; + _rulesItems.Add(it); + } + } + + public void RuleEdit(bool blNew) + { + RulesItem item; + if (blNew) + { + item = new(); + } + else + { + item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id); + if (item is null) + { + return; } } - - public void RuleEdit(bool blNew) + var ret = (new RoutingRuleDetailsWindow(item)).ShowDialog(); + if (ret == true) { - RulesItem item; if (blNew) { - item = new(); + _rules.Add(item); } - else - { - item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id); - if (item is null) - { - return; - } - } - var ret = (new RoutingRuleDetailsWindow(item)).ShowDialog(); - if (ret == true) - { - if (blNew) - { - _rules.Add(item); - } - RefreshRulesItems(); - } - } - - public void RuleRemove() - { - if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty()) - { - UI.Show(ResUI.PleaseSelectRules); - return; - } - if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No) - { - return; - } - foreach (var it in SelectedSources) - { - var item = _rules.FirstOrDefault(t => t.id == it?.id); - if (item != null) - { - _rules.Remove(item); - } - } - RefreshRulesItems(); } - - public void RuleExportSelected() - { - if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty()) - { - UI.Show(ResUI.PleaseSelectRules); - return; - } - - var lst = new List(); - foreach (var it in SelectedSources) - { - var item = _rules.FirstOrDefault(t => t.id == it?.id); - if (item != null) - { - lst.Add(item); - } - } - if (lst.Count > 0) - { - Utils.SetClipboardData(Utils.ToJson(lst)); - //UI.Show(ResUI.OperationSuccess")); - } - } - - public void MoveRule(EMove eMove) - { - if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty()) - { - UI.Show(ResUI.PleaseSelectRules); - return; - } - - var item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id); - if (item == null) - { - return; - } - var index = _rules.IndexOf(item); - if (ConfigHandler.MoveRoutingRule(_rules, index, eMove) == 0) - { - RefreshRulesItems(); - } - } - - private void SaveRouting() - { - string remarks = SelectedRouting.remarks; - if (Utils.IsNullOrEmpty(remarks)) - { - UI.Show(ResUI.PleaseFillRemarks); - return; - } - var item = SelectedRouting; - foreach (var it in _rules) - { - it.id = Utils.GetGUID(false); - } - item.ruleNum = _rules.Count; - item.ruleSet = Utils.ToJson(_rules, false); - - if (ConfigHandler.SaveRoutingItem(ref _config, item) == 0) - { - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - _view.DialogResult = true; - } - else - { - UI.ShowWarning(ResUI.OperationFailed); - } - } - - #region Import rules - - private void ImportRulesFromFile() - { - OpenFileDialog fileDialog = new OpenFileDialog - { - Multiselect = false, - Filter = "Rules|*.json|All|*.*" - }; - if (fileDialog.ShowDialog() != true) - { - return; - } - string fileName = fileDialog.FileName; - if (Utils.IsNullOrEmpty(fileName)) - { - return; - } - string result = Utils.LoadResource(fileName); - if (Utils.IsNullOrEmpty(result)) - { - return; - } - - if (AddBatchRoutingRules(SelectedRouting, result) == 0) - { - RefreshRulesItems(); - UI.Show(ResUI.OperationSuccess); - } - } - - private void ImportRulesFromClipboard() - { - string clipboardData = Utils.GetClipboardData(); - if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0) - { - RefreshRulesItems(); - UI.Show(ResUI.OperationSuccess); - } - } - - private async Task ImportRulesFromUrl() - { - var url = SelectedRouting.url; - if (Utils.IsNullOrEmpty(url)) - { - UI.Show(ResUI.MsgNeedUrl); - return; - } - - DownloadHandle downloadHandle = new DownloadHandle(); - string result = await downloadHandle.TryDownloadString(url, true, ""); - if (AddBatchRoutingRules(SelectedRouting, result) == 0) - { - Application.Current.Dispatcher.Invoke((Action)(() => - { - RefreshRulesItems(); - })); - UI.Show(ResUI.OperationSuccess); - } - } - - private int AddBatchRoutingRules(RoutingItem routingItem, string clipboardData) - { - bool blReplace = false; - if (UI.ShowYesNo(ResUI.AddBatchRoutingRulesYesNo) == MessageBoxResult.No) - { - blReplace = true; - } - if (Utils.IsNullOrEmpty(clipboardData)) - { - return -1; - } - var lstRules = Utils.FromJson>(clipboardData); - if (lstRules == null) - { - return -1; - } - foreach (var rule in lstRules) - { - rule.id = Utils.GetGUID(false); - } - - if (blReplace) - { - _rules = lstRules; - } - else - { - _rules.AddRange(lstRules); - } - return 0; - } - - #endregion Import rules } + + public void RuleRemove() + { + if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty()) + { + UI.Show(ResUI.PleaseSelectRules); + return; + } + if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No) + { + return; + } + foreach (var it in SelectedSources) + { + var item = _rules.FirstOrDefault(t => t.id == it?.id); + if (item != null) + { + _rules.Remove(item); + } + } + + RefreshRulesItems(); + } + + public void RuleExportSelected() + { + if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty()) + { + UI.Show(ResUI.PleaseSelectRules); + return; + } + + var lst = new List(); + foreach (var it in SelectedSources) + { + var item = _rules.FirstOrDefault(t => t.id == it?.id); + if (item != null) + { + lst.Add(item); + } + } + if (lst.Count > 0) + { + Utils.SetClipboardData(Utils.ToJson(lst)); + //UI.Show(ResUI.OperationSuccess")); + } + } + + public void MoveRule(EMove eMove) + { + if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty()) + { + UI.Show(ResUI.PleaseSelectRules); + return; + } + + var item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id); + if (item == null) + { + return; + } + var index = _rules.IndexOf(item); + if (ConfigHandler.MoveRoutingRule(_rules, index, eMove) == 0) + { + RefreshRulesItems(); + } + } + + private void SaveRouting() + { + string remarks = SelectedRouting.remarks; + if (Utils.IsNullOrEmpty(remarks)) + { + UI.Show(ResUI.PleaseFillRemarks); + return; + } + var item = SelectedRouting; + foreach (var it in _rules) + { + it.id = Utils.GetGUID(false); + } + item.ruleNum = _rules.Count; + item.ruleSet = Utils.ToJson(_rules, false); + + if (ConfigHandler.SaveRoutingItem(ref _config, item) == 0) + { + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + _view.DialogResult = true; + } + else + { + UI.ShowWarning(ResUI.OperationFailed); + } + } + + #region Import rules + + private void ImportRulesFromFile() + { + OpenFileDialog fileDialog = new OpenFileDialog + { + Multiselect = false, + Filter = "Rules|*.json|All|*.*" + }; + if (fileDialog.ShowDialog() != true) + { + return; + } + string fileName = fileDialog.FileName; + if (Utils.IsNullOrEmpty(fileName)) + { + return; + } + string result = Utils.LoadResource(fileName); + if (Utils.IsNullOrEmpty(result)) + { + return; + } + + if (AddBatchRoutingRules(SelectedRouting, result) == 0) + { + RefreshRulesItems(); + UI.Show(ResUI.OperationSuccess); + } + } + + private void ImportRulesFromClipboard() + { + string clipboardData = Utils.GetClipboardData(); + if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0) + { + RefreshRulesItems(); + UI.Show(ResUI.OperationSuccess); + } + } + + private async Task ImportRulesFromUrl() + { + var url = SelectedRouting.url; + if (Utils.IsNullOrEmpty(url)) + { + UI.Show(ResUI.MsgNeedUrl); + return; + } + + DownloadHandle downloadHandle = new DownloadHandle(); + string result = await downloadHandle.TryDownloadString(url, true, ""); + if (AddBatchRoutingRules(SelectedRouting, result) == 0) + { + Application.Current.Dispatcher.Invoke((Action)(() => + { + RefreshRulesItems(); + })); + UI.Show(ResUI.OperationSuccess); + } + } + + private int AddBatchRoutingRules(RoutingItem routingItem, string clipboardData) + { + bool blReplace = false; + if (UI.ShowYesNo(ResUI.AddBatchRoutingRulesYesNo) == MessageBoxResult.No) + { + blReplace = true; + } + if (Utils.IsNullOrEmpty(clipboardData)) + { + return -1; + } + var lstRules = Utils.FromJson>(clipboardData); + if (lstRules == null) + { + return -1; + } + foreach (var rule in lstRules) + { + rule.id = Utils.GetGUID(false); + } + + if (blReplace) + { + _rules = lstRules; + } + else + { + _rules.AddRange(lstRules); + } + return 0; + } + + #endregion Import rules } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/RoutingSettingViewModel.cs b/v2rayN/v2rayN/ViewModels/RoutingSettingViewModel.cs index b6ec6fa5..5a53c481 100644 --- a/v2rayN/v2rayN/ViewModels/RoutingSettingViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/RoutingSettingViewModel.cs @@ -10,299 +10,298 @@ using v2rayN.Mode; using v2rayN.Resx; using v2rayN.Views; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class RoutingSettingViewModel : ReactiveObject { - public class RoutingSettingViewModel : ReactiveObject + private static Config _config; + private NoticeHandler? _noticeHandler; + private Window _view; + private RoutingItem _lockedItem; + private List _lockedRules; + + #region Reactive + + private IObservableCollection _routingItems = new ObservableCollectionExtended(); + public IObservableCollection RoutingItems => _routingItems; + + [Reactive] + public RoutingItemModel SelectedSource { get; set; } + + public IList SelectedSources { get; set; } + + [Reactive] + public bool enableRoutingAdvanced { get; set; } + + [Reactive] + public bool enableRoutingBasic { get; set; } + + [Reactive] + public string domainStrategy { get; set; } + + [Reactive] + public string domainMatcher { get; set; } + + [Reactive] + public string domainStrategy4Singbox { get; set; } + + [Reactive] + public string ProxyDomain { get; set; } + + [Reactive] + public string ProxyIP { get; set; } + + [Reactive] + public string DirectDomain { get; set; } + + [Reactive] + public string DirectIP { get; set; } + + [Reactive] + public string BlockDomain { get; set; } + + [Reactive] + public string BlockIP { get; set; } + + public ReactiveCommand RoutingBasicImportRulesCmd { get; } + public ReactiveCommand RoutingAdvancedAddCmd { get; } + public ReactiveCommand RoutingAdvancedRemoveCmd { get; } + public ReactiveCommand RoutingAdvancedSetDefaultCmd { get; } + public ReactiveCommand RoutingAdvancedImportRulesCmd { get; } + + public ReactiveCommand SaveCmd { get; } + public bool IsModified { get; set; } + + #endregion Reactive + + public RoutingSettingViewModel(Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; - private Window _view; - private RoutingItem _lockedItem; - private List _lockedRules; + _config = LazyConfig.Instance.GetConfig(); + _noticeHandler = Locator.Current.GetService(); + _view = view; + SelectedSource = new(); - #region Reactive + ConfigHandler.InitBuiltinRouting(ref _config); - private IObservableCollection _routingItems = new ObservableCollectionExtended(); - public IObservableCollection RoutingItems => _routingItems; + enableRoutingAdvanced = _config.routingBasicItem.enableRoutingAdvanced; + domainStrategy = _config.routingBasicItem.domainStrategy; + domainMatcher = _config.routingBasicItem.domainMatcher; + domainStrategy4Singbox = _config.routingBasicItem.domainStrategy4Singbox; - [Reactive] - public RoutingItemModel SelectedSource { get; set; } + RefreshRoutingItems(); - public IList SelectedSources { get; set; } + BindingLockedData(); - [Reactive] - public bool enableRoutingAdvanced { get; set; } + var canEditRemove = this.WhenAnyValue( + x => x.SelectedSource, + selectedSource => selectedSource != null && !selectedSource.remarks.IsNullOrEmpty()); - [Reactive] - public bool enableRoutingBasic { get; set; } + this.WhenAnyValue( + x => x.enableRoutingAdvanced) + .Subscribe(c => enableRoutingBasic = !enableRoutingAdvanced); - [Reactive] - public string domainStrategy { get; set; } - - [Reactive] - public string domainMatcher { get; set; } - - [Reactive] - public string domainStrategy4Singbox { get; set; } - - [Reactive] - public string ProxyDomain { get; set; } - - [Reactive] - public string ProxyIP { get; set; } - - [Reactive] - public string DirectDomain { get; set; } - - [Reactive] - public string DirectIP { get; set; } - - [Reactive] - public string BlockDomain { get; set; } - - [Reactive] - public string BlockIP { get; set; } - - public ReactiveCommand RoutingBasicImportRulesCmd { get; } - public ReactiveCommand RoutingAdvancedAddCmd { get; } - public ReactiveCommand RoutingAdvancedRemoveCmd { get; } - public ReactiveCommand RoutingAdvancedSetDefaultCmd { get; } - public ReactiveCommand RoutingAdvancedImportRulesCmd { get; } - - public ReactiveCommand SaveCmd { get; } - public bool IsModified { get; set; } - - #endregion Reactive - - public RoutingSettingViewModel(Window view) + RoutingBasicImportRulesCmd = ReactiveCommand.Create(() => { - _config = LazyConfig.Instance.GetConfig(); - _noticeHandler = Locator.Current.GetService(); - _view = view; - SelectedSource = new(); + RoutingBasicImportRules(); + }); - ConfigHandler.InitBuiltinRouting(ref _config); + RoutingAdvancedAddCmd = ReactiveCommand.Create(() => + { + RoutingAdvancedEdit(true); + }); + RoutingAdvancedRemoveCmd = ReactiveCommand.Create(() => + { + RoutingAdvancedRemove(); + }, canEditRemove); + RoutingAdvancedSetDefaultCmd = ReactiveCommand.Create(() => + { + RoutingAdvancedSetDefault(); + }, canEditRemove); + RoutingAdvancedImportRulesCmd = ReactiveCommand.Create(() => + { + RoutingAdvancedImportRules(); + }); - enableRoutingAdvanced = _config.routingBasicItem.enableRoutingAdvanced; - domainStrategy = _config.routingBasicItem.domainStrategy; - domainMatcher = _config.routingBasicItem.domainMatcher; - domainStrategy4Singbox = _config.routingBasicItem.domainStrategy4Singbox; + SaveCmd = ReactiveCommand.Create(() => + { + SaveRouting(); + }); - RefreshRoutingItems(); + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } - BindingLockedData(); + #region locked - var canEditRemove = this.WhenAnyValue( - x => x.SelectedSource, - selectedSource => selectedSource != null && !selectedSource.remarks.IsNullOrEmpty()); + private void BindingLockedData() + { + _lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config); + if (_lockedItem != null) + { + _lockedRules = Utils.FromJson>(_lockedItem.ruleSet); + ProxyDomain = Utils.List2String(_lockedRules[0].domain, true); + ProxyIP = Utils.List2String(_lockedRules[0].ip, true); - this.WhenAnyValue( - x => x.enableRoutingAdvanced) - .Subscribe(c => enableRoutingBasic = !enableRoutingAdvanced); + DirectDomain = Utils.List2String(_lockedRules[1].domain, true); + DirectIP = Utils.List2String(_lockedRules[1].ip, true); - RoutingBasicImportRulesCmd = ReactiveCommand.Create(() => - { - RoutingBasicImportRules(); - }); - - RoutingAdvancedAddCmd = ReactiveCommand.Create(() => - { - RoutingAdvancedEdit(true); - }); - RoutingAdvancedRemoveCmd = ReactiveCommand.Create(() => - { - RoutingAdvancedRemove(); - }, canEditRemove); - RoutingAdvancedSetDefaultCmd = ReactiveCommand.Create(() => - { - RoutingAdvancedSetDefault(); - }, canEditRemove); - RoutingAdvancedImportRulesCmd = ReactiveCommand.Create(() => - { - RoutingAdvancedImportRules(); - }); - - SaveCmd = ReactiveCommand.Create(() => - { - SaveRouting(); - }); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + BlockDomain = Utils.List2String(_lockedRules[2].domain, true); + BlockIP = Utils.List2String(_lockedRules[2].ip, true); } + } - #region locked - - private void BindingLockedData() + private void EndBindingLockedData() + { + if (_lockedItem != null) { - _lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config); - if (_lockedItem != null) - { - _lockedRules = Utils.FromJson>(_lockedItem.ruleSet); - ProxyDomain = Utils.List2String(_lockedRules[0].domain, true); - ProxyIP = Utils.List2String(_lockedRules[0].ip, true); + _lockedRules[0].domain = Utils.String2List(Utils.Convert2Comma(ProxyDomain.TrimEx())); + _lockedRules[0].ip = Utils.String2List(Utils.Convert2Comma(ProxyIP.TrimEx())); - DirectDomain = Utils.List2String(_lockedRules[1].domain, true); - DirectIP = Utils.List2String(_lockedRules[1].ip, true); + _lockedRules[1].domain = Utils.String2List(Utils.Convert2Comma(DirectDomain.TrimEx())); + _lockedRules[1].ip = Utils.String2List(Utils.Convert2Comma(DirectIP.TrimEx())); - BlockDomain = Utils.List2String(_lockedRules[2].domain, true); - BlockIP = Utils.List2String(_lockedRules[2].ip, true); - } + _lockedRules[2].domain = Utils.String2List(Utils.Convert2Comma(BlockDomain.TrimEx())); + _lockedRules[2].ip = Utils.String2List(Utils.Convert2Comma(BlockIP.TrimEx())); + + _lockedItem.ruleSet = Utils.ToJson(_lockedRules, false); + + ConfigHandler.SaveRoutingItem(ref _config, _lockedItem); } + } - private void EndBindingLockedData() + #endregion locked + + #region Refresh Save + + public void RefreshRoutingItems() + { + _routingItems.Clear(); + + var routings = LazyConfig.Instance.RoutingItems(); + foreach (var item in routings) { - if (_lockedItem != null) + bool def = false; + if (item.id == _config.routingBasicItem.routingIndexId) { - _lockedRules[0].domain = Utils.String2List(Utils.Convert2Comma(ProxyDomain.TrimEx())); - _lockedRules[0].ip = Utils.String2List(Utils.Convert2Comma(ProxyIP.TrimEx())); - - _lockedRules[1].domain = Utils.String2List(Utils.Convert2Comma(DirectDomain.TrimEx())); - _lockedRules[1].ip = Utils.String2List(Utils.Convert2Comma(DirectIP.TrimEx())); - - _lockedRules[2].domain = Utils.String2List(Utils.Convert2Comma(BlockDomain.TrimEx())); - _lockedRules[2].ip = Utils.String2List(Utils.Convert2Comma(BlockIP.TrimEx())); - - _lockedItem.ruleSet = Utils.ToJson(_lockedRules, false); - - ConfigHandler.SaveRoutingItem(ref _config, _lockedItem); + def = true; } + + var it = new RoutingItemModel() + { + isActive = def, + ruleNum = item.ruleNum, + id = item.id, + remarks = item.remarks, + url = item.url, + customIcon = item.customIcon, + sort = item.sort, + }; + _routingItems.Add(it); } + } - #endregion locked + private void SaveRouting() + { + _config.routingBasicItem.domainStrategy = domainStrategy; + _config.routingBasicItem.enableRoutingAdvanced = enableRoutingAdvanced; + _config.routingBasicItem.domainMatcher = domainMatcher; + _config.routingBasicItem.domainStrategy4Singbox = domainStrategy4Singbox; - #region Refresh Save + EndBindingLockedData(); - public void RefreshRoutingItems() + if (ConfigHandler.SaveConfig(ref _config) == 0) { - _routingItems.Clear(); - - var routings = LazyConfig.Instance.RoutingItems(); - foreach (var item in routings) - { - bool def = false; - if (item.id == _config.routingBasicItem.routingIndexId) - { - def = true; - } - - var it = new RoutingItemModel() - { - isActive = def, - ruleNum = item.ruleNum, - id = item.id, - remarks = item.remarks, - url = item.url, - customIcon = item.customIcon, - sort = item.sort, - }; - _routingItems.Add(it); - } + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + _view.DialogResult = true; } - - private void SaveRouting() + else { - _config.routingBasicItem.domainStrategy = domainStrategy; - _config.routingBasicItem.enableRoutingAdvanced = enableRoutingAdvanced; - _config.routingBasicItem.domainMatcher = domainMatcher; - _config.routingBasicItem.domainStrategy4Singbox = domainStrategy4Singbox; - - EndBindingLockedData(); - - if (ConfigHandler.SaveConfig(ref _config) == 0) - { - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - _view.DialogResult = true; - } - else - { - UI.ShowWarning(ResUI.OperationFailed); - } + UI.ShowWarning(ResUI.OperationFailed); } + } - #endregion Refresh Save + #endregion Refresh Save - private void RoutingBasicImportRules() + private void RoutingBasicImportRules() + { + //Extra to bypass the mainland + ProxyDomain = "geosite:google"; + DirectDomain = "geosite:cn"; + DirectIP = "geoip:private,geoip:cn"; + BlockDomain = "geosite:category-ads-all"; + + //_noticeHandler?.Enqueue(ResUI.OperationSuccess); + UI.Show(ResUI.OperationSuccess); + } + + public void RoutingAdvancedEdit(bool blNew) + { + RoutingItem item; + if (blNew) { - //Extra to bypass the mainland - ProxyDomain = "geosite:google"; - DirectDomain = "geosite:cn"; - DirectIP = "geoip:private,geoip:cn"; - BlockDomain = "geosite:category-ads-all"; - - //_noticeHandler?.Enqueue(ResUI.OperationSuccess); - UI.Show(ResUI.OperationSuccess); + item = new(); } - - public void RoutingAdvancedEdit(bool blNew) + else { - RoutingItem item; - if (blNew) - { - item = new(); - } - else - { - item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id); - if (item is null) - { - return; - } - } - var ret = (new RoutingRuleSettingWindow(item)).ShowDialog(); - if (ret == true) - { - RefreshRoutingItems(); - IsModified = true; - } - } - - public void RoutingAdvancedRemove() - { - if (SelectedSource is null || SelectedSource.remarks.IsNullOrEmpty()) - { - UI.Show(ResUI.PleaseSelectRules); - return; - } - if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No) + item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id); + if (item is null) { return; } - foreach (var it in SelectedSources) - { - var item = LazyConfig.Instance.GetRoutingItem(it?.id); - if (item != null) - { - ConfigHandler.RemoveRoutingItem(item); - } - } - + } + var ret = (new RoutingRuleSettingWindow(item)).ShowDialog(); + if (ret == true) + { RefreshRoutingItems(); IsModified = true; } + } - public void RoutingAdvancedSetDefault() + public void RoutingAdvancedRemove() + { + if (SelectedSource is null || SelectedSource.remarks.IsNullOrEmpty()) { - var item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id); - if (item is null) + UI.Show(ResUI.PleaseSelectRules); + return; + } + if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No) + { + return; + } + foreach (var it in SelectedSources) + { + var item = LazyConfig.Instance.GetRoutingItem(it?.id); + if (item != null) { - UI.Show(ResUI.PleaseSelectRules); - return; - } - - if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0) - { - RefreshRoutingItems(); - IsModified = true; + ConfigHandler.RemoveRoutingItem(item); } } - private void RoutingAdvancedImportRules() + RefreshRoutingItems(); + IsModified = true; + } + + public void RoutingAdvancedSetDefault() + { + var item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id); + if (item is null) { - if (ConfigHandler.InitBuiltinRouting(ref _config, true) == 0) - { - RefreshRoutingItems(); - IsModified = true; - } + UI.Show(ResUI.PleaseSelectRules); + return; + } + + if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0) + { + RefreshRoutingItems(); + IsModified = true; + } + } + + private void RoutingAdvancedImportRules() + { + if (ConfigHandler.InitBuiltinRouting(ref _config, true) == 0) + { + RefreshRoutingItems(); + IsModified = true; } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/SubEditViewModel.cs b/v2rayN/v2rayN/ViewModels/SubEditViewModel.cs index 3c4e1e54..603e3433 100644 --- a/v2rayN/v2rayN/ViewModels/SubEditViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/SubEditViewModel.cs @@ -8,79 +8,78 @@ using v2rayN.Handler; using v2rayN.Mode; using v2rayN.Resx; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class SubEditViewModel : ReactiveObject { - public class SubEditViewModel : ReactiveObject + private static Config _config; + private NoticeHandler? _noticeHandler; + private Window _view; + + [Reactive] + public SubItem SelectedSource { get; set; } + + public ReactiveCommand SaveCmd { get; } + + public SubEditViewModel(SubItem subItem, Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; - private Window _view; + _config = LazyConfig.Instance.GetConfig(); + _noticeHandler = Locator.Current.GetService(); + _view = view; - [Reactive] - public SubItem SelectedSource { get; set; } - - public ReactiveCommand SaveCmd { get; } - - public SubEditViewModel(SubItem subItem, Window view) + if (subItem.id.IsNullOrEmpty()) { - _config = LazyConfig.Instance.GetConfig(); - _noticeHandler = Locator.Current.GetService(); - _view = view; - - if (subItem.id.IsNullOrEmpty()) - { - SelectedSource = subItem; - } - else - { - SelectedSource = Utils.DeepCopy(subItem); - } - - SaveCmd = ReactiveCommand.Create(() => - { - SaveSub(); - }); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + SelectedSource = subItem; + } + else + { + SelectedSource = Utils.DeepCopy(subItem); } - private void SaveSub() + SaveCmd = ReactiveCommand.Create(() => { - string remarks = SelectedSource.remarks; - if (string.IsNullOrEmpty(remarks)) - { - UI.Show(ResUI.PleaseFillRemarks); - return; - } + SaveSub(); + }); - var item = LazyConfig.Instance.GetSubItem(SelectedSource.id); - if (item is null) - { - item = SelectedSource; - } - else - { - item.remarks = SelectedSource.remarks; - item.url = SelectedSource.url; - item.moreUrl = SelectedSource.moreUrl; - item.enabled = SelectedSource.enabled; - item.autoUpdateInterval = SelectedSource.autoUpdateInterval; - item.userAgent = SelectedSource.userAgent; - item.sort = SelectedSource.sort; - item.filter = SelectedSource.filter; - item.convertTarget = SelectedSource.convertTarget; - } + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } - if (ConfigHandler.AddSubItem(ref _config, item) == 0) - { - _noticeHandler?.Enqueue(ResUI.OperationSuccess); - _view.DialogResult = true; - //_view?.Close(); - } - else - { - _noticeHandler?.Enqueue(ResUI.OperationFailed); - } + private void SaveSub() + { + string remarks = SelectedSource.remarks; + if (string.IsNullOrEmpty(remarks)) + { + UI.Show(ResUI.PleaseFillRemarks); + return; + } + + var item = LazyConfig.Instance.GetSubItem(SelectedSource.id); + if (item is null) + { + item = SelectedSource; + } + else + { + item.remarks = SelectedSource.remarks; + item.url = SelectedSource.url; + item.moreUrl = SelectedSource.moreUrl; + item.enabled = SelectedSource.enabled; + item.autoUpdateInterval = SelectedSource.autoUpdateInterval; + item.userAgent = SelectedSource.userAgent; + item.sort = SelectedSource.sort; + item.filter = SelectedSource.filter; + item.convertTarget = SelectedSource.convertTarget; + } + + if (ConfigHandler.AddSubItem(ref _config, item) == 0) + { + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + _view.DialogResult = true; + //_view?.Close(); + } + else + { + _noticeHandler?.Enqueue(ResUI.OperationFailed); } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/SubSettingViewModel.cs b/v2rayN/v2rayN/ViewModels/SubSettingViewModel.cs index e2da43a7..404f52c6 100644 --- a/v2rayN/v2rayN/ViewModels/SubSettingViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/SubSettingViewModel.cs @@ -12,119 +12,118 @@ using v2rayN.Mode; using v2rayN.Resx; using v2rayN.Views; -namespace v2rayN.ViewModels +namespace v2rayN.ViewModels; + +public class SubSettingViewModel : ReactiveObject { - public class SubSettingViewModel : ReactiveObject + private static Config _config; + private NoticeHandler? _noticeHandler; + + private IObservableCollection _subItems = new ObservableCollectionExtended(); + public IObservableCollection SubItems => _subItems; + + [Reactive] + public SubItem SelectedSource { get; set; } + + public IList SelectedSources { get; set; } + + public ReactiveCommand SubAddCmd { get; } + public ReactiveCommand SubDeleteCmd { get; } + public ReactiveCommand SubEditCmd { get; } + public ReactiveCommand SubShareCmd { get; } + public bool IsModified { get; set; } + + public SubSettingViewModel(Window view) { - private static Config _config; - private NoticeHandler? _noticeHandler; + _config = LazyConfig.Instance.GetConfig(); + _noticeHandler = Locator.Current.GetService(); - private IObservableCollection _subItems = new ObservableCollectionExtended(); - public IObservableCollection SubItems => _subItems; + SelectedSource = new(); - [Reactive] - public SubItem SelectedSource { get; set; } + RefreshSubItems(); - public IList SelectedSources { get; set; } + var canEditRemove = this.WhenAnyValue( + x => x.SelectedSource, + selectedSource => selectedSource != null && !selectedSource.id.IsNullOrEmpty()); - public ReactiveCommand SubAddCmd { get; } - public ReactiveCommand SubDeleteCmd { get; } - public ReactiveCommand SubEditCmd { get; } - public ReactiveCommand SubShareCmd { get; } - public bool IsModified { get; set; } - - public SubSettingViewModel(Window view) + SubAddCmd = ReactiveCommand.Create(() => { - _config = LazyConfig.Instance.GetConfig(); - _noticeHandler = Locator.Current.GetService(); + EditSub(true); + }); + SubDeleteCmd = ReactiveCommand.Create(() => + { + DeleteSub(); + }, canEditRemove); + SubEditCmd = ReactiveCommand.Create(() => + { + EditSub(false); + }, canEditRemove); + SubShareCmd = ReactiveCommand.Create(() => + { + SubShare(); + }, canEditRemove); - SelectedSource = new(); + Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + } - RefreshSubItems(); + public void RefreshSubItems() + { + _subItems.Clear(); + _subItems.AddRange(LazyConfig.Instance.SubItems().OrderBy(t => t.sort)); + } - var canEditRemove = this.WhenAnyValue( - x => x.SelectedSource, - selectedSource => selectedSource != null && !selectedSource.id.IsNullOrEmpty()); - - SubAddCmd = ReactiveCommand.Create(() => - { - EditSub(true); - }); - SubDeleteCmd = ReactiveCommand.Create(() => - { - DeleteSub(); - }, canEditRemove); - SubEditCmd = ReactiveCommand.Create(() => - { - EditSub(false); - }, canEditRemove); - SubShareCmd = ReactiveCommand.Create(() => - { - SubShare(); - }, canEditRemove); - - Utils.SetDarkBorder(view, _config.uiItem.colorModeDark); + public void EditSub(bool blNew) + { + SubItem item; + if (blNew) + { + item = new(); } - - public void RefreshSubItems() + else { - _subItems.Clear(); - _subItems.AddRange(LazyConfig.Instance.SubItems().OrderBy(t => t.sort)); - } - - public void EditSub(bool blNew) - { - SubItem item; - if (blNew) - { - item = new(); - } - else - { - item = LazyConfig.Instance.GetSubItem(SelectedSource?.id); - if (item is null) - { - return; - } - } - var ret = (new SubEditWindow(item)).ShowDialog(); - if (ret == true) - { - RefreshSubItems(); - IsModified = true; - } - } - - private void DeleteSub() - { - if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No) + item = LazyConfig.Instance.GetSubItem(SelectedSource?.id); + if (item is null) { return; } - - foreach (var it in SelectedSources) - { - ConfigHandler.DeleteSubItem(ref _config, it?.id); - } + } + var ret = (new SubEditWindow(item)).ShowDialog(); + if (ret == true) + { RefreshSubItems(); - _noticeHandler?.Enqueue(ResUI.OperationSuccess); IsModified = true; } + } - private async void SubShare() + private void DeleteSub() + { + if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No) { - if (Utils.IsNullOrEmpty(SelectedSource?.url)) - { - return; - } - var img = QRCodeHelper.GetQRCode(SelectedSource?.url); - var dialog = new QrcodeView() - { - imgQrcode = { Source = img }, - txtContent = { Text = SelectedSource?.url }, - }; - - await DialogHost.Show(dialog, "SubDialog"); + return; } + + foreach (var it in SelectedSources) + { + ConfigHandler.DeleteSubItem(ref _config, it?.id); + } + RefreshSubItems(); + _noticeHandler?.Enqueue(ResUI.OperationSuccess); + IsModified = true; + } + + private async void SubShare() + { + if (Utils.IsNullOrEmpty(SelectedSource?.url)) + { + return; + } + var img = QRCodeHelper.GetQRCode(SelectedSource?.url); + var dialog = new QrcodeView() + { + imgQrcode = { Source = img }, + txtContent = { Text = SelectedSource?.url }, + }; + + await DialogHost.Show(dialog, "SubDialog"); } } \ No newline at end of file diff --git a/v2rayN/v2rayN/v2rayN.csproj b/v2rayN/v2rayN/v2rayN.csproj index a96e2997..84267d24 100644 --- a/v2rayN/v2rayN/v2rayN.csproj +++ b/v2rayN/v2rayN/v2rayN.csproj @@ -1,122 +1,115 @@  - true WinExe - net6.0-windows + net7.0-windows enable - true + true app.manifest enable v2rayN.ico Copyright © 2017-2023 (GPLv3) 6.28 - - + - + - + - + - + - - - - - Never - + + + + Never + Never - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - - - Never - + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + + + Never + Never - Never - - - Never - - - - - - Always - - - - - - + Never + + + Never + + + + + + Always + + + + + - True True ResUI.resx - + - - - PublicResXFileCodeGenerator - ResUI.Designer.cs - - - PublicResXFileCodeGenerator - - - PublicResXFileCodeGenerator - - - PublicResXFileCodeGenerator - - - PublicResXFileCodeGenerator - + + PublicResXFileCodeGenerator + ResUI.Designer.cs + + + PublicResXFileCodeGenerator + + + PublicResXFileCodeGenerator + + + PublicResXFileCodeGenerator + + + PublicResXFileCodeGenerator + - - + \ No newline at end of file diff --git a/v2rayN/v2rayUpgrade/MainForm.cs b/v2rayN/v2rayUpgrade/MainForm.cs index 02f0e933..8020fd8d 100644 --- a/v2rayN/v2rayUpgrade/MainForm.cs +++ b/v2rayN/v2rayUpgrade/MainForm.cs @@ -6,143 +6,142 @@ using System.Text; using System.Web; using System.Windows.Forms; -namespace v2rayUpgrade -{ - public partial class MainForm : Form - { - private readonly string defaultFilename = "v2ray-windows.zip"; - private string? fileName; +namespace v2rayUpgrade; - public MainForm(string[] args) +public partial class MainForm : Form +{ + private readonly string defaultFilename = "v2ray-windows.zip"; + private string? fileName; + + public MainForm(string[] args) + { + InitializeComponent(); + if (args.Length > 0) { - InitializeComponent(); - if (args.Length > 0) + fileName = HttpUtility.UrlDecode(string.Join(" ", args)); + } + else + { + fileName = defaultFilename; + } + } + + private void ShowWarn(string message) + { + MessageBox.Show(message, "", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + private void btnOK_Click(object sender, EventArgs e) + { + try + { + Process[] existing = Process.GetProcessesByName("v2rayN"); + foreach (Process p in existing) { - fileName = HttpUtility.UrlDecode(string.Join(" ", args)); + string? path = p.MainModule?.FileName; + if (path == GetPath("v2rayN.exe")) + { + p.Kill(); + p.WaitForExit(100); + } } - else + } + catch (Exception ex) + { + // Access may be denied without admin right. The user may not be an administrator. + ShowWarn("Failed to close v2rayN(关闭v2rayN失败).\n" + + "Close it manually, or the upgrade may fail.(请手动关闭正在运行的v2rayN,否则可能升级失败。\n\n" + ex.StackTrace); + } + + if (!File.Exists(fileName)) + { + if (File.Exists(defaultFilename)) { fileName = defaultFilename; } - } - - private void ShowWarn(string message) - { - MessageBox.Show(message, "", MessageBoxButtons.OK, MessageBoxIcon.Warning); - } - - private void btnOK_Click(object sender, EventArgs e) - { - try + else { - Process[] existing = Process.GetProcessesByName("v2rayN"); - foreach (Process p in existing) - { - string? path = p.MainModule?.FileName; - if (path == GetPath("v2rayN.exe")) - { - p.Kill(); - p.WaitForExit(100); - } - } - } - catch (Exception ex) - { - // Access may be denied without admin right. The user may not be an administrator. - ShowWarn("Failed to close v2rayN(关闭v2rayN失败).\n" + - "Close it manually, or the upgrade may fail.(请手动关闭正在运行的v2rayN,否则可能升级失败。\n\n" + ex.StackTrace); - } - - if (!File.Exists(fileName)) - { - if (File.Exists(defaultFilename)) - { - fileName = defaultFilename; - } - else - { - ShowWarn("Upgrade Failed, File Not Exist(升级失败,文件不存在)."); - return; - } - } - - StringBuilder sb = new(); - try - { - string thisAppOldFile = $"{Application.ExecutablePath}.tmp"; - File.Delete(thisAppOldFile); - string startKey = "v2rayN/"; - - using ZipArchive archive = ZipFile.OpenRead(fileName); - foreach (ZipArchiveEntry entry in archive.Entries) - { - try - { - if (entry.Length == 0) - { - continue; - } - string fullName = entry.FullName; - if (fullName.StartsWith(startKey)) - { - fullName = fullName[startKey.Length..]; - } - if (string.Equals(Application.ExecutablePath, GetPath(fullName), StringComparison.OrdinalIgnoreCase)) - { - File.Move(Application.ExecutablePath, thisAppOldFile); - } - - string entryOutputPath = GetPath(fullName); - Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!); - entry.ExtractToFile(entryOutputPath, true); - } - catch (Exception ex) - { - sb.Append(ex.StackTrace); - } - } - } - catch (Exception ex) - { - ShowWarn("Upgrade Failed(升级失败)." + ex.StackTrace); + ShowWarn("Upgrade Failed, File Not Exist(升级失败,文件不存在)."); return; } - if (sb.Length > 0) + } + + StringBuilder sb = new(); + try + { + string thisAppOldFile = $"{Application.ExecutablePath}.tmp"; + File.Delete(thisAppOldFile); + string startKey = "v2rayN/"; + + using ZipArchive archive = ZipFile.OpenRead(fileName); + foreach (ZipArchiveEntry entry in archive.Entries) { - ShowWarn("Upgrade Failed,Hold ctrl + c to copy to clipboard.\n" + - "(升级失败,按住ctrl+c可以复制到剪贴板)." + sb.ToString()); - return; + try + { + if (entry.Length == 0) + { + continue; + } + string fullName = entry.FullName; + if (fullName.StartsWith(startKey)) + { + fullName = fullName[startKey.Length..]; + } + if (string.Equals(Application.ExecutablePath, GetPath(fullName), StringComparison.OrdinalIgnoreCase)) + { + File.Move(Application.ExecutablePath, thisAppOldFile); + } + + string entryOutputPath = GetPath(fullName); + Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!); + entry.ExtractToFile(entryOutputPath, true); + } + catch (Exception ex) + { + sb.Append(ex.StackTrace); + } } - - Process.Start("v2rayN.exe"); - MessageBox.Show("Upgrade successed(升级成功)", "", MessageBoxButtons.OK, MessageBoxIcon.Information); - - Close(); } - - private void btnClose_Click(object sender, EventArgs e) + catch (Exception ex) { - Close(); + ShowWarn("Upgrade Failed(升级失败)." + ex.StackTrace); + return; + } + if (sb.Length > 0) + { + ShowWarn("Upgrade Failed,Hold ctrl + c to copy to clipboard.\n" + + "(升级失败,按住ctrl+c可以复制到剪贴板)." + sb.ToString()); + return; } - public static string GetExePath() - { - return Application.ExecutablePath; - } + Process.Start("v2rayN.exe"); + MessageBox.Show("Upgrade successed(升级成功)", "", MessageBoxButtons.OK, MessageBoxIcon.Information); - public static string StartupPath() - { - return Application.StartupPath; - } + Close(); + } - public static string GetPath(string fileName) + private void btnClose_Click(object sender, EventArgs e) + { + Close(); + } + + public static string GetExePath() + { + return Application.ExecutablePath; + } + + public static string StartupPath() + { + return Application.StartupPath; + } + + public static string GetPath(string fileName) + { + string startupPath = StartupPath(); + if (string.IsNullOrEmpty(fileName)) { - string startupPath = StartupPath(); - if (string.IsNullOrEmpty(fileName)) - { - return startupPath; - } - return Path.Combine(startupPath, fileName); + return startupPath; } + return Path.Combine(startupPath, fileName); } } diff --git a/v2rayN/v2rayUpgrade/Program.cs b/v2rayN/v2rayUpgrade/Program.cs index ed454bd0..691c9cc8 100644 --- a/v2rayN/v2rayUpgrade/Program.cs +++ b/v2rayN/v2rayUpgrade/Program.cs @@ -1,20 +1,16 @@ using System; using System.Windows.Forms; -namespace v2rayUpgrade +namespace v2rayUpgrade; + +internal static class Program { - internal static class Program + [STAThread] + private static void Main(string[] args) { - /// - /// 应用程序的主入口点。 - /// - [STAThread] - private static void Main(string[] args) - { - Application.EnableVisualStyles(); - Application.SetHighDpiMode(HighDpiMode.SystemAware); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm(args)); - } + Application.EnableVisualStyles(); + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm(args)); } } \ No newline at end of file diff --git a/v2rayN/v2rayUpgrade/v2rayUpgrade.csproj b/v2rayN/v2rayUpgrade/v2rayUpgrade.csproj index 40ddb328..187045fe 100644 --- a/v2rayN/v2rayUpgrade/v2rayUpgrade.csproj +++ b/v2rayN/v2rayUpgrade/v2rayUpgrade.csproj @@ -1,11 +1,11 @@  - net6.0-windows - WinExe - true - Copyright © 2019-2023 (GPLv3) - 1.1.0.0 - app.manifest - enable + net7.0-windows + WinExe + true + Copyright © 2019-2023 (GPLv3) + 1.1.0.0 + app.manifest + enable \ No newline at end of file