mirror of
https://github.com/2dust/v2rayN.git
synced 2026-04-19 05:55:44 +00:00
- Update Project and Libraries to .Net 7
- Update Nuget Packages - Update Namespaces to C# 10 Style - Change BinaryFormatter to XmlSerializer
This commit is contained in:
parent
4f30e3f0e3
commit
e1cd0bbf63
86 changed files with 14564 additions and 14665 deletions
|
|
@ -1,10 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Resources.Designer.cs">
|
<Compile Update="Resources.Designer.cs">
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
|
|
@ -16,5 +14,4 @@
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -1,20 +1,17 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
<ItemGroup>
|
<Protobuf Include="Statistics.proto" />
|
||||||
<Protobuf Include="Statistics.proto" />
|
</ItemGroup>
|
||||||
</ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Google.Protobuf" Version="3.24.2" />
|
||||||
<ItemGroup>
|
<PackageReference Include="Grpc.Net.Client" Version="2.56.0" />
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.23.3" />
|
<PackageReference Include="Grpc.Tools" Version="2.57.0">
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.54.0" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.54.0">
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
</PackageReference>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
</ItemGroup>
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
using ProtosLib.Statistics;
|
using ProtosLib.Statistics;
|
||||||
|
|
||||||
namespace ProtosLib
|
namespace ProtosLib;
|
||||||
{
|
|
||||||
public class Tests
|
|
||||||
{
|
|
||||||
private StatsService.StatsServiceClient client_;
|
|
||||||
|
|
||||||
public Tests()
|
public class Tests
|
||||||
{
|
{
|
||||||
}
|
private StatsService.StatsServiceClient client_;
|
||||||
|
|
||||||
|
public Tests()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,87 +4,86 @@ using v2rayN.Handler;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Tool;
|
using v2rayN.Tool;
|
||||||
|
|
||||||
namespace v2rayN
|
namespace v2rayN;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
/// <summary>
|
public static EventWaitHandle ProgramStarted;
|
||||||
/// Interaction logic for App.xaml
|
private static Config _config;
|
||||||
/// </summary>
|
|
||||||
public partial class App : Application
|
public App()
|
||||||
{
|
{
|
||||||
public static EventWaitHandle ProgramStarted;
|
// Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());
|
||||||
private static Config _config;
|
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||||
|
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
||||||
|
}
|
||||||
|
|
||||||
public App()
|
/// <summary>
|
||||||
|
/// 只打开一个进程
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
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());
|
ProgramStarted.Set();
|
||||||
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
|
Current.Shutdown();
|
||||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
Environment.Exit(0);
|
||||||
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
Global.processJob = new Job();
|
||||||
/// 只打开一个进程
|
|
||||||
/// </summary>
|
Logging.Setup();
|
||||||
/// <param name="e"></param>
|
Init();
|
||||||
protected override void OnStartup(StartupEventArgs e)
|
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());
|
UI.ShowWarning($"Loading GUI configuration file is abnormal,please restart the application{Environment.NewLine}加载GUI配置文件异常,请重启应用");
|
||||||
|
Application.Current.Shutdown();
|
||||||
var rebootas = (e.Args ?? new string[] { }).Any(t => t == Global.RebootAs);
|
Environment.Exit(0);
|
||||||
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, Global.ExePathKey, out bool bCreatedNew);
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
//if (RuntimeInformation.ProcessArchitecture != Architecture.X86 && RuntimeInformation.ProcessArchitecture != Architecture.X64)
|
||||||
|
//{
|
||||||
|
// _config.guiItem.enableStatistics = false;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
private void Init()
|
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
if (ConfigHandler.LoadConfig(ref _config) != 0)
|
Utils.SaveLog("App_DispatcherUnhandledException", e.Exception);
|
||||||
{
|
e.Handled = true;
|
||||||
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)
|
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.ExceptionObject != null)
|
||||||
{
|
{
|
||||||
Utils.SaveLog("App_DispatcherUnhandledException", e.Exception);
|
Utils.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
Utils.SaveLog("TaskScheduler_UnobservedTaskException", e.Exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,180 +2,179 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace v2rayN.Base
|
namespace v2rayN.Base;
|
||||||
|
|
||||||
|
internal class DownloaderHelper
|
||||||
{
|
{
|
||||||
internal class DownloaderHelper
|
private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
|
||||||
|
public static DownloaderHelper Instance => _instance.Value;
|
||||||
|
|
||||||
|
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
|
||||||
{
|
{
|
||||||
private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
|
if (string.IsNullOrEmpty(url))
|
||||||
public static DownloaderHelper Instance => _instance.Value;
|
|
||||||
|
|
||||||
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(url))
|
return null;
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
|
Uri uri = new(url);
|
||||||
|
//Authorization Header
|
||||||
|
var headers = new WebHeaderCollection();
|
||||||
|
if (!Utils.IsNullOrEmpty(uri.UserInfo))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(url))
|
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout)
|
var downloadOpt = new DownloadConfiguration()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(url))
|
Timeout = timeout * 1000,
|
||||||
{
|
MaxTryAgainOnFailover = 2,
|
||||||
throw new ArgumentNullException(nameof(url));
|
RequestConfiguration =
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(fileName))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(fileName));
|
|
||||||
}
|
|
||||||
if (File.Exists(fileName))
|
|
||||||
{
|
|
||||||
File.Delete(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
var downloadOpt = new DownloadConfiguration()
|
|
||||||
{
|
{
|
||||||
|
Headers = headers,
|
||||||
|
UserAgent = userAgent,
|
||||||
Timeout = timeout * 1000,
|
Timeout = timeout * 1000,
|
||||||
MaxTryAgainOnFailover = 2,
|
Proxy = webProxy
|
||||||
RequestConfiguration =
|
}
|
||||||
{
|
};
|
||||||
Timeout= timeout * 1000,
|
|
||||||
Proxy = webProxy
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var progressPercentage = 0;
|
using var downloader = new DownloadService(downloadOpt);
|
||||||
var hasValue = false;
|
downloader.DownloadFileCompleted += (sender, value) =>
|
||||||
using var downloader = new DownloadService(downloadOpt);
|
{
|
||||||
downloader.DownloadStarted += (sender, value) =>
|
if (value.Error != null)
|
||||||
{
|
{
|
||||||
progress?.Report(0);
|
throw value.Error;
|
||||||
};
|
}
|
||||||
downloader.DownloadProgressChanged += (sender, value) =>
|
};
|
||||||
|
|
||||||
|
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<string> 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;
|
hasValue = true;
|
||||||
var percent = (int)value.ProgressPercentage;// Convert.ToInt32((totalRead * 1d) / (total * 1d) * 100);
|
totalSecond = ts.Seconds;
|
||||||
if (progressPercentage != percent && percent % 10 == 0)
|
if (value.BytesPerSecondSpeed > maxSpeed)
|
||||||
{
|
{
|
||||||
progressPercentage = percent;
|
maxSpeed = value.BytesPerSecondSpeed;
|
||||||
progress.Report(percent);
|
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(value.Error?.Message);
|
||||||
{
|
|
||||||
progress.Report(101);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
//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();
|
downloadOpt = null;
|
||||||
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
|
}
|
||||||
|
|
||||||
downloadOpt = null;
|
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,154 +3,153 @@ using System.Net.Http;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace v2rayN.Base
|
namespace v2rayN.Base;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// </summary>
|
||||||
|
public class HttpClientHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
private static readonly Lazy<HttpClientHelper> _instance = new(() =>
|
||||||
/// </summary>
|
|
||||||
public class HttpClientHelper
|
|
||||||
{
|
{
|
||||||
private static readonly Lazy<HttpClientHelper> _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<string?> GetAsync(string url)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(url)) return null;
|
||||||
|
return await httpClient.GetStringAsync(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> 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<string, string> 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<double>? 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 };
|
token.ThrowIfCancellationRequested();
|
||||||
HttpClientHelper helper = new(new HttpClient(handler));
|
|
||||||
return helper;
|
|
||||||
});
|
|
||||||
|
|
||||||
public static HttpClientHelper Instance => _instance.Value;
|
var read = await stream.ReadAsync(buffer, token);
|
||||||
private readonly HttpClient httpClient;
|
totalRead += read;
|
||||||
|
|
||||||
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
|
if (read == 0) break;
|
||||||
|
file.Write(buffer, 0, read);
|
||||||
|
|
||||||
public async Task<string?> GetAsync(string url)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(url)) return null;
|
|
||||||
return await httpClient.GetStringAsync(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string?> 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<string, string> 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<double>? 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)
|
if (canReportProgress)
|
||||||
{
|
{
|
||||||
progress!.Report(101);
|
var percent = (int)(100.0 * totalRead / total);
|
||||||
|
//if (progressPercentage != percent && percent % 10 == 0)
|
||||||
|
{
|
||||||
|
progressPercentage = percent;
|
||||||
|
progress!.Report(percent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (canReportProgress)
|
||||||
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(url))
|
progress!.Report(101);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> 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));
|
if (totalRead > 0)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
token.ThrowIfCancellationRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var read = await stream.ReadAsync(buffer, token);
|
|
||||||
|
|
||||||
if (read == 0)
|
|
||||||
{
|
|
||||||
isMoreToRead = false;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var data = new byte[read];
|
token.ThrowIfCancellationRequested();
|
||||||
buffer.ToList().CopyTo(0, data, 0, read);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
totalRead += read;
|
var read = await stream.ReadAsync(buffer, token);
|
||||||
|
|
||||||
TimeSpan ts = (DateTime.Now - totalDatetime);
|
if (read == 0)
|
||||||
if (progress != null && ts.Seconds > totalSecond)
|
{
|
||||||
|
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;
|
progressSpeed = speed;
|
||||||
var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0");
|
progress.Report(speed);
|
||||||
if (progressSpeed != speed)
|
|
||||||
{
|
|
||||||
progressSpeed = speed;
|
|
||||||
progress.Report(speed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (isMoreToRead);
|
}
|
||||||
}
|
} while (isMoreToRead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
using System.Windows.Controls;
|
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; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,122 +1,121 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
|
||||||
namespace v2rayN.Base
|
namespace v2rayN.Base;
|
||||||
|
|
||||||
|
public sealed class SqliteHelper
|
||||||
{
|
{
|
||||||
public sealed class SqliteHelper
|
private static readonly Lazy<SqliteHelper> _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<SqliteHelper> _instance = new(() => new());
|
_connstr = Utils.GetConfigPath(Global.ConfigDB);
|
||||||
public static SqliteHelper Instance => _instance.Value;
|
_db = new SQLiteConnection(_connstr, false);
|
||||||
private string _connstr;
|
_dbAsync = new SQLiteAsyncConnection(_connstr, false);
|
||||||
private SQLiteConnection _db;
|
}
|
||||||
private SQLiteAsyncConnection _dbAsync;
|
|
||||||
private static readonly object objLock = new();
|
|
||||||
|
|
||||||
public SqliteHelper()
|
public CreateTableResult CreateTable<T>()
|
||||||
{
|
{
|
||||||
_connstr = Utils.GetConfigPath(Global.ConfigDB);
|
return _db.CreateTable<T>();
|
||||||
_db = new SQLiteConnection(_connstr, false);
|
}
|
||||||
_dbAsync = new SQLiteAsyncConnection(_connstr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CreateTableResult CreateTable<T>()
|
public int Insert(object model)
|
||||||
{
|
{
|
||||||
return _db.CreateTable<T>();
|
return _db.Insert(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Insert(object model)
|
public int InsertAll(IEnumerable models)
|
||||||
|
{
|
||||||
|
lock (objLock)
|
||||||
{
|
{
|
||||||
return _db.Insert(model);
|
return _db.InsertAll(models);
|
||||||
}
|
|
||||||
|
|
||||||
public int InsertAll(IEnumerable models)
|
|
||||||
{
|
|
||||||
lock (objLock)
|
|
||||||
{
|
|
||||||
return _db.InsertAll(models);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> InsertAsync(object model)
|
|
||||||
{
|
|
||||||
return await _dbAsync.InsertAsync(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Replace(object model)
|
|
||||||
{
|
|
||||||
lock (objLock)
|
|
||||||
{
|
|
||||||
return _db.InsertOrReplace(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> Replacesync(object model)
|
|
||||||
{
|
|
||||||
return await _dbAsync.InsertOrReplaceAsync(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Update(object model)
|
|
||||||
{
|
|
||||||
lock (objLock)
|
|
||||||
{
|
|
||||||
return _db.Update(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> 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<int> DeleteAsync(object model)
|
|
||||||
{
|
|
||||||
return await _dbAsync.DeleteAsync(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> Query<T>(string sql) where T : new()
|
|
||||||
{
|
|
||||||
return _db.Query<T>(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
|
|
||||||
{
|
|
||||||
return await _dbAsync.QueryAsync<T>(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Execute(string sql)
|
|
||||||
{
|
|
||||||
return _db.Execute(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> ExecuteAsync(string sql)
|
|
||||||
{
|
|
||||||
return await _dbAsync.ExecuteAsync(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TableQuery<T> Table<T>() where T : new()
|
|
||||||
{
|
|
||||||
return _db.Table<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AsyncTableQuery<T> TableAsync<T>() where T : new()
|
|
||||||
{
|
|
||||||
return _dbAsync.Table<T>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<int> InsertAsync(object model)
|
||||||
|
{
|
||||||
|
return await _dbAsync.InsertAsync(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Replace(object model)
|
||||||
|
{
|
||||||
|
lock (objLock)
|
||||||
|
{
|
||||||
|
return _db.InsertOrReplace(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> Replacesync(object model)
|
||||||
|
{
|
||||||
|
return await _dbAsync.InsertOrReplaceAsync(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Update(object model)
|
||||||
|
{
|
||||||
|
lock (objLock)
|
||||||
|
{
|
||||||
|
return _db.Update(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> 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<int> DeleteAsync(object model)
|
||||||
|
{
|
||||||
|
return await _dbAsync.DeleteAsync(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> Query<T>(string sql) where T : new()
|
||||||
|
{
|
||||||
|
return _db.Query<T>(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
|
||||||
|
{
|
||||||
|
return await _dbAsync.QueryAsync<T>(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Execute(string sql)
|
||||||
|
{
|
||||||
|
return _db.Execute(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> ExecuteAsync(string sql)
|
||||||
|
{
|
||||||
|
return await _dbAsync.ExecuteAsync(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableQuery<T> Table<T>() where T : new()
|
||||||
|
{
|
||||||
|
return _db.Table<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncTableQuery<T> TableAsync<T>() where T : new()
|
||||||
|
{
|
||||||
|
return _dbAsync.Table<T>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,84 +1,83 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
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<char> 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<string> NonWhiteSpaceLines(this TextReader reader)
|
||||||
|
{
|
||||||
|
string? line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
return string.IsNullOrWhiteSpace(value);
|
if (line.IsWhiteSpace()) continue;
|
||||||
}
|
yield return line;
|
||||||
|
|
||||||
public static bool BeginWithAny(this string s, IEnumerable<char> 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<string> 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,25 +1,24 @@
|
||||||
using System.Windows.Data;
|
using System.Windows.Data;
|
||||||
using System.Windows.Media;
|
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)
|
if (delay <= 0)
|
||||||
return new SolidColorBrush(Colors.Red);
|
return new SolidColorBrush(Colors.Red);
|
||||||
if (delay <= 200)
|
if (delay <= 200)
|
||||||
return new SolidColorBrush(Colors.Green);
|
return new SolidColorBrush(Colors.Green);
|
||||||
else
|
else
|
||||||
return new SolidColorBrush(Colors.IndianRed);
|
return new SolidColorBrush(Colors.IndianRed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
public object? ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Windows.Data;
|
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 object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
public class InverseBooleanConverter : IValueConverter
|
|
||||||
{
|
{
|
||||||
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");
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The target must be a boolean");
|
|
||||||
}
|
|
||||||
|
|
||||||
return !(bool)value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
return !(bool)value;
|
||||||
{
|
}
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,27 +1,26 @@
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using v2rayN.Handler;
|
using v2rayN.Handler;
|
||||||
|
|
||||||
namespace v2rayN.Converters
|
namespace v2rayN.Converters;
|
||||||
{
|
|
||||||
public class MaterialDesignFonts
|
|
||||||
{
|
|
||||||
public static FontFamily MyFont { get; }
|
|
||||||
|
|
||||||
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;
|
var fontPath = Utils.GetFontsPath();
|
||||||
if (!string.IsNullOrEmpty(fontFamily))
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 = "<COMMA>";
|
||||||
|
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<string> 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<string> SubConvertUrls = new List<string> {
|
||||||
|
@"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<string> SubConvertConfig = new List<string> {
|
||||||
|
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly List<string> SubConvertTargets = new List<string> {
|
||||||
|
"",
|
||||||
|
"mixed",
|
||||||
|
"v2ray",
|
||||||
|
"clash",
|
||||||
|
"ss",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly List<string> SpeedTestUrls = new() {
|
||||||
|
@"http://cachefly.cachefly.net/100mb.test",
|
||||||
|
@"http://cachefly.cachefly.net/10mb.test"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static readonly Dictionary<string, string> 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 static readonly List<string> vmessSecuritys = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" };
|
||||||
public const string githubApiUrl = "https://api.github.com/repos";
|
public static readonly List<string> ssSecuritys = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" };
|
||||||
public const string v2rayWebsiteUrl = @"https://www.v2fly.org/";
|
public static readonly List<string> 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 const string AboutUrl = @"https://github.com/2dust/v2rayN";
|
public static readonly List<string> 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 const string UpdateUrl = AboutUrl + @"/releases";
|
public static readonly List<string> flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
|
||||||
public const string v2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases";
|
public static readonly List<string> networks = new() { "tcp", "kcp", "ws", "h2", "quic", "grpc" };
|
||||||
public const string xrayCoreUrl = "https://github.com/XTLS/Xray-core/releases";
|
public static readonly List<string> kcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
|
||||||
public const string SagerNetCoreUrl = "https://github.com/SagerNet/v2ray-core/releases";
|
public static readonly List<string> coreTypes = new() { "v2fly", "SagerNet", "Xray", "v2fly_v5", "sing_box" };
|
||||||
public const string NUrl = @"https://github.com/2dust/v2rayN/releases";
|
public static readonly List<string> coreTypes4VLESS = new() { "Xray", "sing_box" };
|
||||||
public const string clashCoreUrl = "https://github.com/Dreamacro/clash/releases";
|
public static readonly List<string> domainStrategys = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
|
||||||
public const string clashMetaCoreUrl = "https://github.com/MetaCubeX/Clash.Meta/releases";
|
public static readonly List<string> domainStrategys4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
|
||||||
public const string hysteriaCoreUrl = "https://github.com/apernet/hysteria/releases";
|
public static readonly List<string> domainMatchers = new() { "linear", "mph", "" };
|
||||||
public const string naiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases";
|
public static readonly List<string> fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
|
||||||
public const string tuicCoreUrl = "https://github.com/EAimTY/tuic/releases";
|
public static readonly List<string> userAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
|
||||||
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 static readonly List<string> allowInsecures = new() { "true", "false", "" };
|
||||||
public const string ConfigFileName = "guiNConfig.json";
|
public static readonly List<string> domainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
|
||||||
public const string ConfigDB = "guiNDB.db";
|
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
|
||||||
public const string coreConfigFileName = "config.json";
|
public static readonly List<string> alpns = new() { "h2", "http/1.1", "h2,http/1.1", "" };
|
||||||
public const string corePreConfigFileName = "configPre.json";
|
public static readonly List<string> LogLevel = new() { "debug", "info", "warning", "error", "none" };
|
||||||
|
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
|
||||||
|
public static readonly List<string> Protocols = new() { "http", "tls", "bittorrent" };
|
||||||
|
public static readonly List<string> TunMtus = new() { "9000", "1500" };
|
||||||
|
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
|
||||||
|
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
|
||||||
|
public static readonly List<string> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
|
||||||
|
|
||||||
public const string v2raySampleClient = "v2rayN.Sample.SampleClientConfig";
|
#endregion const
|
||||||
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";
|
#region global variable
|
||||||
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 static int statePort { get; set; }
|
||||||
public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal";
|
public static Job processJob { get; set; }
|
||||||
|
public static bool ShowInTaskbar { get; set; }
|
||||||
|
public static string ExePathKey { get; set; }
|
||||||
|
|
||||||
public const string DefaultSecurity = "auto";
|
#endregion global variable
|
||||||
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 = "<COMMA>";
|
|
||||||
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<string> 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<string> SubConvertUrls = new List<string> {
|
|
||||||
@"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<string> SubConvertConfig = new List<string> {
|
|
||||||
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly List<string> SubConvertTargets = new List<string> {
|
|
||||||
"",
|
|
||||||
"mixed",
|
|
||||||
"v2ray",
|
|
||||||
"clash",
|
|
||||||
"ss",
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly List<string> SpeedTestUrls = new() {
|
|
||||||
@"http://cachefly.cachefly.net/100mb.test",
|
|
||||||
@"http://cachefly.cachefly.net/10mb.test"
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly Dictionary<string, string> 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<string> vmessSecuritys = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" };
|
|
||||||
public static readonly List<string> ssSecuritys = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" };
|
|
||||||
public static readonly List<string> 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<string> 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<string> flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
|
|
||||||
public static readonly List<string> networks = new() { "tcp", "kcp", "ws", "h2", "quic", "grpc" };
|
|
||||||
public static readonly List<string> kcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
|
|
||||||
public static readonly List<string> coreTypes = new() { "v2fly", "SagerNet", "Xray", "v2fly_v5", "sing_box" };
|
|
||||||
public static readonly List<string> coreTypes4VLESS = new() { "Xray", "sing_box" };
|
|
||||||
public static readonly List<string> domainStrategys = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
|
|
||||||
public static readonly List<string> domainStrategys4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
|
|
||||||
public static readonly List<string> domainMatchers = new() { "linear", "mph", "" };
|
|
||||||
public static readonly List<string> fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
|
|
||||||
public static readonly List<string> userAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
|
|
||||||
|
|
||||||
public static readonly List<string> allowInsecures = new() { "true", "false", "" };
|
|
||||||
public static readonly List<string> domainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
|
|
||||||
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
|
|
||||||
public static readonly List<string> alpns = new() { "h2", "http/1.1", "h2,http/1.1", "" };
|
|
||||||
public static readonly List<string> LogLevel = new() { "debug", "info", "warning", "error", "none" };
|
|
||||||
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
|
|
||||||
public static readonly List<string> Protocols = new() { "http", "tls", "bittorrent" };
|
|
||||||
public static readonly List<string> TunMtus = new() { "9000", "1500" };
|
|
||||||
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
|
|
||||||
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
|
|
||||||
public static readonly List<string> 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,155 +2,154 @@
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Core configuration file processing class
|
||||||
|
/// </summary>
|
||||||
|
internal class CoreConfigHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
public static int GenerateClientConfig(ProfileItem node, string? fileName, out string msg, out string content)
|
||||||
/// Core configuration file processing class
|
|
||||||
/// </summary>
|
|
||||||
internal class CoreConfigHandler
|
|
||||||
{
|
{
|
||||||
public static int GenerateClientConfig(ProfileItem node, string? fileName, out string msg, out string content)
|
content = string.Empty;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
content = string.Empty;
|
if (node == null)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
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;
|
return -1;
|
||||||
}
|
}
|
||||||
var config = LazyConfig.Instance.GetConfig();
|
if (Utils.IsNullOrEmpty(fileName))
|
||||||
|
|
||||||
msg = ResUI.InitialConfiguration;
|
|
||||||
if (node.configType == EConfigType.Custom)
|
|
||||||
{
|
{
|
||||||
return GenerateClientCustomConfig(node, fileName, out msg);
|
content = Utils.ToJson(singboxConfig);
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var coreConfigV2ray = new CoreConfigV2ray(config);
|
Utils.ToJsonFile(singboxConfig, fileName, false);
|
||||||
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)
|
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;
|
msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
File.Copy(addressFileName, fileName);
|
||||||
}
|
|
||||||
|
|
||||||
private static int GenerateClientCustomConfig(ProfileItem node, string? fileName, out string msg)
|
//check again
|
||||||
{
|
if (!File.Exists(fileName))
|
||||||
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))
|
|
||||||
{
|
|
||||||
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;
|
msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GenerateClientSpeedtestConfigString(Config config, List<ServerTestItem> selecteds, out string msg)
|
//overwrite port
|
||||||
{
|
if (node.preSocksPort <= 0)
|
||||||
var coreConfigV2ray = new CoreConfigV2ray(config);
|
{
|
||||||
return coreConfigV2ray.GenerateClientSpeedtestConfigString(selecteds, out msg);
|
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<ServerTestItem> selecteds, out string msg)
|
||||||
|
{
|
||||||
|
var coreConfigV2ray = new CoreConfigV2ray(config);
|
||||||
|
return coreConfigV2ray.GenerateClientSpeedtestConfigString(selecteds, out msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -4,359 +4,358 @@ using System.Text;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Core process processing class
|
||||||
|
/// </summary>
|
||||||
|
internal class CoreHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
private Config _config;
|
||||||
/// Core process processing class
|
private Process? _process;
|
||||||
/// </summary>
|
private Process? _processPre;
|
||||||
internal class CoreHandler
|
private Action<bool, string> _updateFunc;
|
||||||
|
|
||||||
|
public CoreHandler(Config config, Action<bool, string> update)
|
||||||
{
|
{
|
||||||
private Config _config;
|
_config = config;
|
||||||
private Process? _process;
|
_updateFunc = update;
|
||||||
private Process? _processPre;
|
|
||||||
private Action<bool, string> _updateFunc;
|
|
||||||
|
|
||||||
public CoreHandler(Config config, Action<bool, string> 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;
|
ShowMsg(false, ResUI.CheckServerSettings);
|
||||||
_updateFunc = update;
|
return;
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable("v2ray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
|
||||||
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
ShowMsg(false, msg);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public int LoadCoreConfigString(List<ServerTestItem> _selecteds)
|
|
||||||
{
|
{
|
||||||
int pid = -1;
|
ShowMsg(false, msg);
|
||||||
string configStr = CoreConfigHandler.GenerateClientSpeedtestConfigString(_config, _selecteds, out string msg);
|
ShowMsg(true, $"{node.GetSummary()}");
|
||||||
if (configStr == "")
|
CoreStop();
|
||||||
{
|
CoreStart(node);
|
||||||
ShowMsg(false, msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ShowMsg(false, msg);
|
|
||||||
pid = CoreStartViaString(configStr);
|
|
||||||
}
|
|
||||||
return pid;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void CoreStop()
|
public int LoadCoreConfigString(List<ServerTestItem> _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;
|
KillProcess(_process);
|
||||||
if (_process != null)
|
_process.Dispose();
|
||||||
{
|
_process = null;
|
||||||
KillProcess(_process);
|
hasProc = true;
|
||||||
_process.Dispose();
|
}
|
||||||
_process = null;
|
|
||||||
hasProc = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_processPre != null)
|
if (_processPre != null)
|
||||||
{
|
{
|
||||||
KillProcess(_processPre);
|
KillProcess(_processPre);
|
||||||
_processPre.Dispose();
|
_processPre.Dispose();
|
||||||
_processPre = null;
|
_processPre = null;
|
||||||
hasProc = true;
|
hasProc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasProc)
|
if (!hasProc)
|
||||||
|
{
|
||||||
|
var coreInfos = LazyConfig.Instance.GetCoreInfos();
|
||||||
|
foreach (var it in coreInfos)
|
||||||
{
|
{
|
||||||
var coreInfos = LazyConfig.Instance.GetCoreInfos();
|
if (it.coreType == ECoreType.v2rayN)
|
||||||
foreach (var it in coreInfos)
|
|
||||||
{
|
{
|
||||||
if (it.coreType == ECoreType.v2rayN)
|
continue;
|
||||||
|
}
|
||||||
|
foreach (string vName in it.coreExes)
|
||||||
|
{
|
||||||
|
Process[] existing = Process.GetProcessesByName(vName);
|
||||||
|
foreach (Process p in existing)
|
||||||
{
|
{
|
||||||
continue;
|
string? path = p.MainModule?.FileName;
|
||||||
}
|
if (path == $"{Utils.GetBinPath(vName, it.coreType)}.exe")
|
||||||
foreach (string vName in it.coreExes)
|
|
||||||
{
|
|
||||||
Process[] existing = Process.GetProcessesByName(vName);
|
|
||||||
foreach (Process p in existing)
|
|
||||||
{
|
{
|
||||||
string? path = p.MainModule?.FileName;
|
KillProcess(p);
|
||||||
if (path == $"{Utils.GetBinPath(vName, it.coreType)}.exe")
|
|
||||||
{
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Utils.IsNullOrEmpty(fileName))
|
||||||
public void CoreStopPid(int pid)
|
|
||||||
{
|
{
|
||||||
try
|
string msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.coreType), string.Join(", ", coreInfo.coreExes.ToArray()), coreInfo.coreUrl);
|
||||||
{
|
Utils.SaveLog(msg);
|
||||||
Process _p = Process.GetProcessById(pid);
|
ShowMsg(false, msg);
|
||||||
KillProcess(_p);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utils.SaveLog(ex.Message, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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;
|
coreType = ECoreType.sing_box;
|
||||||
foreach (string name in coreInfo.coreExes)
|
}
|
||||||
|
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";
|
var itemSocks = new ProfileItem()
|
||||||
vName = Utils.GetBinPath(vName, coreInfo.coreType);
|
|
||||||
if (File.Exists(vName))
|
|
||||||
{
|
{
|
||||||
fileName = vName;
|
coreType = ECoreType.sing_box,
|
||||||
break;
|
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<bool, string> update)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string fileName = CoreFindexe(coreInfo);
|
||||||
if (Utils.IsNullOrEmpty(fileName))
|
if (Utils.IsNullOrEmpty(fileName))
|
||||||
{
|
{
|
||||||
string msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.coreType), string.Join(", ", coreInfo.coreExes.ToArray()), coreInfo.coreUrl);
|
return null;
|
||||||
Utils.SaveLog(msg);
|
|
||||||
ShowMsg(false, msg);
|
|
||||||
}
|
}
|
||||||
return fileName;
|
Process proc = new()
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
coreType = ECoreType.sing_box;
|
StartInfo = new ProcessStartInfo
|
||||||
}
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
var itemSocks = new ProfileItem()
|
FileName = fileName,
|
||||||
{
|
Arguments = string.Format(coreInfo.arguments, configPath),
|
||||||
coreType = ECoreType.sing_box,
|
WorkingDirectory = Utils.GetConfigPath(),
|
||||||
configType = EConfigType.Socks,
|
UseShellExecute = false,
|
||||||
address = Global.Loopback,
|
RedirectStandardOutput = displayLog,
|
||||||
port = node.preSocksPort
|
RedirectStandardError = displayLog,
|
||||||
};
|
CreateNoWindow = true,
|
||||||
string fileName2 = Utils.GetConfigPath(Global.corePreConfigFileName);
|
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
|
||||||
if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0)
|
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
if (displayLog)
|
||||||
|
|
||||||
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);
|
proc.OutputDataReceived += (sender, e) =>
|
||||||
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))
|
if (!string.IsNullOrEmpty(e.Data))
|
||||||
{
|
{
|
||||||
string msg = e.Data + Environment.NewLine;
|
string msg = e.Data + Environment.NewLine;
|
||||||
ShowMsg(false, msg);
|
update(false, msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
p.Start();
|
proc.ErrorDataReceived += (sender, e) =>
|
||||||
p.BeginOutputReadLine();
|
|
||||||
p.BeginErrorReadLine();
|
|
||||||
|
|
||||||
p.StandardInput.Write(configStr);
|
|
||||||
p.StandardInput.Close();
|
|
||||||
|
|
||||||
if (p.WaitForExit(1000))
|
|
||||||
{
|
{
|
||||||
throw new Exception(p.StandardError.ReadToEnd());
|
if (!string.IsNullOrEmpty(e.Data))
|
||||||
}
|
|
||||||
|
|
||||||
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<bool, string> update)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string fileName = CoreFindexe(coreInfo);
|
|
||||||
if (Utils.IsNullOrEmpty(fileName))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Process proc = new()
|
|
||||||
{
|
|
||||||
StartInfo = new ProcessStartInfo
|
|
||||||
{
|
{
|
||||||
FileName = fileName,
|
string msg = e.Data + Environment.NewLine;
|
||||||
Arguments = string.Format(coreInfo.arguments, configPath),
|
update(false, msg);
|
||||||
WorkingDirectory = Utils.GetConfigPath(),
|
|
||||||
UseShellExecute = false,
|
|
||||||
RedirectStandardOutput = displayLog,
|
|
||||||
RedirectStandardError = displayLog,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
|
|
||||||
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
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);
|
proc.BeginOutputReadLine();
|
||||||
string msg = ex.Message;
|
proc.BeginErrorReadLine();
|
||||||
update(true, msg);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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
|
Utils.SaveLog(ex.Message, ex);
|
||||||
{
|
string msg = ex.Message;
|
||||||
p.CloseMainWindow();
|
update(true, msg);
|
||||||
p.WaitForExit(100);
|
return null;
|
||||||
if (!p.HasExited)
|
|
||||||
{
|
|
||||||
p.Kill();
|
|
||||||
p.WaitForExit(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utils.SaveLog(ex.Message, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#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
|
||||||
}
|
}
|
||||||
|
|
@ -7,272 +7,266 @@ using System.Net.Sockets;
|
||||||
using v2rayN.Base;
|
using v2rayN.Base;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///Download
|
||||||
|
/// </summary>
|
||||||
|
internal class DownloadHandle
|
||||||
{
|
{
|
||||||
/// <summary>
|
public event EventHandler<ResultEventArgs>? UpdateCompleted;
|
||||||
///Download
|
|
||||||
/// </summary>
|
public event ErrorEventHandler? Error;
|
||||||
internal class DownloadHandle
|
|
||||||
|
public class ResultEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public event EventHandler<ResultEventArgs>? UpdateCompleted;
|
public bool Success;
|
||||||
|
public string Msg;
|
||||||
|
|
||||||
public event ErrorEventHandler? Error;
|
public ResultEventArgs(bool success, string msg)
|
||||||
|
|
||||||
public class ResultEventArgs : EventArgs
|
|
||||||
{
|
{
|
||||||
public bool Success;
|
Success = success;
|
||||||
public string Msg;
|
Msg = msg;
|
||||||
|
|
||||||
public ResultEventArgs(bool success, string msg)
|
|
||||||
{
|
|
||||||
Success = success;
|
|
||||||
Msg = msg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action<bool, string> update)
|
public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action<bool, string> update)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
|
||||||
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
|
||||||
|
|
||||||
var progress = new Progress<string>();
|
|
||||||
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<double>();
|
|
||||||
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<string?> UrlRedirectAsync(string url, bool blProxy)
|
|
||||||
{
|
{
|
||||||
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
||||||
var webRequestHandler = new SocketsHttpHandler
|
|
||||||
|
var progress = new Progress<string>();
|
||||||
|
progress.ProgressChanged += (sender, value) =>
|
||||||
{
|
{
|
||||||
AllowAutoRedirect = false,
|
if (update != null)
|
||||||
Proxy = GetWebProxy(blProxy)
|
{
|
||||||
|
string msg = $"{value}";
|
||||||
|
update(false, msg);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
HttpClient client = new(webRequestHandler);
|
|
||||||
|
|
||||||
HttpResponseMessage response = await client.GetAsync(url);
|
await DownloaderHelper.Instance.DownloadDataAsync4Speed(webProxy,
|
||||||
if (response.StatusCode == HttpStatusCode.Redirect && response.Headers.Location is not null)
|
url,
|
||||||
|
progress,
|
||||||
|
downloadTimeout);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
update(false, ex.Message);
|
||||||
|
if (ex.InnerException != null)
|
||||||
{
|
{
|
||||||
return response.Headers.Location.ToString();
|
update(false, ex.InnerException.Message);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Utils.SaveLog("StatusCode error: " + url);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<string?> TryDownloadString(string url, bool blProxy, string userAgent)
|
public async Task DownloadFileAsync(string url, bool blProxy, int downloadTimeout)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
||||||
{
|
UpdateCompleted?.Invoke(this, new ResultEventArgs(false, $"{ResUI.Downloading} {url}"));
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
var progress = new Progress<double>();
|
||||||
|
progress.ProgressChanged += (sender, value) =>
|
||||||
{
|
{
|
||||||
var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent);
|
UpdateCompleted?.Invoke(this, new ResultEventArgs(value > 100, $"...{value}%"));
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
var webProxy = GetWebProxy(blProxy);
|
||||||
{
|
await DownloaderHelper.Instance.DownloadFileAsync(webProxy,
|
||||||
using var wc = new WebClient();
|
url,
|
||||||
wc.Proxy = GetWebProxy(blProxy);
|
Utils.GetTempPath(Utils.GetDownloadFileName(url)),
|
||||||
var result3 = await wc.DownloadStringTaskAsync(url);
|
progress,
|
||||||
if (!Utils.IsNullOrEmpty(result3))
|
downloadTimeout);
|
||||||
{
|
}
|
||||||
return result3;
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
}
|
Utils.SaveLog(ex.Message, ex);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Error?.Invoke(this, new ErrorEventArgs(ex));
|
||||||
|
if (ex.InnerException != null)
|
||||||
|
{
|
||||||
|
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
public async Task<string?> TryDownloadString(string url, bool blProxy, string userAgent)
|
||||||
/// DownloadString
|
{
|
||||||
/// </summary>
|
try
|
||||||
/// <param name="url"></param>
|
|
||||||
public async Task<string?> DownloadStringAsync(string url, bool blProxy, string userAgent)
|
|
||||||
{
|
{
|
||||||
try
|
var result1 = await DownloadStringAsync(url, blProxy, userAgent);
|
||||||
|
if (!Utils.IsNullOrEmpty(result1))
|
||||||
{
|
{
|
||||||
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
return result1;
|
||||||
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)
|
}
|
||||||
|
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.InnerException));
|
||||||
Error?.Invoke(this, new ErrorEventArgs(ex));
|
|
||||||
if (ex.InnerException != null)
|
|
||||||
{
|
|
||||||
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
try
|
||||||
/// DownloadString
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="url"></param>
|
|
||||||
public async Task<string?> DownloadStringViaDownloader(string url, bool blProxy, string userAgent)
|
|
||||||
{
|
{
|
||||||
try
|
var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent);
|
||||||
|
if (!Utils.IsNullOrEmpty(result2))
|
||||||
{
|
{
|
||||||
Utils.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
return result2;
|
||||||
|
|
||||||
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)
|
}
|
||||||
|
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.InnerException));
|
||||||
Error?.Invoke(this, new ErrorEventArgs(ex));
|
|
||||||
if (ex.InnerException != null)
|
|
||||||
{
|
|
||||||
Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DownloadString
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
public async Task<string?> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DownloadString
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url"></param>
|
||||||
|
public async Task<string?> 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<int> RunAvailabilityCheck(IWebProxy? webProxy)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (webProxy == null)
|
||||||
|
{
|
||||||
|
webProxy = GetWebProxy(true);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (webProxy == null)
|
var config = LazyConfig.Instance.GetConfig();
|
||||||
{
|
int responseTime = await GetRealPingTime(config.speedTestItem.speedPingTestUrl, webProxy, 10);
|
||||||
webProxy = GetWebProxy(true);
|
return responseTime;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -280,60 +274,65 @@ namespace v2rayN.Handler
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
|
|
||||||
{
|
{
|
||||||
int responseTime = -1;
|
Utils.SaveLog(ex.Message, ex);
|
||||||
try
|
return -1;
|
||||||
{
|
}
|
||||||
Stopwatch timer = Stopwatch.StartNew();
|
}
|
||||||
|
|
||||||
using var cts = new CancellationTokenSource();
|
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
|
||||||
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
|
{
|
||||||
using var client = new HttpClient(new SocketsHttpHandler()
|
int responseTime = -1;
|
||||||
{
|
try
|
||||||
Proxy = webProxy,
|
{
|
||||||
UseProxy = webProxy != null
|
Stopwatch timer = Stopwatch.StartNew();
|
||||||
});
|
|
||||||
await client.GetAsync(url, cts.Token);
|
|
||||||
|
|
||||||
responseTime = timer.Elapsed.Milliseconds;
|
using var cts = new CancellationTokenSource();
|
||||||
}
|
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
|
||||||
catch (Exception ex)
|
using var client = new HttpClient(new SocketsHttpHandler()
|
||||||
{
|
{
|
||||||
//Utils.SaveLog(ex.Message, ex);
|
Proxy = webProxy,
|
||||||
}
|
UseProxy = webProxy != null
|
||||||
return responseTime;
|
});
|
||||||
|
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)
|
return new WebProxy(Global.Loopback, httpPort);
|
||||||
{
|
}
|
||||||
if (!blProxy)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var httpPort = LazyConfig.Instance.GetLocalPort(Global.InboundHttp);
|
|
||||||
if (!SocketCheck(Global.Loopback, httpPort))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
catch (Exception)
|
||||||
private bool SocketCheck(string ip, int port)
|
|
||||||
{
|
{
|
||||||
try
|
return false;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,174 +7,173 @@ using System.Windows.Interop;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
|
||||||
|
public sealed class HotkeyHandler
|
||||||
{
|
{
|
||||||
public sealed class HotkeyHandler
|
private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
|
||||||
|
public static HotkeyHandler Instance = _instance.Value;
|
||||||
|
|
||||||
|
private const int WmHotkey = 0x0312;
|
||||||
|
|
||||||
|
private Config _config
|
||||||
{
|
{
|
||||||
private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
|
get => LazyConfig.Instance.GetConfig();
|
||||||
public static HotkeyHandler Instance = _instance.Value;
|
}
|
||||||
|
|
||||||
private const int WmHotkey = 0x0312;
|
private Dictionary<int, List<EGlobalHotkey>> _hotkeyTriggerDic;
|
||||||
|
|
||||||
private Config _config
|
public bool IsPause { get; set; } = false;
|
||||||
|
|
||||||
|
public event Action<bool, string>? UpdateViewEvent;
|
||||||
|
|
||||||
|
public event Action<EGlobalHotkey>? 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();
|
if (item.KeyCode != null && item.KeyCode != Key.None)
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<int, List<EGlobalHotkey>> _hotkeyTriggerDic;
|
|
||||||
|
|
||||||
public bool IsPause { get; set; } = false;
|
|
||||||
|
|
||||||
public event Action<bool, string>? UpdateViewEvent;
|
|
||||||
|
|
||||||
public event Action<EGlobalHotkey>? 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)
|
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);
|
_hotkeyTriggerDic.Add(key, new() { item.eGlobalHotkey });
|
||||||
KeyModifiers modifiers = KeyModifiers.None;
|
}
|
||||||
if (item.Control) modifiers |= KeyModifiers.Ctrl;
|
else
|
||||||
if (item.Shift) modifiers |= KeyModifiers.Shift;
|
{
|
||||||
if (item.Alt) modifiers |= KeyModifiers.Alt;
|
if (!_hotkeyTriggerDic[key].Contains(item.eGlobalHotkey))
|
||||||
key = (key << 16) | (int)modifiers;
|
_hotkeyTriggerDic[key].Add(item.eGlobalHotkey);
|
||||||
if (!_hotkeyTriggerDic.ContainsKey(key))
|
|
||||||
{
|
|
||||||
_hotkeyTriggerDic.Add(key, new() { item.eGlobalHotkey });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_hotkeyTriggerDic[key].Contains(item.eGlobalHotkey))
|
|
||||||
_hotkeyTriggerDic[key].Add(item.eGlobalHotkey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public void Load()
|
|
||||||
{
|
public void Load()
|
||||||
foreach (var _hotkeyCode in _hotkeyTriggerDic.Keys)
|
{
|
||||||
{
|
foreach (var _hotkeyCode in _hotkeyTriggerDic.Keys)
|
||||||
var hotkeyInfo = GetHotkeyInfo(_hotkeyCode);
|
{
|
||||||
bool isSuccess = false;
|
var hotkeyInfo = GetHotkeyInfo(_hotkeyCode);
|
||||||
string msg;
|
bool isSuccess = false;
|
||||||
|
string msg;
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
isSuccess = RegisterHotKey(IntPtr.Zero, _hotkeyCode, hotkeyInfo.fsModifiers, hotkeyInfo.vKey);
|
{
|
||||||
});
|
isSuccess = RegisterHotKey(IntPtr.Zero, _hotkeyCode, hotkeyInfo.fsModifiers, hotkeyInfo.vKey);
|
||||||
foreach (var name in hotkeyInfo.Names)
|
});
|
||||||
{
|
foreach (var name in hotkeyInfo.Names)
|
||||||
if (isSuccess)
|
{
|
||||||
{
|
if (isSuccess)
|
||||||
msg = string.Format(ResUI.RegisterGlobalHotkeySuccessfully, $"{name}({hotkeyInfo.hotkeyStr})");
|
{
|
||||||
}
|
msg = string.Format(ResUI.RegisterGlobalHotkeySuccessfully, $"{name}({hotkeyInfo.hotkeyStr})");
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
var errInfo = new Win32Exception(Marshal.GetLastWin32Error()).Message;
|
{
|
||||||
msg = string.Format(ResUI.RegisterGlobalHotkeyFailed, $"{name}({hotkeyInfo.hotkeyStr})", errInfo);
|
var errInfo = new Win32Exception(Marshal.GetLastWin32Error()).Message;
|
||||||
}
|
msg = string.Format(ResUI.RegisterGlobalHotkeyFailed, $"{name}({hotkeyInfo.hotkeyStr})", errInfo);
|
||||||
UpdateViewEvent?.Invoke(false, msg);
|
}
|
||||||
}
|
UpdateViewEvent?.Invoke(false, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public void ReLoad()
|
|
||||||
{
|
public void ReLoad()
|
||||||
foreach (var hotkey in _hotkeyTriggerDic.Keys)
|
{
|
||||||
{
|
foreach (var hotkey in _hotkeyTriggerDic.Keys)
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
{
|
||||||
{
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
UnregisterHotKey(IntPtr.Zero, hotkey);
|
{
|
||||||
});
|
UnregisterHotKey(IntPtr.Zero, hotkey);
|
||||||
}
|
});
|
||||||
Init();
|
}
|
||||||
Load();
|
Init();
|
||||||
}
|
Load();
|
||||||
|
}
|
||||||
private (int fsModifiers, int vKey, string hotkeyStr, List<string> Names) GetHotkeyInfo(int hotkeycode)
|
|
||||||
{
|
private (int fsModifiers, int vKey, string hotkeyStr, List<string> Names) GetHotkeyInfo(int hotkeycode)
|
||||||
var _fsModifiers = hotkeycode & 0xffff;
|
{
|
||||||
var _vkey = (hotkeycode >> 16) & 0xffff;
|
var _fsModifiers = hotkeycode & 0xffff;
|
||||||
var _hotkeyStr = new StringBuilder();
|
var _vkey = (hotkeycode >> 16) & 0xffff;
|
||||||
var _names = new List<string>();
|
var _hotkeyStr = new StringBuilder();
|
||||||
|
var _names = new List<string>();
|
||||||
var mdif = (KeyModifiers)_fsModifiers;
|
|
||||||
var key = KeyInterop.KeyFromVirtualKey(_vkey);
|
var mdif = (KeyModifiers)_fsModifiers;
|
||||||
if ((mdif & KeyModifiers.Ctrl) == KeyModifiers.Ctrl) _hotkeyStr.Append($"{KeyModifiers.Ctrl}+");
|
var key = KeyInterop.KeyFromVirtualKey(_vkey);
|
||||||
if ((mdif & KeyModifiers.Alt) == KeyModifiers.Alt) _hotkeyStr.Append($"{KeyModifiers.Alt}+");
|
if ((mdif & KeyModifiers.Ctrl) == KeyModifiers.Ctrl) _hotkeyStr.Append($"{KeyModifiers.Ctrl}+");
|
||||||
if ((mdif & KeyModifiers.Shift) == KeyModifiers.Shift) _hotkeyStr.Append($"{KeyModifiers.Shift}+");
|
if ((mdif & KeyModifiers.Alt) == KeyModifiers.Alt) _hotkeyStr.Append($"{KeyModifiers.Alt}+");
|
||||||
_hotkeyStr.Append(key.ToString());
|
if ((mdif & KeyModifiers.Shift) == KeyModifiers.Shift) _hotkeyStr.Append($"{KeyModifiers.Shift}+");
|
||||||
|
_hotkeyStr.Append(key.ToString());
|
||||||
foreach (var name in _hotkeyTriggerDic[hotkeycode])
|
|
||||||
{
|
foreach (var name in _hotkeyTriggerDic[hotkeycode])
|
||||||
_names.Add(name.ToString());
|
{
|
||||||
}
|
_names.Add(name.ToString());
|
||||||
|
}
|
||||||
return (_fsModifiers, _vkey, _hotkeyStr.ToString(), _names);
|
|
||||||
}
|
return (_fsModifiers, _vkey, _hotkeyStr.ToString(), _names);
|
||||||
|
}
|
||||||
private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled)
|
|
||||||
{
|
private void OnThreadPreProcessMessage(ref MSG msg, ref bool handled)
|
||||||
if (msg.message != WmHotkey || !_hotkeyTriggerDic.ContainsKey((int)msg.lParam))
|
{
|
||||||
{
|
if (msg.message != WmHotkey || !_hotkeyTriggerDic.ContainsKey((int)msg.lParam))
|
||||||
return;
|
{
|
||||||
}
|
return;
|
||||||
handled = true;
|
}
|
||||||
var _hotKeyCode = (int)msg.lParam;
|
handled = true;
|
||||||
if (IsPause)
|
var _hotKeyCode = (int)msg.lParam;
|
||||||
{
|
if (IsPause)
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
{
|
||||||
{
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
UIElement? element = Keyboard.FocusedElement as UIElement;
|
{
|
||||||
if (element != null)
|
UIElement? element = Keyboard.FocusedElement as UIElement;
|
||||||
{
|
if (element != null)
|
||||||
var _keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice,
|
{
|
||||||
PresentationSource.FromVisual(element), 0,
|
var _keyEventArgs = new KeyEventArgs(Keyboard.PrimaryDevice,
|
||||||
KeyInterop.KeyFromVirtualKey(GetHotkeyInfo(_hotKeyCode).vKey))
|
PresentationSource.FromVisual(element), 0,
|
||||||
{
|
KeyInterop.KeyFromVirtualKey(GetHotkeyInfo(_hotKeyCode).vKey))
|
||||||
RoutedEvent = UIElement.KeyDownEvent
|
{
|
||||||
};
|
RoutedEvent = UIElement.KeyDownEvent
|
||||||
element.RaiseEvent(_keyEventArgs);
|
};
|
||||||
}
|
element.RaiseEvent(_keyEventArgs);
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
foreach (var keyEvent in _hotkeyTriggerDic[(int)msg.lParam])
|
{
|
||||||
{
|
foreach (var keyEvent in _hotkeyTriggerDic[(int)msg.lParam])
|
||||||
HotkeyTriggerEvent?.Invoke(keyEvent);
|
{
|
||||||
}
|
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 RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
|
||||||
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||||||
[Flags]
|
|
||||||
private enum KeyModifiers
|
[Flags]
|
||||||
{
|
private enum KeyModifiers
|
||||||
None = 0x0000,
|
{
|
||||||
Alt = 0x0001,
|
None = 0x0000,
|
||||||
Ctrl = 0x0002,
|
Alt = 0x0001,
|
||||||
Shift = 0x0004,
|
Ctrl = 0x0002,
|
||||||
Win = 0x0008,
|
Shift = 0x0004,
|
||||||
NoRepeat = 0x4000
|
Win = 0x0008,
|
||||||
}
|
NoRepeat = 0x4000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,368 +1,367 @@
|
||||||
using v2rayN.Base;
|
using v2rayN.Base;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
|
||||||
|
public sealed class LazyConfig
|
||||||
{
|
{
|
||||||
public sealed class LazyConfig
|
private static readonly Lazy<LazyConfig> _instance = new(() => new());
|
||||||
|
private Config _config;
|
||||||
|
private List<CoreInfo> coreInfos;
|
||||||
|
|
||||||
|
public static LazyConfig Instance => _instance.Value;
|
||||||
|
|
||||||
|
public LazyConfig()
|
||||||
{
|
{
|
||||||
private static readonly Lazy<LazyConfig> _instance = new(() => new());
|
SqliteHelper.Instance.CreateTable<SubItem>();
|
||||||
private Config _config;
|
SqliteHelper.Instance.CreateTable<ProfileItem>();
|
||||||
private List<CoreInfo> coreInfos;
|
SqliteHelper.Instance.CreateTable<ServerStatItem>();
|
||||||
|
SqliteHelper.Instance.CreateTable<RoutingItem>();
|
||||||
|
SqliteHelper.Instance.CreateTable<ProfileExItem>();
|
||||||
|
SqliteHelper.Instance.CreateTable<DNSItem>();
|
||||||
|
}
|
||||||
|
|
||||||
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<SubItem>();
|
|
||||||
SqliteHelper.Instance.CreateTable<ProfileItem>();
|
|
||||||
SqliteHelper.Instance.CreateTable<ServerStatItem>();
|
|
||||||
SqliteHelper.Instance.CreateTable<RoutingItem>();
|
|
||||||
SqliteHelper.Instance.CreateTable<ProfileExItem>();
|
|
||||||
SqliteHelper.Instance.CreateTable<DNSItem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
#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;
|
return localPort;
|
||||||
}
|
}
|
||||||
|
else if (protocol == Global.InboundHttp)
|
||||||
public List<SubItem> SubItems()
|
|
||||||
{
|
{
|
||||||
return SqliteHelper.Instance.Table<SubItem>().ToList();
|
return localPort + 1;
|
||||||
}
|
}
|
||||||
|
else if (protocol == Global.InboundSocks2)
|
||||||
public SubItem GetSubItem(string subid)
|
|
||||||
{
|
{
|
||||||
return SqliteHelper.Instance.Table<SubItem>().FirstOrDefault(t => t.id == subid);
|
return localPort + 2;
|
||||||
}
|
}
|
||||||
|
else if (protocol == Global.InboundHttp2)
|
||||||
public List<ProfileItem> ProfileItems(string subid)
|
|
||||||
{
|
{
|
||||||
if (Utils.IsNullOrEmpty(subid))
|
return localPort + 3;
|
||||||
{
|
|
||||||
return SqliteHelper.Instance.Table<ProfileItem>().ToList();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return SqliteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (protocol == ESysProxyType.Pac.ToString())
|
||||||
public List<string> ProfileItemIndexs(string subid)
|
|
||||||
{
|
{
|
||||||
if (Utils.IsNullOrEmpty(subid))
|
return localPort + 4;
|
||||||
{
|
|
||||||
return SqliteHelper.Instance.Table<ProfileItem>().Select(t => t.indexId).ToList();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return SqliteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).Select(t => t.indexId).ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (protocol == "speedtest")
|
||||||
public List<ProfileItemModel> ProfileItems(string subid, string filter)
|
|
||||||
{
|
{
|
||||||
var sql = @$"select a.*
|
return localPort + 103;
|
||||||
|
}
|
||||||
|
return localPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SubItem> SubItems()
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<SubItem>().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubItem GetSubItem(string subid)
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<SubItem>().FirstOrDefault(t => t.id == subid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ProfileItem> ProfileItems(string subid)
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(subid))
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<ProfileItem>().ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> ProfileItemIndexs(string subid)
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(subid))
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<ProfileItem>().Select(t => t.indexId).ToList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).Select(t => t.indexId).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ProfileItemModel> ProfileItems(string subid, string filter)
|
||||||
|
{
|
||||||
|
var sql = @$"select a.*
|
||||||
,b.remarks subRemarks
|
,b.remarks subRemarks
|
||||||
from ProfileItem a
|
from ProfileItem a
|
||||||
left join SubItem b on a.subid = b.id
|
left join SubItem b on a.subid = b.id
|
||||||
where 1=1 ";
|
where 1=1 ";
|
||||||
if (!Utils.IsNullOrEmpty(subid))
|
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<ProfileItemModel>(sql).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProfileItem? GetProfileItem(string indexId)
|
|
||||||
{
|
{
|
||||||
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<ProfileItem>().FirstOrDefault(it => it.indexId == indexId);
|
sql += String.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RoutingItem> RoutingItems()
|
return SqliteHelper.Instance.Query<ProfileItemModel>(sql).ToList();
|
||||||
{
|
|
||||||
return SqliteHelper.Instance.Table<RoutingItem>().Where(it => it.locked == false).OrderBy(t => t.sort).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingItem GetRoutingItem(string id)
|
|
||||||
{
|
|
||||||
return SqliteHelper.Instance.Table<RoutingItem>().FirstOrDefault(it => it.locked == false && it.id == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DNSItem> DNSItems()
|
|
||||||
{
|
|
||||||
return SqliteHelper.Instance.Table<DNSItem>().ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DNSItem GetDNSItem(ECoreType eCoreType)
|
|
||||||
{
|
|
||||||
return SqliteHelper.Instance.Table<DNSItem>().FirstOrDefault(it => it.coreType == eCoreType);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Config
|
|
||||||
|
|
||||||
#region Core Type
|
|
||||||
|
|
||||||
public List<string> 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<CoreInfo>? 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<string> { "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<string> { "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<string> { "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<string> { "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<string> { "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<string> { "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<string> { "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<string> { "naiveproxy", "naive" },
|
|
||||||
arguments = "config.json",
|
|
||||||
coreUrl = Global.naiveproxyCoreUrl,
|
|
||||||
redirectInfo = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
coreInfos.Add(new CoreInfo
|
|
||||||
{
|
|
||||||
coreType = ECoreType.tuic,
|
|
||||||
coreExes = new List<string> { "tuic-client", "tuic" },
|
|
||||||
arguments = "-c config.json",
|
|
||||||
coreUrl = Global.tuicCoreUrl,
|
|
||||||
redirectInfo = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
coreInfos.Add(new CoreInfo
|
|
||||||
{
|
|
||||||
coreType = ECoreType.sing_box,
|
|
||||||
coreExes = new List<string> { "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<string> { "juicity-client", "juicity" },
|
|
||||||
arguments = "run -c config.json",
|
|
||||||
coreUrl = Global.juicityCoreUrl
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Core Type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ProfileItem? GetProfileItem(string indexId)
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(indexId))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return SqliteHelper.Instance.Table<ProfileItem>().FirstOrDefault(it => it.indexId == indexId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RoutingItem> RoutingItems()
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<RoutingItem>().Where(it => it.locked == false).OrderBy(t => t.sort).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutingItem GetRoutingItem(string id)
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<RoutingItem>().FirstOrDefault(it => it.locked == false && it.id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DNSItem> DNSItems()
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<DNSItem>().ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DNSItem GetDNSItem(ECoreType eCoreType)
|
||||||
|
{
|
||||||
|
return SqliteHelper.Instance.Table<DNSItem>().FirstOrDefault(it => it.coreType == eCoreType);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Config
|
||||||
|
|
||||||
|
#region Core Type
|
||||||
|
|
||||||
|
public List<string> 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<CoreInfo>? 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<string> { "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<string> { "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<string> { "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<string> { "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<string> { "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<string> { "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<string> { "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<string> { "naiveproxy", "naive" },
|
||||||
|
arguments = "config.json",
|
||||||
|
coreUrl = Global.naiveproxyCoreUrl,
|
||||||
|
redirectInfo = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
coreInfos.Add(new CoreInfo
|
||||||
|
{
|
||||||
|
coreType = ECoreType.tuic,
|
||||||
|
coreExes = new List<string> { "tuic-client", "tuic" },
|
||||||
|
arguments = "-c config.json",
|
||||||
|
coreUrl = Global.tuicCoreUrl,
|
||||||
|
redirectInfo = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
coreInfos.Add(new CoreInfo
|
||||||
|
{
|
||||||
|
coreType = ECoreType.sing_box,
|
||||||
|
coreExes = new List<string> { "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<string> { "juicity-client", "juicity" },
|
||||||
|
arguments = "run -c config.json",
|
||||||
|
coreUrl = Global.juicityCoreUrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Core Type
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,224 +5,223 @@ using System.Windows.Media.Imaging;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
|
||||||
|
public sealed class MainFormHandler
|
||||||
{
|
{
|
||||||
public sealed class MainFormHandler
|
private static readonly Lazy<MainFormHandler> instance = new(() => new());
|
||||||
|
public static MainFormHandler Instance => instance.Value;
|
||||||
|
|
||||||
|
public Icon GetNotifyIcon(Config config)
|
||||||
{
|
{
|
||||||
private static readonly Lazy<MainFormHandler> instance = new(() => new());
|
try
|
||||||
public static MainFormHandler Instance => instance.Value;
|
|
||||||
|
|
||||||
public Icon GetNotifyIcon(Config config)
|
|
||||||
{
|
{
|
||||||
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;
|
return createdIcon;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utils.SaveLog(ex.Message, ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Export2ClientConfig(ProfileItem item, Config config)
|
//Load from local file
|
||||||
{
|
var fileName = Utils.GetPath($"NotifyIcon{index + 1}.ico");
|
||||||
if (item == null)
|
if (File.Exists(fileName))
|
||||||
{
|
{
|
||||||
return;
|
return new Icon(fileName);
|
||||||
}
|
}
|
||||||
if (item.configType == EConfigType.Custom)
|
return index switch
|
||||||
{
|
{
|
||||||
UI.Show(ResUI.NonVmessService);
|
0 => Properties.Resources.NotifyIcon1,
|
||||||
return;
|
1 => Properties.Resources.NotifyIcon2,
|
||||||
}
|
2 => Properties.Resources.NotifyIcon3,
|
||||||
|
3 => Properties.Resources.NotifyIcon2,
|
||||||
SaveFileDialog fileDialog = new()
|
_ => Properties.Resources.NotifyIcon1, // default
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
public void UpdateTask(Config config, Action<bool, string> update)
|
|
||||||
{
|
{
|
||||||
Task.Run(() => UpdateTaskRunSubscription(config, update));
|
Utils.SaveLog(ex.Message, ex);
|
||||||
Task.Run(() => UpdateTaskRunGeo(config, update));
|
return Properties.Resources.NotifyIcon1;
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> 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<bool, string> 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<EGlobalHotkey> handler, Action<bool, string> update)
|
|
||||||
{
|
|
||||||
HotkeyHandler.Instance.UpdateViewEvent += update;
|
|
||||||
HotkeyHandler.Instance.HotkeyTriggerEvent += handler;
|
|
||||||
HotkeyHandler.Instance.Load();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<bool, string> update)
|
||||||
|
{
|
||||||
|
Task.Run(() => UpdateTaskRunSubscription(config, update));
|
||||||
|
Task.Run(() => UpdateTaskRunGeo(config, update));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> 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<bool, string> 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<EGlobalHotkey> handler, Action<bool, string> update)
|
||||||
|
{
|
||||||
|
HotkeyHandler.Instance.UpdateViewEvent += update;
|
||||||
|
HotkeyHandler.Instance.HotkeyTriggerEvent += handler;
|
||||||
|
HotkeyHandler.Instance.Load();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,33 +1,32 @@
|
||||||
using MaterialDesignThemes.Wpf;
|
using MaterialDesignThemes.Wpf;
|
||||||
using ReactiveUI;
|
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;
|
||||||
{
|
}
|
||||||
_snackbarMessageQueue = snackbarMessageQueue ?? throw new ArgumentNullException(nameof(snackbarMessageQueue));
|
|
||||||
|
|
||||||
//_snackbarMessageQueue = snackbarMessageQueue;
|
public void Enqueue(object content)
|
||||||
}
|
{
|
||||||
|
_snackbarMessageQueue?.Enqueue(content);
|
||||||
|
}
|
||||||
|
|
||||||
public void Enqueue(object content)
|
public void SendMessage(string msg)
|
||||||
{
|
{
|
||||||
_snackbarMessageQueue?.Enqueue(content);
|
MessageBus.Current.SendMessage(msg, "MsgView");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendMessage(string msg)
|
public void SendMessage(string msg, bool time)
|
||||||
{
|
{
|
||||||
MessageBus.Current.SendMessage(msg, "MsgView");
|
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,143 +3,142 @@ using System.Reactive.Linq;
|
||||||
using v2rayN.Base;
|
using v2rayN.Base;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
|
||||||
|
internal class ProfileExHandler
|
||||||
{
|
{
|
||||||
internal class ProfileExHandler
|
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
|
||||||
|
private ConcurrentBag<ProfileExItem> _lstProfileEx;
|
||||||
|
private Queue<string> _queIndexIds = new();
|
||||||
|
public ConcurrentBag<ProfileExItem> ProfileExs => _lstProfileEx;
|
||||||
|
public static ProfileExHandler Instance => _instance.Value;
|
||||||
|
|
||||||
|
public ProfileExHandler()
|
||||||
{
|
{
|
||||||
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
|
Init();
|
||||||
private ConcurrentBag<ProfileExItem> _lstProfileEx;
|
}
|
||||||
private Queue<string> _queIndexIds = new();
|
|
||||||
public ConcurrentBag<ProfileExItem> ProfileExs => _lstProfileEx;
|
|
||||||
public static ProfileExHandler Instance => _instance.Value;
|
|
||||||
|
|
||||||
public ProfileExHandler()
|
private void Init()
|
||||||
|
{
|
||||||
|
SqliteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
|
||||||
|
|
||||||
|
_lstProfileEx = new(SqliteHelper.Instance.Table<ProfileExItem>());
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
Init();
|
while (true)
|
||||||
}
|
|
||||||
|
|
||||||
private void Init()
|
|
||||||
{
|
|
||||||
SqliteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
|
|
||||||
|
|
||||||
_lstProfileEx = new(SqliteHelper.Instance.Table<ProfileExItem>());
|
|
||||||
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
while (true)
|
var cnt = _queIndexIds.Count;
|
||||||
|
for (int i = 0; i < cnt; i++)
|
||||||
{
|
{
|
||||||
var cnt = _queIndexIds.Count;
|
var id = _queIndexIds.Dequeue();
|
||||||
for (int i = 0; i < cnt; i++)
|
var item = _lstProfileEx.FirstOrDefault(t => t.indexId == id);
|
||||||
|
if (item is not null)
|
||||||
{
|
{
|
||||||
var id = _queIndexIds.Dequeue();
|
SqliteHelper.Instance.Replace(item);
|
||||||
var item = _lstProfileEx.FirstOrDefault(t => t.indexId == id);
|
|
||||||
if (item is not null)
|
|
||||||
{
|
|
||||||
SqliteHelper.Instance.Replace(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await Task.Delay(1000 * 60);
|
|
||||||
}
|
}
|
||||||
});
|
await Task.Delay(1000 * 60);
|
||||||
}
|
|
||||||
|
|
||||||
private void IndexIdEnqueue(string indexId)
|
|
||||||
{
|
|
||||||
if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
|
||||||
{
|
|
||||||
_queIndexIds.Enqueue(indexId);
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void AddProfileEx(string indexId, ref ProfileExItem profileEx)
|
private void IndexIdEnqueue(string indexId)
|
||||||
|
{
|
||||||
|
if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||||
{
|
{
|
||||||
profileEx = new()
|
_queIndexIds.Enqueue(indexId);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,216 +1,215 @@
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System.Runtime.InteropServices;
|
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();
|
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
|
||||||
|
m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
|
||||||
int optionCount = 1;
|
}
|
||||||
if (type == 1)
|
else if (type == 4)
|
||||||
{
|
{
|
||||||
optionCount = 1;
|
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL);
|
||||||
}
|
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region WinInet structures
|
//int optionCount = Utils.IsNullOrEmpty(strProxy) ? 1 : (Utils.IsNullOrEmpty(exceptions) ? 2 : 3);
|
||||||
|
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
// USE a proxy server ...
|
||||||
public struct InternetPerConnOptionList
|
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
|
options[1].m_Option = m_Option;
|
||||||
public IntPtr szConnection; // connection name to set/query options
|
options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy);
|
||||||
public int dwOptionCount; // number of options to set/query
|
// except for these addresses ...
|
||||||
public int dwOptionError; // on error, which option failed
|
if (optionCount > 2)
|
||||||
|
|
||||||
//[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()
|
|
||||||
{
|
{
|
||||||
Size = Marshal.SizeOf(typeof(InternetConnectionOption));
|
options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS;
|
||||||
}
|
options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions);
|
||||||
|
|
||||||
// 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
|
// default stuff
|
||||||
|
list.dwSize = Marshal.SizeOf(list);
|
||||||
|
list.szConnection = IntPtr.Zero;
|
||||||
|
list.dwOptionCount = options.Length;
|
||||||
|
list.dwOptionError = 0;
|
||||||
|
|
||||||
#region WinInet enums
|
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
|
||||||
|
// make a pointer out of all that ...
|
||||||
//
|
IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length);
|
||||||
// options manifests for Internet{Query|Set}Option
|
// copy the array over into that spot in memory ...
|
||||||
//
|
for (int i = 0; i < options.Length; ++i)
|
||||||
public enum InternetOption : uint
|
|
||||||
{
|
{
|
||||||
INTERNET_OPTION_PER_CONNECTION_OPTION = 75
|
if (Environment.Is64BitOperatingSystem)
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// 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;
|
IntPtr opt = new(optionsPtr.ToInt64() + (i * optSize));
|
||||||
|
Marshal.StructureToPtr(options[i], opt, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
IntPtr opt = new(optionsPtr.ToInt32() + (i * optSize));
|
||||||
|
Marshal.StructureToPtr(options[i], opt, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//获得代理的IP和端口
|
list.options = optionsPtr;
|
||||||
public static string? GetProxyProxyServer()
|
|
||||||
|
// 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);
|
Size = Marshal.SizeOf(typeof(InternetConnectionOption));
|
||||||
string ProxyServer = rk.GetValue("ProxyServer").ToString();
|
}
|
||||||
return ProxyServer;
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,27 +2,22 @@
|
||||||
using QRCoder.Xaml;
|
using QRCoder.Xaml;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
public class QRCodeHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
public static DrawingImage? GetQRCode(string strContent)
|
||||||
/// 含有QR码的描述类和包装编码和渲染
|
|
||||||
/// </summary>
|
|
||||||
public class QRCodeHelper
|
|
||||||
{
|
{
|
||||||
public static DrawingImage? GetQRCode(string strContent)
|
try
|
||||||
{
|
{
|
||||||
try
|
QRCodeGenerator qrGenerator = new();
|
||||||
{
|
QRCodeData qrCodeData = qrGenerator.CreateQrCode(strContent, QRCodeGenerator.ECCLevel.H);
|
||||||
QRCodeGenerator qrGenerator = new();
|
XamlQRCode qrCode = new(qrCodeData);
|
||||||
QRCodeData qrCodeData = qrGenerator.CreateQrCode(strContent, QRCodeGenerator.ECCLevel.H);
|
DrawingImage qrCodeAsXaml = qrCode.GetGraphic(40);
|
||||||
XamlQRCode qrCode = new(qrCodeData);
|
return qrCodeAsXaml;
|
||||||
DrawingImage qrCodeAsXaml = qrCode.GetGraphic(40);
|
}
|
||||||
return qrCodeAsXaml;
|
catch
|
||||||
}
|
{
|
||||||
catch
|
return null;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,423 +5,422 @@ using System.Net.Sockets;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
{
|
|
||||||
internal class SpeedtestHandler
|
|
||||||
{
|
|
||||||
private Config _config;
|
|
||||||
private CoreHandler _coreHandler;
|
|
||||||
private List<ServerTestItem> _selecteds;
|
|
||||||
private ESpeedActionType _actionType;
|
|
||||||
private Action<string, string, string> _updateFunc;
|
|
||||||
|
|
||||||
public SpeedtestHandler(Config config)
|
internal class SpeedtestHandler
|
||||||
|
{
|
||||||
|
private Config _config;
|
||||||
|
private CoreHandler _coreHandler;
|
||||||
|
private List<ServerTestItem> _selecteds;
|
||||||
|
private ESpeedActionType _actionType;
|
||||||
|
private Action<string, string, string> _updateFunc;
|
||||||
|
|
||||||
|
public SpeedtestHandler(Config config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpeedtestHandler(Config config, CoreHandler coreHandler, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<string, string, string> update)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
_coreHandler = coreHandler;
|
||||||
|
_actionType = actionType;
|
||||||
|
_updateFunc = update;
|
||||||
|
|
||||||
|
_selecteds = new List<ServerTestItem>();
|
||||||
|
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<ProfileItem> selecteds, ESpeedActionType actionType, Action<string, string, string> update)
|
switch (actionType)
|
||||||
{
|
{
|
||||||
_config = config;
|
case ESpeedActionType.Ping:
|
||||||
_coreHandler = coreHandler;
|
Task.Run(RunPing);
|
||||||
_actionType = actionType;
|
break;
|
||||||
_updateFunc = update;
|
|
||||||
|
|
||||||
_selecteds = new List<ServerTestItem>();
|
case ESpeedActionType.Tcping:
|
||||||
foreach (var it in selecteds)
|
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<ServerTestItem> 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<Task> tasks = new();
|
||||||
|
foreach (var it in _selecteds)
|
||||||
|
{
|
||||||
|
if (!it.allowTest)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (it.configType == EConfigType.Custom)
|
if (it.configType == EConfigType.Custom)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (it.port <= 0)
|
tasks.Add(Task.Run(async () =>
|
||||||
{
|
|
||||||
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<ServerTestItem> updateFun)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (var it in _selecteds.Where(it => it.configType != EConfigType.Custom))
|
|
||||||
{
|
{
|
||||||
try
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Utils.SaveLog(ex.Message, 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);
|
||||||
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()
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
int pid = -1;
|
_coreHandler.CoreStopPid(pid);
|
||||||
try
|
}
|
||||||
|
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);
|
var item = LazyConfig.Instance.GetProfileItem(it.indexId);
|
||||||
if (pid < 0)
|
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);
|
ProfileExHandler.Instance.SetTestSpeed(it.indexId, msg);
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
UpdateFunc(it.indexId, "", msg);
|
||||||
|
});
|
||||||
|
await Task.Delay(2000);
|
||||||
|
}
|
||||||
|
|
||||||
DownloadHandle downloadHandle = new DownloadHandle();
|
await Task.Delay((timeout + 2) * 1000);
|
||||||
|
|
||||||
List<Task> tasks = new();
|
if (pid > 0)
|
||||||
foreach (var it in _selecteds)
|
{
|
||||||
|
_coreHandler.CoreStopPid(pid);
|
||||||
|
}
|
||||||
|
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
||||||
|
ProfileExHandler.Instance.SaveTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunMixedtestAsync()
|
||||||
|
{
|
||||||
|
await RunRealPing();
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
await RunSpeedTestMulti();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ping
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
if (it.configType == EConfigType.Custom)
|
if (roundtripTime < 0 || reply.RoundtripTime < roundtripTime)
|
||||||
{
|
{
|
||||||
continue;
|
roundtripTime = reply.RoundtripTime;
|
||||||
}
|
|
||||||
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<string> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ping
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="host"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utils.SaveLog(ex.Message, ex);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return roundtripTime;
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
private string FormatOut(object time, string unit)
|
|
||||||
{
|
{
|
||||||
//if (time.ToString().Equals("-1"))
|
Utils.SaveLog(ex.Message, ex);
|
||||||
//{
|
return -1;
|
||||||
// return "Timeout";
|
|
||||||
//}
|
|
||||||
return $"{time}";
|
|
||||||
}
|
}
|
||||||
|
return roundtripTime;
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateFunc(string indexId, string delay, string speed = "")
|
private string FormatOut(object time, string unit)
|
||||||
{
|
{
|
||||||
_updateFunc(indexId, delay, speed);
|
//if (time.ToString().Equals("-1"))
|
||||||
}
|
//{
|
||||||
|
// return "Timeout";
|
||||||
|
//}
|
||||||
|
return $"{time}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFunc(string indexId, string delay, string speed = "")
|
||||||
|
{
|
||||||
|
_updateFunc(indexId, delay, speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,159 +3,158 @@ using System.Net.Sockets;
|
||||||
using v2rayN.Base;
|
using v2rayN.Base;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
|
|
||||||
namespace v2rayN.Handler
|
namespace v2rayN.Handler;
|
||||||
|
|
||||||
|
internal class StatisticsHandler
|
||||||
{
|
{
|
||||||
internal class StatisticsHandler
|
private Config _config;
|
||||||
|
private ServerStatItem? _serverStatItem;
|
||||||
|
private List<ServerStatItem> _lstServerStat;
|
||||||
|
private Action<ServerSpeedItem> _updateFunc;
|
||||||
|
private StatisticsV2ray? _statisticsV2Ray;
|
||||||
|
private StatisticsSingbox? _statisticsSingbox;
|
||||||
|
|
||||||
|
public List<ServerStatItem> ServerStat => _lstServerStat;
|
||||||
|
public bool Enable { get; set; }
|
||||||
|
|
||||||
|
public StatisticsHandler(Config config, Action<ServerSpeedItem> update)
|
||||||
{
|
{
|
||||||
private Config _config;
|
_config = config;
|
||||||
private ServerStatItem? _serverStatItem;
|
Enable = config.guiItem.enableStatistics;
|
||||||
private List<ServerStatItem> _lstServerStat;
|
if (!Enable)
|
||||||
private Action<ServerSpeedItem> _updateFunc;
|
|
||||||
private StatisticsV2ray? _statisticsV2Ray;
|
|
||||||
private StatisticsSingbox? _statisticsSingbox;
|
|
||||||
|
|
||||||
public List<ServerStatItem> ServerStat => _lstServerStat;
|
|
||||||
public bool Enable { get; set; }
|
|
||||||
|
|
||||||
public StatisticsHandler(Config config, Action<ServerSpeedItem> update)
|
|
||||||
{
|
{
|
||||||
_config = config;
|
return;
|
||||||
Enable = config.guiItem.enableStatistics;
|
|
||||||
if (!Enable)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateFunc = update;
|
|
||||||
|
|
||||||
Init();
|
|
||||||
Global.statePort = GetFreePort();
|
|
||||||
|
|
||||||
_statisticsV2Ray = new StatisticsV2ray(config, UpdateServerStat);
|
|
||||||
_statisticsSingbox = new StatisticsSingbox(config, UpdateServerStat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
_statisticsV2Ray?.Close();
|
|
||||||
_statisticsSingbox?.Close();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utils.SaveLog(ex.Message, ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
public void ClearAllServerStatistics()
|
|
||||||
{
|
{
|
||||||
SqliteHelper.Instance.Execute($"delete from ServerStatItem ");
|
Utils.SaveLog(ex.Message, ex);
|
||||||
_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<ServerStatItem>().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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<ServerStatItem>().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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,127 +2,126 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using v2rayN.Mode;
|
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<ServerSpeedItem> _updateFunc;
|
||||||
|
|
||||||
|
public StatisticsSingbox(Config config, Action<ServerSpeedItem> update)
|
||||||
{
|
{
|
||||||
private Config _config;
|
_config = config;
|
||||||
private bool _exitFlag;
|
_updateFunc = update;
|
||||||
private ClientWebSocket? webSocket;
|
_exitFlag = false;
|
||||||
private string url = string.Empty;
|
|
||||||
private Action<ServerSpeedItem> _updateFunc;
|
|
||||||
|
|
||||||
public StatisticsSingbox(Config config, Action<ServerSpeedItem> update)
|
Task.Run(() => Run());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Init()
|
||||||
|
{
|
||||||
|
await Task.Delay(5000);
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_config = config;
|
url = $"ws://{Global.Loopback}:{Global.statePort}/traffic";
|
||||||
_updateFunc = update;
|
|
||||||
_exitFlag = false;
|
|
||||||
|
|
||||||
Task.Run(() => Run());
|
if (webSocket == null)
|
||||||
}
|
|
||||||
|
|
||||||
private async void Init()
|
|
||||||
{
|
|
||||||
await Task.Delay(5000);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
url = $"ws://{Global.Loopback}:{Global.statePort}/traffic";
|
webSocket = new ClientWebSocket();
|
||||||
|
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
|
||||||
if (webSocket == null)
|
|
||||||
{
|
|
||||||
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
|
try
|
||||||
{
|
{
|
||||||
_exitFlag = true;
|
|
||||||
if (webSocket != null)
|
if (webSocket != null)
|
||||||
{
|
{
|
||||||
webSocket.Abort();
|
if (webSocket.State == WebSocketState.Aborted
|
||||||
webSocket = null;
|
|| webSocket.State == WebSocketState.Closed)
|
||||||
}
|
|
||||||
}
|
|
||||||
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.Abort();
|
||||||
|| webSocket.State == WebSocketState.Closed)
|
webSocket = null;
|
||||||
{
|
Init();
|
||||||
webSocket.Abort();
|
continue;
|
||||||
webSocket = null;
|
|
||||||
Init();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (webSocket.State != WebSocketState.Open)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = new byte[1024];
|
|
||||||
var res = await webSocket.ReceiveAsync(new ArraySegment<byte>(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<byte>(buffer), CancellationToken.None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await Task.Delay(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ParseOutput(string source, out ulong up, out ulong down)
|
if (webSocket.State != WebSocketState.Open)
|
||||||
{
|
{
|
||||||
up = 0; down = 0;
|
continue;
|
||||||
try
|
}
|
||||||
{
|
|
||||||
var trafficItem = Utils.FromJson<TrafficItem>(source);
|
var buffer = new byte[1024];
|
||||||
if (trafficItem != null)
|
var res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||||
{
|
while (!res.CloseStatus.HasValue)
|
||||||
up = trafficItem.up;
|
{
|
||||||
down = trafficItem.down;
|
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<byte>(buffer), CancellationToken.None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
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<TrafficItem>(source);
|
||||||
|
if (trafficItem != null)
|
||||||
|
{
|
||||||
|
up = trafficItem.up;
|
||||||
|
down = trafficItem.down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,117 +3,116 @@ using Grpc.Net.Client;
|
||||||
using ProtosLib.Statistics;
|
using ProtosLib.Statistics;
|
||||||
using v2rayN.Mode;
|
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<ServerSpeedItem> _updateFunc;
|
||||||
|
|
||||||
|
public StatisticsV2ray(Mode.Config config, Action<ServerSpeedItem> update)
|
||||||
{
|
{
|
||||||
private Mode.Config _config;
|
_config = config;
|
||||||
private GrpcChannel _channel;
|
_updateFunc = update;
|
||||||
private StatsService.StatsServiceClient _client;
|
_exitFlag = false;
|
||||||
private bool _exitFlag;
|
|
||||||
private Action<ServerSpeedItem> _updateFunc;
|
|
||||||
|
|
||||||
public StatisticsV2ray(Mode.Config config, Action<ServerSpeedItem> update)
|
GrpcInit();
|
||||||
|
|
||||||
|
Task.Run(Run);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GrpcInit()
|
||||||
|
{
|
||||||
|
if (_channel == null)
|
||||||
{
|
{
|
||||||
_config = config;
|
_channel = GrpcChannel.ForAddress($"{Global.httpProtocol}{Global.Loopback}:{Global.statePort}");
|
||||||
_updateFunc = update;
|
_client = new StatsService.StatsServiceClient(_channel);
|
||||||
_exitFlag = false;
|
|
||||||
|
|
||||||
GrpcInit();
|
|
||||||
|
|
||||||
Task.Run(Run);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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<Stat> source, out ServerSpeedItem server)
|
|
||||||
{
|
|
||||||
server = new();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (Stat stat in source)
|
if (_channel.State == ConnectivityState.Ready)
|
||||||
{
|
{
|
||||||
string name = stat.Name;
|
QueryStatsResponse? res = null;
|
||||||
long value = stat.Value / 1024; //KByte
|
try
|
||||||
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")
|
res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
|
||||||
{
|
|
||||||
server.proxyUp = value;
|
|
||||||
}
|
|
||||||
else if (type == "downlink")
|
|
||||||
{
|
|
||||||
server.proxyDown = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (name == Global.directTag)
|
catch
|
||||||
{
|
{
|
||||||
if (type == "uplink")
|
}
|
||||||
{
|
|
||||||
server.directUp = value;
|
if (res != null)
|
||||||
}
|
{
|
||||||
else if (type == "downlink")
|
ParseOutput(res.Stat, out ServerSpeedItem server);
|
||||||
{
|
_updateFunc(server);
|
||||||
server.directDown = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await Task.Delay(1000);
|
||||||
|
await _channel.ConnectAsync();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> 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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,217 +6,216 @@ using v2rayN.Mode;
|
||||||
using v2rayN.Properties;
|
using v2rayN.Properties;
|
||||||
using v2rayN.Tool;
|
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:
|
||||||
|
// <flags><CR-LF>
|
||||||
|
// <proxy-server><CR-LF>
|
||||||
|
// <bypass-list><CR-LF>
|
||||||
|
// <pac-url>
|
||||||
|
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;
|
static SysProxyHandle()
|
||||||
|
{
|
||||||
// In general, this won't change
|
try
|
||||||
// format:
|
|
||||||
// <flags><CR-LF>
|
|
||||||
// <proxy-server><CR-LF>
|
|
||||||
// <bypass-list><CR-LF>
|
|
||||||
// <pac-url>
|
|
||||||
private static SysproxyConfig? _userSettings = null;
|
|
||||||
|
|
||||||
private enum RET_ERRORS : int
|
|
||||||
{
|
{
|
||||||
RET_NO_ERROR = 0,
|
FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"),
|
||||||
INVALID_FORMAT = 1,
|
Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (IOException ex)
|
||||||
public static bool UpdateSysProxy(Config config, bool forceDisable)
|
|
||||||
{
|
{
|
||||||
var type = config.sysProxyType;
|
Utils.SaveLog(ex.Message, ex);
|
||||||
|
|
||||||
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;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
//}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,14 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
{
|
|
||||||
public class ComboItem
|
|
||||||
{
|
|
||||||
public string ID
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Text
|
public class ComboItem
|
||||||
{
|
{
|
||||||
get; set;
|
public string ID
|
||||||
}
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,37 +1,36 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本软件配置文件实体类
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class Config
|
||||||
{
|
{
|
||||||
/// <summary>
|
#region property
|
||||||
/// 本软件配置文件实体类
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class Config
|
|
||||||
{
|
|
||||||
#region property
|
|
||||||
|
|
||||||
public string indexId { get; set; }
|
public string indexId { get; set; }
|
||||||
public string subIndexId { get; set; }
|
public string subIndexId { get; set; }
|
||||||
public ESysProxyType sysProxyType { get; set; }
|
public ESysProxyType sysProxyType { get; set; }
|
||||||
public string systemProxyExceptions { get; set; }
|
public string systemProxyExceptions { get; set; }
|
||||||
public string systemProxyAdvancedProtocol { get; set; }
|
public string systemProxyAdvancedProtocol { get; set; }
|
||||||
|
|
||||||
#endregion property
|
#endregion property
|
||||||
|
|
||||||
#region other entities
|
#region other entities
|
||||||
|
|
||||||
public CoreBasicItem coreBasicItem { get; set; }
|
public CoreBasicItem coreBasicItem { get; set; }
|
||||||
public TunModeItem tunModeItem { get; set; }
|
public TunModeItem tunModeItem { get; set; }
|
||||||
public KcpItem kcpItem { get; set; }
|
public KcpItem kcpItem { get; set; }
|
||||||
public GrpcItem grpcItem { get; set; }
|
public GrpcItem grpcItem { get; set; }
|
||||||
public RoutingBasicItem routingBasicItem { get; set; }
|
public RoutingBasicItem routingBasicItem { get; set; }
|
||||||
public GUIItem guiItem { get; set; }
|
public GUIItem guiItem { get; set; }
|
||||||
public UIItem uiItem { get; set; }
|
public UIItem uiItem { get; set; }
|
||||||
public ConstItem constItem { get; set; }
|
public ConstItem constItem { get; set; }
|
||||||
public SpeedTestItem speedTestItem { get; set; }
|
public SpeedTestItem speedTestItem { get; set; }
|
||||||
public Mux4Sbox mux4Sbox { get; set; }
|
public Mux4Sbox mux4Sbox { get; set; }
|
||||||
public List<InItem> inbound { get; set; }
|
public List<InItem> inbound { get; set; }
|
||||||
public List<KeyEventItem> globalHotkeys { get; set; }
|
public List<KeyEventItem> globalHotkeys { get; set; }
|
||||||
public List<CoreTypeItem> coreTypeItem { get; set; }
|
public List<CoreTypeItem> coreTypeItem { get; set; }
|
||||||
|
|
||||||
#endregion other entities
|
#endregion other entities
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,209 +1,208 @@
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class CoreBasicItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
/// <summary>
|
||||||
public class CoreBasicItem
|
/// 允许日志
|
||||||
{
|
/// </summary>
|
||||||
/// <summary>
|
public bool logEnabled { get; set; }
|
||||||
/// 允许日志
|
|
||||||
/// </summary>
|
|
||||||
public bool logEnabled { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 日志等级
|
/// 日志等级
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string loglevel { get; set; }
|
public string loglevel { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 允许Mux多路复用
|
/// 允许Mux多路复用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool muxEnabled { get; set; }
|
public bool muxEnabled { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否允许不安全连接
|
/// 是否允许不安全连接
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool defAllowInsecure { get; set; }
|
public bool defAllowInsecure { get; set; }
|
||||||
|
|
||||||
public string defFingerprint { get; set; }
|
public string defFingerprint { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 默认用户代理
|
/// 默认用户代理
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string defUserAgent { get; set; }
|
public string defUserAgent { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class InItem
|
public class InItem
|
||||||
{
|
{
|
||||||
public int localPort { get; set; }
|
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 sniffingEnabled { get; set; } = true;
|
||||||
public bool routeOnly { get; set; }
|
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]
|
[Serializable]
|
||||||
public class KcpItem
|
public class KcpItem
|
||||||
{
|
{
|
||||||
public int mtu { get; set; }
|
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]
|
[Serializable]
|
||||||
public class GrpcItem
|
public class GrpcItem
|
||||||
{
|
{
|
||||||
public int idle_timeout { get; set; }
|
public int idle_timeout { get; set; }
|
||||||
public int health_check_timeout { get; set; }
|
public int health_check_timeout { get; set; }
|
||||||
public bool permit_without_stream { get; set; }
|
public bool permit_without_stream { get; set; }
|
||||||
public int initial_windows_size { get; set; }
|
public int initial_windows_size { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class GUIItem
|
public class GUIItem
|
||||||
{
|
{
|
||||||
public bool autoRun { get; set; }
|
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]
|
[Serializable]
|
||||||
public class UIItem
|
public class UIItem
|
||||||
{
|
{
|
||||||
public bool enableAutoAdjustMainLvColWidth { get; set; }
|
public bool enableAutoAdjustMainLvColWidth { get; set; }
|
||||||
public double mainWidth { get; set; }
|
public double mainWidth { get; set; }
|
||||||
public double mainHeight { get; set; }
|
public double mainHeight { get; set; }
|
||||||
public double mainGirdHeight1 { get; set; }
|
public double mainGirdHeight1 { get; set; }
|
||||||
public double mainGirdHeight2 { get; set; }
|
public double mainGirdHeight2 { get; set; }
|
||||||
public bool colorModeDark { get; set; }
|
public bool colorModeDark { get; set; }
|
||||||
public bool followSystemTheme { get; set; }
|
public bool followSystemTheme { get; set; }
|
||||||
public string? colorPrimaryName { get; set; }
|
public string? colorPrimaryName { get; set; }
|
||||||
public string currentLanguage { get; set; }
|
public string currentLanguage { get; set; }
|
||||||
public string currentFontFamily { get; set; }
|
public string currentFontFamily { get; set; }
|
||||||
public int currentFontSize { get; set; }
|
public int currentFontSize { get; set; }
|
||||||
public bool enableDragDropSort { get; set; }
|
public bool enableDragDropSort { get; set; }
|
||||||
public bool doubleClick2Activate { get; set; }
|
public bool doubleClick2Activate { get; set; }
|
||||||
public bool autoHideStartup { get; set; } = true;
|
public bool autoHideStartup { get; set; } = true;
|
||||||
public string mainMsgFilter { get; set; }
|
public string mainMsgFilter { get; set; }
|
||||||
public bool showTrayTip { get; set; }
|
public bool showTrayTip { get; set; }
|
||||||
public List<ColumnItem> mainColumnItem { get; set; }
|
public List<ColumnItem> mainColumnItem { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ConstItem
|
public class ConstItem
|
||||||
{
|
{
|
||||||
public string defIEProxyExceptions { get; set; }
|
public string defIEProxyExceptions { get; set; }
|
||||||
public string subConvertUrl { get; set; } = string.Empty;
|
public string subConvertUrl { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class KeyEventItem
|
public class KeyEventItem
|
||||||
{
|
{
|
||||||
public EGlobalHotkey eGlobalHotkey { get; set; }
|
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]
|
[Serializable]
|
||||||
public class CoreTypeItem
|
public class CoreTypeItem
|
||||||
{
|
{
|
||||||
public EConfigType configType { get; set; }
|
public EConfigType configType { get; set; }
|
||||||
|
|
||||||
public ECoreType coreType { get; set; }
|
public ECoreType coreType { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class TunModeItem
|
public class TunModeItem
|
||||||
{
|
{
|
||||||
public bool enableTun { get; set; }
|
public bool enableTun { get; set; }
|
||||||
public bool strictRoute { get; set; } = true;
|
public bool strictRoute { get; set; } = true;
|
||||||
public string stack { get; set; }
|
public string stack { get; set; }
|
||||||
public int mtu { get; set; }
|
public int mtu { get; set; }
|
||||||
public bool enableExInbound { get; set; }
|
public bool enableExInbound { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class SpeedTestItem
|
public class SpeedTestItem
|
||||||
{
|
{
|
||||||
public int speedTestTimeout { get; set; }
|
public int speedTestTimeout { get; set; }
|
||||||
public string speedTestUrl { get; set; }
|
public string speedTestUrl { get; set; }
|
||||||
public string speedPingTestUrl { get; set; }
|
public string speedPingTestUrl { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class RoutingBasicItem
|
public class RoutingBasicItem
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 域名解析策略
|
/// 域名解析策略
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string domainStrategy { get; set; }
|
public string domainStrategy { get; set; }
|
||||||
|
|
||||||
public string domainStrategy4Singbox { get; set; }
|
public string domainStrategy4Singbox { get; set; }
|
||||||
|
|
||||||
public string domainMatcher { get; set; }
|
public string domainMatcher { get; set; }
|
||||||
public string routingIndexId { get; set; }
|
public string routingIndexId { get; set; }
|
||||||
public bool enableRoutingAdvanced { get; set; }
|
public bool enableRoutingAdvanced { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ColumnItem
|
public class ColumnItem
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public int Width { get; set; }
|
public int Width { get; set; }
|
||||||
public int Index { get; set; }
|
public int Index { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class Mux4Sbox
|
public class Mux4Sbox
|
||||||
{
|
{
|
||||||
public string protocol { get; set; }
|
public string protocol { get; set; }
|
||||||
public int max_connections { get; set; }
|
public int max_connections { get; set; }
|
||||||
public int min_streams { get; set; }
|
public int min_streams { get; set; }
|
||||||
public int max_streams { get; set; }
|
public int max_streams { get; set; }
|
||||||
public bool padding { get; set; }
|
public bool padding { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,440 +1,439 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ConfigOld
|
||||||
{
|
{
|
||||||
[Serializable]
|
#region property
|
||||||
public class ConfigOld
|
|
||||||
|
/// <summary>
|
||||||
|
/// 允许日志
|
||||||
|
/// </summary>
|
||||||
|
public bool logEnabled
|
||||||
{
|
{
|
||||||
#region property
|
get; set;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 允许日志
|
|
||||||
/// </summary>
|
|
||||||
public bool logEnabled
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 日志等级
|
|
||||||
/// </summary>
|
|
||||||
public string loglevel
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string indexId
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 允许Mux多路复用
|
|
||||||
/// </summary>
|
|
||||||
public bool muxEnabled
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public ESysProxyType sysProxyType
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启用实时网速和流量统计
|
|
||||||
/// </summary>
|
|
||||||
public bool enableStatistics
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 去重时优先保留较旧(顶部)节点
|
|
||||||
/// </summary>
|
|
||||||
public bool keepOlderDedupl
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 视图刷新率
|
|
||||||
/// </summary>
|
|
||||||
public int statisticsFreshRate
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义远程DNS
|
|
||||||
/// </summary>
|
|
||||||
public string remoteDNS
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Outbound Freedom domainStrategy
|
|
||||||
/// </summary>
|
|
||||||
public string domainStrategy4Freedom
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否允许不安全连接
|
|
||||||
/// </summary>
|
|
||||||
public bool defAllowInsecure
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 域名解析策略
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// systemProxyExceptions
|
|
||||||
/// </summary>
|
|
||||||
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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 本地监听
|
|
||||||
/// </summary>
|
|
||||||
public List<InItem> inbound
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// vmess服务器信息
|
|
||||||
/// </summary>
|
|
||||||
public List<VmessItem> vmess
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// KcpItem
|
|
||||||
/// </summary>
|
|
||||||
public KcpItem kcpItem
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 订阅
|
|
||||||
/// </summary>
|
|
||||||
public List<SubItem> subItem
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UI
|
|
||||||
/// </summary>
|
|
||||||
public UIItem uiItem
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<RoutingItemOld> routings
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConstItem constItem
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<KeyEventItem> globalHotkeys
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<CoreTypeItem> coreTypeItem
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion other entities
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
/// <summary>
|
||||||
public class VmessItem
|
/// 日志等级
|
||||||
|
/// </summary>
|
||||||
|
public string loglevel
|
||||||
{
|
{
|
||||||
public VmessItem()
|
get; set;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// config type(1=normal,2=custom)
|
|
||||||
/// </summary>
|
|
||||||
public EConfigType configType
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 版本(现在=2)
|
|
||||||
/// </summary>
|
|
||||||
public int configVersion
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int sort
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程服务器地址
|
|
||||||
/// </summary>
|
|
||||||
public string address
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程服务器端口
|
|
||||||
/// </summary>
|
|
||||||
public int port
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程服务器ID
|
|
||||||
/// </summary>
|
|
||||||
public string id
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程服务器额外ID
|
|
||||||
/// </summary>
|
|
||||||
public int alterId
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 本地安全策略
|
|
||||||
/// </summary>
|
|
||||||
public string security
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// tcp,kcp,ws,h2,quic
|
|
||||||
/// </summary>
|
|
||||||
public string network
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string remarks
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 伪装类型
|
|
||||||
/// </summary>
|
|
||||||
public string headerType
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 伪装的域名
|
|
||||||
/// </summary>
|
|
||||||
public string requestHost
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ws h2 path
|
|
||||||
/// </summary>
|
|
||||||
public string path
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 传输层安全
|
|
||||||
/// </summary>
|
|
||||||
public string streamSecurity
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否允许不安全连接(用于客户端)
|
|
||||||
/// </summary>
|
|
||||||
public string allowInsecure
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string testResult
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SubItem id
|
|
||||||
/// </summary>
|
|
||||||
public string subid
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// VLESS flow
|
|
||||||
/// </summary>
|
|
||||||
public string flow
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// tls sni
|
|
||||||
/// </summary>
|
|
||||||
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 string indexId
|
||||||
public class RoutingItemOld
|
|
||||||
{
|
{
|
||||||
public string remarks
|
get; set;
|
||||||
{
|
}
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string url
|
/// <summary>
|
||||||
{
|
/// 允许Mux多路复用
|
||||||
get; set;
|
/// </summary>
|
||||||
}
|
public bool muxEnabled
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
public List<RulesItem> rules
|
/// <summary>
|
||||||
{
|
///
|
||||||
get; set;
|
/// </summary>
|
||||||
}
|
public ESysProxyType sysProxyType
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
public bool enabled { get; set; } = true;
|
/// <summary>
|
||||||
|
/// 启用实时网速和流量统计
|
||||||
|
/// </summary>
|
||||||
|
public bool enableStatistics
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
public bool locked
|
/// <summary>
|
||||||
{
|
/// 去重时优先保留较旧(顶部)节点
|
||||||
get; set;
|
/// </summary>
|
||||||
}
|
public bool keepOlderDedupl
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
public string customIcon
|
/// <summary>
|
||||||
{
|
/// 视图刷新率
|
||||||
get; set;
|
/// </summary>
|
||||||
}
|
public int statisticsFreshRate
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义远程DNS
|
||||||
|
/// </summary>
|
||||||
|
public string remoteDNS
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Outbound Freedom domainStrategy
|
||||||
|
/// </summary>
|
||||||
|
public string domainStrategy4Freedom
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否允许不安全连接
|
||||||
|
/// </summary>
|
||||||
|
public bool defAllowInsecure
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 域名解析策略
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// systemProxyExceptions
|
||||||
|
/// </summary>
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本地监听
|
||||||
|
/// </summary>
|
||||||
|
public List<InItem> inbound
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// vmess服务器信息
|
||||||
|
/// </summary>
|
||||||
|
public List<VmessItem> vmess
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// KcpItem
|
||||||
|
/// </summary>
|
||||||
|
public KcpItem kcpItem
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 订阅
|
||||||
|
/// </summary>
|
||||||
|
public List<SubItem> subItem
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI
|
||||||
|
/// </summary>
|
||||||
|
public UIItem uiItem
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RoutingItemOld> routings
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConstItem constItem
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeyEventItem> globalHotkeys
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CoreTypeItem> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// config type(1=normal,2=custom)
|
||||||
|
/// </summary>
|
||||||
|
public EConfigType configType
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 版本(现在=2)
|
||||||
|
/// </summary>
|
||||||
|
public int configVersion
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sort
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程服务器地址
|
||||||
|
/// </summary>
|
||||||
|
public string address
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程服务器端口
|
||||||
|
/// </summary>
|
||||||
|
public int port
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程服务器ID
|
||||||
|
/// </summary>
|
||||||
|
public string id
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程服务器额外ID
|
||||||
|
/// </summary>
|
||||||
|
public int alterId
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本地安全策略
|
||||||
|
/// </summary>
|
||||||
|
public string security
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// tcp,kcp,ws,h2,quic
|
||||||
|
/// </summary>
|
||||||
|
public string network
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string remarks
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 伪装类型
|
||||||
|
/// </summary>
|
||||||
|
public string headerType
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 伪装的域名
|
||||||
|
/// </summary>
|
||||||
|
public string requestHost
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ws h2 path
|
||||||
|
/// </summary>
|
||||||
|
public string path
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 传输层安全
|
||||||
|
/// </summary>
|
||||||
|
public string streamSecurity
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否允许不安全连接(用于客户端)
|
||||||
|
/// </summary>
|
||||||
|
public string allowInsecure
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string testResult
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SubItem id
|
||||||
|
/// </summary>
|
||||||
|
public string subid
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// VLESS flow
|
||||||
|
/// </summary>
|
||||||
|
public string flow
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// tls sni
|
||||||
|
/// </summary>
|
||||||
|
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<RulesItem> rules
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool enabled { get; set; } = true;
|
||||||
|
|
||||||
|
public bool locked
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string customIcon
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,27 +1,26 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class CoreInfo
|
||||||
{
|
{
|
||||||
[Serializable]
|
public ECoreType coreType { get; set; }
|
||||||
public class CoreInfo
|
|
||||||
{
|
|
||||||
public ECoreType coreType { get; set; }
|
|
||||||
|
|
||||||
public List<string> coreExes { get; set; }
|
public List<string> 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 match { get; set; }
|
||||||
public string versionArg { get; set; }
|
public string versionArg { get; set; }
|
||||||
|
|
||||||
public bool redirectInfo { get; set; }
|
public bool redirectInfo { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public class DNSItem
|
|
||||||
{
|
|
||||||
[PrimaryKey]
|
|
||||||
public string id { get; set; }
|
|
||||||
|
|
||||||
public string remarks { get; set; }
|
[Serializable]
|
||||||
public bool enabled { get; set; } = true;
|
public class DNSItem
|
||||||
public ECoreType coreType { get; set; }
|
{
|
||||||
public string? normalDNS { get; set; }
|
[PrimaryKey]
|
||||||
public string? tunDNS { get; set; }
|
public string id { get; set; }
|
||||||
public string? domainStrategy4Freedom { 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; }
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
public enum EConfigType
|
||||||
{
|
{
|
||||||
public enum EConfigType
|
VMess = 1,
|
||||||
{
|
Custom = 2,
|
||||||
VMess = 1,
|
Shadowsocks = 3,
|
||||||
Custom = 2,
|
Socks = 4,
|
||||||
Shadowsocks = 3,
|
VLESS = 5,
|
||||||
Socks = 4,
|
Trojan = 6
|
||||||
VLESS = 5,
|
|
||||||
Trojan = 6
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
public enum ECoreType
|
||||||
{
|
{
|
||||||
public enum ECoreType
|
v2fly = 1,
|
||||||
{
|
Xray = 2,
|
||||||
v2fly = 1,
|
SagerNet = 3,
|
||||||
Xray = 2,
|
v2fly_v5 = 4,
|
||||||
SagerNet = 3,
|
clash = 11,
|
||||||
v2fly_v5 = 4,
|
clash_meta = 12,
|
||||||
clash = 11,
|
hysteria = 21,
|
||||||
clash_meta = 12,
|
naiveproxy = 22,
|
||||||
hysteria = 21,
|
tuic = 23,
|
||||||
naiveproxy = 22,
|
sing_box = 24,
|
||||||
tuic = 23,
|
juicity = 25,
|
||||||
sing_box = 24,
|
v2rayN = 99
|
||||||
juicity = 25,
|
|
||||||
v2rayN = 99
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
public enum EGlobalHotkey
|
||||||
{
|
{
|
||||||
public enum EGlobalHotkey
|
ShowForm = 0,
|
||||||
{
|
SystemProxyClear = 1,
|
||||||
ShowForm = 0,
|
SystemProxySet = 2,
|
||||||
SystemProxyClear = 1,
|
SystemProxyUnchanged = 3,
|
||||||
SystemProxySet = 2,
|
SystemProxyPac = 4,
|
||||||
SystemProxyUnchanged = 3,
|
|
||||||
SystemProxyPac = 4,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
public enum EMove
|
||||||
{
|
{
|
||||||
public enum EMove
|
Top = 1,
|
||||||
{
|
Up = 2,
|
||||||
Top = 1,
|
Down = 3,
|
||||||
Up = 2,
|
Bottom = 4,
|
||||||
Down = 3,
|
Position = 5
|
||||||
Bottom = 4,
|
|
||||||
Position = 5
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +1,21 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
{
|
|
||||||
public enum EServerColName
|
|
||||||
{
|
|
||||||
def = 0,
|
|
||||||
configType,
|
|
||||||
remarks,
|
|
||||||
address,
|
|
||||||
port,
|
|
||||||
security,
|
|
||||||
network,
|
|
||||||
streamSecurity,
|
|
||||||
subRemarks,
|
|
||||||
delayVal,
|
|
||||||
speedVal,
|
|
||||||
|
|
||||||
todayDown,
|
public enum EServerColName
|
||||||
todayUp,
|
{
|
||||||
totalDown,
|
def = 0,
|
||||||
totalUp
|
configType,
|
||||||
}
|
remarks,
|
||||||
|
address,
|
||||||
|
port,
|
||||||
|
security,
|
||||||
|
network,
|
||||||
|
streamSecurity,
|
||||||
|
subRemarks,
|
||||||
|
delayVal,
|
||||||
|
speedVal,
|
||||||
|
|
||||||
|
todayDown,
|
||||||
|
todayUp,
|
||||||
|
totalDown,
|
||||||
|
totalUp
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
public enum ESpeedActionType
|
||||||
{
|
{
|
||||||
public enum ESpeedActionType
|
Ping,
|
||||||
{
|
Tcping,
|
||||||
Ping,
|
Realping,
|
||||||
Tcping,
|
Speedtest,
|
||||||
Realping,
|
Mixedtest
|
||||||
Speedtest,
|
|
||||||
Mixedtest
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
public enum ESysProxyType
|
||||||
{
|
{
|
||||||
public enum ESysProxyType
|
ForcedClear = 0,
|
||||||
{
|
ForcedChange = 1,
|
||||||
ForcedClear = 0,
|
Unchanged = 2,
|
||||||
ForcedChange = 1,
|
Pac = 3
|
||||||
Unchanged = 2,
|
|
||||||
Pac = 3
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
public enum EViewAction
|
||||||
{
|
{
|
||||||
public enum EViewAction
|
AdjustMainLvColWidth,
|
||||||
{
|
ProfilesFocus
|
||||||
AdjustMainLvColWidth,
|
|
||||||
ProfilesFocus
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,68 +1,67 @@
|
||||||
using Newtonsoft.Json;
|
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
|
public class GitHubRelease
|
||||||
{
|
{
|
||||||
[JsonProperty("url")] public string Url { get; set; }
|
[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<GitHubReleaseAsset> Assets { get; set; }
|
[JsonProperty("assets")] public List<GitHubReleaseAsset> 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; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public class ProfileExItem
|
|
||||||
{
|
|
||||||
[PrimaryKey]
|
|
||||||
public string indexId { get; set; }
|
|
||||||
|
|
||||||
public int delay { get; set; }
|
[Serializable]
|
||||||
public decimal speed { get; set; }
|
public class ProfileExItem
|
||||||
public int sort { get; set; }
|
{
|
||||||
}
|
[PrimaryKey]
|
||||||
|
public string indexId { get; set; }
|
||||||
|
|
||||||
|
public int delay { get; set; }
|
||||||
|
public decimal speed { get; set; }
|
||||||
|
public int sort { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -1,195 +1,194 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
using v2rayN.Base;
|
using v2rayN.Base;
|
||||||
|
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ProfileItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
public ProfileItem()
|
||||||
public class ProfileItem
|
|
||||||
{
|
{
|
||||||
public ProfileItem()
|
indexId = string.Empty;
|
||||||
{
|
configType = EConfigType.VMess;
|
||||||
indexId = string.Empty;
|
configVersion = 2;
|
||||||
configType = EConfigType.VMess;
|
address = string.Empty;
|
||||||
configVersion = 2;
|
port = 0;
|
||||||
address = string.Empty;
|
id = string.Empty;
|
||||||
port = 0;
|
alterId = 0;
|
||||||
id = string.Empty;
|
security = string.Empty;
|
||||||
alterId = 0;
|
network = string.Empty;
|
||||||
security = string.Empty;
|
remarks = string.Empty;
|
||||||
network = string.Empty;
|
headerType = string.Empty;
|
||||||
remarks = string.Empty;
|
requestHost = string.Empty;
|
||||||
headerType = string.Empty;
|
path = string.Empty;
|
||||||
requestHost = string.Empty;
|
streamSecurity = string.Empty;
|
||||||
path = string.Empty;
|
allowInsecure = string.Empty;
|
||||||
streamSecurity = string.Empty;
|
subid = string.Empty;
|
||||||
allowInsecure = string.Empty;
|
flow = 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<string> 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; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// config type(1=normal,2=custom)
|
|
||||||
/// </summary>
|
|
||||||
public EConfigType configType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 版本(现在=2)
|
|
||||||
/// </summary>
|
|
||||||
public int configVersion { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程服务器地址
|
|
||||||
/// </summary>
|
|
||||||
public string address { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程服务器端口
|
|
||||||
/// </summary>
|
|
||||||
public int port { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程服务器ID
|
|
||||||
/// </summary>
|
|
||||||
public string id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程服务器额外ID
|
|
||||||
/// </summary>
|
|
||||||
public int alterId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 本地安全策略
|
|
||||||
/// </summary>
|
|
||||||
public string security { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// tcp,kcp,ws,h2,quic
|
|
||||||
/// </summary>
|
|
||||||
public string network { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string remarks { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 伪装类型
|
|
||||||
/// </summary>
|
|
||||||
public string headerType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 伪装的域名
|
|
||||||
/// </summary>
|
|
||||||
public string requestHost { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ws h2 path
|
|
||||||
/// </summary>
|
|
||||||
public string path { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 传输层安全
|
|
||||||
/// </summary>
|
|
||||||
public string streamSecurity { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否允许不安全连接(用于客户端)
|
|
||||||
/// </summary>
|
|
||||||
public string allowInsecure { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// SubItem id
|
|
||||||
/// </summary>
|
|
||||||
public string subid { get; set; }
|
|
||||||
|
|
||||||
public bool isSub { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// VLESS flow
|
|
||||||
/// </summary>
|
|
||||||
public string flow { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// tls sni
|
|
||||||
/// </summary>
|
|
||||||
public string sni { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// tls alpn
|
|
||||||
/// </summary>
|
|
||||||
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; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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<string> 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; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// config type(1=normal,2=custom)
|
||||||
|
/// </summary>
|
||||||
|
public EConfigType configType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 版本(现在=2)
|
||||||
|
/// </summary>
|
||||||
|
public int configVersion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程服务器地址
|
||||||
|
/// </summary>
|
||||||
|
public string address { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程服务器端口
|
||||||
|
/// </summary>
|
||||||
|
public int port { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程服务器ID
|
||||||
|
/// </summary>
|
||||||
|
public string id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程服务器额外ID
|
||||||
|
/// </summary>
|
||||||
|
public int alterId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本地安全策略
|
||||||
|
/// </summary>
|
||||||
|
public string security { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// tcp,kcp,ws,h2,quic
|
||||||
|
/// </summary>
|
||||||
|
public string network { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string remarks { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 伪装类型
|
||||||
|
/// </summary>
|
||||||
|
public string headerType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 伪装的域名
|
||||||
|
/// </summary>
|
||||||
|
public string requestHost { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ws h2 path
|
||||||
|
/// </summary>
|
||||||
|
public string path { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 传输层安全
|
||||||
|
/// </summary>
|
||||||
|
public string streamSecurity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否允许不安全连接(用于客户端)
|
||||||
|
/// </summary>
|
||||||
|
public string allowInsecure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SubItem id
|
||||||
|
/// </summary>
|
||||||
|
public string subid { get; set; }
|
||||||
|
|
||||||
|
public bool isSub { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// VLESS flow
|
||||||
|
/// </summary>
|
||||||
|
public string flow { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// tls sni
|
||||||
|
/// </summary>
|
||||||
|
public string sni { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// tls alpn
|
||||||
|
/// </summary>
|
||||||
|
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; }
|
||||||
}
|
}
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ProfileItemModel : ProfileItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
public bool isActive { get; set; }
|
||||||
public class ProfileItemModel : ProfileItem
|
public string subRemarks { get; set; }
|
||||||
{
|
public int delay { get; set; }
|
||||||
public bool isActive { get; set; }
|
public decimal speed { get; set; }
|
||||||
public string subRemarks { get; set; }
|
public int sort { get; set; }
|
||||||
public int delay { get; set; }
|
public string delayVal { get; set; }
|
||||||
public decimal speed { get; set; }
|
public string speedVal { get; set; }
|
||||||
public int sort { get; set; }
|
public string todayUp { get; set; }
|
||||||
public string delayVal { get; set; }
|
public string todayDown { get; set; }
|
||||||
public string speedVal { get; set; }
|
public string totalUp { get; set; }
|
||||||
public string todayUp { get; set; }
|
public string totalDown { get; set; }
|
||||||
public string todayDown { get; set; }
|
|
||||||
public string totalUp { get; set; }
|
|
||||||
public string totalDown { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +1,21 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
{
|
|
||||||
[Serializable]
|
|
||||||
public class RoutingItem
|
|
||||||
{
|
|
||||||
[PrimaryKey]
|
|
||||||
public string id { get; set; }
|
|
||||||
|
|
||||||
public string remarks { get; set; }
|
[Serializable]
|
||||||
public string url { get; set; }
|
public class RoutingItem
|
||||||
public string ruleSet { get; set; }
|
{
|
||||||
public int ruleNum { get; set; }
|
[PrimaryKey]
|
||||||
public bool enabled { get; set; } = true;
|
public string id { get; set; }
|
||||||
public bool locked { get; set; }
|
|
||||||
public string customIcon { get; set; }
|
public string remarks { get; set; }
|
||||||
public string domainStrategy { get; set; }
|
public string url { get; set; }
|
||||||
public string domainStrategy4Singbox { get; set; }
|
public string ruleSet { get; set; }
|
||||||
public int sort { 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; }
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class RoutingItemModel : RoutingItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
public bool isActive { get; set; }
|
||||||
public class RoutingItemModel : RoutingItem
|
|
||||||
{
|
|
||||||
public bool isActive { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,25 +1,24 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class RulesItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
public string id { get; set; }
|
||||||
public class RulesItem
|
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<string> inboundTag { get; set; }
|
public List<string> inboundTag { get; set; }
|
||||||
|
|
||||||
public string outboundTag { get; set; }
|
public string outboundTag { get; set; }
|
||||||
|
|
||||||
public List<string> ip { get; set; }
|
public List<string> ip { get; set; }
|
||||||
|
|
||||||
public List<string> domain { get; set; }
|
public List<string> domain { get; set; }
|
||||||
|
|
||||||
public List<string> protocol { get; set; }
|
public List<string> protocol { get; set; }
|
||||||
|
|
||||||
public List<string> process { get; set; }
|
public List<string> process { get; set; }
|
||||||
|
|
||||||
public bool enabled { get; set; } = true;
|
public bool enabled { get; set; } = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class RulesItemModel : RulesItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
public string inboundTags { get; set; }
|
||||||
public class RulesItemModel : RulesItem
|
|
||||||
{
|
|
||||||
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; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,40 +1,39 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
internal class ServerSpeedItem : ServerStatItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
public long proxyUp
|
||||||
internal class ServerSpeedItem : ServerStatItem
|
|
||||||
{
|
{
|
||||||
public long proxyUp
|
get; set;
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long proxyDown
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long directUp
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long directDown
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
public long proxyDown
|
||||||
public class TrafficItem
|
|
||||||
{
|
{
|
||||||
public ulong up
|
get; set;
|
||||||
{
|
}
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong down
|
public long directUp
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long directDown
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class TrafficItem
|
||||||
|
{
|
||||||
|
public ulong up
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong down
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,39 +1,38 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class ServerStatItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
[PrimaryKey]
|
||||||
public class ServerStatItem
|
public string indexId
|
||||||
{
|
{
|
||||||
[PrimaryKey]
|
get; set;
|
||||||
public string indexId
|
}
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long totalUp
|
public long totalUp
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long totalDown
|
public long totalDown
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long todayUp
|
public long todayUp
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long todayDown
|
public long todayDown
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long dateNow
|
public long dateNow
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
internal class ServerTestItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
public string indexId { get; set; }
|
||||||
internal class ServerTestItem
|
public string address { get; set; }
|
||||||
{
|
public int port { get; set; }
|
||||||
public string indexId { get; set; }
|
public EConfigType configType { get; set; }
|
||||||
public string address { get; set; }
|
public bool allowTest { get; set; }
|
||||||
public int port { get; set; }
|
public int delay { get; set; }
|
||||||
public EConfigType configType { get; set; }
|
|
||||||
public bool allowTest { get; set; }
|
|
||||||
public int delay { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -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 Log4Sbox log { get; set; }
|
public List<Inbound4Sbox> inbounds { get; set; }
|
||||||
public object dns { get; set; }
|
public List<Outbound4Sbox> outbounds { get; set; }
|
||||||
public List<Inbound4Sbox> inbounds { get; set; }
|
public Route4Sbox route { get; set; }
|
||||||
public List<Outbound4Sbox> outbounds { get; set; }
|
public Experimental4Sbox experimental { get; set; }
|
||||||
public Route4Sbox route { get; set; }
|
}
|
||||||
public Experimental4Sbox experimental { get; set; }
|
|
||||||
}
|
public class Log4Sbox
|
||||||
|
{
|
||||||
public class Log4Sbox
|
public bool? disabled { get; set; }
|
||||||
{
|
public string level { get; set; }
|
||||||
public bool? disabled { get; set; }
|
public string output { get; set; }
|
||||||
public string level { get; set; }
|
public bool timestamp { get; set; }
|
||||||
public string output { get; set; }
|
}
|
||||||
public bool timestamp { get; set; }
|
|
||||||
}
|
public class Dns4Sbox
|
||||||
|
{
|
||||||
public class Dns4Sbox
|
public List<Server4Sbox> servers { get; set; }
|
||||||
{
|
public List<Rule4Sbox> rules { get; set; }
|
||||||
public List<Server4Sbox> servers { get; set; }
|
public string? final { get; set; }
|
||||||
public List<Rule4Sbox> rules { get; set; }
|
public string? strategy { get; set; }
|
||||||
public string? final { get; set; }
|
public bool? disable_cache { get; set; }
|
||||||
public string? strategy { get; set; }
|
public bool? disable_expire { get; set; }
|
||||||
public bool? disable_cache { get; set; }
|
public bool? independent_cache { get; set; }
|
||||||
public bool? disable_expire { get; set; }
|
public bool? reverse_mapping { get; set; }
|
||||||
public bool? independent_cache { get; set; }
|
public Fakeip4Sbox? fakeip { get; set; }
|
||||||
public bool? reverse_mapping { get; set; }
|
}
|
||||||
public Fakeip4Sbox? fakeip { get; set; }
|
|
||||||
}
|
public class Route4Sbox
|
||||||
|
{
|
||||||
public class Route4Sbox
|
public bool? auto_detect_interface { get; set; }
|
||||||
{
|
public List<Rule4Sbox> rules { get; set; }
|
||||||
public bool? auto_detect_interface { get; set; }
|
}
|
||||||
public List<Rule4Sbox> rules { get; set; }
|
|
||||||
}
|
[Serializable]
|
||||||
|
public class Rule4Sbox
|
||||||
[Serializable]
|
{
|
||||||
public class Rule4Sbox
|
public string outbound { get; set; }
|
||||||
{
|
public string server { get; set; }
|
||||||
public string outbound { get; set; }
|
public bool? disable_cache { get; set; }
|
||||||
public string server { get; set; }
|
public List<string>? inbound { get; set; }
|
||||||
public bool? disable_cache { get; set; }
|
public List<string>? protocol { get; set; }
|
||||||
public List<string>? inbound { get; set; }
|
public string type { get; set; }
|
||||||
public List<string>? protocol { get; set; }
|
public string mode { get; set; }
|
||||||
public string type { get; set; }
|
public string network { get; set; }
|
||||||
public string mode { get; set; }
|
public List<int>? port { get; set; }
|
||||||
public string network { get; set; }
|
public List<string>? port_range { get; set; }
|
||||||
public List<int>? port { get; set; }
|
public List<string>? geosite { get; set; }
|
||||||
public List<string>? port_range { get; set; }
|
public List<string>? domain { get; set; }
|
||||||
public List<string>? geosite { get; set; }
|
public List<string>? domain_suffix { get; set; }
|
||||||
public List<string>? domain { get; set; }
|
public List<string>? domain_keyword { get; set; }
|
||||||
public List<string>? domain_suffix { get; set; }
|
public List<string>? domain_regex { get; set; }
|
||||||
public List<string>? domain_keyword { get; set; }
|
public List<string>? geoip { get; set; }
|
||||||
public List<string>? domain_regex { get; set; }
|
public List<string>? ip_cidr { get; set; }
|
||||||
public List<string>? geoip { get; set; }
|
public List<string>? source_ip_cidr { get; set; }
|
||||||
public List<string>? ip_cidr { get; set; }
|
|
||||||
public List<string>? source_ip_cidr { get; set; }
|
public List<string>? process_name { get; set; }
|
||||||
|
}
|
||||||
public List<string>? process_name { get; set; }
|
|
||||||
}
|
[Serializable]
|
||||||
|
public class Inbound4Sbox
|
||||||
[Serializable]
|
{
|
||||||
public class Inbound4Sbox
|
public string type { get; set; }
|
||||||
{
|
public string tag { get; set; }
|
||||||
public string type { get; set; }
|
public string listen { get; set; }
|
||||||
public string tag { get; set; }
|
public int? listen_port { get; set; }
|
||||||
public string listen { get; set; }
|
public string? domain_strategy { get; set; }
|
||||||
public int? listen_port { get; set; }
|
public string interface_name { get; set; }
|
||||||
public string? domain_strategy { get; set; }
|
public string inet4_address { get; set; }
|
||||||
public string interface_name { get; set; }
|
public string inet6_address { get; set; }
|
||||||
public string inet4_address { get; set; }
|
public int? mtu { get; set; }
|
||||||
public string inet6_address { get; set; }
|
public bool? auto_route { get; set; }
|
||||||
public int? mtu { get; set; }
|
public bool? strict_route { get; set; }
|
||||||
public bool? auto_route { get; set; }
|
public bool? endpoint_independent_nat { get; set; }
|
||||||
public bool? strict_route { get; set; }
|
public string? stack { get; set; }
|
||||||
public bool? endpoint_independent_nat { get; set; }
|
public bool? sniff { get; set; }
|
||||||
public string? stack { get; set; }
|
public bool? sniff_override_destination { get; set; }
|
||||||
public bool? sniff { get; set; }
|
public List<User4Sbox> users { get; set; }
|
||||||
public bool? sniff_override_destination { get; set; }
|
}
|
||||||
public List<User4Sbox> users { get; set; }
|
|
||||||
}
|
public class User4Sbox
|
||||||
|
{
|
||||||
public class User4Sbox
|
public string username { get; set; }
|
||||||
{
|
public string password { get; set; }
|
||||||
public string username { get; set; }
|
}
|
||||||
public string password { get; set; }
|
|
||||||
}
|
public class Outbound4Sbox
|
||||||
|
{
|
||||||
public class Outbound4Sbox
|
public string type { get; set; }
|
||||||
{
|
public string tag { get; set; }
|
||||||
public string type { get; set; }
|
public string server { get; set; }
|
||||||
public string tag { get; set; }
|
public int? server_port { get; set; }
|
||||||
public string server { get; set; }
|
public string uuid { get; set; }
|
||||||
public int? server_port { get; set; }
|
public string security { get; set; }
|
||||||
public string uuid { get; set; }
|
public int? alter_id { get; set; }
|
||||||
public string security { get; set; }
|
public string flow { get; set; }
|
||||||
public int? alter_id { get; set; }
|
public int? up_mbps { get; set; }
|
||||||
public string flow { get; set; }
|
public int? down_mbps { get; set; }
|
||||||
public int? up_mbps { get; set; }
|
public string auth_str { get; set; }
|
||||||
public int? down_mbps { get; set; }
|
public int? recv_window_conn { get; set; }
|
||||||
public string auth_str { get; set; }
|
public int? recv_window { get; set; }
|
||||||
public int? recv_window_conn { get; set; }
|
public bool? disable_mtu_discovery { get; set; }
|
||||||
public int? recv_window { get; set; }
|
public string detour { get; set; }
|
||||||
public bool? disable_mtu_discovery { get; set; }
|
public string method { get; set; }
|
||||||
public string detour { get; set; }
|
public string username { get; set; }
|
||||||
public string method { get; set; }
|
public string password { get; set; }
|
||||||
public string username { get; set; }
|
public string? version { get; set; }
|
||||||
public string password { get; set; }
|
public string? network { get; set; }
|
||||||
public string? version { get; set; }
|
public string packet_encoding { get; set; }
|
||||||
public string? network { get; set; }
|
public Tls4Sbox tls { get; set; }
|
||||||
public string packet_encoding { get; set; }
|
public Multiplex4Sbox multiplex { get; set; }
|
||||||
public Tls4Sbox tls { get; set; }
|
public Transport4Sbox transport { get; set; }
|
||||||
public Multiplex4Sbox multiplex { get; set; }
|
}
|
||||||
public Transport4Sbox transport { get; set; }
|
|
||||||
}
|
public class Tls4Sbox
|
||||||
|
{
|
||||||
public class Tls4Sbox
|
public bool enabled { get; set; }
|
||||||
{
|
public string server_name { get; set; }
|
||||||
public bool enabled { get; set; }
|
public bool? insecure { get; set; }
|
||||||
public string server_name { get; set; }
|
public List<string> alpn { get; set; }
|
||||||
public bool? insecure { get; set; }
|
public Utls4Sbox utls { get; set; }
|
||||||
public List<string> alpn { get; set; }
|
public Reality4Sbox reality { get; set; }
|
||||||
public Utls4Sbox utls { get; set; }
|
}
|
||||||
public Reality4Sbox reality { get; set; }
|
|
||||||
}
|
public class Multiplex4Sbox
|
||||||
|
{
|
||||||
public class Multiplex4Sbox
|
public bool enabled { get; set; }
|
||||||
{
|
public string protocol { get; set; }
|
||||||
public bool enabled { get; set; }
|
public int max_connections { get; set; }
|
||||||
public string protocol { get; set; }
|
public int min_streams { get; set; }
|
||||||
public int max_connections { get; set; }
|
public int max_streams { get; set; }
|
||||||
public int min_streams { get; set; }
|
public bool padding { get; set; }
|
||||||
public int max_streams { get; set; }
|
}
|
||||||
public bool padding { get; set; }
|
|
||||||
}
|
public class Utls4Sbox
|
||||||
|
{
|
||||||
public class Utls4Sbox
|
public bool enabled { get; set; }
|
||||||
{
|
public string fingerprint { get; set; }
|
||||||
public bool enabled { get; set; }
|
}
|
||||||
public string fingerprint { get; set; }
|
|
||||||
}
|
public class Reality4Sbox
|
||||||
|
{
|
||||||
public class Reality4Sbox
|
public bool enabled { get; set; }
|
||||||
{
|
public string public_key { get; set; }
|
||||||
public bool enabled { get; set; }
|
public string short_id { get; set; }
|
||||||
public string public_key { get; set; }
|
}
|
||||||
public string short_id { get; set; }
|
|
||||||
}
|
public class Transport4Sbox
|
||||||
|
{
|
||||||
public class Transport4Sbox
|
public string type { get; set; }
|
||||||
{
|
public List<string>? host { get; set; }
|
||||||
public string type { get; set; }
|
public string? path { get; set; }
|
||||||
public List<string>? host { get; set; }
|
public Headers4Sbox? headers { 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 service_name { get; set; }
|
public string ping_timeout { get; set; }
|
||||||
public string idle_timeout { get; set; }
|
public bool? permit_without_stream { get; set; }
|
||||||
public string ping_timeout { get; set; }
|
}
|
||||||
public bool? permit_without_stream { get; set; }
|
|
||||||
}
|
public class Headers4Sbox
|
||||||
|
{
|
||||||
public class Headers4Sbox
|
public string? Host { get; set; }
|
||||||
{
|
}
|
||||||
public string? Host { get; set; }
|
|
||||||
}
|
public class Server4Sbox
|
||||||
|
{
|
||||||
public class Server4Sbox
|
public string tag { get; set; }
|
||||||
{
|
public string address { get; set; }
|
||||||
public string tag { get; set; }
|
public string address_resolver { get; set; }
|
||||||
public string address { get; set; }
|
public string strategy { get; set; }
|
||||||
public string address_resolver { get; set; }
|
public string detour { get; set; }
|
||||||
public string strategy { get; set; }
|
}
|
||||||
public string detour { get; set; }
|
|
||||||
}
|
public class Experimental4Sbox
|
||||||
|
{
|
||||||
public class Experimental4Sbox
|
public V2ray_Api4Sbox v2ray_api { get; set; }
|
||||||
{
|
public Clash_Api4Sbox clash_api { get; set; }
|
||||||
public V2ray_Api4Sbox v2ray_api { get; set; }
|
}
|
||||||
public Clash_Api4Sbox clash_api { get; set; }
|
|
||||||
}
|
public class V2ray_Api4Sbox
|
||||||
|
{
|
||||||
public class V2ray_Api4Sbox
|
public string listen { get; set; }
|
||||||
{
|
public Stats4Sbox stats { get; set; }
|
||||||
public string listen { get; set; }
|
}
|
||||||
public Stats4Sbox stats { get; set; }
|
|
||||||
}
|
public class Clash_Api4Sbox
|
||||||
|
{
|
||||||
public class Clash_Api4Sbox
|
public string external_controller { get; set; }
|
||||||
{
|
public bool store_selected { get; set; }
|
||||||
public string external_controller { get; set; }
|
}
|
||||||
public bool store_selected { get; set; }
|
|
||||||
}
|
public class Stats4Sbox
|
||||||
|
{
|
||||||
public class Stats4Sbox
|
public bool enabled { get; set; }
|
||||||
{
|
public List<string>? inbounds { get; set; }
|
||||||
public bool enabled { get; set; }
|
public List<string>? outbounds { get; set; }
|
||||||
public List<string>? inbounds { get; set; }
|
public List<string>? users { get; set; }
|
||||||
public List<string>? outbounds { get; set; }
|
}
|
||||||
public List<string>? users { get; set; }
|
|
||||||
}
|
public class Fakeip4Sbox
|
||||||
|
{
|
||||||
public class Fakeip4Sbox
|
public bool enabled { get; set; }
|
||||||
{
|
public string inet4_range { get; set; }
|
||||||
public bool enabled { get; set; }
|
public string inet6_range { get; set; }
|
||||||
public string inet4_range { get; set; }
|
|
||||||
public string inet6_range { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
{
|
|
||||||
public class SsSIP008
|
|
||||||
{
|
|
||||||
public List<SsServer> servers { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
public class SsSIP008
|
||||||
public class SsServer
|
{
|
||||||
{
|
public List<SsServer> servers { get; set; }
|
||||||
public string remarks { get; set; }
|
}
|
||||||
public string server { get; set; }
|
|
||||||
public string server_port { get; set; }
|
[Serializable]
|
||||||
public string method { get; set; }
|
public class SsServer
|
||||||
public string password { get; set; }
|
{
|
||||||
public string plugin { get; set; }
|
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; }
|
||||||
}
|
}
|
||||||
|
|
@ -1,31 +1,30 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class SubItem
|
||||||
{
|
{
|
||||||
[Serializable]
|
[PrimaryKey]
|
||||||
public class SubItem
|
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; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
{
|
|
||||||
internal class SysproxyConfig
|
|
||||||
{
|
|
||||||
public bool UserSettingsRecorded;
|
|
||||||
public string Flags;
|
|
||||||
public string ProxyServer;
|
|
||||||
public string BypassList;
|
|
||||||
public string PacUrl;
|
|
||||||
|
|
||||||
public SysproxyConfig()
|
internal class SysproxyConfig
|
||||||
{
|
{
|
||||||
UserSettingsRecorded = false;
|
public bool UserSettingsRecorded;
|
||||||
Flags = "1";
|
public string Flags;
|
||||||
ProxyServer = "";
|
public string ProxyServer;
|
||||||
BypassList = "";
|
public string BypassList;
|
||||||
PacUrl = "";
|
public string PacUrl;
|
||||||
}
|
|
||||||
|
public SysproxyConfig()
|
||||||
|
{
|
||||||
|
UserSettingsRecorded = false;
|
||||||
|
Flags = "1";
|
||||||
|
ProxyServer = "";
|
||||||
|
BypassList = "";
|
||||||
|
PacUrl = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,21 +1,20 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tcp伪装http的Request,只要Host
|
||||||
|
/// </summary>
|
||||||
|
public class V2rayTcpRequest
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tcp伪装http的Request,只要Host
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class V2rayTcpRequest
|
public RequestHeaders headers { get; set; }
|
||||||
{
|
}
|
||||||
/// <summary>
|
|
||||||
///
|
public class RequestHeaders
|
||||||
/// </summary>
|
{
|
||||||
public RequestHeaders headers { get; set; }
|
/// <summary>
|
||||||
}
|
///
|
||||||
|
/// </summary>
|
||||||
public class RequestHeaders
|
public List<string> Host { get; set; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public List<string> Host { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,84 +1,83 @@
|
||||||
namespace v2rayN.Mode
|
namespace v2rayN.Mode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// https://github.com/2dust/v2rayN/wiki/
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
internal class VmessQRCode
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// https://github.com/2dust/v2rayN/wiki/
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
public string v { get; set; } = string.Empty;
|
||||||
internal class VmessQRCode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string v { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ps { get; set; } = string.Empty;
|
public string ps { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string add { get; set; } = string.Empty;
|
public string add { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string port { get; set; } = string.Empty;
|
public string port { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string id { get; set; } = string.Empty;
|
public string id { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string aid { get; set; } = string.Empty;
|
public string aid { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string scy { get; set; } = string.Empty;
|
public string scy { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string net { get; set; } = string.Empty;
|
public string net { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string type { get; set; } = string.Empty;
|
public string type { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string host { get; set; } = string.Empty;
|
public string host { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string path { get; set; } = string.Empty;
|
public string path { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TLS
|
/// TLS
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string tls { get; set; } = string.Empty;
|
public string tls { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TLS SNI
|
/// TLS SNI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string sni { get; set; } = string.Empty;
|
public string sni { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TLS alpn
|
/// TLS alpn
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string alpn { get; set; } = string.Empty;
|
public string alpn { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TLS fingerprint
|
/// TLS fingerprint
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string fp { get; set; } = string.Empty;
|
public string fp { get; set; } = string.Empty;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,89 +2,88 @@
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Text;
|
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;
|
||||||
File.WriteAllBytes(fileName, content);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utils.SaveLog(ex.Message, ex);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
public static void UncompressFile(string fileName, byte[] content)
|
|
||||||
{
|
{
|
||||||
try
|
Utils.SaveLog(ex.Message, ex);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
public static string NonExclusiveReadAllText(string path, Encoding encoding)
|
|
||||||
{
|
{
|
||||||
try
|
Utils.SaveLog(ex.Message, ex);
|
||||||
{
|
|
||||||
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)
|
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);
|
if (entry.Length == 0)
|
||||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
|
||||||
{
|
{
|
||||||
if (entry.Length == 0)
|
continue;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Utils.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try
|
entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
|
||||||
{
|
}
|
||||||
if (!Utils.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName))
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
continue;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,176 +1,175 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
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
|
||||||
{
|
{
|
||||||
/*
|
private IntPtr handle = IntPtr.Zero;
|
||||||
* See:
|
|
||||||
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class Job : IDisposable
|
public Job()
|
||||||
{
|
{
|
||||||
private IntPtr handle = IntPtr.Zero;
|
handle = CreateJobObject(IntPtr.Zero, null);
|
||||||
|
IntPtr extendedInfoPtr = IntPtr.Zero;
|
||||||
public Job()
|
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new()
|
||||||
{
|
{
|
||||||
handle = CreateJobObject(IntPtr.Zero, null);
|
LimitFlags = 0x2000
|
||||||
IntPtr extendedInfoPtr = IntPtr.Zero;
|
};
|
||||||
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new()
|
|
||||||
{
|
|
||||||
LimitFlags = 0x2000
|
|
||||||
};
|
|
||||||
|
|
||||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new()
|
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new()
|
||||||
{
|
{
|
||||||
BasicLimitInformation = info
|
BasicLimitInformation = info
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
||||||
extendedInfoPtr = Marshal.AllocHGlobal(length);
|
extendedInfoPtr = Marshal.AllocHGlobal(length);
|
||||||
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
|
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
|
||||||
|
|
||||||
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
|
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
|
||||||
(uint)length))
|
(uint)length))
|
||||||
throw new Exception(string.Format("Unable to set information. Error: {0}",
|
throw new Exception(string.Format("Unable to set information. Error: {0}",
|
||||||
Marshal.GetLastWin32Error()));
|
Marshal.GetLastWin32Error()));
|
||||||
}
|
}
|
||||||
finally
|
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);
|
Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
|
||||||
|
|
||||||
if (!succ)
|
|
||||||
{
|
|
||||||
Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
|
|
||||||
}
|
|
||||||
|
|
||||||
return succ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
if (handle != IntPtr.Zero)
|
||||||
|
|
||||||
private bool disposed;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
{
|
||||||
Dispose(true);
|
CloseHandle(handle);
|
||||||
GC.SuppressFinalize(this);
|
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
|
~Job()
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct IO_COUNTERS
|
|
||||||
{
|
{
|
||||||
public UInt64 ReadOperationCount;
|
Dispose(false);
|
||||||
public UInt64 WriteOperationCount;
|
|
||||||
public UInt64 OtherOperationCount;
|
|
||||||
public UInt64 ReadTransferCount;
|
|
||||||
public UInt64 WriteTransferCount;
|
|
||||||
public UInt64 OtherTransferCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
#endregion IDisposable
|
||||||
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)]
|
#region Interop
|
||||||
public struct SECURITY_ATTRIBUTES
|
|
||||||
{
|
|
||||||
public UInt32 nLength;
|
|
||||||
public IntPtr lpSecurityDescriptor;
|
|
||||||
public Int32 bInheritHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||||
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
|
||||||
{
|
|
||||||
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
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
{
|
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
|
||||||
AssociateCompletionPortInformation = 7,
|
|
||||||
BasicLimitInformation = 2,
|
|
||||||
BasicUIRestrictions = 4,
|
|
||||||
EndOfJobTimeInformation = 6,
|
|
||||||
ExtendedLimitInformation = 9,
|
|
||||||
SecurityLimitInformation = 5,
|
|
||||||
GroupInformation = 11
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Helper classes
|
[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
|
||||||
|
|
|
||||||
|
|
@ -3,53 +3,52 @@ using NLog.Config;
|
||||||
using NLog.Targets;
|
using NLog.Targets;
|
||||||
using System.IO;
|
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();
|
||||||
LoggingConfiguration config = new();
|
config.AddTarget("file", fileTarget);
|
||||||
FileTarget fileTarget = new();
|
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
|
||||||
config.AddTarget("file", fileTarget);
|
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
|
||||||
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
|
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
|
||||||
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
|
LogManager.Configuration = config;
|
||||||
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();
|
||||||
{
|
|
||||||
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 { }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 { }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,50 +1,49 @@
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace v2rayN.Tool
|
namespace v2rayN.Tool;
|
||||||
|
|
||||||
|
public static class QueryableExtension
|
||||||
{
|
{
|
||||||
public static class QueryableExtension
|
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName)
|
||||||
{
|
{
|
||||||
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName)
|
return _OrderBy<T>(query, propertyName, false);
|
||||||
{
|
}
|
||||||
return _OrderBy<T>(query, propertyName, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName)
|
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName)
|
||||||
{
|
{
|
||||||
return _OrderBy<T>(query, propertyName, true);
|
return _OrderBy<T>(query, propertyName, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc)
|
private static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc)
|
||||||
{
|
{
|
||||||
string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
|
string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
|
||||||
|
|
||||||
var memberProp = typeof(T).GetProperty(propertyName);
|
var memberProp = typeof(T).GetProperty(propertyName);
|
||||||
|
|
||||||
var method = typeof(QueryableExtension).GetMethod(methodname)
|
var method = typeof(QueryableExtension).GetMethod(methodname)
|
||||||
.MakeGenericMethod(typeof(T), memberProp.PropertyType);
|
.MakeGenericMethod(typeof(T), memberProp.PropertyType);
|
||||||
|
|
||||||
return (IOrderedQueryable<T>)method.Invoke(null, new object[] { query, memberProp });
|
return (IOrderedQueryable<T>)method.Invoke(null, new object[] { query, memberProp });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IOrderedQueryable<T> OrderByInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
|
public static IOrderedQueryable<T> OrderByInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
|
||||||
{//public
|
{//public
|
||||||
return query.OrderBy(_GetLamba<T, TProp>(memberProperty));
|
return query.OrderBy(_GetLamba<T, TProp>(memberProperty));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IOrderedQueryable<T> OrderByDescendingInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
|
public static IOrderedQueryable<T> OrderByDescendingInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
|
||||||
{//public
|
{//public
|
||||||
return query.OrderByDescending(_GetLamba<T, TProp>(memberProperty));
|
return query.OrderByDescending(_GetLamba<T, TProp>(memberProperty));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Expression<Func<T, TProp>> _GetLamba<T, TProp>(PropertyInfo memberProperty)
|
private static Expression<Func<T, TProp>> _GetLamba<T, TProp>(PropertyInfo memberProperty)
|
||||||
{
|
{
|
||||||
if (memberProperty.PropertyType != typeof(TProp)) throw new Exception();
|
if (memberProperty.PropertyType != typeof(TProp)) throw new Exception();
|
||||||
|
|
||||||
var thisArg = Expression.Parameter(typeof(T));
|
var thisArg = Expression.Parameter(typeof(T));
|
||||||
var lamba = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg);
|
var lamba = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg);
|
||||||
|
|
||||||
return lamba;
|
return lamba;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,182 +1,181 @@
|
||||||
using v2rayN.Base;
|
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;
|
this.major = major;
|
||||||
private int minor;
|
this.minor = minor;
|
||||||
private int patch;
|
this.patch = patch;
|
||||||
private string version;
|
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;
|
string[] parts = this.version.Split('.');
|
||||||
this.minor = minor;
|
if (parts.Length == 2)
|
||||||
this.patch = patch;
|
|
||||||
this.version = $"{major}.{minor}.{patch}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public SemanticVersion(string version)
|
|
||||||
{
|
|
||||||
this.version = version.RemovePrefix('v');
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
string[] parts = this.version.Split('.');
|
this.major = int.Parse(parts[0]);
|
||||||
if (parts.Length == 2)
|
this.minor = int.Parse(parts[1]);
|
||||||
{
|
|
||||||
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.patch = 0;
|
this.patch = 0;
|
||||||
//this.version = "0.0.0";
|
|
||||||
}
|
}
|
||||||
}
|
else if (parts.Length == 3)
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
if (obj is SemanticVersion other)
|
|
||||||
{
|
{
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
return false;
|
throw new ArgumentException("Invalid version string");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
{
|
||||||
return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode();
|
this.major = 0;
|
||||||
|
this.minor = 0;
|
||||||
|
this.patch = 0;
|
||||||
|
//this.version = "0.0.0";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
public override bool Equals(object? obj)
|
||||||
/// Use ToVersionString(string? prefix) instead if possible.
|
{
|
||||||
/// </summary>
|
if (obj is SemanticVersion other)
|
||||||
/// <returns>major.minor.patch</returns>
|
{
|
||||||
public override string ToString()
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use ToVersionString(string? prefix) instead if possible.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>major.minor.patch</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ToVersionString(string? prefix = null)
|
||||||
|
{
|
||||||
|
if (prefix == null)
|
||||||
{
|
{
|
||||||
return this.version;
|
return this.version;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public string ToVersionString(string? prefix = null)
|
|
||||||
{
|
{
|
||||||
if (prefix == null)
|
return $"{prefix}{this.version}";
|
||||||
{
|
|
||||||
return this.version;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
using System.Windows;
|
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)
|
public static void ShowWarning(string msg)
|
||||||
{
|
{
|
||||||
MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
|
MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ShowWarning(string msg)
|
public static MessageBoxResult ShowYesNo(string msg)
|
||||||
{
|
{
|
||||||
MessageBox.Show(msg, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK);
|
return MessageBox.Show(msg, caption, MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||||
}
|
|
||||||
|
|
||||||
public static MessageBoxResult ShowYesNo(string msg)
|
|
||||||
{
|
|
||||||
return MessageBox.Show(msg, caption, MessageBoxButton.YesNo, MessageBoxImage.Question);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -11,150 +11,149 @@ using v2rayN.Handler;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
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<Unit, Unit> BrowseServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SaveServerCmd { get; }
|
||||||
|
public bool IsModified { get; set; }
|
||||||
|
|
||||||
|
public AddServer2ViewModel(ProfileItem profileItem, Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
private NoticeHandler? _noticeHandler;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private Window _view;
|
|
||||||
|
|
||||||
[Reactive]
|
if (profileItem.indexId.IsNullOrEmpty())
|
||||||
public ProfileItem SelectedSource { get; set; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> BrowseServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveServerCmd { get; }
|
|
||||||
public bool IsModified { get; set; }
|
|
||||||
|
|
||||||
public AddServer2ViewModel(ProfileItem profileItem, Window view)
|
|
||||||
{
|
{
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
SelectedSource = profileItem;
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
}
|
||||||
|
else
|
||||||
if (profileItem.indexId.IsNullOrEmpty())
|
{
|
||||||
{
|
SelectedSource = Utils.DeepCopy(profileItem);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveServer()
|
_view = view;
|
||||||
|
|
||||||
|
BrowseServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
string remarks = SelectedSource.remarks;
|
BrowseServer();
|
||||||
if (Utils.IsNullOrEmpty(remarks))
|
});
|
||||||
{
|
|
||||||
UI.Show(ResUI.PleaseFillRemarks);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Utils.IsNullOrEmpty(SelectedSource.address))
|
EditServerCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
UI.Show(ResUI.FillServerAddressCustom);
|
EditServer();
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId);
|
SaveServerCmd = ReactiveCommand.Create(() =>
|
||||||
if (item is null)
|
{
|
||||||
{
|
SaveServer();
|
||||||
item = SelectedSource;
|
});
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item.remarks = SelectedSource.remarks;
|
|
||||||
item.address = SelectedSource.address;
|
|
||||||
item.coreType = SelectedSource.coreType;
|
|
||||||
item.displayLog = SelectedSource.displayLog;
|
|
||||||
item.preSocksPort = SelectedSource.preSocksPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ConfigHandler.EditCustomServer(ref _config, item) == 0)
|
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||||
{
|
}
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
|
||||||
_view.DialogResult = true;
|
private void SaveServer()
|
||||||
}
|
{
|
||||||
else
|
string remarks = SelectedSource.remarks;
|
||||||
{
|
if (Utils.IsNullOrEmpty(remarks))
|
||||||
UI.Show(ResUI.OperationFailed);
|
{
|
||||||
}
|
UI.Show(ResUI.PleaseFillRemarks);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BrowseServer()
|
if (Utils.IsNullOrEmpty(SelectedSource.address))
|
||||||
{
|
{
|
||||||
UI.Show(ResUI.CustomServerTips);
|
UI.Show(ResUI.FillServerAddressCustom);
|
||||||
|
return;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EditServer()
|
var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId);
|
||||||
|
if (item is null)
|
||||||
{
|
{
|
||||||
var address = SelectedSource.address;
|
item = SelectedSource;
|
||||||
if (Utils.IsNullOrEmpty(address))
|
}
|
||||||
{
|
else
|
||||||
UI.Show(ResUI.FillServerAddressCustom);
|
{
|
||||||
return;
|
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 (ConfigHandler.EditCustomServer(ref _config, item) == 0)
|
||||||
if (File.Exists(address))
|
{
|
||||||
|
_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);
|
SelectedSource = Utils.DeepCopy(item);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_noticeHandler?.Enqueue(ResUI.FailedReadConfiguration);
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,155 +8,154 @@ using v2rayN.Handler;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
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<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
|
public AddServerViewModel(ProfileItem profileItem, Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private NoticeHandler? _noticeHandler;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
private Window _view;
|
_view = view;
|
||||||
|
|
||||||
[Reactive]
|
if (profileItem.id.IsNullOrEmpty())
|
||||||
public ProfileItem SelectedSource { get; set; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
|
||||||
|
|
||||||
public AddServerViewModel(ProfileItem profileItem, Window view)
|
|
||||||
{
|
{
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
profileItem.network = Global.DefaultNetwork;
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
profileItem.headerType = Global.None;
|
||||||
_view = view;
|
profileItem.requestHost = "";
|
||||||
|
profileItem.streamSecurity = "";
|
||||||
if (profileItem.id.IsNullOrEmpty())
|
SelectedSource = profileItem;
|
||||||
{
|
}
|
||||||
profileItem.network = Global.DefaultNetwork;
|
else
|
||||||
profileItem.headerType = Global.None;
|
{
|
||||||
profileItem.requestHost = "";
|
SelectedSource = Utils.DeepCopy(profileItem);
|
||||||
profileItem.streamSecurity = "";
|
|
||||||
SelectedSource = profileItem;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedSource = Utils.DeepCopy(profileItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
SaveServer();
|
|
||||||
});
|
|
||||||
|
|
||||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
if (Utils.IsNullOrEmpty(SelectedSource.security))
|
||||||
if (Utils.IsNullOrEmpty(SelectedSource.address))
|
|
||||||
{
|
{
|
||||||
UI.Show(ResUI.FillServerAddress);
|
UI.Show(ResUI.PleaseSelectEncryption);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var port = SelectedSource.port.ToString();
|
}
|
||||||
if (Utils.IsNullOrEmpty(port) || !Utils.IsNumberic(port)
|
if (SelectedSource.configType != EConfigType.Socks)
|
||||||
|| SelectedSource.port <= 0 || SelectedSource.port >= Global.MaxPort)
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(SelectedSource.id))
|
||||||
{
|
{
|
||||||
UI.Show(ResUI.FillCorrectServerPort);
|
UI.Show(ResUI.FillUUID);
|
||||||
return;
|
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);
|
var item = LazyConfig.Instance.GetProfileItem(SelectedSource.indexId);
|
||||||
if (item is null)
|
if (item is null)
|
||||||
{
|
{
|
||||||
item = SelectedSource;
|
item = SelectedSource;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
item.coreType = SelectedSource.coreType;
|
item.coreType = SelectedSource.coreType;
|
||||||
item.remarks = SelectedSource.remarks;
|
item.remarks = SelectedSource.remarks;
|
||||||
item.address = SelectedSource.address;
|
item.address = SelectedSource.address;
|
||||||
item.port = SelectedSource.port;
|
item.port = SelectedSource.port;
|
||||||
|
|
||||||
item.id = SelectedSource.id;
|
item.id = SelectedSource.id;
|
||||||
item.alterId = SelectedSource.alterId;
|
item.alterId = SelectedSource.alterId;
|
||||||
item.security = SelectedSource.security;
|
item.security = SelectedSource.security;
|
||||||
item.flow = SelectedSource.flow;
|
item.flow = SelectedSource.flow;
|
||||||
|
|
||||||
item.network = SelectedSource.network;
|
item.network = SelectedSource.network;
|
||||||
item.headerType = SelectedSource.headerType;
|
item.headerType = SelectedSource.headerType;
|
||||||
item.requestHost = SelectedSource.requestHost;
|
item.requestHost = SelectedSource.requestHost;
|
||||||
item.path = SelectedSource.path;
|
item.path = SelectedSource.path;
|
||||||
|
|
||||||
item.streamSecurity = SelectedSource.streamSecurity;
|
item.streamSecurity = SelectedSource.streamSecurity;
|
||||||
item.sni = SelectedSource.sni;
|
item.sni = SelectedSource.sni;
|
||||||
item.allowInsecure = SelectedSource.allowInsecure;
|
item.allowInsecure = SelectedSource.allowInsecure;
|
||||||
item.fingerprint = SelectedSource.fingerprint;
|
item.fingerprint = SelectedSource.fingerprint;
|
||||||
item.alpn = SelectedSource.alpn;
|
item.alpn = SelectedSource.alpn;
|
||||||
|
|
||||||
item.publicKey = SelectedSource.publicKey;
|
item.publicKey = SelectedSource.publicKey;
|
||||||
item.shortId = SelectedSource.shortId;
|
item.shortId = SelectedSource.shortId;
|
||||||
item.spiderX = SelectedSource.spiderX;
|
item.spiderX = SelectedSource.spiderX;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
switch (item.configType)
|
switch (item.configType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
ret = ConfigHandler.AddServer(ref _config, item);
|
ret = ConfigHandler.AddServer(ref _config, item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
ret = ConfigHandler.AddShadowsocksServer(ref _config, item);
|
ret = ConfigHandler.AddShadowsocksServer(ref _config, item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Socks:
|
case EConfigType.Socks:
|
||||||
ret = ConfigHandler.AddSocksServer(ref _config, item);
|
ret = ConfigHandler.AddSocksServer(ref _config, item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
ret = ConfigHandler.AddVlessServer(ref _config, item);
|
ret = ConfigHandler.AddVlessServer(ref _config, item);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
ret = ConfigHandler.AddTrojanServer(ref _config, item);
|
ret = ConfigHandler.AddTrojanServer(ref _config, item);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||||
_view.DialogResult = true;
|
_view.DialogResult = true;
|
||||||
//_view?.Close();
|
//_view?.Close();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UI.Show(ResUI.OperationFailed);
|
UI.Show(ResUI.OperationFailed);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,104 +7,103 @@ using v2rayN.Handler;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
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<Unit, Unit> SaveCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCmd { get; }
|
||||||
|
|
||||||
|
public DNSSettingViewModel(Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private NoticeHandler? _noticeHandler;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
private Window _view;
|
_view = view;
|
||||||
|
|
||||||
[Reactive] public string domainStrategy4Freedom { get; set; }
|
var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray);
|
||||||
[Reactive] public string normalDNS { get; set; }
|
domainStrategy4Freedom = item?.domainStrategy4Freedom!;
|
||||||
[Reactive] public string normalDNS2 { get; set; }
|
normalDNS = item?.normalDNS!;
|
||||||
[Reactive] public string tunDNS2 { get; set; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
var item2 = LazyConfig.Instance.GetDNSItem(ECoreType.sing_box);
|
||||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCmd { get; }
|
normalDNS2 = item2?.normalDNS!;
|
||||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCmd { get; }
|
tunDNS2 = item2?.tunDNS!;
|
||||||
|
|
||||||
public DNSSettingViewModel(Window view)
|
SaveCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
SaveSetting();
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
});
|
||||||
_view = view;
|
|
||||||
|
|
||||||
var item = LazyConfig.Instance.GetDNSItem(ECoreType.Xray);
|
ImportDefConfig4V2rayCmd = ReactiveCommand.Create(() =>
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
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<Dns4Sbox>(normalDNS2);
|
if (normalDNS.Contains("{") || normalDNS.Contains("}"))
|
||||||
if (obj2 == null)
|
|
||||||
{
|
{
|
||||||
UI.Show(ResUI.FillCorrectDNSText);
|
UI.Show(ResUI.FillCorrectDNSText);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!Utils.IsNullOrEmpty(tunDNS2))
|
|
||||||
{
|
|
||||||
var obj2 = Utils.FromJson<Dns4Sbox>(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<Dns4Sbox>(normalDNS2);
|
||||||
|
if (obj2 == null)
|
||||||
|
{
|
||||||
|
UI.Show(ResUI.FillCorrectDNSText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Utils.IsNullOrEmpty(tunDNS2))
|
||||||
|
{
|
||||||
|
var obj2 = Utils.FromJson<Dns4Sbox>(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,362 +7,361 @@ using v2rayN.Handler;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
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<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
|
public OptionSettingViewModel(Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private NoticeHandler? _noticeHandler;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
private Window _view;
|
_view = view;
|
||||||
|
|
||||||
#region Core
|
#region Core
|
||||||
|
|
||||||
[Reactive] public int localPort { get; set; }
|
var inbound = _config.inbound[0];
|
||||||
[Reactive] public bool udpEnabled { get; set; }
|
localPort = inbound.localPort;
|
||||||
[Reactive] public bool sniffingEnabled { get; set; }
|
udpEnabled = inbound.udpEnabled;
|
||||||
[Reactive] public bool routeOnly { get; set; }
|
sniffingEnabled = inbound.sniffingEnabled;
|
||||||
[Reactive] public bool allowLANConn { get; set; }
|
routeOnly = inbound.routeOnly;
|
||||||
[Reactive] public bool newPort4LAN { get; set; }
|
allowLANConn = inbound.allowLANConn;
|
||||||
[Reactive] public string user { get; set; }
|
newPort4LAN = inbound.newPort4LAN;
|
||||||
[Reactive] public string pass { get; set; }
|
user = inbound.user;
|
||||||
[Reactive] public bool muxEnabled { get; set; }
|
pass = inbound.pass;
|
||||||
[Reactive] public bool logEnabled { get; set; }
|
muxEnabled = _config.coreBasicItem.muxEnabled;
|
||||||
[Reactive] public string loglevel { get; set; }
|
logEnabled = _config.coreBasicItem.logEnabled;
|
||||||
[Reactive] public bool defAllowInsecure { get; set; }
|
loglevel = _config.coreBasicItem.loglevel;
|
||||||
[Reactive] public string defFingerprint { get; set; }
|
defAllowInsecure = _config.coreBasicItem.defAllowInsecure;
|
||||||
[Reactive] public string defUserAgent { get; set; }
|
defFingerprint = _config.coreBasicItem.defFingerprint;
|
||||||
[Reactive] public string mux4SboxProtocol { get; set; }
|
defUserAgent = _config.coreBasicItem.defUserAgent;
|
||||||
|
mux4SboxProtocol = _config.mux4Sbox.protocol;
|
||||||
|
|
||||||
#endregion Core
|
#endregion Core
|
||||||
|
|
||||||
#region Core KCP
|
#region Core KCP
|
||||||
|
|
||||||
//[Reactive] public int Kcpmtu { get; set; }
|
//Kcpmtu = _config.kcpItem.mtu;
|
||||||
//[Reactive] public int Kcptti { get; set; }
|
//Kcptti = _config.kcpItem.tti;
|
||||||
//[Reactive] public int KcpuplinkCapacity { get; set; }
|
//KcpuplinkCapacity = _config.kcpItem.uplinkCapacity;
|
||||||
//[Reactive] public int KcpdownlinkCapacity { get; set; }
|
//KcpdownlinkCapacity = _config.kcpItem.downlinkCapacity;
|
||||||
//[Reactive] public int KcpreadBufferSize { get; set; }
|
//KcpreadBufferSize = _config.kcpItem.readBufferSize;
|
||||||
//[Reactive] public int KcpwriteBufferSize { get; set; }
|
//KcpwriteBufferSize = _config.kcpItem.writeBufferSize;
|
||||||
//[Reactive] public bool Kcpcongestion { get; set; }
|
//Kcpcongestion = _config.kcpItem.congestion;
|
||||||
|
|
||||||
#endregion Core KCP
|
#endregion Core KCP
|
||||||
|
|
||||||
#region UI
|
#region UI
|
||||||
|
|
||||||
[Reactive] public bool AutoRun { get; set; }
|
AutoRun = _config.guiItem.autoRun;
|
||||||
[Reactive] public bool EnableStatistics { get; set; }
|
EnableStatistics = _config.guiItem.enableStatistics;
|
||||||
[Reactive] public bool KeepOlderDedupl { get; set; }
|
KeepOlderDedupl = _config.guiItem.keepOlderDedupl;
|
||||||
[Reactive] public bool IgnoreGeoUpdateCore { get; set; }
|
IgnoreGeoUpdateCore = _config.guiItem.ignoreGeoUpdateCore;
|
||||||
[Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; }
|
EnableAutoAdjustMainLvColWidth = _config.uiItem.enableAutoAdjustMainLvColWidth;
|
||||||
[Reactive] public bool EnableSecurityProtocolTls13 { get; set; }
|
EnableSecurityProtocolTls13 = _config.guiItem.enableSecurityProtocolTls13;
|
||||||
[Reactive] public bool AutoHideStartup { get; set; }
|
AutoHideStartup = _config.uiItem.autoHideStartup;
|
||||||
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
|
EnableCheckPreReleaseUpdate = _config.guiItem.checkPreReleaseUpdate;
|
||||||
[Reactive] public bool EnableDragDropSort { get; set; }
|
EnableDragDropSort = _config.uiItem.enableDragDropSort;
|
||||||
[Reactive] public bool DoubleClick2Activate { get; set; }
|
DoubleClick2Activate = _config.uiItem.doubleClick2Activate;
|
||||||
[Reactive] public int autoUpdateInterval { get; set; }
|
autoUpdateInterval = _config.guiItem.autoUpdateInterval;
|
||||||
[Reactive] public int trayMenuServersLimit { get; set; }
|
trayMenuServersLimit = _config.guiItem.trayMenuServersLimit;
|
||||||
[Reactive] public string currentFontFamily { get; set; }
|
currentFontFamily = _config.uiItem.currentFontFamily;
|
||||||
[Reactive] public int SpeedTestTimeout { get; set; }
|
SpeedTestTimeout = _config.speedTestItem.speedTestTimeout;
|
||||||
[Reactive] public string SpeedTestUrl { get; set; }
|
SpeedTestUrl = _config.speedTestItem.speedTestUrl;
|
||||||
[Reactive] public bool EnableHWA { get; set; }
|
EnableHWA = _config.guiItem.enableHWA;
|
||||||
[Reactive] public string SubConvertUrl { get; set; }
|
SubConvertUrl = _config.constItem.subConvertUrl;
|
||||||
|
|
||||||
#endregion UI
|
#endregion UI
|
||||||
|
|
||||||
#region System proxy
|
#region System proxy
|
||||||
|
|
||||||
[Reactive] public string systemProxyAdvancedProtocol { get; set; }
|
systemProxyAdvancedProtocol = _config.systemProxyAdvancedProtocol;
|
||||||
[Reactive] public string systemProxyExceptions { get; set; }
|
systemProxyExceptions = _config.systemProxyExceptions;
|
||||||
|
|
||||||
#endregion System proxy
|
#endregion System proxy
|
||||||
|
|
||||||
#region Tun mode
|
#region Tun mode
|
||||||
|
|
||||||
[Reactive] public bool TunStrictRoute { get; set; }
|
TunStrictRoute = _config.tunModeItem.strictRoute;
|
||||||
[Reactive] public string TunStack { get; set; }
|
TunStack = _config.tunModeItem.stack;
|
||||||
[Reactive] public int TunMtu { get; set; }
|
TunMtu = _config.tunModeItem.mtu;
|
||||||
|
|
||||||
#endregion Tun mode
|
#endregion Tun mode
|
||||||
|
|
||||||
#region CoreType
|
InitCoreType();
|
||||||
|
|
||||||
[Reactive] public string CoreType1 { get; set; }
|
SaveCmd = ReactiveCommand.Create(() =>
|
||||||
[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<Unit, Unit> SaveCmd { get; }
|
|
||||||
|
|
||||||
public OptionSettingViewModel(Window view)
|
|
||||||
{
|
{
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
SaveSetting();
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
});
|
||||||
_view = view;
|
|
||||||
|
|
||||||
#region Core
|
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||||
|
}
|
||||||
|
|
||||||
var inbound = _config.inbound[0];
|
private void InitCoreType()
|
||||||
localPort = inbound.localPort;
|
{
|
||||||
udpEnabled = inbound.udpEnabled;
|
if (_config.coreTypeItem == null)
|
||||||
sniffingEnabled = inbound.sniffingEnabled;
|
{
|
||||||
routeOnly = inbound.routeOnly;
|
_config.coreTypeItem = new List<CoreTypeItem>();
|
||||||
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()
|
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<CoreTypeItem>();
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (EConfigType it in Enum.GetValues(typeof(EConfigType)))
|
_config.coreTypeItem.Add(new CoreTypeItem()
|
||||||
{
|
{
|
||||||
if (_config.coreTypeItem.FindIndex(t => t.configType == it) >= 0)
|
configType = it,
|
||||||
{
|
coreType = ECoreType.Xray
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
_config.coreTypeItem.ForEach(it =>
|
||||||
private void SaveSetting()
|
|
||||||
{
|
{
|
||||||
if (Utils.IsNullOrEmpty(localPort.ToString()) || !Utils.IsNumberic(localPort.ToString())
|
var type = it.coreType.ToString();
|
||||||
|| localPort <= 0 || localPort >= Global.MaxPort)
|
switch ((int)it.configType)
|
||||||
{
|
{
|
||||||
UI.Show(ResUI.FillLocalListeningPort);
|
case 1:
|
||||||
return;
|
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())
|
private void SaveSetting()
|
||||||
// || Utils.IsNullOrEmpty(Kcptti.ToString()) || !Utils.IsNumberic(Kcptti.ToString())
|
{
|
||||||
// || Utils.IsNullOrEmpty(KcpuplinkCapacity.ToString()) || !Utils.IsNumberic(KcpuplinkCapacity.ToString())
|
if (Utils.IsNullOrEmpty(localPort.ToString()) || !Utils.IsNumberic(localPort.ToString())
|
||||||
// || Utils.IsNullOrEmpty(KcpdownlinkCapacity.ToString()) || !Utils.IsNumberic(KcpdownlinkCapacity.ToString())
|
|| localPort <= 0 || localPort >= Global.MaxPort)
|
||||||
// || Utils.IsNullOrEmpty(KcpreadBufferSize.ToString()) || !Utils.IsNumberic(KcpreadBufferSize.ToString())
|
{
|
||||||
// || Utils.IsNullOrEmpty(KcpwriteBufferSize.ToString()) || !Utils.IsNumberic(KcpwriteBufferSize.ToString()))
|
UI.Show(ResUI.FillLocalListeningPort);
|
||||||
//{
|
return;
|
||||||
// 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 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++)
|
_config.inbound.RemoveAt(1);
|
||||||
{
|
}
|
||||||
var item = _config.coreTypeItem[k - 1];
|
_config.coreBasicItem.logEnabled = logEnabled;
|
||||||
var type = string.Empty;
|
_config.coreBasicItem.loglevel = loglevel;
|
||||||
switch ((int)item.configType)
|
_config.coreBasicItem.muxEnabled = muxEnabled;
|
||||||
{
|
_config.coreBasicItem.defAllowInsecure = defAllowInsecure;
|
||||||
case 1:
|
_config.coreBasicItem.defFingerprint = defFingerprint;
|
||||||
type = CoreType1;
|
_config.coreBasicItem.defUserAgent = defUserAgent;
|
||||||
break;
|
_config.mux4Sbox.protocol = mux4SboxProtocol;
|
||||||
|
|
||||||
case 2:
|
//Kcp
|
||||||
type = CoreType2;
|
//_config.kcpItem.mtu = Kcpmtu;
|
||||||
break;
|
//_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:
|
//UI
|
||||||
type = CoreType3;
|
Utils.SetAutoRun(AutoRun);
|
||||||
break;
|
_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:
|
//systemProxy
|
||||||
type = CoreType4;
|
_config.systemProxyExceptions = systemProxyExceptions;
|
||||||
break;
|
_config.systemProxyAdvancedProtocol = systemProxyAdvancedProtocol;
|
||||||
|
|
||||||
case 5:
|
//tun mode
|
||||||
type = CoreType5;
|
_config.tunModeItem.strictRoute = TunStrictRoute;
|
||||||
break;
|
_config.tunModeItem.stack = TunStack;
|
||||||
|
_config.tunModeItem.mtu = TunMtu;
|
||||||
|
|
||||||
case 6:
|
//coreType
|
||||||
type = CoreType6;
|
SaveCoreType();
|
||||||
break;
|
|
||||||
}
|
if (ConfigHandler.SaveConfig(ref _config) == 0)
|
||||||
item.coreType = (ECoreType)Enum.Parse(typeof(ECoreType), type);
|
{
|
||||||
}
|
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||||
return 0;
|
_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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,98 +8,97 @@ using v2rayN.Handler;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
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<string> ProtocolItems { get; set; }
|
||||||
|
public IList<string> 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<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
|
public RoutingRuleDetailsViewModel(RulesItem rulesItem, Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private NoticeHandler? _noticeHandler;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
private Window _view;
|
_view = view;
|
||||||
|
|
||||||
public IList<string> ProtocolItems { get; set; }
|
if (rulesItem.id.IsNullOrEmpty())
|
||||||
public IList<string> 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<Unit, Unit> SaveCmd { get; }
|
|
||||||
|
|
||||||
public RoutingRuleDetailsViewModel(RulesItem rulesItem, Window view)
|
|
||||||
{
|
{
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
rulesItem.id = Utils.GetGUID(false);
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
rulesItem.outboundTag = Global.agentTag;
|
||||||
_view = view;
|
rulesItem.enabled = true;
|
||||||
|
SelectedSource = rulesItem;
|
||||||
if (rulesItem.id.IsNullOrEmpty())
|
}
|
||||||
{
|
else
|
||||||
rulesItem.id = Utils.GetGUID(false);
|
{
|
||||||
rulesItem.outboundTag = Global.agentTag;
|
SelectedSource = rulesItem;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
SaveRules();
|
||||||
IP = Utils.Convert2Comma(IP);
|
});
|
||||||
Process = Utils.Convert2Comma(Process);
|
|
||||||
|
|
||||||
if (AutoSort)
|
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||||
{
|
}
|
||||||
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
|
private void SaveRules()
|
||||||
|| SelectedSource.ip?.Count > 0
|
{
|
||||||
|| SelectedSource.protocol?.Count > 0
|
Domain = Utils.Convert2Comma(Domain);
|
||||||
|| SelectedSource.process?.Count > 0
|
IP = Utils.Convert2Comma(IP);
|
||||||
|| !Utils.IsNullOrEmpty(SelectedSource.port);
|
Process = Utils.Convert2Comma(Process);
|
||||||
|
|
||||||
if (!hasRule)
|
if (AutoSort)
|
||||||
{
|
{
|
||||||
UI.ShowWarning(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Port/Protocol/Domain/IP/Process"));
|
SelectedSource.domain = Utils.String2ListSorted(Domain);
|
||||||
return;
|
SelectedSource.ip = Utils.String2ListSorted(IP);
|
||||||
}
|
SelectedSource.process = Utils.String2ListSorted(Process);
|
||||||
//_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
|
||||||
_view.DialogResult = true;
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,349 +12,348 @@ using v2rayN.Resx;
|
||||||
using v2rayN.Views;
|
using v2rayN.Views;
|
||||||
using Application = System.Windows.Application;
|
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<RulesItem> _rules;
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public RoutingItem SelectedRouting { get; set; }
|
||||||
|
|
||||||
|
private IObservableCollection<RulesItemModel> _rulesItems = new ObservableCollectionExtended<RulesItemModel>();
|
||||||
|
public IObservableCollection<RulesItemModel> RulesItems => _rulesItems;
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public RulesItemModel SelectedSource { get; set; }
|
||||||
|
|
||||||
|
public IList<RulesItemModel> SelectedSources { get; set; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> RuleAddCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ImportRulesFromFileCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ImportRulesFromClipboardCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> ImportRulesFromUrlCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RuleRemoveCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RuleExportSelectedCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
|
public RoutingRuleSettingViewModel(RoutingItem routingItem, Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private NoticeHandler? _noticeHandler;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
private Window _view;
|
_view = view;
|
||||||
private List<RulesItem> _rules;
|
SelectedSource = new();
|
||||||
|
|
||||||
[Reactive]
|
if (routingItem.id.IsNullOrEmpty())
|
||||||
public RoutingItem SelectedRouting { get; set; }
|
|
||||||
|
|
||||||
private IObservableCollection<RulesItemModel> _rulesItems = new ObservableCollectionExtended<RulesItemModel>();
|
|
||||||
public IObservableCollection<RulesItemModel> RulesItems => _rulesItems;
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public RulesItemModel SelectedSource { get; set; }
|
|
||||||
|
|
||||||
public IList<RulesItemModel> SelectedSources { get; set; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> RuleAddCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> ImportRulesFromFileCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> ImportRulesFromClipboardCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> ImportRulesFromUrlCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RuleRemoveCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RuleExportSelectedCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
|
||||||
|
|
||||||
public RoutingRuleSettingViewModel(RoutingItem routingItem, Window view)
|
|
||||||
{
|
{
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
SelectedRouting = routingItem;
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
_rules = new();
|
||||||
_view = view;
|
}
|
||||||
SelectedSource = new();
|
else
|
||||||
|
{
|
||||||
if (routingItem.id.IsNullOrEmpty())
|
SelectedRouting = routingItem;
|
||||||
{
|
_rules = Utils.FromJson<List<RulesItem>>(SelectedRouting.ruleSet);
|
||||||
SelectedRouting = routingItem;
|
|
||||||
_rules = new();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedRouting = routingItem;
|
|
||||||
_rules = Utils.FromJson<List<RulesItem>>(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshRulesItems()
|
RefreshRulesItems();
|
||||||
{
|
|
||||||
_rulesItems.Clear();
|
|
||||||
|
|
||||||
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,
|
||||||
id = item.id,
|
port = item.port,
|
||||||
outboundTag = item.outboundTag,
|
protocols = Utils.List2String(item.protocol),
|
||||||
port = item.port,
|
inboundTags = Utils.List2String(item.inboundTag),
|
||||||
protocols = Utils.List2String(item.protocol),
|
domains = Utils.List2String(item.domain),
|
||||||
inboundTags = Utils.List2String(item.inboundTag),
|
ips = Utils.List2String(item.ip),
|
||||||
domains = Utils.List2String(item.domain),
|
enabled = item.enabled,
|
||||||
ips = Utils.List2String(item.ip),
|
};
|
||||||
enabled = item.enabled,
|
_rulesItems.Add(it);
|
||||||
};
|
}
|
||||||
_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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var ret = (new RoutingRuleDetailsWindow(item)).ShowDialog();
|
||||||
public void RuleEdit(bool blNew)
|
if (ret == true)
|
||||||
{
|
{
|
||||||
RulesItem item;
|
|
||||||
if (blNew)
|
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();
|
RefreshRulesItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RuleExportSelected()
|
|
||||||
{
|
|
||||||
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
UI.Show(ResUI.PleaseSelectRules);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lst = new List<RulesItem>();
|
|
||||||
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<List<RulesItem>>(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<RulesItem>();
|
||||||
|
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<List<RulesItem>>(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
|
||||||
}
|
}
|
||||||
|
|
@ -10,299 +10,298 @@ using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
using v2rayN.Views;
|
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<RulesItem> _lockedRules;
|
||||||
|
|
||||||
|
#region Reactive
|
||||||
|
|
||||||
|
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
|
||||||
|
public IObservableCollection<RoutingItemModel> RoutingItems => _routingItems;
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public RoutingItemModel SelectedSource { get; set; }
|
||||||
|
|
||||||
|
public IList<RoutingItemModel> 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<Unit, Unit> RoutingBasicImportRulesCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> RoutingAdvancedImportRulesCmd { get; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
public bool IsModified { get; set; }
|
||||||
|
|
||||||
|
#endregion Reactive
|
||||||
|
|
||||||
|
public RoutingSettingViewModel(Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private NoticeHandler? _noticeHandler;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
private Window _view;
|
_view = view;
|
||||||
private RoutingItem _lockedItem;
|
SelectedSource = new();
|
||||||
private List<RulesItem> _lockedRules;
|
|
||||||
|
|
||||||
#region Reactive
|
ConfigHandler.InitBuiltinRouting(ref _config);
|
||||||
|
|
||||||
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
|
enableRoutingAdvanced = _config.routingBasicItem.enableRoutingAdvanced;
|
||||||
public IObservableCollection<RoutingItemModel> RoutingItems => _routingItems;
|
domainStrategy = _config.routingBasicItem.domainStrategy;
|
||||||
|
domainMatcher = _config.routingBasicItem.domainMatcher;
|
||||||
|
domainStrategy4Singbox = _config.routingBasicItem.domainStrategy4Singbox;
|
||||||
|
|
||||||
[Reactive]
|
RefreshRoutingItems();
|
||||||
public RoutingItemModel SelectedSource { get; set; }
|
|
||||||
|
|
||||||
public IList<RoutingItemModel> SelectedSources { get; set; }
|
BindingLockedData();
|
||||||
|
|
||||||
[Reactive]
|
var canEditRemove = this.WhenAnyValue(
|
||||||
public bool enableRoutingAdvanced { get; set; }
|
x => x.SelectedSource,
|
||||||
|
selectedSource => selectedSource != null && !selectedSource.remarks.IsNullOrEmpty());
|
||||||
|
|
||||||
[Reactive]
|
this.WhenAnyValue(
|
||||||
public bool enableRoutingBasic { get; set; }
|
x => x.enableRoutingAdvanced)
|
||||||
|
.Subscribe(c => enableRoutingBasic = !enableRoutingAdvanced);
|
||||||
|
|
||||||
[Reactive]
|
RoutingBasicImportRulesCmd = ReactiveCommand.Create(() =>
|
||||||
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<Unit, Unit> RoutingBasicImportRulesCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedImportRulesCmd { get; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
|
||||||
public bool IsModified { get; set; }
|
|
||||||
|
|
||||||
#endregion Reactive
|
|
||||||
|
|
||||||
public RoutingSettingViewModel(Window view)
|
|
||||||
{
|
{
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
RoutingBasicImportRules();
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
});
|
||||||
_view = view;
|
|
||||||
SelectedSource = new();
|
|
||||||
|
|
||||||
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;
|
SaveCmd = ReactiveCommand.Create(() =>
|
||||||
domainStrategy = _config.routingBasicItem.domainStrategy;
|
{
|
||||||
domainMatcher = _config.routingBasicItem.domainMatcher;
|
SaveRouting();
|
||||||
domainStrategy4Singbox = _config.routingBasicItem.domainStrategy4Singbox;
|
});
|
||||||
|
|
||||||
RefreshRoutingItems();
|
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||||
|
}
|
||||||
|
|
||||||
BindingLockedData();
|
#region locked
|
||||||
|
|
||||||
var canEditRemove = this.WhenAnyValue(
|
private void BindingLockedData()
|
||||||
x => x.SelectedSource,
|
{
|
||||||
selectedSource => selectedSource != null && !selectedSource.remarks.IsNullOrEmpty());
|
_lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config);
|
||||||
|
if (_lockedItem != null)
|
||||||
|
{
|
||||||
|
_lockedRules = Utils.FromJson<List<RulesItem>>(_lockedItem.ruleSet);
|
||||||
|
ProxyDomain = Utils.List2String(_lockedRules[0].domain, true);
|
||||||
|
ProxyIP = Utils.List2String(_lockedRules[0].ip, true);
|
||||||
|
|
||||||
this.WhenAnyValue(
|
DirectDomain = Utils.List2String(_lockedRules[1].domain, true);
|
||||||
x => x.enableRoutingAdvanced)
|
DirectIP = Utils.List2String(_lockedRules[1].ip, true);
|
||||||
.Subscribe(c => enableRoutingBasic = !enableRoutingAdvanced);
|
|
||||||
|
|
||||||
RoutingBasicImportRulesCmd = ReactiveCommand.Create(() =>
|
BlockDomain = Utils.List2String(_lockedRules[2].domain, true);
|
||||||
{
|
BlockIP = Utils.List2String(_lockedRules[2].ip, true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region locked
|
private void EndBindingLockedData()
|
||||||
|
{
|
||||||
private void BindingLockedData()
|
if (_lockedItem != null)
|
||||||
{
|
{
|
||||||
_lockedItem = ConfigHandler.GetLockedRoutingItem(ref _config);
|
_lockedRules[0].domain = Utils.String2List(Utils.Convert2Comma(ProxyDomain.TrimEx()));
|
||||||
if (_lockedItem != null)
|
_lockedRules[0].ip = Utils.String2List(Utils.Convert2Comma(ProxyIP.TrimEx()));
|
||||||
{
|
|
||||||
_lockedRules = Utils.FromJson<List<RulesItem>>(_lockedItem.ruleSet);
|
|
||||||
ProxyDomain = Utils.List2String(_lockedRules[0].domain, true);
|
|
||||||
ProxyIP = Utils.List2String(_lockedRules[0].ip, true);
|
|
||||||
|
|
||||||
DirectDomain = Utils.List2String(_lockedRules[1].domain, true);
|
_lockedRules[1].domain = Utils.String2List(Utils.Convert2Comma(DirectDomain.TrimEx()));
|
||||||
DirectIP = Utils.List2String(_lockedRules[1].ip, true);
|
_lockedRules[1].ip = Utils.String2List(Utils.Convert2Comma(DirectIP.TrimEx()));
|
||||||
|
|
||||||
BlockDomain = Utils.List2String(_lockedRules[2].domain, true);
|
_lockedRules[2].domain = Utils.String2List(Utils.Convert2Comma(BlockDomain.TrimEx()));
|
||||||
BlockIP = Utils.List2String(_lockedRules[2].ip, true);
|
_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()));
|
def = true;
|
||||||
_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
||||||
|
_view.DialogResult = true;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
private void SaveRouting()
|
|
||||||
{
|
{
|
||||||
_config.routingBasicItem.domainStrategy = domainStrategy;
|
UI.ShowWarning(ResUI.OperationFailed);
|
||||||
_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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#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
|
item = new();
|
||||||
ProxyDomain = "geosite:google";
|
|
||||||
DirectDomain = "geosite:cn";
|
|
||||||
DirectIP = "geoip:private,geoip:cn";
|
|
||||||
BlockDomain = "geosite:category-ads-all";
|
|
||||||
|
|
||||||
//_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
|
||||||
UI.Show(ResUI.OperationSuccess);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public void RoutingAdvancedEdit(bool blNew)
|
|
||||||
{
|
{
|
||||||
RoutingItem item;
|
item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id);
|
||||||
if (blNew)
|
if (item is null)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach (var it in SelectedSources)
|
}
|
||||||
{
|
var ret = (new RoutingRuleSettingWindow(item)).ShowDialog();
|
||||||
var item = LazyConfig.Instance.GetRoutingItem(it?.id);
|
if (ret == true)
|
||||||
if (item != null)
|
{
|
||||||
{
|
|
||||||
ConfigHandler.RemoveRoutingItem(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshRoutingItems();
|
RefreshRoutingItems();
|
||||||
IsModified = true;
|
IsModified = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void RoutingAdvancedSetDefault()
|
public void RoutingAdvancedRemove()
|
||||||
|
{
|
||||||
|
if (SelectedSource is null || SelectedSource.remarks.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
var item = LazyConfig.Instance.GetRoutingItem(SelectedSource?.id);
|
UI.Show(ResUI.PleaseSelectRules);
|
||||||
if (item is null)
|
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);
|
ConfigHandler.RemoveRoutingItem(item);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0)
|
|
||||||
{
|
|
||||||
RefreshRoutingItems();
|
|
||||||
IsModified = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
UI.Show(ResUI.PleaseSelectRules);
|
||||||
{
|
return;
|
||||||
RefreshRoutingItems();
|
}
|
||||||
IsModified = true;
|
|
||||||
}
|
if (ConfigHandler.SetDefaultRouting(ref _config, item) == 0)
|
||||||
|
{
|
||||||
|
RefreshRoutingItems();
|
||||||
|
IsModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RoutingAdvancedImportRules()
|
||||||
|
{
|
||||||
|
if (ConfigHandler.InitBuiltinRouting(ref _config, true) == 0)
|
||||||
|
{
|
||||||
|
RefreshRoutingItems();
|
||||||
|
IsModified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,79 +8,78 @@ using v2rayN.Handler;
|
||||||
using v2rayN.Mode;
|
using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
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<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
|
public SubEditViewModel(SubItem subItem, Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private NoticeHandler? _noticeHandler;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
private Window _view;
|
_view = view;
|
||||||
|
|
||||||
[Reactive]
|
if (subItem.id.IsNullOrEmpty())
|
||||||
public SubItem SelectedSource { get; set; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
|
||||||
|
|
||||||
public SubEditViewModel(SubItem subItem, Window view)
|
|
||||||
{
|
{
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
SelectedSource = subItem;
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
}
|
||||||
_view = view;
|
else
|
||||||
|
{
|
||||||
if (subItem.id.IsNullOrEmpty())
|
SelectedSource = Utils.DeepCopy(subItem);
|
||||||
{
|
|
||||||
SelectedSource = subItem;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedSource = Utils.DeepCopy(subItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
SaveSub();
|
|
||||||
});
|
|
||||||
|
|
||||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveSub()
|
SaveCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
string remarks = SelectedSource.remarks;
|
SaveSub();
|
||||||
if (string.IsNullOrEmpty(remarks))
|
});
|
||||||
{
|
|
||||||
UI.Show(ResUI.PleaseFillRemarks);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = LazyConfig.Instance.GetSubItem(SelectedSource.id);
|
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
||||||
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)
|
private void SaveSub()
|
||||||
{
|
{
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
string remarks = SelectedSource.remarks;
|
||||||
_view.DialogResult = true;
|
if (string.IsNullOrEmpty(remarks))
|
||||||
//_view?.Close();
|
{
|
||||||
}
|
UI.Show(ResUI.PleaseFillRemarks);
|
||||||
else
|
return;
|
||||||
{
|
}
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationFailed);
|
|
||||||
}
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,119 +12,118 @@ using v2rayN.Mode;
|
||||||
using v2rayN.Resx;
|
using v2rayN.Resx;
|
||||||
using v2rayN.Views;
|
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<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
|
||||||
|
public IObservableCollection<SubItem> SubItems => _subItems;
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public SubItem SelectedSource { get; set; }
|
||||||
|
|
||||||
|
public IList<SubItem> SelectedSources { get; set; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> SubAddCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SubDeleteCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SubEditCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SubShareCmd { get; }
|
||||||
|
public bool IsModified { get; set; }
|
||||||
|
|
||||||
|
public SubSettingViewModel(Window view)
|
||||||
{
|
{
|
||||||
private static Config _config;
|
_config = LazyConfig.Instance.GetConfig();
|
||||||
private NoticeHandler? _noticeHandler;
|
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||||
|
|
||||||
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
|
SelectedSource = new();
|
||||||
public IObservableCollection<SubItem> SubItems => _subItems;
|
|
||||||
|
|
||||||
[Reactive]
|
RefreshSubItems();
|
||||||
public SubItem SelectedSource { get; set; }
|
|
||||||
|
|
||||||
public IList<SubItem> SelectedSources { get; set; }
|
var canEditRemove = this.WhenAnyValue(
|
||||||
|
x => x.SelectedSource,
|
||||||
|
selectedSource => selectedSource != null && !selectedSource.id.IsNullOrEmpty());
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SubAddCmd { get; }
|
SubAddCmd = ReactiveCommand.Create(() =>
|
||||||
public ReactiveCommand<Unit, Unit> SubDeleteCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> SubEditCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> SubShareCmd { get; }
|
|
||||||
public bool IsModified { get; set; }
|
|
||||||
|
|
||||||
public SubSettingViewModel(Window view)
|
|
||||||
{
|
{
|
||||||
_config = LazyConfig.Instance.GetConfig();
|
EditSub(true);
|
||||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
});
|
||||||
|
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(
|
public void EditSub(bool blNew)
|
||||||
x => x.SelectedSource,
|
{
|
||||||
selectedSource => selectedSource != null && !selectedSource.id.IsNullOrEmpty());
|
SubItem item;
|
||||||
|
if (blNew)
|
||||||
SubAddCmd = ReactiveCommand.Create(() =>
|
{
|
||||||
{
|
item = new();
|
||||||
EditSub(true);
|
|
||||||
});
|
|
||||||
SubDeleteCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
DeleteSub();
|
|
||||||
}, canEditRemove);
|
|
||||||
SubEditCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
EditSub(false);
|
|
||||||
}, canEditRemove);
|
|
||||||
SubShareCmd = ReactiveCommand.Create(() =>
|
|
||||||
{
|
|
||||||
SubShare();
|
|
||||||
}, canEditRemove);
|
|
||||||
|
|
||||||
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public void RefreshSubItems()
|
|
||||||
{
|
{
|
||||||
_subItems.Clear();
|
item = LazyConfig.Instance.GetSubItem(SelectedSource?.id);
|
||||||
_subItems.AddRange(LazyConfig.Instance.SubItems().OrderBy(t => t.sort));
|
if (item is null)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
foreach (var it in SelectedSources)
|
var ret = (new SubEditWindow(item)).ShowDialog();
|
||||||
{
|
if (ret == true)
|
||||||
ConfigHandler.DeleteSubItem(ref _config, it?.id);
|
{
|
||||||
}
|
|
||||||
RefreshSubItems();
|
RefreshSubItems();
|
||||||
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
|
|
||||||
IsModified = true;
|
IsModified = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void SubShare()
|
private void DeleteSub()
|
||||||
|
{
|
||||||
|
if (UI.ShowYesNo(ResUI.RemoveServer) == MessageBoxResult.No)
|
||||||
{
|
{
|
||||||
if (Utils.IsNullOrEmpty(SelectedSource?.url))
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var img = QRCodeHelper.GetQRCode(SelectedSource?.url);
|
|
||||||
var dialog = new QrcodeView()
|
|
||||||
{
|
|
||||||
imgQrcode = { Source = img },
|
|
||||||
txtContent = { Text = SelectedSource?.url },
|
|
||||||
};
|
|
||||||
|
|
||||||
await DialogHost.Show(dialog, "SubDialog");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
|
@ -12,86 +11,82 @@
|
||||||
<Copyright>Copyright © 2017-2023 (GPLv3)</Copyright>
|
<Copyright>Copyright © 2017-2023 (GPLv3)</Copyright>
|
||||||
<FileVersion>6.28</FileVersion>
|
<FileVersion>6.28</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Downloader" Version="3.0.6" />
|
<PackageReference Include="Downloader" Version="3.0.6" />
|
||||||
<PackageReference Include="MaterialDesignThemes" Version="4.9.0" />
|
<PackageReference Include="MaterialDesignThemes" Version="4.9.0" />
|
||||||
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.0.108" />
|
<PackageReference Include="H.NotifyIcon.Wpf" Version="2.0.115" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="QRCoder.Xaml" Version="1.4.3" />
|
<PackageReference Include="QRCoder.Xaml" Version="1.4.3" />
|
||||||
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
|
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
|
||||||
<PackageReference Include="TaskScheduler" Version="2.10.1" />
|
<PackageReference Include="TaskScheduler" Version="2.10.1" />
|
||||||
<PackageReference Include="ZXing.Net.Bindings.Windows.Compatibility" Version="0.16.12" />
|
<PackageReference Include="ZXing.Net.Bindings.Windows.Compatibility" Version="0.16.12" />
|
||||||
<PackageReference Include="ReactiveUI.Fody" Version="18.4.1" />
|
<PackageReference Include="ReactiveUI.Fody" Version="18.4.1" />
|
||||||
<PackageReference Include="ReactiveUI.Validation" Version="3.0.22" />
|
<PackageReference Include="ReactiveUI.Validation" Version="3.1.7" />
|
||||||
<PackageReference Include="ReactiveUI.WPF" Version="18.4.1" />
|
<PackageReference Include="ReactiveUI.WPF" Version="18.4.1" />
|
||||||
<PackageReference Include="Splat.NLog" Version="14.6.37" />
|
<PackageReference Include="Splat.NLog" Version="14.7.1" />
|
||||||
<PackageReference Include="System.Reactive" Version="6.0.0" />
|
<PackageReference Include="System.Reactive" Version="6.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="app.manifest" />
|
<AdditionalFiles Include="app.manifest" />
|
||||||
<EmbeddedResource Include="Sample\SingboxSampleClientConfig">
|
<EmbeddedResource Include="Sample\SingboxSampleClientConfig">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\tun_singbox_dns">
|
<EmbeddedResource Include="Sample\tun_singbox_dns">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\custom_routing_black">
|
<EmbeddedResource Include="Sample\custom_routing_black">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\custom_routing_global">
|
<EmbeddedResource Include="Sample\custom_routing_global">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\custom_routing_locked">
|
<EmbeddedResource Include="Sample\custom_routing_locked">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\custom_routing_rules">
|
<EmbeddedResource Include="Sample\custom_routing_rules">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\custom_routing_white">
|
<EmbeddedResource Include="Sample\custom_routing_white">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\SampleClientConfig">
|
<EmbeddedResource Include="Sample\SampleClientConfig">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\SampleHttprequest">
|
<EmbeddedResource Include="Sample\SampleHttprequest">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\SampleHttpresponse">
|
<EmbeddedResource Include="Sample\SampleHttpresponse">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\SampleInbound">
|
<EmbeddedResource Include="Sample\SampleInbound">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\tun_singbox_rules">
|
<EmbeddedResource Include="Sample\tun_singbox_rules">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\tun_singbox_inbound">
|
<EmbeddedResource Include="Sample\tun_singbox_inbound">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\dns_v2ray_normal">
|
<EmbeddedResource Include="Sample\dns_v2ray_normal">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Sample\dns_singbox_normal">
|
<EmbeddedResource Include="Sample\dns_singbox_normal">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="v2rayN.ico">
|
<EmbeddedResource Include="v2rayN.ico">
|
||||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<Resource Include="Resources\NotifyIcon1.ico" />
|
<Resource Include="Resources\NotifyIcon1.ico" />
|
||||||
<Resource Include="Resources\NotifyIcon2.ico" />
|
<Resource Include="Resources\NotifyIcon2.ico" />
|
||||||
<Resource Include="Resources\NotifyIcon3.ico" />
|
<Resource Include="Resources\NotifyIcon3.ico" />
|
||||||
<Resource Include="v2rayN.ico">
|
<Resource Include="v2rayN.ico">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Resource>
|
</Resource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
<ItemGroup>
|
<ProjectReference Include="..\PacLib\PacLib.csproj" />
|
||||||
<ProjectReference Include="..\PacLib\PacLib.csproj" />
|
<ProjectReference Include="..\ProtosLib\ProtosLib.csproj" />
|
||||||
<ProjectReference Include="..\ProtosLib\ProtosLib.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Resx\ResUI.Designer.cs">
|
<Compile Update="Resx\ResUI.Designer.cs">
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
|
|
@ -99,24 +94,22 @@
|
||||||
<DependentUpon>ResUI.resx</DependentUpon>
|
<DependentUpon>ResUI.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Update="Resx\ResUI.resx">
|
<EmbeddedResource Update="Resx\ResUI.resx">
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>ResUI.Designer.cs</LastGenOutput>
|
<LastGenOutput>ResUI.Designer.cs</LastGenOutput>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Update="Resx\ResUI.zh-Hans.resx">
|
<EmbeddedResource Update="Resx\ResUI.zh-Hans.resx">
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Update="Resx\ResUI.zh-Hant.resx">
|
<EmbeddedResource Update="Resx\ResUI.zh-Hant.resx">
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Update="Resx\ResUI.fa-Ir.resx">
|
<EmbeddedResource Update="Resx\ResUI.fa-Ir.resx">
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Update="Resx\ResUI.ru.resx">
|
<EmbeddedResource Update="Resx\ResUI.ru.resx">
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -6,143 +6,142 @@ using System.Text;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace v2rayUpgrade
|
namespace v2rayUpgrade;
|
||||||
{
|
|
||||||
public partial class MainForm : Form
|
|
||||||
{
|
|
||||||
private readonly string defaultFilename = "v2ray-windows.zip";
|
|
||||||
private string? fileName;
|
|
||||||
|
|
||||||
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();
|
fileName = HttpUtility.UrlDecode(string.Join(" ", args));
|
||||||
if (args.Length > 0)
|
}
|
||||||
|
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;
|
fileName = defaultFilename;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
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");
|
ShowWarn("Upgrade Failed, File Not Exist(升级失败,文件不存在).");
|
||||||
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);
|
|
||||||
return;
|
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" +
|
try
|
||||||
"(升级失败,按住ctrl+c可以复制到剪贴板)." + sb.ToString());
|
{
|
||||||
return;
|
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();
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
private void btnClose_Click(object sender, EventArgs e)
|
|
||||||
{
|
{
|
||||||
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()
|
Process.Start("v2rayN.exe");
|
||||||
{
|
MessageBox.Show("Upgrade successed(升级成功)", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
return Application.ExecutablePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string StartupPath()
|
Close();
|
||||||
{
|
}
|
||||||
return Application.StartupPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
return startupPath;
|
||||||
if (string.IsNullOrEmpty(fileName))
|
|
||||||
{
|
|
||||||
return startupPath;
|
|
||||||
}
|
|
||||||
return Path.Combine(startupPath, fileName);
|
|
||||||
}
|
}
|
||||||
|
return Path.Combine(startupPath, fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace v2rayUpgrade
|
namespace v2rayUpgrade;
|
||||||
|
|
||||||
|
internal static class Program
|
||||||
{
|
{
|
||||||
internal static class Program
|
[STAThread]
|
||||||
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
/// <summary>
|
Application.EnableVisualStyles();
|
||||||
/// 应用程序的主入口点。
|
Application.SetHighDpiMode(HighDpiMode.SystemAware);
|
||||||
/// </summary>
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
[STAThread]
|
Application.Run(new MainForm(args));
|
||||||
private static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Application.EnableVisualStyles();
|
|
||||||
Application.SetHighDpiMode(HighDpiMode.SystemAware);
|
|
||||||
Application.SetCompatibleTextRenderingDefault(false);
|
|
||||||
Application.Run(new MainForm(args));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
<Copyright>Copyright © 2019-2023 (GPLv3)</Copyright>
|
<Copyright>Copyright © 2019-2023 (GPLv3)</Copyright>
|
||||||
<FileVersion>1.1.0.0</FileVersion>
|
<FileVersion>1.1.0.0</FileVersion>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
Loading…
Reference in a new issue