Compare commits

..

No commits in common. "24ccfb80776585bee04b0544999f2ba58eb9e21c" and "6ad07627314ebd7a6f523264884e18c1046cbd69" have entirely different histories.

204 changed files with 24063 additions and 24146 deletions

View file

@ -1,25 +1,26 @@
namespace AmazTool; namespace AmazTool
internal static class Program
{ {
[STAThread] internal static class Program
private static void Main(string[] args)
{ {
if (args.Length == 0) [STAThread]
private static void Main(string[] args)
{ {
Console.WriteLine(Resx.Resource.Guidelines); if (args.Length == 0)
Thread.Sleep(5000); {
return; Console.WriteLine(Resx.Resource.Guidelines);
} Thread.Sleep(5000);
return;
}
var argData = Uri.UnescapeDataString(string.Join(" ", args)); var argData = Uri.UnescapeDataString(string.Join(" ", args));
if (argData.Equals("rebootas")) if (argData.Equals("rebootas"))
{ {
Thread.Sleep(1000); Thread.Sleep(1000);
Utils.StartV2RayN(); Utils.StartV2RayN();
return; return;
} }
UpgradeApp.Upgrade(argData); UpgradeApp.Upgrade(argData);
}
} }
} }

View file

@ -2,115 +2,116 @@ using System.Diagnostics;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
namespace AmazTool; namespace AmazTool
internal class UpgradeApp
{ {
public static void Upgrade(string fileName) internal class UpgradeApp
{ {
Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}"); public static void Upgrade(string fileName)
Utils.Waiting(5);
if (!File.Exists(fileName))
{ {
Console.WriteLine(Resx.Resource.UpgradeFileNotFound); Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}");
return;
}
Console.WriteLine(Resx.Resource.TryTerminateProcess); Utils.Waiting(5);
try
{ if (!File.Exists(fileName))
var existing = Process.GetProcessesByName(Utils.V2rayN);
foreach (var pp in existing)
{ {
var path = pp.MainModule?.FileName ?? ""; Console.WriteLine(Resx.Resource.UpgradeFileNotFound);
if (path.StartsWith(Utils.GetPath(Utils.V2rayN))) return;
}
Console.WriteLine(Resx.Resource.TryTerminateProcess);
try
{
var existing = Process.GetProcessesByName(Utils.V2rayN);
foreach (var pp in existing)
{ {
pp?.Kill(); var path = pp.MainModule?.FileName ?? "";
pp?.WaitForExit(1000); if (path.StartsWith(Utils.GetPath(Utils.V2rayN)))
{
pp?.Kill();
pp?.WaitForExit(1000);
}
} }
} }
} catch (Exception ex)
catch (Exception ex)
{
// Access may be denied without admin right. The user may not be an administrator.
Console.WriteLine(Resx.Resource.FailedTerminateProcess + ex.StackTrace);
}
Console.WriteLine(Resx.Resource.StartUnzipping);
StringBuilder sb = new();
try
{
var thisAppOldFile = $"{Utils.GetExePath()}.tmp";
File.Delete(thisAppOldFile);
var splitKey = "/";
using var archive = ZipFile.OpenRead(fileName);
foreach (var entry in archive.Entries)
{ {
try // Access may be denied without admin right. The user may not be an administrator.
Console.WriteLine(Resx.Resource.FailedTerminateProcess + ex.StackTrace);
}
Console.WriteLine(Resx.Resource.StartUnzipping);
StringBuilder sb = new();
try
{
var thisAppOldFile = $"{Utils.GetExePath()}.tmp";
File.Delete(thisAppOldFile);
var splitKey = "/";
using var archive = ZipFile.OpenRead(fileName);
foreach (var entry in archive.Entries)
{ {
if (entry.Length == 0)
{
continue;
}
Console.WriteLine(entry.FullName);
var lst = entry.FullName.Split(splitKey);
if (lst.Length == 1)
{
continue;
}
var fullName = string.Join(splitKey, lst[1..lst.Length]);
if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
{
File.Move(Utils.GetExePath(), thisAppOldFile);
}
var entryOutputPath = Utils.GetPath(fullName);
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
//In the bin folder, if the file already exists, it will be skipped
if (fullName.StartsWith("bin") && File.Exists(entryOutputPath))
{
continue;
}
try try
{ {
entry.ExtractToFile(entryOutputPath, true); if (entry.Length == 0)
} {
catch continue;
{ }
Thread.Sleep(1000);
entry.ExtractToFile(entryOutputPath, true);
}
Console.WriteLine(entryOutputPath); Console.WriteLine(entry.FullName);
}
catch (Exception ex) var lst = entry.FullName.Split(splitKey);
{ if (lst.Length == 1)
sb.Append(ex.StackTrace); {
continue;
}
var fullName = string.Join(splitKey, lst[1..lst.Length]);
if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
{
File.Move(Utils.GetExePath(), thisAppOldFile);
}
var entryOutputPath = Utils.GetPath(fullName);
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
//In the bin folder, if the file already exists, it will be skipped
if (fullName.StartsWith("bin") && File.Exists(entryOutputPath))
{
continue;
}
try
{
entry.ExtractToFile(entryOutputPath, true);
}
catch
{
Thread.Sleep(1000);
entry.ExtractToFile(entryOutputPath, true);
}
Console.WriteLine(entryOutputPath);
}
catch (Exception ex)
{
sb.Append(ex.StackTrace);
}
} }
} }
} catch (Exception ex)
catch (Exception ex) {
{ Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace);
Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace); //return;
//return; }
} if (sb.Length > 0)
if (sb.Length > 0) {
{ Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString());
Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString()); //return;
//return; }
}
Console.WriteLine(Resx.Resource.Restartv2rayN); Console.WriteLine(Resx.Resource.Restartv2rayN);
Utils.Waiting(2); Utils.Waiting(2);
Utils.StartV2RayN(); Utils.StartV2RayN();
}
} }
} }

View file

@ -1,51 +1,52 @@
using System.Diagnostics; using System.Diagnostics;
namespace AmazTool; namespace AmazTool
internal class Utils
{ {
public static string GetExePath() internal class Utils
{ {
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty; public static string GetExePath()
}
public static string StartupPath()
{
return AppDomain.CurrentDomain.BaseDirectory;
}
public static string GetPath(string fileName)
{
var startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{ {
return startupPath; return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
} }
return Path.Combine(startupPath, fileName);
}
public static string V2rayN => "v2rayN"; public static string StartupPath()
public static void StartV2RayN()
{
Process process = new()
{ {
StartInfo = new() return AppDomain.CurrentDomain.BaseDirectory;
}
public static string GetPath(string fileName)
{
var startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{ {
UseShellExecute = true, return startupPath;
FileName = V2rayN,
WorkingDirectory = StartupPath()
} }
}; return Path.Combine(startupPath, fileName);
process.Start(); }
}
public static void Waiting(int second) public static string V2rayN => "v2rayN";
{
for (var i = second; i > 0; i--) public static void StartV2RayN()
{ {
Console.WriteLine(i); Process process = new()
Thread.Sleep(1000); {
StartInfo = new()
{
UseShellExecute = true,
FileName = V2rayN,
WorkingDirectory = StartupPath()
}
};
process.Start();
}
public static void Waiting(int second)
{
for (var i = second; i > 0; i--)
{
Console.WriteLine(i);
Thread.Sleep(1000);
}
} }
} }
} }

View file

@ -1,14 +1,14 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.11.0</Version> <Version>7.10.5</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058</NoWarn> <NoWarn>CA1031;CS1591;NU1507;CA1416</NoWarn>
<Nullable>annotations</Nullable> <Nullable>annotations</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Authors>2dust</Authors> <Authors>2dust</Authors>

View file

@ -5,10 +5,10 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled> <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.6" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.5" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.6" /> <PackageVersion Include="Avalonia.Desktop" Version="11.2.5" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.6" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.2.5" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.6" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.5" />
<PackageVersion Include="CliWrap" Version="3.8.2" /> <PackageVersion Include="CliWrap" Version="3.8.2" />
<PackageVersion Include="Downloader" Version="3.3.4" /> <PackageVersion Include="Downloader" Version="3.3.4" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
@ -18,12 +18,12 @@
<PackageVersion Include="ReactiveUI" Version="20.2.45" /> <PackageVersion Include="ReactiveUI" Version="20.2.45" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.2.45" /> <PackageVersion Include="ReactiveUI.WPF" Version="20.2.45" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.6" /> <PackageVersion Include="Semi.Avalonia" Version="11.2.1.5" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.6" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.5" />
<PackageVersion Include="Splat.NLog" Version="15.3.1" /> <PackageVersion Include="Splat.NLog" Version="15.3.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.1" /> <PackageVersion Include="TaskScheduler" Version="2.12.1" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" /> <PackageVersion Include="WebDav.Client" Version="2.8.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" /> <PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" /> <PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup> </ItemGroup>

View file

@ -1,9 +1,10 @@
using ReactiveUI; using ReactiveUI;
namespace ServiceLib.Base; namespace ServiceLib.Base
public class MyReactiveObject : ReactiveObject
{ {
protected static Config? _config; public class MyReactiveObject : ReactiveObject
protected Func<EViewAction, object?, Task<bool>>? _updateView; {
} protected static Config? _config;
protected Func<EViewAction, object?, Task<bool>>? _updateView;
}
}

View file

@ -1,100 +1,101 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace ServiceLib.Common; namespace ServiceLib.Common
public class AesUtils
{ {
private const int KeySize = 256; // AES-256 public class AesUtils
private const int IvSize = 16; // AES block size
private const int Iterations = 10000;
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
/// <summary>
/// Encrypt
/// </summary>
/// <param name="text">Plain text</param>
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// <returns>Base64 encoded cipher text with IV</returns>
public static string Encrypt(string text, string? password = null)
{ {
if (string.IsNullOrEmpty(text)) private const int KeySize = 256; // AES-256
return string.Empty; private const int IvSize = 16; // AES block size
private const int Iterations = 10000;
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
var plaintext = Encoding.UTF8.GetBytes(text); /// <summary>
var key = GetKey(password); /// Encrypt
var iv = GenerateIv(); /// </summary>
/// <param name="text">Plain text</param>
using var aes = Aes.Create(); /// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
aes.Key = key; /// <returns>Base64 encoded cipher text with IV</returns>
aes.IV = iv; public static string Encrypt(string text, string? password = null)
using var ms = new MemoryStream();
ms.Write(iv, 0, iv.Length);
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{ {
cs.Write(plaintext, 0, plaintext.Length); if (string.IsNullOrEmpty(text))
cs.FlushFinalBlock(); return string.Empty;
var plaintext = Encoding.UTF8.GetBytes(text);
var key = GetKey(password);
var iv = GenerateIv();
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
ms.Write(iv, 0, iv.Length);
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(plaintext, 0, plaintext.Length);
cs.FlushFinalBlock();
}
var cipherTextWithIv = ms.ToArray();
return Convert.ToBase64String(cipherTextWithIv);
} }
var cipherTextWithIv = ms.ToArray(); /// <summary>
return Convert.ToBase64String(cipherTextWithIv); /// Decrypt
} /// </summary>
/// <param name="cipherTextWithIv">Base64 encoded cipher text with IV</param>
/// <summary> /// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// Decrypt /// <returns>Plain text</returns>
/// </summary> public static string Decrypt(string cipherTextWithIv, string? password = null)
/// <param name="cipherTextWithIv">Base64 encoded cipher text with IV</param>
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// <returns>Plain text</returns>
public static string Decrypt(string cipherTextWithIv, string? password = null)
{
if (string.IsNullOrEmpty(cipherTextWithIv))
return string.Empty;
var cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv);
var key = GetKey(password);
var iv = new byte[IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize);
var cipherText = new byte[cipherTextWithIvBytes.Length - IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length);
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{ {
cs.Write(cipherText, 0, cipherText.Length); if (string.IsNullOrEmpty(cipherTextWithIv))
cs.FlushFinalBlock(); return string.Empty;
var cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv);
var key = GetKey(password);
var iv = new byte[IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize);
var cipherText = new byte[cipherTextWithIvBytes.Length - IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length);
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
cs.FlushFinalBlock();
}
var plainText = ms.ToArray();
return Encoding.UTF8.GetString(plainText);
} }
var plainText = ms.ToArray(); private static byte[] GetKey(string? password)
return Encoding.UTF8.GetString(plainText);
}
private static byte[] GetKey(string? password)
{
if (password.IsNullOrEmpty())
{ {
password = DefaultPassword; if (password.IsNullOrEmpty())
{
password = DefaultPassword;
}
using var pbkdf2 = new Rfc2898DeriveBytes(password, Salt, Iterations, HashAlgorithmName.SHA256);
return pbkdf2.GetBytes(KeySize / 8);
} }
using var pbkdf2 = new Rfc2898DeriveBytes(password, Salt, Iterations, HashAlgorithmName.SHA256); private static byte[] GenerateIv()
return pbkdf2.GetBytes(KeySize / 8); {
} var randomNumber = new byte[IvSize];
private static byte[] GenerateIv() using var rng = RandomNumberGenerator.Create();
{ rng.GetBytes(randomNumber);
var randomNumber = new byte[IvSize]; return randomNumber;
}
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomNumber);
return randomNumber;
} }
} }

View file

@ -1,74 +1,75 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace ServiceLib.Common; namespace ServiceLib.Common
public class DesUtils
{ {
/// <summary> public class DesUtils
/// Encrypt
/// </summary>
/// <param name="text"></param>
/// /// <param name="key"></param>
/// <returns></returns>
public static string Encrypt(string? text, string? key = null)
{ {
if (text.IsNullOrEmpty()) /// <summary>
/// Encrypt
/// </summary>
/// <param name="text"></param>
/// /// <param name="key"></param>
/// <returns></returns>
public static string Encrypt(string? text, string? key = null)
{ {
return string.Empty; if (text.IsNullOrEmpty())
} {
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv); return string.Empty;
var dsp = DES.Create(); }
using var memStream = new MemoryStream(); GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write); var dsp = DES.Create();
using var sWriter = new StreamWriter(cryStream); using var memStream = new MemoryStream();
sWriter.Write(text); using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
sWriter.Flush(); using var sWriter = new StreamWriter(cryStream);
cryStream.FlushFinalBlock(); sWriter.Write(text);
memStream.Flush(); sWriter.Flush();
return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length); cryStream.FlushFinalBlock();
} memStream.Flush();
return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length);
/// <summary>
/// Decrypt
/// </summary>
/// <param name="encryptText"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string Decrypt(string? encryptText, string? key = null)
{
if (encryptText.IsNullOrEmpty())
{
return string.Empty;
}
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
var dsp = DES.Create();
var buffer = Convert.FromBase64String(encryptText);
using var memStream = new MemoryStream();
using var cryStream = new CryptoStream(memStream, dsp.CreateDecryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
cryStream.Write(buffer, 0, buffer.Length);
cryStream.FlushFinalBlock();
return Encoding.UTF8.GetString(memStream.ToArray());
}
private static void GetKeyIv(string key, out byte[] rgbKey, out byte[] rgbIv)
{
if (key.IsNullOrEmpty())
{
throw new ArgumentNullException("The key cannot be null");
}
if (key.Length <= 8)
{
throw new ArgumentNullException("The key length cannot be less than 8 characters.");
} }
rgbKey = Encoding.ASCII.GetBytes(key.Substring(0, 8)); /// <summary>
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8)); /// Decrypt
} /// </summary>
/// <param name="encryptText"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string Decrypt(string? encryptText, string? key = null)
{
if (encryptText.IsNullOrEmpty())
{
return string.Empty;
}
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
var dsp = DES.Create();
var buffer = Convert.FromBase64String(encryptText);
private static string GetDefaultKey() using var memStream = new MemoryStream();
{ using var cryStream = new CryptoStream(memStream, dsp.CreateDecryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils"); cryStream.Write(buffer, 0, buffer.Length);
cryStream.FlushFinalBlock();
return Encoding.UTF8.GetString(memStream.ToArray());
}
private static void GetKeyIv(string key, out byte[] rgbKey, out byte[] rgbIv)
{
if (key.IsNullOrEmpty())
{
throw new ArgumentNullException("The key cannot be null");
}
if (key.Length <= 8)
{
throw new ArgumentNullException("The key length cannot be less than 8 characters.");
}
rgbKey = Encoding.ASCII.GetBytes(key.Substring(0, 8));
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8));
}
private static string GetDefaultKey()
{
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
}
} }
} }

View file

@ -1,180 +1,181 @@
using System.Net; using System.Net;
using Downloader; using Downloader;
namespace ServiceLib.Common; namespace ServiceLib.Common
public class DownloaderHelper
{ {
private static readonly Lazy<DownloaderHelper> _instance = new(() => new()); public class DownloaderHelper
public static DownloaderHelper Instance => _instance.Value;
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{ {
if (url.IsNullOrEmpty()) private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
{ public static DownloaderHelper Instance => _instance.Value;
return null;
}
Uri uri = new(url); public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
//Authorization Header
var headers = new WebHeaderCollection();
if (uri.UserInfo.IsNotEmpty())
{ {
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo)); if (url.IsNullOrEmpty())
} {
return null;
}
var downloadOpt = new DownloadConfiguration() Uri uri = new(url);
{ //Authorization Header
Timeout = timeout * 1000, var headers = new WebHeaderCollection();
MaxTryAgainOnFailover = 2, if (uri.UserInfo.IsNotEmpty())
RequestConfiguration = {
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
}
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
RequestConfiguration =
{ {
Headers = headers, Headers = headers,
UserAgent = userAgent, UserAgent = userAgent,
Timeout = timeout * 1000, Timeout = timeout * 1000,
Proxy = webProxy Proxy = webProxy
} }
}; };
await using var downloader = new Downloader.DownloadService(downloadOpt); await using var downloader = new Downloader.DownloadService(downloadOpt);
downloader.DownloadFileCompleted += (sender, value) => downloader.DownloadFileCompleted += (sender, value) =>
{
if (value.Error != null)
{ {
throw value.Error; if (value.Error != null)
}
};
using var cts = new CancellationTokenSource();
await using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
using StreamReader reader = new(stream);
downloadOpt = null;
return await reader.ReadToEndAsync(cts.Token);
}
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
{
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
RequestConfiguration =
{
Timeout= timeout * 1000,
Proxy = webProxy
}
};
var totalDatetime = DateTime.Now;
var totalSecond = 0;
var hasValue = false;
double maxSpeed = 0;
await using var downloader = new Downloader.DownloadService(downloadOpt);
//downloader.DownloadStarted += (sender, value) =>
//{
// if (progress != null)
// {
// progress.Report("Start download data...");
// }
//};
downloader.DownloadProgressChanged += (sender, value) =>
{
var 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);
await 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)
{
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
if (fileName.IsNullOrEmpty())
{
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;
await using var downloader = new Downloader.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);
}
else if (value.Error != null)
{ {
throw value.Error; throw value.Error;
} }
};
using var cts = new CancellationTokenSource();
await using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
using StreamReader reader = new(stream);
downloadOpt = null;
return await reader.ReadToEndAsync(cts.Token);
}
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
{
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
} }
};
using var cts = new CancellationTokenSource(); var downloadOpt = new DownloadConfiguration()
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token); {
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
RequestConfiguration =
{
Timeout= timeout * 1000,
Proxy = webProxy
}
};
downloadOpt = null; var totalDatetime = DateTime.Now;
var totalSecond = 0;
var hasValue = false;
double maxSpeed = 0;
await using var downloader = new Downloader.DownloadService(downloadOpt);
//downloader.DownloadStarted += (sender, value) =>
//{
// if (progress != null)
// {
// progress.Report("Start download data...");
// }
//};
downloader.DownloadProgressChanged += (sender, value) =>
{
var 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);
await 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)
{
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
if (fileName.IsNullOrEmpty())
{
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;
await using var downloader = new Downloader.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);
}
else if (value.Error != null)
{
throw value.Error;
}
}
};
using var cts = new CancellationTokenSource();
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token);
downloadOpt = null;
}
} }
} }

View file

@ -2,225 +2,226 @@ using System.Formats.Tar;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
namespace ServiceLib.Common; namespace ServiceLib.Common
public static class FileManager
{ {
private static readonly string _tag = "FileManager"; public static class FileManager
public static bool ByteArrayToFile(string fileName, byte[] content)
{ {
try private static readonly string _tag = "FileManager";
{
File.WriteAllBytes(fileName, content);
return true;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return false;
}
public static void DecompressFile(string fileName, byte[] content) public static bool ByteArrayToFile(string fileName, byte[] content)
{
try
{ {
using var fs = File.Create(fileName); try
using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false);
input.CopyTo(fs);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public static void DecompressFile(string fileName, string toPath, string? toName)
{
try
{
FileInfo fileInfo = new(fileName);
using var originalFileStream = fileInfo.OpenRead();
using var decompressedFileStream = File.Create(toName != null ? Path.Combine(toPath, toName) : toPath);
using GZipStream decompressionStream = new(originalFileStream, CompressionMode.Decompress);
decompressionStream.CopyTo(decompressedFileStream);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public static void DecompressTarFile(string fileName, string toPath)
{
try
{
using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
using var gz = new GZipStream(fs, CompressionMode.Decompress, leaveOpen: true);
TarFile.ExtractToDirectory(gz, toPath, overwriteFiles: true);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public static string NonExclusiveReadAllText(string path)
{
return NonExclusiveReadAllText(path, Encoding.Default);
}
private static string NonExclusiveReadAllText(string path, Encoding encoding)
{
try
{
using FileStream fs = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader sr = new(fs, encoding);
return sr.ReadToEnd();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
throw;
}
}
public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName)
{
try
{
using var archive = ZipFile.OpenRead(fileName);
foreach (var entry in archive.Entries)
{ {
if (entry.Length == 0) File.WriteAllBytes(fileName, content);
return true;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return false;
}
public static void DecompressFile(string fileName, byte[] content)
{
try
{
using var fs = File.Create(fileName);
using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false);
input.CopyTo(fs);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public static void DecompressFile(string fileName, string toPath, string? toName)
{
try
{
FileInfo fileInfo = new(fileName);
using var originalFileStream = fileInfo.OpenRead();
using var decompressedFileStream = File.Create(toName != null ? Path.Combine(toPath, toName) : toPath);
using GZipStream decompressionStream = new(originalFileStream, CompressionMode.Decompress);
decompressionStream.CopyTo(decompressedFileStream);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public static void DecompressTarFile(string fileName, string toPath)
{
try
{
using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
using var gz = new GZipStream(fs, CompressionMode.Decompress, leaveOpen: true);
TarFile.ExtractToDirectory(gz, toPath, overwriteFiles: true);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public static string NonExclusiveReadAllText(string path)
{
return NonExclusiveReadAllText(path, Encoding.Default);
}
private static string NonExclusiveReadAllText(string path, Encoding encoding)
{
try
{
using FileStream fs = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader sr = new(fs, encoding);
return sr.ReadToEnd();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
throw;
}
}
public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName)
{
try
{
using var archive = ZipFile.OpenRead(fileName);
foreach (var entry in archive.Entries)
{ {
continue; if (entry.Length == 0)
}
try
{
if (ignoredName.IsNotEmpty() && entry.Name.Contains(ignoredName))
{ {
continue; continue;
} }
entry.ExtractToFile(Path.Combine(toPath, entry.Name), true); try
{
if (ignoredName.IsNotEmpty() && entry.Name.Contains(ignoredName))
{
continue;
}
entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
}
catch (IOException ex)
{
Logging.SaveLog(_tag, ex);
}
} }
catch (IOException ex) }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return false;
}
return true;
}
public static List<string>? GetFilesFromZip(string fileName)
{
if (!File.Exists(fileName))
{
return null;
}
try
{
using var archive = ZipFile.OpenRead(fileName);
return archive.Entries.Select(entry => entry.FullName).ToList();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return null;
}
}
public static bool CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName)
{
try
{
if (File.Exists(destinationArchiveFileName))
{ {
Logging.SaveLog(_tag, ex); File.Delete(destinationArchiveFileName);
} }
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.SmallestSize, true);
} }
} catch (Exception ex)
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return false;
}
return true;
}
public static List<string>? GetFilesFromZip(string fileName)
{
if (!File.Exists(fileName))
{
return null;
}
try
{
using var archive = ZipFile.OpenRead(fileName);
return archive.Entries.Select(entry => entry.FullName).ToList();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return null;
}
}
public static bool CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName)
{
try
{
if (File.Exists(destinationArchiveFileName))
{ {
File.Delete(destinationArchiveFileName); Logging.SaveLog(_tag, ex);
return false;
}
return true;
}
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, bool overwrite, string? ignoredName = null)
{
// Get information about the source directory
var dir = new DirectoryInfo(sourceDir);
// Check if the source directory exists
if (!dir.Exists)
{
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
} }
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.SmallestSize, true); // Cache directories before we start copying
} var dirs = dir.GetDirectories();
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return false;
}
return true;
}
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, bool overwrite, string? ignoredName = null) // Create the destination directory
{ _ = Directory.CreateDirectory(destinationDir);
// Get information about the source directory
var dir = new DirectoryInfo(sourceDir);
// Check if the source directory exists // Get the files in the source directory and copy to the destination directory
if (!dir.Exists) foreach (var file in dir.GetFiles())
{
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
}
// Cache directories before we start copying
var dirs = dir.GetDirectories();
// Create the destination directory
_ = Directory.CreateDirectory(destinationDir);
// Get the files in the source directory and copy to the destination directory
foreach (var file in dir.GetFiles())
{
if (ignoredName.IsNotEmpty() && file.Name.Contains(ignoredName))
{ {
continue; if (ignoredName.IsNotEmpty() && file.Name.Contains(ignoredName))
}
if (file.Extension == file.Name)
{
continue;
}
var targetFilePath = Path.Combine(destinationDir, file.Name);
if (!overwrite && File.Exists(targetFilePath))
{
continue;
}
_ = file.CopyTo(targetFilePath, overwrite);
}
// If recursive and copying subdirectories, recursively call this method
if (recursive)
{
foreach (var subDir in dirs)
{
var newDestinationDir = Path.Combine(destinationDir, subDir.Name);
CopyDirectory(subDir.FullName, newDestinationDir, true, overwrite, ignoredName);
}
}
}
public static void DeleteExpiredFiles(string sourceDir, DateTime dtLine)
{
try
{
var files = Directory.GetFiles(sourceDir, "*.*");
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
if (file.CreationTime >= dtLine)
{ {
continue; continue;
} }
file.Delete(); if (file.Extension == file.Name)
{
continue;
}
var targetFilePath = Path.Combine(destinationDir, file.Name);
if (!overwrite && File.Exists(targetFilePath))
{
continue;
}
_ = file.CopyTo(targetFilePath, overwrite);
}
// If recursive and copying subdirectories, recursively call this method
if (recursive)
{
foreach (var subDir in dirs)
{
var newDestinationDir = Path.Combine(destinationDir, subDir.Name);
CopyDirectory(subDir.FullName, newDestinationDir, true, overwrite, ignoredName);
}
} }
} }
catch
public static void DeleteExpiredFiles(string sourceDir, DateTime dtLine)
{ {
// ignored try
{
var files = Directory.GetFiles(sourceDir, "*.*");
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
if (file.CreationTime >= dtLine)
{
continue;
}
file.Delete();
}
}
catch
{
// ignored
}
} }
} }
} }

View file

@ -2,204 +2,205 @@ using System.Net.Http.Headers;
using System.Net.Mime; using System.Net.Mime;
using System.Text; using System.Text;
namespace ServiceLib.Common; namespace ServiceLib.Common
/// <summary>
/// </summary>
public class HttpClientHelper
{ {
private static readonly Lazy<HttpClientHelper> _instance = new(() => /// <summary>
/// </summary>
public class HttpClientHelper
{ {
SocketsHttpHandler handler = new() { UseCookies = false }; private static readonly Lazy<HttpClientHelper> _instance = new(() =>
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?> TryGetAsync(string url)
{
if (url.IsNullOrEmpty())
{ {
return null; SocketsHttpHandler 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;
} }
try public async Task<string?> TryGetAsync(string url)
{ {
var response = await httpClient.GetAsync(url); if (url.IsNullOrEmpty())
return await response.Content.ReadAsStringAsync();
}
catch
{
return null;
}
}
public async Task<string?> GetAsync(string url)
{
if (url.IsNullOrEmpty())
{
return null;
}
return await httpClient.GetStringAsync(url);
}
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{
if (url.IsNullOrEmpty())
{
return null;
}
return await client.GetStringAsync(url, token);
}
public async Task PutAsync(string url, Dictionary<string, string> headers)
{
var jsonContent = JsonUtils.Serialize(headers);
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
await httpClient.PutAsync(url, content);
}
public async Task PatchAsync(string url, Dictionary<string, string> headers)
{
var myContent = JsonUtils.Serialize(headers);
var buffer = Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
await httpClient.PatchAsync(url, byteContent);
}
public async Task DeleteAsync(string url)
{
await httpClient.DeleteAsync(url);
}
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;
await using var stream = await response.Content.ReadAsStreamAsync(token);
await 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; return null;
}
await file.WriteAsync(buffer.AsMemory(0, read), token);
if (canReportProgress)
{
var percent = (int)(100.0 * totalRead / total);
//if (progressPercentage != percent && percent % 10 == 0)
{
progressPercentage = percent;
progress?.Report(percent);
}
}
}
if (canReportProgress)
{
progress?.Report(101);
}
}
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{
if (url.IsNullOrEmpty())
{
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;
await using var stream = await response.Content.ReadAsStreamAsync(token);
var totalRead = 0L;
var buffer = new byte[1024 * 64];
var isMoreToRead = true;
var progressSpeed = string.Empty;
var totalDatetime = DateTime.Now;
var totalSecond = 0;
do
{
if (token.IsCancellationRequested)
{
if (totalRead > 0)
{
return;
}
else
{
token.ThrowIfCancellationRequested();
}
} }
var read = await stream.ReadAsync(buffer, token); try
if (read == 0)
{ {
isMoreToRead = false; var response = await httpClient.GetAsync(url);
return await response.Content.ReadAsStringAsync();
} }
else catch
{ {
var data = new byte[read]; return null;
buffer.ToList().CopyTo(0, data, 0, read); }
}
public async Task<string?> GetAsync(string url)
{
if (url.IsNullOrEmpty())
{
return null;
}
return await httpClient.GetStringAsync(url);
}
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{
if (url.IsNullOrEmpty())
{
return null;
}
return await client.GetStringAsync(url, token);
}
public async Task PutAsync(string url, Dictionary<string, string> headers)
{
var jsonContent = JsonUtils.Serialize(headers);
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
await httpClient.PutAsync(url, content);
}
public async Task PatchAsync(string url, Dictionary<string, string> headers)
{
var myContent = JsonUtils.Serialize(headers);
var buffer = Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
await httpClient.PatchAsync(url, byteContent);
}
public async Task DeleteAsync(string url)
{
await httpClient.DeleteAsync(url);
}
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;
await using var stream = await response.Content.ReadAsStreamAsync(token);
await 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; totalRead += read;
var ts = DateTime.Now - totalDatetime; if (read == 0)
if (progress != null && ts.Seconds > totalSecond)
{ {
totalSecond = ts.Seconds; break;
var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0"); }
if (progressSpeed != speed) await file.WriteAsync(buffer.AsMemory(0, read), token);
if (canReportProgress)
{
var percent = (int)(100.0 * totalRead / total);
//if (progressPercentage != percent && percent % 10 == 0)
{ {
progressSpeed = speed; progressPercentage = percent;
progress.Report(speed); progress?.Report(percent);
} }
} }
} }
} while (isMoreToRead); if (canReportProgress)
{
progress?.Report(101);
}
}
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{
if (url.IsNullOrEmpty())
{
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;
await using var stream = await response.Content.ReadAsStreamAsync(token);
var totalRead = 0L;
var buffer = new byte[1024 * 64];
var isMoreToRead = true;
var progressSpeed = string.Empty;
var totalDatetime = DateTime.Now;
var totalSecond = 0;
do
{
if (token.IsCancellationRequested)
{
if (totalRead > 0)
{
return;
}
else
{
token.ThrowIfCancellationRequested();
}
}
var read = await stream.ReadAsync(buffer, token);
if (read == 0)
{
isMoreToRead = false;
}
else
{
var data = new byte[read];
buffer.ToList().CopyTo(0, data, 0, read);
totalRead += read;
var 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)
{
progressSpeed = speed;
progress.Report(speed);
}
}
}
} while (isMoreToRead);
}
} }
} }

View file

@ -1,7 +1,8 @@
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ServiceLib.Common; namespace ServiceLib.Common
{
/* /*
* See: * See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net * http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
@ -177,4 +178,4 @@ namespace ServiceLib.Common;
} }
#endregion Helper classes #endregion Helper classes
}

View file

@ -1,130 +1,131 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ServiceLib.Common; namespace ServiceLib.Common
public class JsonUtils
{ {
private static readonly string _tag = "JsonUtils"; public class JsonUtils
/// <summary>
/// DeepCopy
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T DeepCopy<T>(T obj)
{ {
return Deserialize<T>(Serialize(obj, false))!; private static readonly string _tag = "JsonUtils";
}
/// <summary> /// <summary>
/// Deserialize to object /// DeepCopy
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="strJson"></param> /// <param name="obj"></param>
/// <returns></returns> /// <returns></returns>
public static T? Deserialize<T>(string? strJson) public static T DeepCopy<T>(T obj)
{
try
{ {
if (string.IsNullOrWhiteSpace(strJson)) return Deserialize<T>(Serialize(obj, false))!;
}
/// <summary>
/// Deserialize to object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="strJson"></param>
/// <returns></returns>
public static T? Deserialize<T>(string? strJson)
{
try
{
if (string.IsNullOrWhiteSpace(strJson))
{
return default;
}
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<T>(strJson, options);
}
catch
{ {
return default; return default;
} }
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<T>(strJson, options);
} }
catch
{
return default;
}
}
/// <summary> /// <summary>
/// parse /// parse
/// </summary> /// </summary>
/// <param name="strJson"></param> /// <param name="strJson"></param>
/// <returns></returns> /// <returns></returns>
public static JsonNode? ParseJson(string strJson) public static JsonNode? ParseJson(string strJson)
{
try
{ {
if (string.IsNullOrWhiteSpace(strJson)) try
{ {
if (string.IsNullOrWhiteSpace(strJson))
{
return null;
}
return JsonNode.Parse(strJson);
}
catch
{
//SaveLog(ex.Message, ex);
return null; return null;
} }
return JsonNode.Parse(strJson);
} }
catch
{
//SaveLog(ex.Message, ex);
return null;
}
}
/// <summary> /// <summary>
/// Serialize Object to Json string /// Serialize Object to Json string
/// </summary> /// </summary>
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="indented"></param> /// <param name="indented"></param>
/// <param name="nullValue"></param> /// <param name="nullValue"></param>
/// <returns></returns> /// <returns></returns>
public static string Serialize(object? obj, bool indented = true, bool nullValue = false) public static string Serialize(object? obj, bool indented = true, bool nullValue = false)
{
var result = string.Empty;
try
{ {
if (obj == null) var result = string.Empty;
try
{ {
return result; if (obj == null)
{
return result;
}
var options = new JsonSerializerOptions
{
WriteIndented = indented,
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
};
result = JsonSerializer.Serialize(obj, options);
} }
var options = new JsonSerializerOptions catch (Exception ex)
{ {
WriteIndented = indented, Logging.SaveLog(_tag, ex);
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
};
result = JsonSerializer.Serialize(obj, options);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return result;
}
/// <summary>
/// Serialize Object to Json string
/// </summary>
/// <param name="obj"></param>
/// <param name="options"></param>
/// <returns></returns>
public static string Serialize(object? obj, JsonSerializerOptions options)
{
var result = string.Empty;
try
{
if (obj == null)
{
return result;
} }
result = JsonSerializer.Serialize(obj, options); return result;
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return result;
}
/// <summary> /// <summary>
/// SerializeToNode /// Serialize Object to Json string
/// </summary> /// </summary>
/// <param name="obj"></param> /// <param name="obj"></param>
/// <returns></returns> /// <param name="options"></param>
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj); /// <returns></returns>
} public static string Serialize(object? obj, JsonSerializerOptions options)
{
var result = string.Empty;
try
{
if (obj == null)
{
return result;
}
result = JsonSerializer.Serialize(obj, options);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return result;
}
/// <summary>
/// SerializeToNode
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
}
}

View file

@ -2,54 +2,53 @@ using NLog;
using NLog.Config; using NLog.Config;
using NLog.Targets; using NLog.Targets;
namespace ServiceLib.Common; namespace ServiceLib.Common
public class Logging
{ {
private static readonly Logger _logger1 = LogManager.GetLogger("Log1"); public class Logging
private static readonly Logger _logger2 = LogManager.GetLogger("Log2");
public static void Setup()
{ {
LoggingConfiguration config = new(); public static void Setup()
FileTarget fileTarget = new();
config.AddTarget("file", fileTarget);
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
LogManager.Configuration = config;
}
public static void LoggingEnabled(bool enable)
{
if (!enable)
{ {
LogManager.SuspendLogging(); LoggingConfiguration config = new();
} FileTarget fileTarget = new();
} config.AddTarget("file", fileTarget);
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
public static void SaveLog(string strContent) fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
{ config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
if (!LogManager.IsLoggingEnabled()) LogManager.Configuration = config;
{
return;
} }
_logger1.Info(strContent); public static void LoggingEnabled(bool enable)
}
public static void SaveLog(string strTitle, Exception ex)
{
if (!LogManager.IsLoggingEnabled())
{ {
return; if (!enable)
{
LogManager.SuspendLogging();
}
} }
_logger2.Debug($"{strTitle},{ex.Message}"); public static void SaveLog(string strContent)
_logger2.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{ {
_logger2.Error(ex.InnerException); if (!LogManager.IsLoggingEnabled())
{
return;
}
LogManager.GetLogger("Log1").Info(strContent);
}
public static void SaveLog(string strTitle, Exception ex)
{
if (!LogManager.IsLoggingEnabled())
{
return;
}
var logger = LogManager.GetLogger("Log2");
logger.Debug($"{strTitle},{ex.Message}");
logger.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{
logger.Error(ex.InnerException);
}
} }
} }
} }

View file

@ -1,89 +1,90 @@
using QRCoder; using QRCoder;
using SkiaSharp; using SkiaSharp;
using ZXing.SkiaSharp; using ZXing.SkiaSharp;
namespace ServiceLib.Common; namespace ServiceLib.Common
public class QRCodeHelper
{ {
public static byte[]? GenQRCode(string? url) public class QRCodeHelper
{ {
using QRCodeGenerator qrGenerator = new(); public static byte[]? GenQRCode(string? url)
using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q);
using PngByteQRCode qrCode = new(qrCodeData);
return qrCode.GetGraphic(20);
}
public static string? ParseBarcode(string? fileName)
{
if (fileName == null || !File.Exists(fileName))
{ {
using QRCodeGenerator qrGenerator = new();
using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q);
using PngByteQRCode qrCode = new(qrCodeData);
return qrCode.GetGraphic(20);
}
public static string? ParseBarcode(string? fileName)
{
if (fileName == null || !File.Exists(fileName))
{
return null;
}
try
{
var image = SKImage.FromEncodedData(fileName);
var bitmap = SKBitmap.FromImage(image);
return ReaderBarcode(bitmap);
}
catch
{
// ignored
}
return null; return null;
} }
try public static string? ParseBarcode(byte[]? bytes)
{ {
var image = SKImage.FromEncodedData(fileName); try
var bitmap = SKBitmap.FromImage(image); {
var bitmap = SKBitmap.Decode(bytes);
//using var stream = new FileStream("test2.png", FileMode.Create, FileAccess.Write);
//using var image = SKImage.FromBitmap(bitmap);
//using var encodedImage = image.Encode();
//encodedImage.SaveTo(stream);
return ReaderBarcode(bitmap);
}
catch
{
// ignored
}
return ReaderBarcode(bitmap); return null;
}
catch
{
// ignored
} }
return null; private static string? ReaderBarcode(SKBitmap? bitmap)
{
var reader = new BarcodeReader();
var result = reader.Decode(bitmap);
if (result != null && result.Text.IsNotEmpty())
{
return result.Text;
}
//FlipBitmap
var result2 = reader.Decode(FlipBitmap(bitmap));
return result2?.Text;
}
private static SKBitmap FlipBitmap(SKBitmap bmp)
{
// Create a bitmap (to return)
var flipped = new SKBitmap(bmp.Width, bmp.Height, bmp.Info.ColorType, bmp.Info.AlphaType);
// Create a canvas to draw into the bitmap
using var canvas = new SKCanvas(flipped);
// Set a transform matrix which moves the bitmap to the right,
// and then "scales" it by -1, which just flips the pixels
// horizontally
canvas.Translate(bmp.Width, 0);
canvas.Scale(-1, 1);
canvas.DrawBitmap(bmp, 0, 0);
return flipped;
}
} }
}
public static string? ParseBarcode(byte[]? bytes)
{
try
{
var bitmap = SKBitmap.Decode(bytes);
//using var stream = new FileStream("test2.png", FileMode.Create, FileAccess.Write);
//using var image = SKImage.FromBitmap(bitmap);
//using var encodedImage = image.Encode();
//encodedImage.SaveTo(stream);
return ReaderBarcode(bitmap);
}
catch
{
// ignored
}
return null;
}
private static string? ReaderBarcode(SKBitmap? bitmap)
{
var reader = new BarcodeReader();
var result = reader.Decode(bitmap);
if (result != null && result.Text.IsNotEmpty())
{
return result.Text;
}
//FlipBitmap
var result2 = reader.Decode(FlipBitmap(bitmap));
return result2?.Text;
}
private static SKBitmap FlipBitmap(SKBitmap bmp)
{
// Create a bitmap (to return)
var flipped = new SKBitmap(bmp.Width, bmp.Height, bmp.Info.ColorType, bmp.Info.AlphaType);
// Create a canvas to draw into the bitmap
using var canvas = new SKCanvas(flipped);
// Set a transform matrix which moves the bitmap to the right,
// and then "scales" it by -1, which just flips the pixels
// horizontally
canvas.Translate(bmp.Width, 0);
canvas.Scale(-1, 1);
canvas.DrawBitmap(bmp, 0, 0);
return flipped;
}
}

View file

@ -1,186 +1,187 @@
namespace ServiceLib.Common; namespace ServiceLib.Common
public class SemanticVersion
{ {
private readonly int major; public class SemanticVersion
private readonly int minor;
private readonly int patch;
private readonly string version;
public SemanticVersion(int major, int minor, int patch)
{ {
this.major = major; private readonly int major;
this.minor = minor; private readonly int minor;
this.patch = patch; private readonly int patch;
version = $"{major}.{minor}.{patch}"; private readonly string version;
}
public SemanticVersion(string? version) public SemanticVersion(int major, int minor, int patch)
{
try
{ {
if (string.IsNullOrEmpty(version)) this.major = major;
this.minor = minor;
this.patch = patch;
version = $"{major}.{minor}.{patch}";
}
public SemanticVersion(string? version)
{
try
{
if (string.IsNullOrEmpty(version))
{
major = 0;
minor = 0;
patch = 0;
return;
}
this.version = version.RemovePrefix('v');
var parts = this.version.Split('.');
if (parts.Length == 2)
{
major = int.Parse(parts.First());
minor = int.Parse(parts.Last());
patch = 0;
}
else if (parts.Length is 3 or 4)
{
major = int.Parse(parts[0]);
minor = int.Parse(parts[1]);
patch = int.Parse(parts[2]);
}
else
{
throw new ArgumentException("Invalid version string");
}
}
catch
{ {
major = 0; major = 0;
minor = 0; minor = 0;
patch = 0; patch = 0;
return;
} }
this.version = version.RemovePrefix('v'); }
var parts = this.version.Split('.'); public override bool Equals(object? obj)
if (parts.Length == 2) {
if (obj is SemanticVersion other)
{ {
major = int.Parse(parts.First()); return major == other.major && minor == other.minor && patch == other.patch;
minor = int.Parse(parts.Last());
patch = 0;
}
else if (parts.Length is 3 or 4)
{
major = int.Parse(parts[0]);
minor = int.Parse(parts[1]);
patch = int.Parse(parts[2]);
} }
else else
{ {
throw new ArgumentException("Invalid version string"); return false;
} }
} }
catch
public override int GetHashCode()
{ {
major = 0; return major.GetHashCode() ^ minor.GetHashCode() ^ patch.GetHashCode();
minor = 0;
patch = 0;
} }
}
public override bool Equals(object? obj) /// <summary>
{ /// Use ToVersionString(string? prefix) instead if possible.
if (obj is SemanticVersion other) /// </summary>
{ /// <returns>major.minor.patch</returns>
return major == other.major && minor == other.minor && patch == other.patch; public override string ToString()
}
else
{
return false;
}
}
public override int GetHashCode()
{
return major.GetHashCode() ^ minor.GetHashCode() ^ patch.GetHashCode();
}
/// <summary>
/// Use ToVersionString(string? prefix) instead if possible.
/// </summary>
/// <returns>major.minor.patch</returns>
public override string ToString()
{
return version;
}
public string ToVersionString(string? prefix = null)
{
if (prefix == null)
{ {
return version; return version;
} }
else
public string ToVersionString(string? prefix = null)
{ {
return $"{prefix}{version}"; if (prefix == null)
{
return version;
}
else
{
return $"{prefix}{version}";
}
} }
}
public static bool operator ==(SemanticVersion v1, SemanticVersion v2) public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
{ return v1.Equals(v2); } { return v1.Equals(v2); }
public static bool operator !=(SemanticVersion v1, SemanticVersion v2) public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
{ return !v1.Equals(v2); } { return !v1.Equals(v2); }
public static bool operator >=(SemanticVersion v1, SemanticVersion v2) public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
{ return v1.GreaterEquals(v2); } { return v1.GreaterEquals(v2); }
public static bool operator <=(SemanticVersion v1, SemanticVersion v2) public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
{ return v1.LessEquals(v2); } { return v1.LessEquals(v2); }
#region Private #region Private
private bool GreaterEquals(SemanticVersion other) private bool GreaterEquals(SemanticVersion other)
{
if (major < other.major)
{ {
return false; if (major < other.major)
}
else if (major > other.major)
{
return true;
}
else
{
if (minor < other.minor)
{ {
return false; return false;
} }
else if (minor > other.minor) else if (major > other.major)
{ {
return true; return true;
} }
else else
{ {
if (patch < other.patch) if (minor < other.minor)
{ {
return false; return false;
} }
else if (patch > other.patch) else if (minor > other.minor)
{ {
return true; return true;
} }
else else
{ {
return true; if (patch < other.patch)
{
return false;
}
else if (patch > other.patch)
{
return true;
}
else
{
return true;
}
} }
} }
} }
}
private bool LessEquals(SemanticVersion other) private bool LessEquals(SemanticVersion other)
{
if (major < other.major)
{ {
return true; if (major < other.major)
}
else if (major > other.major)
{
return false;
}
else
{
if (minor < other.minor)
{ {
return true; return true;
} }
else if (minor > other.minor) else if (major > other.major)
{ {
return false; return false;
} }
else else
{ {
if (patch < other.patch) if (minor < other.minor)
{ {
return true; return true;
} }
else if (patch > other.patch) else if (minor > other.minor)
{ {
return false; return false;
} }
else else
{ {
return true; if (patch < other.patch)
{
return true;
}
else if (patch > other.patch)
{
return false;
}
else
{
return true;
}
} }
} }
} }
}
#endregion Private #endregion Private
}
} }

View file

@ -1,90 +1,91 @@
using System.Collections; using System.Collections;
using SQLite; using SQLite;
namespace ServiceLib.Common; namespace ServiceLib.Common
public sealed class SQLiteHelper
{ {
private static readonly Lazy<SQLiteHelper> _instance = new(() => new()); public sealed class SQLiteHelper
public static SQLiteHelper Instance => _instance.Value;
private readonly string _connstr;
private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync;
private readonly string _configDB = "guiNDB.db";
public SQLiteHelper()
{ {
_connstr = Utils.GetConfigPath(_configDB); private static readonly Lazy<SQLiteHelper> _instance = new(() => new());
_db = new SQLiteConnection(_connstr, false); public static SQLiteHelper Instance => _instance.Value;
_dbAsync = new SQLiteAsyncConnection(_connstr, false); private readonly string _connstr;
} private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync;
private readonly string _configDB = "guiNDB.db";
public CreateTableResult CreateTable<T>() public SQLiteHelper()
{
return _db.CreateTable<T>();
}
public async Task<int> InsertAllAsync(IEnumerable models)
{
return await _dbAsync.InsertAllAsync(models);
}
public async Task<int> InsertAsync(object model)
{
return await _dbAsync.InsertAsync(model);
}
public async Task<int> ReplaceAsync(object model)
{
return await _dbAsync.InsertOrReplaceAsync(model);
}
public async Task<int> UpdateAsync(object model)
{
return await _dbAsync.UpdateAsync(model);
}
public async Task<int> UpdateAllAsync(IEnumerable models)
{
return await _dbAsync.UpdateAllAsync(models);
}
public async Task<int> DeleteAsync(object model)
{
return await _dbAsync.DeleteAsync(model);
}
public async Task<int> DeleteAllAsync<T>()
{
return await _dbAsync.DeleteAllAsync<T>();
}
public async Task<int> ExecuteAsync(string sql)
{
return await _dbAsync.ExecuteAsync(sql);
}
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
{
return await _dbAsync.QueryAsync<T>(sql);
}
public AsyncTableQuery<T> TableAsync<T>() where T : new()
{
return _dbAsync.Table<T>();
}
public async Task DisposeDbConnectionAsync()
{
await Task.Factory.StartNew(() =>
{ {
_db?.Close(); _connstr = Utils.GetConfigPath(_configDB);
_db?.Dispose(); _db = new SQLiteConnection(_connstr, false);
_db = null; _dbAsync = new SQLiteAsyncConnection(_connstr, false);
}
_dbAsync?.GetConnection()?.Close(); public CreateTableResult CreateTable<T>()
_dbAsync?.GetConnection()?.Dispose(); {
_dbAsync = null; return _db.CreateTable<T>();
}); }
public async Task<int> InsertAllAsync(IEnumerable models)
{
return await _dbAsync.InsertAllAsync(models);
}
public async Task<int> InsertAsync(object model)
{
return await _dbAsync.InsertAsync(model);
}
public async Task<int> ReplaceAsync(object model)
{
return await _dbAsync.InsertOrReplaceAsync(model);
}
public async Task<int> UpdateAsync(object model)
{
return await _dbAsync.UpdateAsync(model);
}
public async Task<int> UpdateAllAsync(IEnumerable models)
{
return await _dbAsync.UpdateAllAsync(models);
}
public async Task<int> DeleteAsync(object model)
{
return await _dbAsync.DeleteAsync(model);
}
public async Task<int> DeleteAllAsync<T>()
{
return await _dbAsync.DeleteAllAsync<T>();
}
public async Task<int> ExecuteAsync(string sql)
{
return await _dbAsync.ExecuteAsync(sql);
}
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
{
return await _dbAsync.QueryAsync<T>(sql);
}
public AsyncTableQuery<T> TableAsync<T>() where T : new()
{
return _dbAsync.Table<T>();
}
public async Task DisposeDbConnectionAsync()
{
await Task.Factory.StartNew(() =>
{
_db?.Close();
_db?.Dispose();
_db = null;
_dbAsync?.GetConnection()?.Close();
_dbAsync?.GetConnection()?.Dispose();
_dbAsync = null;
});
}
} }
} }

View file

@ -1,82 +1,83 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace ServiceLib.Common; namespace ServiceLib.Common
public static class StringEx
{ {
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) public static class StringEx
{ {
return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value); public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
}
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
{
return string.IsNullOrWhiteSpace(value);
}
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
{
return !string.IsNullOrEmpty(value);
}
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{
if (s.IsNullOrEmpty())
{ {
return false; return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value);
} }
return chars.Contains(s.First());
}
private static bool IsWhiteSpace(this string value) public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
{
return value.All(char.IsWhiteSpace);
}
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
{
while (reader.ReadLine() is { } line)
{ {
if (line.IsWhiteSpace()) return string.IsNullOrWhiteSpace(value);
}
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
{
return !string.IsNullOrEmpty(value);
}
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{
if (s.IsNullOrEmpty())
{ {
continue; return false;
} }
yield return line; return chars.Contains(s.First());
} }
}
public static string TrimEx(this string? value) private static bool IsWhiteSpace(this string value)
{
return value == null ? string.Empty : value.Trim();
}
public static string RemovePrefix(this string value, char prefix)
{
return value.StartsWith(prefix) ? value[1..] : value;
}
public static string RemovePrefix(this string value, string prefix)
{
return value.StartsWith(prefix) ? value[prefix.Length..] : value;
}
public static string UpperFirstChar(this string value)
{
if (string.IsNullOrEmpty(value))
{ {
return string.Empty; return value.All(char.IsWhiteSpace);
} }
return char.ToUpper(value.First()) + value[1..]; public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
} {
while (reader.ReadLine() is { } line)
{
if (line.IsWhiteSpace())
{
continue;
}
yield return line;
}
}
public static string AppendQuotes(this string value) public static string TrimEx(this string? value)
{ {
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\""; return value == null ? string.Empty : value.Trim();
} }
public static int ToInt(this string? value, int defaultValue = 0) public static string RemovePrefix(this string value, char prefix)
{ {
return int.TryParse(value, out var result) ? result : defaultValue; return value.StartsWith(prefix) ? value[1..] : value;
}
public static string RemovePrefix(this string value, string prefix)
{
return value.StartsWith(prefix) ? value[prefix.Length..] : value;
}
public static string UpperFirstChar(this string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return char.ToUpper(value.First()) + value[1..];
}
public static string AppendQuotes(this string value)
{
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\"";
}
public static int ToInt(this string? value, int defaultValue = 0)
{
return int.TryParse(value, out var result) ? result : defaultValue;
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -2,72 +2,73 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.Win32; using Microsoft.Win32;
namespace ServiceLib.Common; namespace ServiceLib.Common
internal static class WindowsUtils
{ {
private static readonly string _tag = "WindowsUtils"; internal static class WindowsUtils
public static string? RegReadValue(string path, string name, string def)
{ {
RegistryKey? regKey = null; private static readonly string _tag = "WindowsUtils";
try
{
regKey = Registry.CurrentUser.OpenSubKey(path, false);
var value = regKey?.GetValue(name) as string;
return value.IsNullOrEmpty() ? def : value;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
finally
{
regKey?.Close();
}
return def;
}
public static void RegWriteValue(string path, string name, object value) public static string? RegReadValue(string path, string name, string def)
{
RegistryKey? regKey = null;
try
{ {
regKey = Registry.CurrentUser.CreateSubKey(path); RegistryKey? regKey = null;
if (value.ToString().IsNullOrEmpty()) try
{ {
regKey?.DeleteValue(name, false); regKey = Registry.CurrentUser.OpenSubKey(path, false);
var value = regKey?.GetValue(name) as string;
return value.IsNullOrEmpty() ? def : value;
} }
else catch (Exception ex)
{ {
regKey?.SetValue(name, value); Logging.SaveLog(_tag, ex);
}
finally
{
regKey?.Close();
}
return def;
}
public static void RegWriteValue(string path, string name, object value)
{
RegistryKey? regKey = null;
try
{
regKey = Registry.CurrentUser.CreateSubKey(path);
if (value.ToString().IsNullOrEmpty())
{
regKey?.DeleteValue(name, false);
}
else
{
regKey?.SetValue(name, value);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
finally
{
regKey?.Close();
} }
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
finally
{
regKey?.Close();
}
}
public static async Task RemoveTunDevice() public static async Task RemoveTunDevice()
{
try
{ {
var sum = MD5.HashData(Encoding.UTF8.GetBytes("wintunsingbox_tun")); try
var guid = new Guid(sum); {
var pnpUtilPath = @"C:\Windows\System32\pnputil.exe"; var sum = MD5.HashData(Encoding.UTF8.GetBytes("wintunsingbox_tun"));
var arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """; var guid = new Guid(sum);
var pnpUtilPath = @"C:\Windows\System32\pnputil.exe";
var arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """;
// Try to remove the device // Try to remove the device
_ = await Utils.GetCliWrapOutput(pnpUtilPath, arg); _ = await Utils.GetCliWrapOutput(pnpUtilPath, arg);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
}
} }
} }
} }

View file

@ -2,78 +2,79 @@ using YamlDotNet.Core;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NamingConventions;
namespace ServiceLib.Common; namespace ServiceLib.Common
public class YamlUtils
{ {
private static readonly string _tag = "YamlUtils"; public class YamlUtils
#region YAML
/// <summary>
/// 反序列化成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
/// <returns></returns>
public static T FromYaml<T>(string str)
{ {
var deserializer = new DeserializerBuilder() private static readonly string _tag = "YamlUtils";
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
try
{
var obj = deserializer.Deserialize<T>(str);
return obj;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return deserializer.Deserialize<T>("");
}
}
/// <summary> #region YAML
/// 序列化
/// </summary> /// <summary>
/// <param name="obj"></param> /// 反序列化成对象
/// <returns></returns> /// </summary>
public static string ToYaml(object? obj) /// <typeparam name="T"></typeparam>
{ /// <param name="str"></param>
var result = string.Empty; /// <returns></returns>
if (obj == null) public static T FromYaml<T>(string str)
{ {
var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
try
{
var obj = deserializer.Deserialize<T>(str);
return obj;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return deserializer.Deserialize<T>("");
}
}
/// <summary>
/// 序列化
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToYaml(object? obj)
{
var result = string.Empty;
if (obj == null)
{
return result;
}
var serializer = new SerializerBuilder()
.WithNamingConvention(HyphenatedNamingConvention.Instance)
.Build();
try
{
result = serializer.Serialize(obj);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return result; return result;
} }
var serializer = new SerializerBuilder()
.WithNamingConvention(HyphenatedNamingConvention.Instance)
.Build();
try public static string? PreprocessYaml(string str)
{ {
result = serializer.Serialize(obj); try
{
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
var obj = new DeserializerBuilder().Build().Deserialize(mergingParser);
return ToYaml(obj);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return null;
}
} }
catch (Exception ex)
{ #endregion YAML
Logging.SaveLog(_tag, ex);
}
return result;
} }
public static string? PreprocessYaml(string str)
{
try
{
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
var obj = new DeserializerBuilder().Build().Deserialize(mergingParser);
return ToYaml(obj);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return null;
}
}
#endregion YAML
} }

View file

@ -1,15 +1,16 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EConfigType
{ {
VMess = 1, public enum EConfigType
Custom = 2, {
Shadowsocks = 3, VMess = 1,
SOCKS = 4, Custom = 2,
VLESS = 5, Shadowsocks = 3,
Trojan = 6, SOCKS = 4,
Hysteria2 = 7, VLESS = 5,
TUIC = 8, Trojan = 6,
WireGuard = 9, Hysteria2 = 7,
HTTP = 10 TUIC = 8,
} WireGuard = 9,
HTTP = 10
}
}

View file

@ -1,18 +1,19 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum ECoreType
{ {
v2fly = 1, public enum ECoreType
Xray = 2, {
v2fly_v5 = 4, v2fly = 1,
mihomo = 13, Xray = 2,
hysteria = 21, v2fly_v5 = 4,
naiveproxy = 22, mihomo = 13,
tuic = 23, hysteria = 21,
sing_box = 24, naiveproxy = 22,
juicity = 25, tuic = 23,
hysteria2 = 26, sing_box = 24,
brook = 27, juicity = 25,
overtls = 28, hysteria2 = 26,
v2rayN = 99 brook = 27,
overtls = 28,
v2rayN = 99
}
} }

View file

@ -1,8 +1,9 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EGirdOrientation
{ {
Horizontal, public enum EGirdOrientation
Vertical, {
Tab, Horizontal,
} Vertical,
Tab,
}
}

View file

@ -1,10 +1,11 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EGlobalHotkey
{ {
ShowForm = 0, public enum EGlobalHotkey
SystemProxyClear = 1, {
SystemProxySet = 2, ShowForm = 0,
SystemProxyUnchanged = 3, SystemProxyClear = 1,
SystemProxyPac = 4, SystemProxySet = 2,
} SystemProxyUnchanged = 3,
SystemProxyPac = 4,
}
}

View file

@ -1,13 +1,14 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EInboundProtocol
{ {
socks = 0, public enum EInboundProtocol
socks2, {
socks3, socks = 0,
pac, socks2,
api, socks3,
api2, pac,
mixed, api,
speedtest = 21 api2,
} mixed,
speedtest = 21
}
}

View file

@ -1,10 +1,11 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EMove
{ {
Top = 1, public enum EMove
Up = 2, {
Down = 3, Top = 1,
Bottom = 4, Up = 2,
Position = 5 Down = 3,
} Bottom = 4,
Position = 5
}
}

View file

@ -1,10 +1,11 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EMsgCommand
{ {
ClearMsg, public enum EMsgCommand
SendMsgView, {
SendSnackMsg, ClearMsg,
RefreshProfiles, SendMsgView,
AppExit SendSnackMsg,
RefreshProfiles,
AppExit
}
} }

View file

@ -1,9 +0,0 @@
namespace ServiceLib.Enums;
public enum EMultipleLoad
{
Random,
RoundRobin,
LeastPing,
LeastLoad
}

View file

@ -1,8 +1,9 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EPresetType
{ {
Default = 0, public enum EPresetType
Russia = 1, {
Iran = 2, Default = 0,
} Russia = 1,
Iran = 2,
}
}

View file

@ -1,9 +1,10 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum ERuleMode
{ {
Rule = 0, public enum ERuleMode
Global = 1, {
Direct = 2, Rule = 0,
Unchanged = 3 Global = 1,
} Direct = 2,
Unchanged = 3
}
}

View file

@ -1,20 +1,21 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EServerColName
{ {
Def = 0, public enum EServerColName
ConfigType, {
Remarks, Def = 0,
Address, ConfigType,
Port, Remarks,
Network, Address,
StreamSecurity, Port,
SubRemarks, Network,
DelayVal, StreamSecurity,
SpeedVal, SubRemarks,
DelayVal,
SpeedVal,
TodayDown, TodayDown,
TodayUp, TodayUp,
TotalDown, TotalDown,
TotalUp TotalUp
} }
}

View file

@ -1,9 +1,10 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum ESpeedActionType
{ {
Tcping, public enum ESpeedActionType
Realping, {
Speedtest, Tcping,
Mixedtest Realping,
} Speedtest,
Mixedtest
}
}

View file

@ -1,9 +1,10 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum ESysProxyType
{ {
ForcedClear = 0, public enum ESysProxyType
ForcedChange = 1, {
Unchanged = 2, ForcedClear = 0,
Pac = 3 ForcedChange = 1,
} Unchanged = 2,
Pac = 3
}
}

View file

@ -1,12 +1,13 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum ETheme
{ {
FollowSystem, public enum ETheme
Dark, {
Light, FollowSystem,
Aquatic, Dark,
Desert, Light,
Dusk, Aquatic,
NightSky Desert,
} Dusk,
NightSky
}
}

View file

@ -1,14 +1,15 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum ETransport
{ {
tcp, public enum ETransport
kcp, {
ws, tcp,
httpupgrade, kcp,
xhttp, ws,
h2, httpupgrade,
http, xhttp,
quic, h2,
grpc http,
} quic,
grpc
}
}

View file

@ -1,45 +1,46 @@
namespace ServiceLib.Enums; namespace ServiceLib.Enums
public enum EViewAction
{ {
CloseWindow, public enum EViewAction
ShowYesNo, {
SaveFileDialog, CloseWindow,
AddBatchRoutingRulesYesNo, ShowYesNo,
AdjustMainLvColWidth, SaveFileDialog,
SetClipboardData, AddBatchRoutingRulesYesNo,
AddServerViaClipboard, AdjustMainLvColWidth,
ImportRulesFromClipboard, SetClipboardData,
ProfilesFocus, AddServerViaClipboard,
ShareSub, ImportRulesFromClipboard,
ShareServer, ProfilesFocus,
ShowHideWindow, ShareSub,
ScanScreenTask, ShareServer,
ScanImageTask, ShowHideWindow,
Shutdown, ScanScreenTask,
BrowseServer, ScanImageTask,
ImportRulesFromFile, Shutdown,
InitSettingFont, BrowseServer,
SubEditWindow, ImportRulesFromFile,
RoutingRuleSettingWindow, InitSettingFont,
RoutingRuleDetailsWindow, SubEditWindow,
AddServerWindow, RoutingRuleSettingWindow,
AddServer2Window, RoutingRuleDetailsWindow,
DNSSettingWindow, AddServerWindow,
RoutingSettingWindow, AddServer2Window,
OptionSettingWindow, DNSSettingWindow,
GlobalHotkeySettingWindow, RoutingSettingWindow,
SubSettingWindow, OptionSettingWindow,
DispatcherSpeedTest, GlobalHotkeySettingWindow,
DispatcherRefreshConnections, SubSettingWindow,
DispatcherRefreshProxyGroups, DispatcherSpeedTest,
DispatcherProxiesDelayTest, DispatcherRefreshConnections,
DispatcherStatistics, DispatcherRefreshProxyGroups,
DispatcherServerAvailability, DispatcherProxiesDelayTest,
DispatcherReload, DispatcherStatistics,
DispatcherRefreshServersBiz, DispatcherServerAvailability,
DispatcherRefreshIcon, DispatcherReload,
DispatcherCheckUpdate, DispatcherRefreshServersBiz,
DispatcherCheckUpdateFinished, DispatcherRefreshIcon,
DispatcherShowMsg, DispatcherCheckUpdate,
} DispatcherCheckUpdateFinished,
DispatcherShowMsg,
}
}

View file

@ -1,155 +1,155 @@
namespace ServiceLib; namespace ServiceLib
public class Global
{ {
#region const public class Global
{
#region const
public const string AppName = "v2rayN"; public const string AppName = "v2rayN";
public const string GithubUrl = "https://github.com"; public const string GithubUrl = "https://github.com";
public const string GithubApiUrl = "https://api.github.com/repos"; public const string GithubApiUrl = "https://api.github.com/repos";
public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat"; public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204"; public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs"; public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
public const string IPAPIUrl = "https://api.ip.sb/geoip"; public const string IPAPIUrl = "https://api.ip.sb/geoip";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
public const string ConfigFileName = "guiNConfig.json"; public const string ConfigFileName = "guiNConfig.json";
public const string CoreConfigFileName = "config.json"; public const string CoreConfigFileName = "config.json";
public const string CorePreConfigFileName = "configPre.json"; public const string CorePreConfigFileName = "configPre.json";
public const string CoreSpeedtestConfigFileName = "configTest{0}.json"; public const string CoreSpeedtestConfigFileName = "configTest{0}.json";
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json"; public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
public const string ClashMixinConfigFileName = "Mixin.yaml"; public const string ClashMixinConfigFileName = "Mixin.yaml";
public const string NamespaceSample = "ServiceLib.Sample."; public const string NamespaceSample = "ServiceLib.Sample.";
public const string V2raySampleClient = NamespaceSample + "SampleClientConfig"; public const string V2raySampleClient = NamespaceSample + "SampleClientConfig";
public const string SingboxSampleClient = NamespaceSample + "SingboxSampleClientConfig"; public const string SingboxSampleClient = NamespaceSample + "SingboxSampleClientConfig";
public const string V2raySampleHttpRequestFileName = NamespaceSample + "SampleHttpRequest"; public const string V2raySampleHttpRequestFileName = NamespaceSample + "SampleHttpRequest";
public const string V2raySampleHttpResponseFileName = NamespaceSample + "SampleHttpResponse"; public const string V2raySampleHttpResponseFileName = NamespaceSample + "SampleHttpResponse";
public const string V2raySampleInbound = NamespaceSample + "SampleInbound"; public const string V2raySampleInbound = NamespaceSample + "SampleInbound";
public const string V2raySampleOutbound = NamespaceSample + "SampleOutbound"; public const string V2raySampleOutbound = NamespaceSample + "SampleOutbound";
public const string SingboxSampleOutbound = NamespaceSample + "SingboxSampleOutbound"; public const string SingboxSampleOutbound = NamespaceSample + "SingboxSampleOutbound";
public const string CustomRoutingFileName = NamespaceSample + "custom_routing_"; public const string CustomRoutingFileName = NamespaceSample + "custom_routing_";
public const string TunSingboxDNSFileName = NamespaceSample + "tun_singbox_dns"; public const string TunSingboxDNSFileName = NamespaceSample + "tun_singbox_dns";
public const string TunSingboxInboundFileName = NamespaceSample + "tun_singbox_inbound"; public const string TunSingboxInboundFileName = NamespaceSample + "tun_singbox_inbound";
public const string TunSingboxRulesFileName = NamespaceSample + "tun_singbox_rules"; public const string TunSingboxRulesFileName = NamespaceSample + "tun_singbox_rules";
public const string DNSV2rayNormalFileName = NamespaceSample + "dns_v2ray_normal"; public const string DNSV2rayNormalFileName = NamespaceSample + "dns_v2ray_normal";
public const string DNSSingboxNormalFileName = NamespaceSample + "dns_singbox_normal"; public const string DNSSingboxNormalFileName = NamespaceSample + "dns_singbox_normal";
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml"; public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml"; public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config"; public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
public const string PacFileName = NamespaceSample + "pac"; public const string PacFileName = NamespaceSample + "pac";
public const string ProxySetOSXShellFileName = NamespaceSample + "proxy_set_osx_sh"; public const string ProxySetOSXShellFileName = NamespaceSample + "proxy_set_osx_sh";
public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh"; public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh";
public const string DefaultSecurity = "auto"; public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp"; public const string DefaultNetwork = "tcp";
public const string TcpHeaderHttp = "http"; public const string TcpHeaderHttp = "http";
public const string None = "none"; public const string None = "none";
public const string ProxyTag = "proxy"; public const string ProxyTag = "proxy";
public const string DirectTag = "direct"; public const string DirectTag = "direct";
public const string BlockTag = "block"; public const string BlockTag = "block";
public const string StreamSecurity = "tls"; public const string StreamSecurity = "tls";
public const string StreamSecurityReality = "reality"; public const string StreamSecurityReality = "reality";
public const string Loopback = "127.0.0.1"; public const string Loopback = "127.0.0.1";
public const string InboundAPIProtocol = "dokodemo-door"; public const string InboundAPIProtocol = "dokodemo-door";
public const string HttpProtocol = "http://"; public const string HttpProtocol = "http://";
public const string HttpsProtocol = "https://"; public const string HttpsProtocol = "https://";
public const string SocksProtocol = "socks://"; public const string SocksProtocol = "socks://";
public const string Socks5Protocol = "socks5://"; public const string Socks5Protocol = "socks5://";
public const string UserEMail = "t@t.tt"; public const string UserEMail = "t@t.tt";
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run"; public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
public const string AutoRunName = "v2rayNAutoRun"; public const string AutoRunName = "v2rayNAutoRun";
public const string SystemProxyExceptionsWindows = "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 SystemProxyExceptionsWindows = "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 SystemProxyExceptionsLinux = "localhost,127.0.0.0/8,::1"; public const string SystemProxyExceptionsLinux = "localhost,127.0.0.0/8,::1";
public const string RoutingRuleComma = "<COMMA>"; public const string RoutingRuleComma = "<COMMA>";
public const string GrpcGunMode = "gun"; public const string GrpcGunMode = "gun";
public const string GrpcMultiMode = "multi"; public const string GrpcMultiMode = "multi";
public const int MaxPort = 65536; public const int MaxPort = 65536;
public const int MinFontSize = 8; public const int MinFontSize = 8;
public const string RebootAs = "rebootas"; public const string RebootAs = "rebootas";
public const string AvaAssets = "avares://v2rayN/Assets/"; public const string AvaAssets = "avares://v2rayN/Assets/";
public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2"; public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2";
public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET"; public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET";
public const string XrayLocalAsset = "XRAY_LOCATION_ASSET"; public const string XrayLocalAsset = "XRAY_LOCATION_ASSET";
public const string XrayLocalCert = "XRAY_LOCATION_CERT"; public const string XrayLocalCert = "XRAY_LOCATION_CERT";
public const int SpeedTestPageSize = 1000; public const int SpeedTestPageSize = 1000;
public const string LinuxBash = "/bin/bash"; public const string LinuxBash = "/bin/bash";
public static readonly List<string> IEProxyProtocols = public static readonly List<string> IEProxyProtocols =
[ [
"{ip}:{http_port}", "{ip}:{http_port}",
"socks={ip}:{socks_port}", "socks={ip}:{socks_port}",
"http={ip}:{http_port};https={ip}:{http_port};ftp={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}", "http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
"" ""
]; ];
public static readonly List<string> SubConvertUrls = public static readonly List<string> SubConvertUrls =
[ [
@"https://sub.xeton.dev/sub?url={0}", @"https://sub.xeton.dev/sub?url={0}",
@"https://api.dler.io/sub?url={0}", @"https://api.dler.io/sub?url={0}",
@"http://127.0.0.1:25500/sub?url={0}", @"http://127.0.0.1:25500/sub?url={0}",
"" ""
]; ];
public static readonly List<string> SubConvertConfig = public static readonly List<string> SubConvertConfig =
[@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"]; [@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"];
public static readonly List<string> SubConvertTargets = public static readonly List<string> SubConvertTargets =
[ [
"", "",
"mixed", "mixed",
"v2ray", "v2ray",
"clash", "clash",
"ss" "ss"
]; ];
public static readonly List<string> SpeedTestUrls = public static readonly List<string> SpeedTestUrls =
[ [
@"https://cachefly.cachefly.net/50mb.test", @"https://cachefly.cachefly.net/50mb.test",
@"https://speed.cloudflare.com/__down?bytes=10000000", @"https://speed.cloudflare.com/__down?bytes=10000000",
@"https://speed.cloudflare.com/__down?bytes=50000000", @"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=100000000", @"https://speed.cloudflare.com/__down?bytes=100000000",
]; ];
public static readonly List<string> SpeedPingTestUrls = public static readonly List<string> SpeedPingTestUrls =
[ [
@"https://www.google.com/generate_204", @"https://www.google.com/generate_204",
@"https://www.gstatic.com/generate_204", @"https://www.gstatic.com/generate_204",
@"https://www.apple.com/library/test/success.html", @"https://www.apple.com/library/test/success.html",
@"http://www.msftconnecttest.com/connecttest.txt" @"http://www.msftconnecttest.com/connecttest.txt"
]; ];
public static readonly List<string> GeoFilesSources = public static readonly List<string> GeoFilesSources =
[ [
"", "",
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat", @"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/{0}.dat" @"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/{0}.dat"
]; ];
public static readonly List<string> SingboxRulesetSources = public static readonly List<string> SingboxRulesetSources =
[ [
"", "",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs", @"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs",
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-sing-box-rules@rule-set/{1}.srs" @"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-sing-box-rules@rule-set/{1}.srs"
]; ];
public static readonly List<string> RoutingRulesSources = public static readonly List<string> RoutingRulesSources =
[ [
"", "",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json", @"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json",
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/template.json" @"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/template.json"
]; ];
public static readonly List<string> DNSTemplateSources = public static readonly List<string> DNSTemplateSources =
[ [
"", "",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/", @"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/",
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/" @"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/"
]; ];
public static readonly Dictionary<string, string> UserAgentTexts = new() public static readonly Dictionary<string, string> UserAgentTexts = 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" }, {"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" }, {"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
@ -158,9 +158,9 @@ public class Global
{"none",""} {"none",""}
}; };
public const string Hysteria2ProtocolShare = "hy2://"; public const string Hysteria2ProtocolShare = "hy2://";
public static readonly Dictionary<EConfigType, string> ProtocolShares = new() public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
{ {
{ EConfigType.VMess, "vmess://" }, { EConfigType.VMess, "vmess://" },
{ EConfigType.Shadowsocks, "ss://" }, { EConfigType.Shadowsocks, "ss://" },
@ -172,7 +172,7 @@ public class Global
{ EConfigType.WireGuard, "wireguard://" } { EConfigType.WireGuard, "wireguard://" }
}; };
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new() public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
{ {
{ EConfigType.VMess, "vmess" }, { EConfigType.VMess, "vmess" },
{ EConfigType.Shadowsocks, "shadowsocks" }, { EConfigType.Shadowsocks, "shadowsocks" },
@ -185,28 +185,28 @@ public class Global
{ EConfigType.WireGuard, "wireguard" } { EConfigType.WireGuard, "wireguard" }
}; };
public static readonly List<string> VmessSecurities = public static readonly List<string> VmessSecurities =
[ [
"aes-128-gcm", "aes-128-gcm",
"chacha20-poly1305", "chacha20-poly1305",
"auto", "auto",
"none", "none",
"zero" "zero"
]; ];
public static readonly List<string> SsSecurities = public static readonly List<string> SsSecurities =
[ [
"aes-256-gcm", "aes-256-gcm",
"aes-128-gcm", "aes-128-gcm",
"chacha20-poly1305", "chacha20-poly1305",
"chacha20-ietf-poly1305", "chacha20-ietf-poly1305",
"none", "none",
"plain" "plain"
]; ];
public static readonly List<string> SsSecuritiesInXray = public static readonly List<string> SsSecuritiesInXray =
[ [
"aes-256-gcm", "aes-256-gcm",
"aes-128-gcm", "aes-128-gcm",
"chacha20-poly1305", "chacha20-poly1305",
"chacha20-ietf-poly1305", "chacha20-ietf-poly1305",
@ -217,11 +217,11 @@ public class Global
"2022-blake3-aes-128-gcm", "2022-blake3-aes-128-gcm",
"2022-blake3-aes-256-gcm", "2022-blake3-aes-256-gcm",
"2022-blake3-chacha20-poly1305" "2022-blake3-chacha20-poly1305"
]; ];
public static readonly List<string> SsSecuritiesInSingbox = public static readonly List<string> SsSecuritiesInSingbox =
[ [
"aes-256-gcm", "aes-256-gcm",
"aes-192-gcm", "aes-192-gcm",
"aes-128-gcm", "aes-128-gcm",
"chacha20-ietf-poly1305", "chacha20-ietf-poly1305",
@ -239,18 +239,18 @@ public class Global
"rc4-md5", "rc4-md5",
"chacha20-ietf", "chacha20-ietf",
"xchacha20" "xchacha20"
]; ];
public static readonly List<string> Flows = public static readonly List<string> Flows =
[ [
"", "",
"xtls-rprx-vision", "xtls-rprx-vision",
"xtls-rprx-vision-udp443" "xtls-rprx-vision-udp443"
]; ];
public static readonly List<string> Networks = public static readonly List<string> Networks =
[ [
"tcp", "tcp",
"kcp", "kcp",
"ws", "ws",
"httpupgrade", "httpupgrade",
@ -258,50 +258,50 @@ public class Global
"h2", "h2",
"quic", "quic",
"grpc" "grpc"
]; ];
public static readonly List<string> KcpHeaderTypes = public static readonly List<string> KcpHeaderTypes =
[ [
"srtp", "srtp",
"utp", "utp",
"wechat-video", "wechat-video",
"dtls", "dtls",
"wireguard", "wireguard",
"dns" "dns"
]; ];
public static readonly List<string> CoreTypes = public static readonly List<string> CoreTypes =
[ [
"Xray", "Xray",
"sing_box" "sing_box"
]; ];
public static readonly List<string> DomainStrategies = public static readonly List<string> DomainStrategies =
[ [
"AsIs", "AsIs",
"IPIfNonMatch", "IPIfNonMatch",
"IPOnDemand" "IPOnDemand"
]; ];
public static readonly List<string> DomainStrategies4Singbox = public static readonly List<string> DomainStrategies4Singbox =
[ [
"ipv4_only", "ipv4_only",
"ipv6_only", "ipv6_only",
"prefer_ipv4", "prefer_ipv4",
"prefer_ipv6", "prefer_ipv6",
"" ""
]; ];
public static readonly List<string> DomainMatchers = public static readonly List<string> DomainMatchers =
[ [
"linear", "linear",
"mph", "mph",
"" ""
]; ];
public static readonly List<string> Fingerprints = public static readonly List<string> Fingerprints =
[ [
"chrome", "chrome",
"firefox", "firefox",
"safari", "safari",
"ios", "ios",
@ -312,174 +312,174 @@ public class Global
"random", "random",
"randomized", "randomized",
"" ""
]; ];
public static readonly List<string> UserAgent = public static readonly List<string> UserAgent =
[ [
"chrome", "chrome",
"firefox", "firefox",
"safari", "safari",
"edge", "edge",
"none" "none"
]; ];
public static readonly List<string> XhttpMode = public static readonly List<string> XhttpMode =
[ [
"auto", "auto",
"packet-up", "packet-up",
"stream-up", "stream-up",
"stream-one" "stream-one"
]; ];
public static readonly List<string> AllowInsecure = public static readonly List<string> AllowInsecure =
[ [
"true", "true",
"false", "false",
"" ""
]; ];
public static readonly List<string> DomainStrategy4Freedoms = public static readonly List<string> DomainStrategy4Freedoms =
[ [
"AsIs", "AsIs",
"UseIP", "UseIP",
"UseIPv4", "UseIPv4",
"UseIPv6", "UseIPv6",
"" ""
]; ];
public static readonly List<string> SingboxDomainStrategy4Out = public static readonly List<string> SingboxDomainStrategy4Out =
[ [
"ipv4_only", "ipv4_only",
"prefer_ipv4", "prefer_ipv4",
"prefer_ipv6", "prefer_ipv6",
"ipv6_only", "ipv6_only",
"" ""
]; ];
public static readonly List<string> DomainDNSAddress = public static readonly List<string> DomainDNSAddress =
[ [
"223.5.5.5", "223.5.5.5",
"223.6.6.6", "223.6.6.6",
"localhost" "localhost"
]; ];
public static readonly List<string> SingboxDomainDNSAddress = public static readonly List<string> SingboxDomainDNSAddress =
[ [
"223.5.5.5", "223.5.5.5",
"223.6.6.6", "223.6.6.6",
"dhcp://auto" "dhcp://auto"
]; ];
public static readonly List<string> Languages = public static readonly List<string> Languages =
[ [
"zh-Hans", "zh-Hans",
"zh-Hant", "zh-Hant",
"en", "en",
"fa-Ir", "fa-Ir",
"ru", "ru",
"hu" "hu"
]; ];
public static readonly List<string> Alpns = public static readonly List<string> Alpns =
[ [
"h3", "h3",
"h2", "h2",
"http/1.1", "http/1.1",
"h3,h2", "h3,h2",
"h2,http/1.1", "h2,http/1.1",
"h3,h2,http/1.1", "h3,h2,http/1.1",
"" ""
]; ];
public static readonly List<string> LogLevels = public static readonly List<string> LogLevels =
[ [
"debug", "debug",
"info", "info",
"warning", "warning",
"error", "error",
"none" "none"
]; ];
public static readonly List<string> InboundTags = public static readonly List<string> InboundTags =
[ [
"socks", "socks",
"socks2", "socks2",
"socks3" "socks3"
]; ];
public static readonly List<string> RuleProtocols = public static readonly List<string> RuleProtocols =
[ [
"http", "http",
"tls", "tls",
"bittorrent" "bittorrent"
]; ];
public static readonly List<string> RuleNetworks = public static readonly List<string> RuleNetworks =
[ [
"", "",
"tcp", "tcp",
"udp", "udp",
"tcp,udp" "tcp,udp"
]; ];
public static readonly List<string> destOverrideProtocols = public static readonly List<string> destOverrideProtocols =
[ [
"http", "http",
"tls", "tls",
"quic", "quic",
"fakedns", "fakedns",
"fakedns+others" "fakedns+others"
]; ];
public static readonly List<int> TunMtus = public static readonly List<int> TunMtus =
[ [
1280, 1280,
1408, 1408,
1500, 1500,
9000 9000
]; ];
public static readonly List<string> TunStacks = public static readonly List<string> TunStacks =
[ [
"gvisor", "gvisor",
"system", "system",
"mixed" "mixed"
]; ];
public static readonly List<string> PresetMsgFilters = public static readonly List<string> PresetMsgFilters =
[ [
"proxy", "proxy",
"direct", "direct",
"block", "block",
"" ""
]; ];
public static readonly List<string> SingboxMuxs = public static readonly List<string> SingboxMuxs =
[ [
"h2mux", "h2mux",
"smux", "smux",
"yamux", "yamux",
"" ""
]; ];
public static readonly List<string> TuicCongestionControls = public static readonly List<string> TuicCongestionControls =
[ [
"cubic", "cubic",
"new_reno", "new_reno",
"bbr" "bbr"
]; ];
public static readonly List<string> allowSelectType = public static readonly List<string> allowSelectType =
[ [
"selector", "selector",
"urltest", "urltest",
"loadbalance", "loadbalance",
"fallback" "fallback"
]; ];
public static readonly List<string> notAllowTestType = public static readonly List<string> notAllowTestType =
[ [
"selector", "selector",
"urltest", "urltest",
"direct", "direct",
"reject", "reject",
@ -487,15 +487,15 @@ public class Global
"pass", "pass",
"loadbalance", "loadbalance",
"fallback" "fallback"
]; ];
public static readonly List<string> proxyVehicleType = public static readonly List<string> proxyVehicleType =
[ [
"file", "file",
"http" "http"
]; ];
public static readonly Dictionary<ECoreType, string> CoreUrls = new() public static readonly Dictionary<ECoreType, string> CoreUrls = new()
{ {
{ ECoreType.v2fly, "v2fly/v2ray-core" }, { ECoreType.v2fly, "v2fly/v2ray-core" },
{ ECoreType.v2fly_v5, "v2fly/v2ray-core" }, { ECoreType.v2fly_v5, "v2fly/v2ray-core" },
@ -512,12 +512,13 @@ public class Global
{ ECoreType.v2rayN, "2dust/v2rayN" }, { ECoreType.v2rayN, "2dust/v2rayN" },
}; };
public static readonly List<string> OtherGeoUrls = public static readonly List<string> OtherGeoUrls =
[ [
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat", @"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat",
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb", @"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb" @"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
]; ];
#endregion const #endregion const
}
} }

View file

@ -1,247 +1,244 @@
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public sealed class AppHandler
{ {
#region Property public sealed class AppHandler
private static readonly Lazy<AppHandler> _instance = new(() => new());
private Config _config;
private int? _statePort;
private int? _statePort2;
private Job? _processJob;
private bool? _isAdministrator;
public static AppHandler Instance => _instance.Value;
public Config Config => _config;
public int StatePort
{ {
get #region Property
private static readonly Lazy<AppHandler> _instance = new(() => new());
private Config _config;
private int? _statePort;
private int? _statePort2;
private Job? _processJob;
private bool? _isAdministrator;
public static AppHandler Instance => _instance.Value;
public Config Config => _config;
public int StatePort
{ {
_statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api)); get
return _statePort.Value;
}
}
public int StatePort2
{
get
{
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
}
}
public bool IsAdministrator
{
get
{
_isAdministrator ??= Utils.IsAdministrator();
return _isAdministrator.Value;
}
}
#endregion Property
#region Init
public bool InitApp()
{
if (Utils.HasWritePermission() == false)
{
Environment.SetEnvironmentVariable(Global.LocalAppData, "1", EnvironmentVariableTarget.Process);
}
Logging.Setup();
var config = ConfigHandler.LoadConfig();
if (config == null)
{
return false;
}
_config = config;
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
//Under Win10
if (Utils.IsWindows() && Environment.OSVersion.Version.Major < 10)
{
Environment.SetEnvironmentVariable("DOTNET_EnableWriteXorExecute", "0", EnvironmentVariableTarget.User);
}
SQLiteHelper.Instance.CreateTable<SubItem>();
SQLiteHelper.Instance.CreateTable<ProfileItem>();
SQLiteHelper.Instance.CreateTable<ServerStatItem>();
SQLiteHelper.Instance.CreateTable<RoutingItem>();
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
SQLiteHelper.Instance.CreateTable<DNSItem>();
return true;
}
public bool InitComponents()
{
Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
//First determine the port value
_ = StatePort;
_ = StatePort2;
return true;
}
public bool Reset()
{
_statePort = null;
_statePort2 = null;
return true;
}
#endregion Init
#region Config
public int GetLocalPort(EInboundProtocol protocol)
{
var localPort = _config.Inbound.FirstOrDefault(t => t.Protocol == nameof(EInboundProtocol.socks))?.LocalPort ?? 10808;
return localPort + (int)protocol;
}
public void AddProcess(IntPtr processHandle)
{
if (Utils.IsWindows())
{
_processJob ??= new();
try
{
_processJob?.AddProcess(processHandle);
}
catch
{ {
_statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
return _statePort.Value;
} }
} }
}
#endregion Config public int StatePort2
#region SqliteHelper
public async Task<List<SubItem>?> SubItems()
{
return await SQLiteHelper.Instance.TableAsync<SubItem>().OrderBy(t => t.Sort).ToListAsync();
}
public async Task<SubItem?> GetSubItem(string? subid)
{
return await SQLiteHelper.Instance.TableAsync<SubItem>().FirstOrDefaultAsync(t => t.Id == subid);
}
public async Task<List<ProfileItem>?> ProfileItems(string subid)
{
if (subid.IsNullOrEmpty())
{ {
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync(); get
{
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
}
} }
else
public bool IsAdministrator
{ {
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().Where(t => t.Subid == subid).ToListAsync(); get
{
_isAdministrator ??= Utils.IsAdministrator();
return _isAdministrator.Value;
}
} }
}
public async Task<List<string>?> ProfileItemIndexes(string subid) #endregion Property
{
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
}
public async Task<List<ProfileItemModel>?> ProfileItems(string subid, string filter) #region Init
{
var sql = @$"select a.* public bool InitApp()
{
if (Utils.HasWritePermission() == false)
{
Environment.SetEnvironmentVariable(Global.LocalAppData, "1", EnvironmentVariableTarget.Process);
}
Logging.Setup();
var config = ConfigHandler.LoadConfig();
if (config == null)
{
return false;
}
_config = config;
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
//Under Win10
if (Utils.IsWindows() && Environment.OSVersion.Version.Major < 10)
{
Environment.SetEnvironmentVariable("DOTNET_EnableWriteXorExecute", "0", EnvironmentVariableTarget.User);
}
SQLiteHelper.Instance.CreateTable<SubItem>();
SQLiteHelper.Instance.CreateTable<ProfileItem>();
SQLiteHelper.Instance.CreateTable<ServerStatItem>();
SQLiteHelper.Instance.CreateTable<RoutingItem>();
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
SQLiteHelper.Instance.CreateTable<DNSItem>();
return true;
}
public bool InitComponents()
{
Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
return true;
}
public bool Reset()
{
_statePort = null;
_statePort2 = null;
return true;
}
#endregion Init
#region Config
public int GetLocalPort(EInboundProtocol protocol)
{
var localPort = _config.Inbound.FirstOrDefault(t => t.Protocol == nameof(EInboundProtocol.socks))?.LocalPort ?? 10808;
return localPort + (int)protocol;
}
public void AddProcess(IntPtr processHandle)
{
if (Utils.IsWindows())
{
_processJob ??= new();
try
{
_processJob?.AddProcess(processHandle);
}
catch
{
}
}
}
#endregion Config
#region SqliteHelper
public async Task<List<SubItem>?> SubItems()
{
return await SQLiteHelper.Instance.TableAsync<SubItem>().OrderBy(t => t.Sort).ToListAsync();
}
public async Task<SubItem?> GetSubItem(string? subid)
{
return await SQLiteHelper.Instance.TableAsync<SubItem>().FirstOrDefaultAsync(t => t.Id == subid);
}
public async Task<List<ProfileItem>?> ProfileItems(string subid)
{
if (subid.IsNullOrEmpty())
{
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync();
}
else
{
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().Where(t => t.Subid == subid).ToListAsync();
}
}
public async Task<List<string>?> ProfileItemIndexes(string subid)
{
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
}
public async Task<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 (subid.IsNotEmpty()) if (subid.IsNotEmpty())
{
sql += $" and a.subid = '{subid}'";
}
if (filter.IsNotEmpty())
{
if (filter.Contains('\''))
{ {
filter = filter.Replace("'", ""); sql += $" and a.subid = '{subid}'";
} }
sql += string.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter); if (filter.IsNotEmpty())
{
if (filter.Contains('\''))
{
filter = filter.Replace("'", "");
}
sql += string.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter);
}
return await SQLiteHelper.Instance.QueryAsync<ProfileItemModel>(sql);
} }
return await SQLiteHelper.Instance.QueryAsync<ProfileItemModel>(sql); public async Task<ProfileItem?> GetProfileItem(string indexId)
}
public async Task<ProfileItem?> GetProfileItem(string indexId)
{
if (indexId.IsNullOrEmpty())
{ {
return null; if (indexId.IsNullOrEmpty())
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
} }
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
}
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks) public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
{
if (remarks.IsNullOrEmpty())
{ {
return null; if (remarks.IsNullOrEmpty())
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
} }
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
}
public async Task<List<RoutingItem>?> RoutingItems() public async Task<List<RoutingItem>?> RoutingItems()
{
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
}
public async Task<RoutingItem?> GetRoutingItem(string id)
{
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Id == id);
}
public async Task<List<DNSItem>?> DNSItems()
{
return await SQLiteHelper.Instance.TableAsync<DNSItem>().ToListAsync();
}
public async Task<DNSItem?> GetDNSItem(ECoreType eCoreType)
{
return await SQLiteHelper.Instance.TableAsync<DNSItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
}
#endregion SqliteHelper
#region Core Type
public List<string> GetShadowsocksSecurities(ProfileItem profileItem)
{
var coreType = GetCoreType(profileItem, EConfigType.Shadowsocks);
switch (coreType)
{ {
case ECoreType.v2fly: return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
return Global.SsSecurities;
case ECoreType.Xray:
return Global.SsSecuritiesInXray;
case ECoreType.sing_box:
return Global.SsSecuritiesInSingbox;
} }
return Global.SsSecuritiesInSingbox;
}
public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType) public async Task<RoutingItem?> GetRoutingItem(string id)
{
if (profileItem?.CoreType != null)
{ {
return (ECoreType)profileItem.CoreType; return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Id == id);
} }
var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType); public async Task<List<DNSItem>?> DNSItems()
return item?.CoreType ?? ECoreType.Xray; {
} return await SQLiteHelper.Instance.TableAsync<DNSItem>().ToListAsync();
}
#endregion Core Type public async Task<DNSItem?> GetDNSItem(ECoreType eCoreType)
{
return await SQLiteHelper.Instance.TableAsync<DNSItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
}
#endregion SqliteHelper
#region Core Type
public List<string> GetShadowsocksSecurities(ProfileItem profileItem)
{
var coreType = GetCoreType(profileItem, EConfigType.Shadowsocks);
switch (coreType)
{
case ECoreType.v2fly:
return Global.SsSecurities;
case ECoreType.Xray:
return Global.SsSecuritiesInXray;
case ECoreType.sing_box:
return Global.SsSecuritiesInSingbox;
}
return Global.SsSecuritiesInSingbox;
}
public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType)
{
if (profileItem?.CoreType != null)
{
return (ECoreType)profileItem.CoreType;
}
var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType);
return item?.CoreType ?? ECoreType.Xray;
}
#endregion Core Type
}
} }

View file

@ -1,223 +1,223 @@
using System.Security.Principal; using System.Security.Principal;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public static class AutoStartupHandler
{ {
private static readonly string _tag = "AutoStartupHandler"; public static class AutoStartupHandler
public static async Task<bool> UpdateTask(Config config)
{ {
if (Utils.IsWindows()) private static readonly string _tag = "AutoStartupHandler";
{
await ClearTaskWindows();
if (config.GuiItem.AutoRun) public static async Task<bool> UpdateTask(Config config)
{
if (Utils.IsWindows())
{ {
await SetTaskWindows(); await ClearTaskWindows();
}
}
else if (Utils.IsLinux())
{
await ClearTaskLinux();
if (config.GuiItem.AutoRun) if (config.GuiItem.AutoRun)
{
await SetTaskWindows();
}
}
else if (Utils.IsLinux())
{ {
await SetTaskLinux(); await ClearTaskLinux();
}
}
else if (Utils.IsOSX())
{
await ClearTaskOSX();
if (config.GuiItem.AutoRun) if (config.GuiItem.AutoRun)
{
await SetTaskLinux();
}
}
else if (Utils.IsOSX())
{ {
await SetTaskOSX(); await ClearTaskOSX();
if (config.GuiItem.AutoRun)
{
await SetTaskOSX();
}
} }
return true;
} }
return true; #region Windows
}
#region Windows private static async Task ClearTaskWindows()
private static async Task ClearTaskWindows()
{
var autoRunName = GetAutoRunNameWindows();
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, "");
if (Utils.IsAdministrator())
{
AutoStartTaskService(autoRunName, "", "");
}
await Task.CompletedTask;
}
private static async Task SetTaskWindows()
{
try
{ {
var autoRunName = GetAutoRunNameWindows(); var autoRunName = GetAutoRunNameWindows();
var exePath = Utils.GetExePath(); WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, "");
if (Utils.IsAdministrator()) if (Utils.IsAdministrator())
{ {
AutoStartTaskService(autoRunName, exePath, ""); AutoStartTaskService(autoRunName, "", "");
} }
else
await Task.CompletedTask;
}
private static async Task SetTaskWindows()
{
try
{ {
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath.AppendQuotes()); var autoRunName = GetAutoRunNameWindows();
var exePath = Utils.GetExePath();
if (Utils.IsAdministrator())
{
AutoStartTaskService(autoRunName, exePath, "");
}
else
{
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath.AppendQuotes());
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
await Task.CompletedTask;
}
/// <summary>
/// Auto Start via TaskService
/// </summary>
/// <param name="taskName"></param>
/// <param name="fileName"></param>
/// <param name="description"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void AutoStartTaskService(string taskName, string fileName, string description)
{
if (taskName.IsNullOrEmpty())
{
return;
}
var logonUser = WindowsIdentity.GetCurrent().Name;
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
var tasks = taskService.RootFolder.GetTasks(new Regex(taskName));
if (fileName.IsNullOrEmpty())
{
foreach (var t in tasks)
{
taskService.RootFolder.DeleteTask(t.Name);
}
return;
}
var task = taskService.NewTask();
task.RegistrationInfo.Description = description;
task.Settings.DisallowStartIfOnBatteries = false;
task.Settings.StopIfGoingOnBatteries = false;
task.Settings.RunOnlyIfIdle = false;
task.Settings.IdleSettings.StopOnIdleEnd = false;
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(30) });
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
taskService.RootFolder.RegisterTaskDefinition(taskName, task);
}
private static string GetAutoRunNameWindows()
{
return $"{Global.AutoRunName}_{Utils.GetMd5(Utils.StartupPath())}";
}
#endregion Windows
#region Linux
private static async Task ClearTaskLinux()
{
try
{
File.Delete(GetHomePathLinux());
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
await Task.CompletedTask;
}
private static async Task SetTaskLinux()
{
try
{
var linuxConfig = EmbedUtils.GetEmbedText(Global.LinuxAutostartConfig);
if (linuxConfig.IsNotEmpty())
{
linuxConfig = linuxConfig.Replace("$ExecPath$", Utils.GetExePath());
Logging.SaveLog(linuxConfig);
var homePath = GetHomePathLinux();
await File.WriteAllTextAsync(homePath, linuxConfig);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
} }
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
await Task.CompletedTask;
}
/// <summary> private static string GetHomePathLinux()
/// Auto Start via TaskService
/// </summary>
/// <param name="taskName"></param>
/// <param name="fileName"></param>
/// <param name="description"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void AutoStartTaskService(string taskName, string fileName, string description)
{
if (taskName.IsNullOrEmpty())
{ {
return; var homePath = Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop");
Directory.CreateDirectory(Path.GetDirectoryName(homePath));
return homePath;
} }
var logonUser = WindowsIdentity.GetCurrent().Name; #endregion Linux
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
var tasks = taskService.RootFolder.GetTasks(new Regex(taskName)); #region macOS
if (fileName.IsNullOrEmpty())
private static async Task ClearTaskOSX()
{ {
foreach (var t in tasks) try
{ {
taskService.RootFolder.DeleteTask(t.Name); var launchAgentPath = GetLaunchAgentPathMacOS();
if (File.Exists(launchAgentPath))
{
var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput(Global.LinuxBash, args);
File.Delete(launchAgentPath);
}
} }
return; catch (Exception ex)
}
var task = taskService.NewTask();
task.RegistrationInfo.Description = description;
task.Settings.DisallowStartIfOnBatteries = false;
task.Settings.StopIfGoingOnBatteries = false;
task.Settings.RunOnlyIfIdle = false;
task.Settings.IdleSettings.StopOnIdleEnd = false;
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(30) });
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
taskService.RootFolder.RegisterTaskDefinition(taskName, task);
}
private static string GetAutoRunNameWindows()
{
return $"{Global.AutoRunName}_{Utils.GetMd5(Utils.StartupPath())}";
}
#endregion Windows
#region Linux
private static async Task ClearTaskLinux()
{
try
{
File.Delete(GetHomePathLinux());
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
await Task.CompletedTask;
}
private static async Task SetTaskLinux()
{
try
{
var linuxConfig = EmbedUtils.GetEmbedText(Global.LinuxAutostartConfig);
if (linuxConfig.IsNotEmpty())
{ {
linuxConfig = linuxConfig.Replace("$ExecPath$", Utils.GetExePath()); Logging.SaveLog(_tag, ex);
Logging.SaveLog(linuxConfig);
var homePath = GetHomePathLinux();
await File.WriteAllTextAsync(homePath, linuxConfig);
} }
} }
catch (Exception ex)
private static async Task SetTaskOSX()
{ {
Logging.SaveLog(_tag, ex); try
}
}
private static string GetHomePathLinux()
{
var homePath = Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop");
Directory.CreateDirectory(Path.GetDirectoryName(homePath));
return homePath;
}
#endregion Linux
#region macOS
private static async Task ClearTaskOSX()
{
try
{
var launchAgentPath = GetLaunchAgentPathMacOS();
if (File.Exists(launchAgentPath))
{ {
var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" }; var plistContent = GenerateLaunchAgentPlist();
var launchAgentPath = GetLaunchAgentPathMacOS();
await File.WriteAllTextAsync(launchAgentPath, plistContent);
var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput(Global.LinuxBash, args); await Utils.GetCliWrapOutput(Global.LinuxBash, args);
}
File.Delete(launchAgentPath); catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
} }
} }
catch (Exception ex)
private static string GetLaunchAgentPathMacOS()
{ {
Logging.SaveLog(_tag, ex); var homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var launchAgentPath = Path.Combine(homePath, "Library", "LaunchAgents", $"{Global.AppName}-LaunchAgent.plist");
Directory.CreateDirectory(Path.GetDirectoryName(launchAgentPath));
return launchAgentPath;
} }
}
private static async Task SetTaskOSX() private static string GenerateLaunchAgentPlist()
{
try
{ {
var plistContent = GenerateLaunchAgentPlist(); var exePath = Utils.GetExePath();
var launchAgentPath = GetLaunchAgentPathMacOS(); var appName = Path.GetFileNameWithoutExtension(exePath);
await File.WriteAllTextAsync(launchAgentPath, plistContent); return $@"<?xml version=""1.0"" encoding=""UTF-8""?>
var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput(Global.LinuxBash, args);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
private static string GetLaunchAgentPathMacOS()
{
var homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var launchAgentPath = Path.Combine(homePath, "Library", "LaunchAgents", $"{Global.AppName}-LaunchAgent.plist");
Directory.CreateDirectory(Path.GetDirectoryName(launchAgentPath));
return launchAgentPath;
}
private static string GenerateLaunchAgentPlist()
{
var exePath = Utils.GetExePath();
var appName = Path.GetFileNameWithoutExtension(exePath);
return $@"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd""> <!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
<plist version=""1.0""> <plist version=""1.0"">
<dict> <dict>
@ -235,7 +235,8 @@ public static class AutoStartupHandler
<false/> <false/>
</dict> </dict>
</plist>"; </plist>";
} }
#endregion macOS #endregion macOS
}
} }

View file

@ -1,187 +1,188 @@
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public sealed class ClashApiHandler
{ {
private static readonly Lazy<ClashApiHandler> instance = new(() => new()); public sealed class ClashApiHandler
public static ClashApiHandler Instance => instance.Value;
private static readonly string _tag = "ClashApiHandler";
private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; }
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync()
{ {
for (var i = 0; i < 3; i++) private static readonly Lazy<ClashApiHandler> instance = new(() => new());
public static ClashApiHandler Instance => instance.Value;
private static readonly string _tag = "ClashApiHandler";
private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; }
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync()
{ {
var url = $"{GetApiUrl()}/proxies"; for (var i = 0; i < 3; i++)
var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashProxies = JsonUtils.Deserialize<ClashProxies>(result);
var url2 = $"{GetApiUrl()}/providers/proxies";
var result2 = await HttpClientHelper.Instance.TryGetAsync(url2);
var clashProviders = JsonUtils.Deserialize<ClashProviders>(result2);
if (clashProxies != null || clashProviders != null)
{ {
_proxies = clashProxies?.proxies; var url = $"{GetApiUrl()}/proxies";
return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders); var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashProxies = JsonUtils.Deserialize<ClashProxies>(result);
var url2 = $"{GetApiUrl()}/providers/proxies";
var result2 = await HttpClientHelper.Instance.TryGetAsync(url2);
var clashProviders = JsonUtils.Deserialize<ClashProviders>(result2);
if (clashProxies != null || clashProviders != null)
{
_proxies = clashProxies?.proxies;
return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders);
}
await Task.Delay(2000);
} }
await Task.Delay(2000); return null;
} }
return null; public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
}
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
{
Task.Run(async () =>
{ {
if (blAll) Task.Run(async () =>
{ {
if (_proxies == null) if (blAll)
{ {
await GetClashProxiesAsync(); if (_proxies == null)
{
await GetClashProxiesAsync();
}
lstProxy = new List<ClashProxyModel>();
foreach (var kv in _proxies ?? [])
{
if (Global.notAllowTestType.Contains(kv.Value.type?.ToLower()))
{
continue;
}
lstProxy.Add(new ClashProxyModel()
{
Name = kv.Value.name,
Type = kv.Value.type?.ToLower(),
});
}
} }
lstProxy = new List<ClashProxyModel>();
foreach (var kv in _proxies ?? []) if (lstProxy is not { Count: > 0 })
{ {
if (Global.notAllowTestType.Contains(kv.Value.type?.ToLower())) return;
}
var urlBase = $"{GetApiUrl()}/proxies";
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
var tasks = new List<Task>();
foreach (var it in lstProxy)
{
if (Global.notAllowTestType.Contains(it.Type.ToLower()))
{ {
continue; continue;
} }
lstProxy.Add(new ClashProxyModel() var name = it.Name;
var url = string.Format(urlBase, name);
tasks.Add(Task.Run(async () =>
{ {
Name = kv.Value.name, var result = await HttpClientHelper.Instance.TryGetAsync(url);
Type = kv.Value.type?.ToLower(), updateFunc?.Invoke(it, result);
}); }));
} }
} await Task.WhenAll(tasks);
await Task.Delay(1000);
updateFunc?.Invoke(null, "");
});
}
if (lstProxy is not { Count: > 0 }) public List<ProxiesItem>? GetClashProxyGroups()
{
try
{
var fileContent = ProfileContent;
if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
{
return null;
}
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return null;
}
}
public async Task ClashSetActiveProxy(string name, string nameNode)
{
try
{
var url = $"{GetApiUrl()}/proxies/{name}";
var headers = new Dictionary<string, string>();
headers.Add("name", nameNode);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public async Task ClashConfigUpdate(Dictionary<string, string> headers)
{
if (_proxies == null)
{ {
return; return;
} }
var urlBase = $"{GetApiUrl()}/proxies";
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
var tasks = new List<Task>(); var urlBase = $"{GetApiUrl()}/configs";
foreach (var it in lstProxy)
{
if (Global.notAllowTestType.Contains(it.Type.ToLower()))
{
continue;
}
var name = it.Name;
var url = string.Format(urlBase, name);
tasks.Add(Task.Run(async () =>
{
var result = await HttpClientHelper.Instance.TryGetAsync(url);
updateFunc?.Invoke(it, result);
}));
}
await Task.WhenAll(tasks);
await Task.Delay(1000);
updateFunc?.Invoke(null, "");
});
}
public List<ProxiesItem>? GetClashProxyGroups() await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
{
try
{
var fileContent = ProfileContent;
if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
{
return null;
}
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
} }
catch (Exception ex)
public async Task ClashConfigReload(string filePath)
{ {
Logging.SaveLog(_tag, ex); await ClashConnectionClose("");
try
{
var url = $"{GetApiUrl()}/configs?force=true";
var headers = new Dictionary<string, string>();
headers.Add("path", filePath);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public async Task<ClashConnections?> GetClashConnectionsAsync()
{
try
{
var url = $"{GetApiUrl()}/connections";
var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashConnections = JsonUtils.Deserialize<ClashConnections>(result);
return clashConnections;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return null; return null;
} }
}
public async Task ClashSetActiveProxy(string name, string nameNode) public async Task ClashConnectionClose(string id)
{
try
{ {
var url = $"{GetApiUrl()}/proxies/{name}"; try
var headers = new Dictionary<string, string>(); {
headers.Add("name", nameNode); var url = $"{GetApiUrl()}/connections/{id}";
await HttpClientHelper.Instance.PutAsync(url, headers); await HttpClientHelper.Instance.DeleteAsync(url);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
} }
}
public async Task ClashConfigUpdate(Dictionary<string, string> headers)
{
if (_proxies == null)
{
return;
} }
var urlBase = $"{GetApiUrl()}/configs"; private string GetApiUrl()
await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
}
public async Task ClashConfigReload(string filePath)
{
await ClashConnectionClose("");
try
{ {
var url = $"{GetApiUrl()}/configs?force=true"; return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
var headers = new Dictionary<string, string>();
headers.Add("path", filePath);
await HttpClientHelper.Instance.PutAsync(url, headers);
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public async Task<ClashConnections?> GetClashConnectionsAsync()
{
try
{
var url = $"{GetApiUrl()}/connections";
var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashConnections = JsonUtils.Deserialize<ClashConnections>(result);
return clashConnections;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return null;
}
public async Task ClashConnectionClose(string id)
{
try
{
var url = $"{GetApiUrl()}/connections/{id}";
await HttpClientHelper.Instance.DeleteAsync(url);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
private string GetApiUrl()
{
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,155 +1,156 @@
namespace ServiceLib.Handler; namespace ServiceLib.Handler
/// <summary>
/// Core configuration file processing class
/// </summary>
public class CoreConfigHandler
{ {
private static readonly string _tag = "CoreConfigHandler"; /// <summary>
/// Core configuration file processing class
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName) /// </summary>
public class CoreConfigHandler
{ {
var config = AppHandler.Instance.Config; private static readonly string _tag = "CoreConfigHandler";
var result = new RetResult();
if (node.ConfigType == EConfigType.Custom) public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
{ {
result = node.CoreType switch var config = AppHandler.Instance.Config;
var result = new RetResult();
if (node.ConfigType == EConfigType.Custom)
{ {
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName), result = node.CoreType switch
ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName), {
_ => await GenerateClientCustomConfig(node, fileName) ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
}; ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName),
} _ => await GenerateClientCustomConfig(node, fileName)
else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) };
{ }
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node); else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
} {
else result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
{ }
result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node); else
} {
if (result.Success != true) result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node);
{ }
if (result.Success != true)
{
return result;
}
if (fileName.IsNotEmpty() && result.Data != null)
{
await File.WriteAllTextAsync(fileName, result.Data.ToString());
}
return result; return result;
} }
if (fileName.IsNotEmpty() && result.Data != null)
private static async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
{ {
var ret = new RetResult();
try
{
if (node == null || fileName is null)
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
if (File.Exists(fileName))
{
File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail
File.Delete(fileName);
}
string addressFileName = node.Address;
if (!File.Exists(addressFileName))
{
addressFileName = Utils.GetConfigPath(addressFileName);
}
if (!File.Exists(addressFileName))
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
File.Copy(addressFileName, fileName);
File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file.
//check again
if (!File.Exists(fileName))
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
return await Task.FromResult(ret);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
{
var result = new RetResult();
if (coreType == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds);
}
else if (coreType == ECoreType.Xray)
{
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString()); await File.WriteAllTextAsync(fileName, result.Data.ToString());
}
return result;
}
private static async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
{
var ret = new RetResult();
try
{
if (node == null || fileName is null)
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
if (File.Exists(fileName))
{
File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail
File.Delete(fileName);
}
string addressFileName = node.Address;
if (!File.Exists(addressFileName))
{
addressFileName = Utils.GetConfigPath(addressFileName);
}
if (!File.Exists(addressFileName))
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
File.Copy(addressFileName, fileName);
File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file.
//check again
if (!File.Exists(fileName))
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
return await Task.FromResult(ret);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
{
var result = new RetResult();
if (coreType == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds);
}
else if (coreType == ECoreType.Xray)
{
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
{
var result = new RetResult();
var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
testItem.Port = port;
if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
}
else
{
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port);
}
if (result.Success != true)
{
return result; return result;
} }
await File.WriteAllTextAsync(fileName, result.Data.ToString()); public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName)
return result; {
} var result = new RetResult();
var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
var port = Utils.GetFreePort(initPort + testItem.QueueNum);
testItem.Port = port;
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad) if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
{ {
var result = new RetResult(); result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port);
if (coreType == ECoreType.sing_box) }
{ else
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds); {
} result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port);
else }
{ if (result.Success != true)
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad); {
} return result;
}
if (result.Success != true) await File.WriteAllTextAsync(fileName, result.Data.ToString());
{ return result;
}
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType)
{
var result = new RetResult();
if (coreType == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
}
else if (coreType == ECoreType.Xray)
{
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result; return result;
} }
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
} }
} }

View file

@ -1,409 +1,410 @@
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
namespace ServiceLib.Handler; namespace ServiceLib.Handler
/// <summary>
/// Core process processing class
/// </summary>
public class CoreHandler
{ {
private static readonly Lazy<CoreHandler> _instance = new(() => new()); /// <summary>
public static CoreHandler Instance => _instance.Value; /// Core process processing class
private Config _config; /// </summary>
private Process? _process; public class CoreHandler
private Process? _processPre;
private int _linuxSudoPid = -1;
private Action<bool, string>? _updateFunc;
private const string _tag = "CoreHandler";
public async Task Init(Config config, Action<bool, string> updateFunc)
{ {
_config = config; private static readonly Lazy<CoreHandler> _instance = new(() => new());
_updateFunc = updateFunc; public static CoreHandler Instance => _instance.Value;
private Config _config;
private Process? _process;
private Process? _processPre;
private int _linuxSudoPid = -1;
private Action<bool, string>? _updateFunc;
private const string _tag = "CoreHandler";
Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); public async Task Init(Config config, Action<bool, string> updateFunc)
Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalCert, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
//Copy the bin folder to the storage location (for init)
if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1")
{ {
var fromPath = Utils.GetBaseDirectory("bin"); _config = config;
var toPath = Utils.GetBinPath(""); _updateFunc = updateFunc;
if (fromPath != toPath)
{
FileManager.CopyDirectory(fromPath, toPath, true, false);
}
}
if (Utils.IsNonWindows()) Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
{ Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(); Environment.SetEnvironmentVariable(Global.XrayLocalCert, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
foreach (var it in coreInfo)
//Copy the bin folder to the storage location (for init)
if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1")
{ {
if (it.CoreType == ECoreType.v2rayN) var fromPath = Utils.GetBaseDirectory("bin");
var toPath = Utils.GetBinPath("");
if (fromPath != toPath)
{ {
if (Utils.UpgradeAppExists(out var upgradeFileName)) FileManager.CopyDirectory(fromPath, toPath, true, false);
{
await Utils.SetLinuxChmod(upgradeFileName);
}
continue;
} }
}
foreach (var name in it.CoreExes) if (Utils.IsNonWindows())
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo)
{ {
var exe = Utils.GetBinPath(Utils.GetExeName(name), it.CoreType.ToString()); if (it.CoreType == ECoreType.v2rayN)
if (File.Exists(exe))
{ {
await Utils.SetLinuxChmod(exe); if (Utils.UpgradeAppExists(out var upgradeFileName))
{
await Utils.SetLinuxChmod(upgradeFileName);
}
continue;
}
foreach (var name in it.CoreExes)
{
var exe = Utils.GetBinPath(Utils.GetExeName(name), it.CoreType.ToString());
if (File.Exists(exe))
{
await Utils.SetLinuxChmod(exe);
}
} }
} }
} }
} }
}
public async Task LoadCore(ProfileItem? node) public async Task LoadCore(ProfileItem? node)
{
if (node == null)
{ {
UpdateFunc(false, ResUI.CheckServerSettings); if (node == null)
return; {
} UpdateFunc(false, ResUI.CheckServerSettings);
return;
}
var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName); var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
if (result.Success != true) if (result.Success != true)
{ {
UpdateFunc(true, result.Msg); UpdateFunc(true, result.Msg);
return; return;
} }
UpdateFunc(false, $"{node.GetSummary()}"); UpdateFunc(false, $"{node.GetSummary()}");
UpdateFunc(false, $"{Utils.GetRuntimeInfo()}"); UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
await CoreStop(); await CoreStop();
await Task.Delay(100);
if (Utils.IsWindows() && _config.TunModeItem.EnableTun)
{
await Task.Delay(100); await Task.Delay(100);
await WindowsUtils.RemoveTunDevice();
}
await CoreStart(node); if (Utils.IsWindows() && _config.TunModeItem.EnableTun)
await CoreStartPreService(node); {
if (_process != null) await Task.Delay(100);
{ await WindowsUtils.RemoveTunDevice();
UpdateFunc(true, $"{node.GetSummary()}"); }
}
}
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds) await CoreStart(node);
{ await CoreStartPreService(node);
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
UpdateFunc(false, result.Msg);
if (result.Success != true)
{
return -1;
}
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
UpdateFunc(false, configPath);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null)
{
return -1;
}
return proc.Id;
}
public async Task<int> LoadCoreConfigSpeedtest(ServerTestItem testItem)
{
var node = await AppHandler.Instance.GetProfileItem(testItem.IndexId);
if (node is null)
{
return -1;
}
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
if (result.Success != true)
{
return -1;
}
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null)
{
return -1;
}
return proc.Id;
}
public async Task CoreStop()
{
try
{
if (_process != null) if (_process != null)
{ {
await ProcUtils.ProcessKill(_process, true); UpdateFunc(true, $"{node.GetSummary()}");
_process = null;
} }
}
if (_processPre != null) public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
UpdateFunc(false, result.Msg);
if (result.Success != true)
{ {
await ProcUtils.ProcessKill(_processPre, true); return -1;
_processPre = null;
} }
if (_linuxSudoPid > 0) UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
UpdateFunc(false, configPath);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null)
{ {
await KillProcessAsLinuxSudo(); return -1;
} }
_linuxSudoPid = -1;
return proc.Id;
} }
catch (Exception ex)
public async Task<int> LoadCoreConfigSpeedtest(ServerTestItem testItem)
{ {
Logging.SaveLog(_tag, ex); var node = await AppHandler.Instance.GetProfileItem(testItem.IndexId);
} if (node is null)
} {
return -1;
}
#region Private var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath);
if (result.Success != true)
{
return -1;
}
private async Task CoreStart(ProfileItem node)
{
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
if (proc is null)
{
return;
}
_process = proc;
}
private async Task CoreStartPreService(ProfileItem node)
{
if (_process != null && !_process.HasExited)
{
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
if (itemSocks != null) var proc = await RunProcess(coreInfo, fileName, true, false);
if (proc is null)
{ {
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box; return -1;
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName); }
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
if (result.Success) return proc.Id;
}
public async Task CoreStop()
{
try
{
if (_process != null)
{ {
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); await ProcUtils.ProcessKill(_process, true);
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); _process = null;
if (proc is null) }
if (_processPre != null)
{
await ProcUtils.ProcessKill(_processPre, true);
_processPre = null;
}
if (_linuxSudoPid > 0)
{
await KillProcessAsLinuxSudo();
}
_linuxSudoPid = -1;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
#region Private
private async Task CoreStart(ProfileItem node)
{
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
if (proc is null)
{
return;
}
_process = proc;
}
private async Task CoreStartPreService(ProfileItem node)
{
if (_process != null && !_process.HasExited)
{
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
if (itemSocks != null)
{
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
if (result.Success)
{ {
return; var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
if (proc is null)
{
return;
}
_processPre = proc;
} }
_processPre = proc;
} }
} }
} }
}
private void UpdateFunc(bool notify, string msg) private void UpdateFunc(bool notify, string msg)
{
_updateFunc?.Invoke(notify, msg);
}
private bool IsNeedSudo(ECoreType eCoreType)
{
return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box
&& (Utils.IsNonWindows())
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
;
}
#endregion Private
#region Process
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{
var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
if (fileName.IsNullOrEmpty())
{ {
UpdateFunc(false, msg); _updateFunc?.Invoke(notify, msg);
return null;
} }
try private bool IsNeedSudo(ECoreType eCoreType)
{ {
return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box
&& (Utils.IsNonWindows())
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
;
}
#endregion Private
#region Process
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{
var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
if (fileName.IsNullOrEmpty())
{
UpdateFunc(false, msg);
return null;
}
try
{
Process proc = new()
{
StartInfo = new()
{
FileName = fileName,
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
CreateNoWindow = true,
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
}
};
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
if (isNeedSudo)
{
await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
}
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNullOrEmpty())
return;
UpdateFunc(false, e.Data + Environment.NewLine);
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNullOrEmpty())
return;
UpdateFunc(false, e.Data + Environment.NewLine);
};
}
proc.Start();
if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
if (isNeedSudo)
_linuxSudoPid = proc.Id;
if (displayLog)
{
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
}
await Task.Delay(500);
AppHandler.Instance.AddProcess(proc.Handle);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
return proc;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
UpdateFunc(mayNeedSudo, ex.Message);
return null;
}
}
#endregion Process
#region Linux
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath;
proc.StartInfo.Arguments = "";
proc.StartInfo.WorkingDirectory = "";
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
}
}
private async Task KillProcessAsLinuxSudo()
{
var cmdLine = $"kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
Process proc = new() Process proc = new()
{ {
StartInfo = new() StartInfo = new()
{ {
FileName = fileName, FileName = shFilePath,
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
CreateNoWindow = true, CreateNoWindow = true,
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, StandardInputEncoding = Encoding.UTF8,
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, RedirectStandardInput = true
} }
}; };
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
if (isNeedSudo)
{
await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
}
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNullOrEmpty())
return;
UpdateFunc(false, e.Data + Environment.NewLine);
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNullOrEmpty())
return;
UpdateFunc(false, e.Data + Environment.NewLine);
};
}
proc.Start(); proc.Start();
if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()) if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{ {
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd); try
await Task.Delay(10); {
await proc.StandardInput.WriteLineAsync(pwd); var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10); await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd); await proc.StandardInput.WriteLineAsync(pwd);
} await Task.Delay(10);
if (isNeedSudo) await proc.StandardInput.WriteLineAsync(pwd);
_linuxSudoPid = proc.Id; }
catch (Exception)
if (displayLog) {
{ // ignored
proc.BeginOutputReadLine(); }
proc.BeginErrorReadLine();
} }
await Task.Delay(500); var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
AppHandler.Instance.AddProcess(proc.Handle); await proc.WaitForExitAsync(timeout.Token);
if (proc is null or { HasExited: true }) await Task.Delay(3000);
{
throw new Exception(ResUI.FailedToRunCore);
}
return proc;
} }
catch (Exception ex)
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{ {
Logging.SaveLog(_tag, ex); //Shell scripts
UpdateFunc(mayNeedSudo, ex.Message); var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
return null; File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (AppHandler.Instance.IsAdministrator)
{
sb.AppendLine($"{cmdLine}");
}
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
{
sb.AppendLine($"pkexec {cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
Logging.SaveLog(shFilePath);
return shFilePath;
} }
#endregion Linux
} }
#endregion Process
#region Linux
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath;
proc.StartInfo.Arguments = "";
proc.StartInfo.WorkingDirectory = "";
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
}
}
private async Task KillProcessAsLinuxSudo()
{
var cmdLine = $"kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
Process proc = new()
{
StartInfo = new()
{
FileName = shFilePath,
UseShellExecute = false,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
RedirectStandardInput = true
}
};
proc.Start();
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
try
{
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
catch (Exception)
{
// ignored
}
}
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await proc.WaitForExitAsync(timeout.Token);
await Task.Delay(3000);
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
//Shell scripts
var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (AppHandler.Instance.IsAdministrator)
{
sb.AppendLine($"{cmdLine}");
}
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
{
sb.AppendLine($"pkexec {cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
Logging.SaveLog(shFilePath);
return shFilePath;
}
#endregion Linux
} }

View file

@ -1,65 +1,65 @@
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public sealed class CoreInfoHandler
{ {
private static readonly Lazy<CoreInfoHandler> _instance = new(() => new()); public sealed class CoreInfoHandler
private List<CoreInfo>? _coreInfo;
public static CoreInfoHandler Instance => _instance.Value;
public CoreInfoHandler()
{ {
InitCoreInfo(); private static readonly Lazy<CoreInfoHandler> _instance = new(() => new());
} private List<CoreInfo>? _coreInfo;
public static CoreInfoHandler Instance => _instance.Value;
public CoreInfo? GetCoreInfo(ECoreType coreType) public CoreInfoHandler()
{
if (_coreInfo == null)
{ {
InitCoreInfo(); InitCoreInfo();
} }
return _coreInfo?.FirstOrDefault(t => t.CoreType == coreType);
}
public List<CoreInfo> GetCoreInfo() public CoreInfo? GetCoreInfo(ECoreType coreType)
{
if (_coreInfo == null)
{ {
InitCoreInfo(); if (_coreInfo == null)
}
return _coreInfo ?? [];
}
public string GetCoreExecFile(CoreInfo? coreInfo, out string msg)
{
var fileName = string.Empty;
msg = string.Empty;
foreach (var name in coreInfo?.CoreExes)
{
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
if (File.Exists(vName))
{ {
fileName = vName; InitCoreInfo();
break;
} }
return _coreInfo?.FirstOrDefault(t => t.CoreType == coreType);
} }
if (fileName.IsNullOrEmpty())
public List<CoreInfo> GetCoreInfo()
{ {
msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo?.CoreType.ToString()), coreInfo?.CoreExes?.LastOrDefault(), coreInfo?.Url); if (_coreInfo == null)
Logging.SaveLog(msg); {
InitCoreInfo();
}
return _coreInfo ?? [];
} }
return fileName;
}
private void InitCoreInfo() public string GetCoreExecFile(CoreInfo? coreInfo, out string msg)
{ {
var urlN = GetCoreUrl(ECoreType.v2rayN); var fileName = string.Empty;
var urlXray = GetCoreUrl(ECoreType.Xray); msg = string.Empty;
var urlMihomo = GetCoreUrl(ECoreType.mihomo); foreach (var name in coreInfo?.CoreExes)
var urlSingbox = GetCoreUrl(ECoreType.sing_box); {
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
if (File.Exists(vName))
{
fileName = vName;
break;
}
}
if (fileName.IsNullOrEmpty())
{
msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo?.CoreType.ToString()), coreInfo?.CoreExes?.LastOrDefault(), coreInfo?.Url);
Logging.SaveLog(msg);
}
return fileName;
}
_coreInfo = private void InitCoreInfo()
[ {
new CoreInfo var urlN = GetCoreUrl(ECoreType.v2rayN);
var urlXray = GetCoreUrl(ECoreType.Xray);
var urlMihomo = GetCoreUrl(ECoreType.mihomo);
var urlSingbox = GetCoreUrl(ECoreType.sing_box);
_coreInfo =
[
new CoreInfo
{ {
CoreType = ECoreType.v2rayN, CoreType = ECoreType.v2rayN,
Url = GetCoreUrl(ECoreType.v2rayN), Url = GetCoreUrl(ECoreType.v2rayN),
@ -202,16 +202,17 @@ public sealed class CoreInfoHandler
AbsolutePath = false, AbsolutePath = false,
} }
]; ];
} }
private static string PortableMode() private static string PortableMode()
{ {
return $" -d {Utils.GetBinPath("").AppendQuotes()}"; return $" -d {Utils.GetBinPath("").AppendQuotes()}";
} }
private static string GetCoreUrl(ECoreType eCoreType) private static string GetCoreUrl(ECoreType eCoreType)
{ {
return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases"; return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases";
}
} }
} }

View file

@ -1,241 +1,242 @@
using System.Collections.Specialized; using System.Collections.Specialized;
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class BaseFmt
{ {
protected static string GetIpv6(string address) public class BaseFmt
{ {
if (Utils.IsIpv6(address)) protected static string GetIpv6(string address)
{ {
// 检查地址是否已经被方括号包围,如果没有,则添加方括号 if (Utils.IsIpv6(address))
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
}
return address; // 如果不是IPv6地址直接返回原地址
}
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
{
if (item.Flow.IsNotEmpty())
{
dicQuery.Add("flow", item.Flow);
}
if (item.StreamSecurity.IsNotEmpty())
{
dicQuery.Add("security", item.StreamSecurity);
}
else
{
if (securityDef != null)
{ {
dicQuery.Add("security", securityDef); // 检查地址是否已经被方括号包围,如果没有,则添加方括号
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
} }
} return address; // 如果不是IPv6地址直接返回原地址
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (item.Fingerprint.IsNotEmpty())
{
dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint));
}
if (item.PublicKey.IsNotEmpty())
{
dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey));
}
if (item.ShortId.IsNotEmpty())
{
dicQuery.Add("sid", Utils.UrlEncode(item.ShortId));
}
if (item.SpiderX.IsNotEmpty())
{
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
}
if (item.AllowInsecure.Equals("true"))
{
dicQuery.Add("allowInsecure", "1");
} }
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp)); protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
switch (item.Network)
{ {
case nameof(ETransport.tcp): if (item.Flow.IsNotEmpty())
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None); {
if (item.RequestHost.IsNotEmpty()) dicQuery.Add("flow", item.Flow);
{ }
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
break;
case nameof(ETransport.kcp): if (item.StreamSecurity.IsNotEmpty())
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None); {
if (item.Path.IsNotEmpty()) dicQuery.Add("security", item.StreamSecurity);
}
else
{
if (securityDef != null)
{ {
dicQuery.Add("seed", Utils.UrlEncode(item.Path)); dicQuery.Add("security", securityDef);
} }
break; }
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (item.Fingerprint.IsNotEmpty())
{
dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint));
}
if (item.PublicKey.IsNotEmpty())
{
dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey));
}
if (item.ShortId.IsNotEmpty())
{
dicQuery.Add("sid", Utils.UrlEncode(item.ShortId));
}
if (item.SpiderX.IsNotEmpty())
{
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
}
if (item.AllowInsecure.Equals("true"))
{
dicQuery.Add("allowInsecure", "1");
}
case nameof(ETransport.ws): dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
case nameof(ETransport.httpupgrade):
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.xhttp): switch (item.Network)
if (item.RequestHost.IsNotEmpty()) {
{ case nameof(ETransport.tcp):
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
} if (item.RequestHost.IsNotEmpty())
if (item.Path.IsNotEmpty()) {
{ dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("path", Utils.UrlEncode(item.Path)); }
} break;
if (item.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(item.HeaderType))
{
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
}
if (item.Extra.IsNotEmpty())
{
dicQuery.Add("extra", Utils.UrlEncode(item.Extra));
}
break;
case nameof(ETransport.http): case nameof(ETransport.kcp):
case nameof(ETransport.h2): dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
dicQuery["type"] = nameof(ETransport.http); if (item.Path.IsNotEmpty())
if (item.RequestHost.IsNotEmpty()) {
{ dicQuery.Add("seed", Utils.UrlEncode(item.Path));
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); }
} break;
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.quic): case nameof(ETransport.ws):
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None); case nameof(ETransport.httpupgrade):
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost)); if (item.RequestHost.IsNotEmpty())
dicQuery.Add("key", Utils.UrlEncode(item.Path)); {
break; dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.grpc): case nameof(ETransport.xhttp):
if (item.Path.IsNotEmpty()) if (item.RequestHost.IsNotEmpty())
{ {
dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path)); }
if (item.HeaderType is Global.GrpcGunMode or Global.GrpcMultiMode) if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
if (item.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(item.HeaderType))
{ {
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType)); dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
} }
} if (item.Extra.IsNotEmpty())
break; {
} dicQuery.Add("extra", Utils.UrlEncode(item.Extra));
return 0; }
} break;
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item) case nameof(ETransport.http):
{ case nameof(ETransport.h2):
item.Flow = query["flow"] ?? ""; dicQuery["type"] = nameof(ETransport.http);
item.StreamSecurity = query["security"] ?? ""; if (item.RequestHost.IsNotEmpty())
item.Sni = query["sni"] ?? ""; {
item.Alpn = Utils.UrlDecode(query["alpn"] ?? ""); dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
item.Fingerprint = Utils.UrlDecode(query["fp"] ?? ""); }
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? ""); if (item.Path.IsNotEmpty())
item.ShortId = Utils.UrlDecode(query["sid"] ?? ""); {
item.SpiderX = Utils.UrlDecode(query["spx"] ?? ""); dicQuery.Add("path", Utils.UrlEncode(item.Path));
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : ""; }
break;
item.Network = query["type"] ?? nameof(ETransport.tcp); case nameof(ETransport.quic):
switch (item.Network) dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
{ dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
case nameof(ETransport.tcp): dicQuery.Add("key", Utils.UrlEncode(item.Path));
item.HeaderType = query["headerType"] ?? Global.None; break;
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
break; case nameof(ETransport.grpc):
if (item.Path.IsNotEmpty())
case nameof(ETransport.kcp): {
item.HeaderType = query["headerType"] ?? Global.None; dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
item.Path = Utils.UrlDecode(query["seed"] ?? ""); dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
break; if (item.HeaderType is Global.GrpcGunMode or Global.GrpcMultiMode)
{
case nameof(ETransport.ws): dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
case nameof(ETransport.httpupgrade): }
item.RequestHost = Utils.UrlDecode(query["host"] ?? ""); }
item.Path = Utils.UrlDecode(query["path"] ?? "/"); break;
break;
case nameof(ETransport.xhttp):
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/");
item.HeaderType = Utils.UrlDecode(query["mode"] ?? "");
item.Extra = Utils.UrlDecode(query["extra"] ?? "");
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.Network = nameof(ETransport.h2);
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.quic):
item.HeaderType = query["headerType"] ?? Global.None;
item.RequestHost = query["quicSecurity"] ?? Global.None;
item.Path = Utils.UrlDecode(query["key"] ?? "");
break;
case nameof(ETransport.grpc):
item.RequestHost = Utils.UrlDecode(query["authority"] ?? "");
item.Path = Utils.UrlDecode(query["serviceName"] ?? "");
item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
break;
default:
break;
}
return 0;
}
protected static bool Contains(string str, params string[] s)
{
foreach (var item in s)
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase))
{
return true;
} }
return 0;
} }
return false;
}
protected static string WriteAllText(string strData, string ext = "json") protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
{ {
var fileName = Utils.GetTempPath($"{Utils.GetGuid(false)}.{ext}"); item.Flow = query["flow"] ?? "";
File.WriteAllText(fileName, strData); item.StreamSecurity = query["security"] ?? "";
return fileName; item.Sni = query["sni"] ?? "";
} item.Alpn = Utils.UrlDecode(query["alpn"] ?? "");
item.Fingerprint = Utils.UrlDecode(query["fp"] ?? "");
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? "");
item.ShortId = Utils.UrlDecode(query["sid"] ?? "");
item.SpiderX = Utils.UrlDecode(query["spx"] ?? "");
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : "";
protected static string ToUri(EConfigType eConfigType, string address, object port, string userInfo, Dictionary<string, string>? dicQuery, string? remark) item.Network = query["type"] ?? nameof(ETransport.tcp);
{ switch (item.Network)
var query = dicQuery != null {
? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray())) case nameof(ETransport.tcp):
: string.Empty; item.HeaderType = query["headerType"] ?? Global.None;
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}"; break;
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
case nameof(ETransport.kcp):
item.HeaderType = query["headerType"] ?? Global.None;
item.Path = Utils.UrlDecode(query["seed"] ?? "");
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.xhttp):
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/");
item.HeaderType = Utils.UrlDecode(query["mode"] ?? "");
item.Extra = Utils.UrlDecode(query["extra"] ?? "");
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.Network = nameof(ETransport.h2);
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.quic):
item.HeaderType = query["headerType"] ?? Global.None;
item.RequestHost = query["quicSecurity"] ?? Global.None;
item.Path = Utils.UrlDecode(query["key"] ?? "");
break;
case nameof(ETransport.grpc):
item.RequestHost = Utils.UrlDecode(query["authority"] ?? "");
item.Path = Utils.UrlDecode(query["serviceName"] ?? "");
item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
break;
default:
break;
}
return 0;
}
protected static bool Contains(string str, params string[] s)
{
foreach (var item in s)
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
protected static string WriteAllText(string strData, string ext = "json")
{
var fileName = Utils.GetTempPath($"{Utils.GetGuid(false)}.{ext}");
File.WriteAllText(fileName, strData);
return fileName;
}
protected static string ToUri(EConfigType eConfigType, string address, object port, string userInfo, Dictionary<string, string>? dicQuery, string? remark)
{
var query = dicQuery != null
? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()))
: string.Empty;
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
}
} }
} }

View file

@ -1,22 +1,23 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class ClashFmt : BaseFmt
{ {
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public class ClashFmt : BaseFmt
{ {
if (Contains(strData, "port", "socks-port", "proxies")) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
var fileName = WriteAllText(strData, "yaml"); if (Contains(strData, "port", "socks-port", "proxies"))
var profileItem = new ProfileItem
{ {
CoreType = ECoreType.mihomo, var fileName = WriteAllText(strData, "yaml");
Address = fileName,
Remarks = subRemarks ?? "clash_custom"
};
return profileItem;
}
return null; var profileItem = new ProfileItem
{
CoreType = ECoreType.mihomo,
Address = fileName,
Remarks = subRemarks ?? "clash_custom"
};
return profileItem;
}
return null;
}
} }
} }

View file

@ -1,91 +1,92 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class FmtHandler
{ {
private static readonly string _tag = "FmtHandler"; public class FmtHandler
public static string? GetShareUri(ProfileItem item)
{ {
try private static readonly string _tag = "FmtHandler";
{
var url = item.ConfigType switch
{
EConfigType.VMess => VmessFmt.ToUri(item),
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
EConfigType.SOCKS => SocksFmt.ToUri(item),
EConfigType.Trojan => TrojanFmt.ToUri(item),
EConfigType.VLESS => VLESSFmt.ToUri(item),
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
EConfigType.TUIC => TuicFmt.ToUri(item),
EConfigType.WireGuard => WireguardFmt.ToUri(item),
_ => null,
};
return url; public static string? GetShareUri(ProfileItem item)
{
try
{
var url = item.ConfigType switch
{
EConfigType.VMess => VmessFmt.ToUri(item),
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
EConfigType.SOCKS => SocksFmt.ToUri(item),
EConfigType.Trojan => TrojanFmt.ToUri(item),
EConfigType.VLESS => VLESSFmt.ToUri(item),
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
EConfigType.TUIC => TuicFmt.ToUri(item),
EConfigType.WireGuard => WireguardFmt.ToUri(item),
_ => null,
};
return url;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return "";
}
} }
catch (Exception ex)
public static ProfileItem? ResolveConfig(string config, out string msg)
{ {
Logging.SaveLog(_tag, ex); msg = ResUI.ConfigurationFormatIncorrect;
return "";
}
}
public static ProfileItem? ResolveConfig(string config, out string msg) try
{ {
msg = ResUI.ConfigurationFormatIncorrect; string str = config.TrimEx();
if (str.IsNullOrEmpty())
{
msg = ResUI.FailedReadConfiguration;
return null;
}
try if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
{ {
string str = config.TrimEx(); return VmessFmt.Resolve(str, out msg);
if (str.IsNullOrEmpty()) }
{ else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
msg = ResUI.FailedReadConfiguration; {
return null; return ShadowsocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.SOCKS]))
{
return SocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Trojan]))
{
return TrojanFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.VLESS]))
{
return VLESSFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare))
{
return Hysteria2Fmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.TUIC]))
{
return TuicFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.WireGuard]))
{
return WireguardFmt.Resolve(str, out msg);
}
else
{
msg = ResUI.NonvmessOrssProtocol;
return null;
}
} }
catch (Exception ex)
if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
{ {
return VmessFmt.Resolve(str, out msg); Logging.SaveLog(_tag, ex);
} msg = ResUI.Incorrectconfiguration;
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
{
return ShadowsocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.SOCKS]))
{
return SocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Trojan]))
{
return TrojanFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.VLESS]))
{
return VLESSFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare))
{
return Hysteria2Fmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.TUIC]))
{
return TuicFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.WireGuard]))
{
return WireguardFmt.Resolve(str, out msg);
}
else
{
msg = ResUI.NonvmessOrssProtocol;
return null; return null;
} }
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
msg = ResUI.Incorrectconfiguration;
return null;
}
} }
} }

View file

@ -1,101 +1,102 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class Hysteria2Fmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public class Hysteria2Fmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; public static ProfileItem? Resolve(string str, out string msg)
ProfileItem item = new()
{ {
ConfigType = EConfigType.Hysteria2 msg = ResUI.ConfigurationFormatIncorrect;
}; ProfileItem item = new()
var url = Utils.TryUri(str);
if (url == null)
return null;
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
item.Ports = Utils.UrlDecode(query["mport"] ?? "").Replace('-', ':');
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
return null;
string url = string.Empty;
string remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (item.Path.IsNotEmpty())
{
dicQuery.Add("obfs", "salamander");
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
}
dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0");
if (item.Ports.IsNotEmpty())
{
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
}
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{ {
CoreType = ECoreType.hysteria, ConfigType = EConfigType.Hysteria2
Address = fileName,
Remarks = subRemarks ?? "hysteria_custom"
}; };
return profileItem;
var url = Utils.TryUri(str);
if (url == null)
return null;
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
item.Ports = Utils.UrlDecode(query["mport"] ?? "").Replace('-', ':');
return item;
} }
return null; public static string? ToUri(ProfileItem? item)
}
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
{
if (Contains(strData, "server", "auth", "up", "down", "listen"))
{ {
var fileName = WriteAllText(strData); if (item == null)
return null;
string url = string.Empty;
var profileItem = new ProfileItem string remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{ {
CoreType = ECoreType.hysteria2, remark = "#" + Utils.UrlEncode(item.Remarks);
Address = fileName, }
Remarks = subRemarks ?? "hysteria2_custom" var dicQuery = new Dictionary<string, string>();
}; if (item.Sni.IsNotEmpty())
return profileItem; {
dicQuery.Add("sni", item.Sni);
}
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (item.Path.IsNotEmpty())
{
dicQuery.Add("obfs", "salamander");
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
}
dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0");
if (item.Ports.IsNotEmpty())
{
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
}
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
} }
return null; public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.hysteria,
Address = fileName,
Remarks = subRemarks ?? "hysteria_custom"
};
return profileItem;
}
return null;
}
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
{
if (Contains(strData, "server", "auth", "up", "down", "listen"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.hysteria2,
Address = fileName,
Remarks = subRemarks ?? "hysteria2_custom"
};
return profileItem;
}
return null;
}
} }
} }

View file

@ -1,22 +1,23 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class NaiveproxyFmt : BaseFmt
{ {
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public class NaiveproxyFmt : BaseFmt
{ {
if (Contains(strData, "listen", "proxy", "<html>", "<body>")) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
var fileName = WriteAllText(strData); if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
var profileItem = new ProfileItem
{ {
CoreType = ECoreType.naiveproxy, var fileName = WriteAllText(strData);
Address = fileName,
Remarks = subRemarks ?? "naiveproxy_custom"
};
return profileItem;
}
return null; var profileItem = new ProfileItem
{
CoreType = ECoreType.naiveproxy,
Address = fileName,
Remarks = subRemarks ?? "naiveproxy_custom"
};
return profileItem;
}
return null;
}
} }
} }

View file

@ -1,179 +1,180 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class ShadowsocksFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public class ShadowsocksFmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; public static ProfileItem? Resolve(string str, out string msg)
ProfileItem? item; {
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
item = ResolveSSLegacy(str) ?? ResolveSip002(str); item = ResolveSSLegacy(str) ?? ResolveSip002(str);
if (item == null) if (item == null)
{
return null;
}
if (item.Address.Length == 0 || item.Port == 0 || item.Security.Length == 0 || item.Id.Length == 0)
{
return null;
}
item.ConfigType = EConfigType.Shadowsocks;
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new Sip002
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
}
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static ProfileItem? ResolveSSLegacy(string result)
{
var match = UrlFinder.Match(result);
if (!match.Success)
{
return null;
}
ProfileItem item = new();
var base64 = match.Groups["base64"].Value.TrimEnd('/');
var tag = match.Groups["tag"].Value;
if (tag.IsNotEmpty())
{
item.Remarks = Utils.UrlDecode(tag);
}
Match details;
try
{
details = DetailsParser.Match(Utils.Base64Decode(base64));
}
catch (FormatException)
{
return null;
}
if (!details.Success)
{
return null;
}
item.Security = details.Groups["method"].Value;
item.Id = details.Groups["password"].Value;
item.Address = details.Groups["hostname"].Value;
item.Port = details.Groups["port"].Value.ToInt();
return item;
}
private static ProfileItem? ResolveSip002(string result)
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
Address = parsedUrl.IdnHost,
Port = parsedUrl.Port,
};
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
//2022-blake3
if (rawUserInfo.Contains(':'))
{
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{ {
return null; return null;
} }
item.Security = userInfoParts.First(); if (item.Address.Length == 0 || item.Port == 0 || item.Security.Length == 0 || item.Id.Length == 0)
item.Id = Utils.UrlDecode(userInfoParts.Last());
}
else
{
// parse base64 UserInfo
var userInfo = Utils.Base64Decode(rawUserInfo);
var userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{ {
return null; return null;
} }
item.Security = userInfoParts.First();
item.Id = userInfoParts.Last(); item.ConfigType = EConfigType.Shadowsocks;
return item;
} }
var queryParameters = Utils.ParseQueryString(parsedUrl.Query); public static string? ToUri(ProfileItem? item)
if (queryParameters["plugin"] != null)
{ {
//obfs-host exists if (item == null)
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && obfsHost.IsNotEmpty())
{ {
obfsHost = obfsHost?.Replace("obfs-host=", ""); return null;
item.Network = Global.DefaultNetwork; }
item.HeaderType = Global.TcpHeaderHttp; var remark = string.Empty;
item.RequestHost = obfsHost ?? ""; if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new Sip002
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
}
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static ProfileItem? ResolveSSLegacy(string result)
{
var match = UrlFinder.Match(result);
if (!match.Success)
{
return null;
}
ProfileItem item = new();
var base64 = match.Groups["base64"].Value.TrimEnd('/');
var tag = match.Groups["tag"].Value;
if (tag.IsNotEmpty())
{
item.Remarks = Utils.UrlDecode(tag);
}
Match details;
try
{
details = DetailsParser.Match(Utils.Base64Decode(base64));
}
catch (FormatException)
{
return null;
}
if (!details.Success)
{
return null;
}
item.Security = details.Groups["method"].Value;
item.Id = details.Groups["password"].Value;
item.Address = details.Groups["hostname"].Value;
item.Port = details.Groups["port"].Value.ToInt();
return item;
}
private static ProfileItem? ResolveSip002(string result)
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
Address = parsedUrl.IdnHost,
Port = parsedUrl.Port,
};
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
//2022-blake3
if (rawUserInfo.Contains(':'))
{
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
item.Security = userInfoParts.First();
item.Id = Utils.UrlDecode(userInfoParts.Last());
} }
else else
{ {
return null; // parse base64 UserInfo
} var userInfo = Utils.Base64Decode(rawUserInfo);
} var userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
return item;
}
public static List<ProfileItem>? ResolveSip008(string result)
{
//SsSIP008
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(result);
if (lstSsServer?.Count <= 0)
{
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(result);
if (ssSIP008?.servers?.Count > 0)
{
lstSsServer = ssSIP008.servers;
}
}
if (lstSsServer?.Count > 0)
{
List<ProfileItem> lst = [];
foreach (var it in lstSsServer)
{
var ssItem = new ProfileItem()
{ {
Remarks = it.remarks, return null;
Security = it.method, }
Id = it.password, item.Security = userInfoParts.First();
Address = it.server, item.Id = userInfoParts.Last();
Port = it.server_port.ToInt()
};
lst.Add(ssItem);
} }
return lst;
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
if (queryParameters["plugin"] != null)
{
//obfs-host exists
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && obfsHost.IsNotEmpty())
{
obfsHost = obfsHost?.Replace("obfs-host=", "");
item.Network = Global.DefaultNetwork;
item.HeaderType = Global.TcpHeaderHttp;
item.RequestHost = obfsHost ?? "";
}
else
{
return null;
}
}
return item;
}
public static List<ProfileItem>? ResolveSip008(string result)
{
//SsSIP008
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(result);
if (lstSsServer?.Count <= 0)
{
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(result);
if (ssSIP008?.servers?.Count > 0)
{
lstSsServer = ssSIP008.servers;
}
}
if (lstSsServer?.Count > 0)
{
List<ProfileItem> lst = [];
foreach (var it in lstSsServer)
{
var ssItem = new ProfileItem()
{
Remarks = it.remarks,
Security = it.method,
Id = it.password,
Address = it.server,
Port = it.server_port.ToInt()
};
lst.Add(ssItem);
}
return lst;
}
return null;
} }
return null;
} }
} }

View file

@ -1,47 +1,48 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class SingboxFmt : BaseFmt
{ {
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks) public class SingboxFmt : BaseFmt
{ {
var configObjects = JsonUtils.Deserialize<object[]>(strData); public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
if (configObjects is not { Length: > 0 })
{ {
return null; var configObjects = JsonUtils.Deserialize<object[]>(strData);
} if (configObjects is not { Length: > 0 })
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var profileIt = ResolveFull(objectString, subRemarks);
if (profileIt != null)
{ {
lstResult.Add(profileIt); return null;
} }
}
return lstResult;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks) List<ProfileItem> lstResult = [];
{ foreach (var configObject in configObjects)
var config = JsonUtils.ParseJson(strData); {
if (config?["inbounds"] == null var objectString = JsonUtils.Serialize(configObject);
|| config["outbounds"] == null var profileIt = ResolveFull(objectString, subRemarks);
|| config["route"] == null if (profileIt != null)
|| config["dns"] == null) {
{ lstResult.Add(profileIt);
return null; }
}
return lstResult;
} }
var fileName = WriteAllText(strData); public static ProfileItem? ResolveFull(string strData, string? subRemarks)
var profileItem = new ProfileItem
{ {
CoreType = ECoreType.sing_box, var config = JsonUtils.ParseJson(strData);
Address = fileName, if (config?["inbounds"] == null
Remarks = subRemarks ?? "singbox_custom" || config["outbounds"] == null
}; || config["route"] == null
|| config["dns"] == null)
{
return null;
}
return profileItem; var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.sing_box,
Address = fileName,
Remarks = subRemarks ?? "singbox_custom"
};
return profileItem;
}
} }
} }

View file

@ -1,114 +1,115 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class SocksFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public class SocksFmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
var item = ResolveSocksNew(str) ?? ResolveSocks(str); var item = ResolveSocksNew(str) ?? ResolveSocks(str);
if (item == null) if (item == null)
{
return null;
}
if (item.Address.Length == 0 || item.Port == 0)
{
return null;
}
item.ConfigType = EConfigType.SOCKS;
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
//new
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
}
private static ProfileItem? ResolveSocks(string result)
{
ProfileItem item = new()
{
ConfigType = EConfigType.SOCKS
};
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
//remark
var indexRemark = result.IndexOf("#");
if (indexRemark > 0)
{
try
{ {
item.Remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1)); return null;
} }
catch { } if (item.Address.Length == 0 || item.Port == 0)
result = result[..indexRemark]; {
} return null;
//part decode }
var indexS = result.IndexOf("@");
if (indexS > 0) item.ConfigType = EConfigType.SOCKS;
{
} return item;
else
{
result = Utils.Base64Decode(result);
} }
var arr1 = result.Split('@'); public static string? ToUri(ProfileItem? item)
if (arr1.Length != 2)
{ {
return null; if (item == null)
} {
var arr21 = arr1.First().Split(':'); return null;
var indexPort = arr1.Last().LastIndexOf(":"); }
if (arr21.Length != 2 || indexPort < 0) var remark = string.Empty;
{ if (item.Remarks.IsNotEmpty())
return null; {
} remark = "#" + Utils.UrlEncode(item.Remarks);
item.Address = arr1[1][..indexPort]; }
item.Port = arr1[1][(indexPort + 1)..].ToInt(); //new
item.Security = arr21.First(); var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
item.Id = arr21[1]; return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
return item;
}
private static ProfileItem? ResolveSocksNew(string result)
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null)
{
return null;
} }
ProfileItem item = new() private static ProfileItem? ResolveSocks(string result)
{ {
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), ProfileItem item = new()
Address = parsedUrl.IdnHost, {
Port = parsedUrl.Port, ConfigType = EConfigType.SOCKS
}; };
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
//remark
var indexRemark = result.IndexOf("#");
if (indexRemark > 0)
{
try
{
item.Remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
}
catch { }
result = result[..indexRemark];
}
//part decode
var indexS = result.IndexOf("@");
if (indexS > 0)
{
}
else
{
result = Utils.Base64Decode(result);
}
// parse base64 UserInfo var arr1 = result.Split('@');
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); if (arr1.Length != 2)
var userInfo = Utils.Base64Decode(rawUserInfo); {
var userInfoParts = userInfo.Split([':'], 2); return null;
if (userInfoParts.Length == 2) }
{ var arr21 = arr1.First().Split(':');
item.Security = userInfoParts.First(); var indexPort = arr1.Last().LastIndexOf(":");
item.Id = userInfoParts[1]; if (arr21.Length != 2 || indexPort < 0)
{
return null;
}
item.Address = arr1[1][..indexPort];
item.Port = arr1[1][(indexPort + 1)..].ToInt();
item.Security = arr21.First();
item.Id = arr21[1];
return item;
} }
return item; private static ProfileItem? ResolveSocksNew(string result)
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
Address = parsedUrl.IdnHost,
Port = parsedUrl.Port,
};
// parse base64 UserInfo
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
var userInfo = Utils.Base64Decode(rawUserInfo);
var userInfoParts = userInfo.Split([':'], 2);
if (userInfoParts.Length == 2)
{
item.Security = userInfoParts.First();
item.Id = userInfoParts[1];
}
return item;
}
} }
} }

View file

@ -1,47 +1,48 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class TrojanFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public class TrojanFmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; public static ProfileItem? Resolve(string str, out string msg)
ProfileItem item = new()
{ {
ConfigType = EConfigType.Trojan msg = ResUI.ConfigurationFormatIncorrect;
};
var url = Utils.TryUri(str); ProfileItem item = new()
if (url == null) {
{ ConfigType = EConfigType.Trojan
return null; };
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
_ = ResolveStdTransport(query, ref item);
return item;
} }
item.Address = url.IdnHost; public static string? ToUri(ProfileItem? item)
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
_ = ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{ {
return null; if (item == null)
} {
var remark = string.Empty; return null;
if (item.Remarks.IsNotEmpty()) }
{ var remark = string.Empty;
remark = "#" + Utils.UrlEncode(item.Remarks); if (item.Remarks.IsNotEmpty())
} {
var dicQuery = new Dictionary<string, string>(); remark = "#" + Utils.UrlEncode(item.Remarks);
_ = GetStdTransport(item, null, ref dicQuery); }
var dicQuery = new Dictionary<string, string>();
_ = GetStdTransport(item, null, ref dicQuery);
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
}
} }
} }

View file

@ -1,63 +1,64 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class TuicFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public class TuicFmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; public static ProfileItem? Resolve(string str, out string msg)
ProfileItem item = new()
{ {
ConfigType = EConfigType.TUIC msg = ResUI.ConfigurationFormatIncorrect;
};
var url = Utils.TryUri(str); ProfileItem item = new()
if (url == null) {
{ ConfigType = EConfigType.TUIC
return null; };
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
var rawUserInfo = Utils.UrlDecode(url.UserInfo);
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2)
{
item.Id = userInfoParts.First();
item.Security = userInfoParts.Last();
}
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.HeaderType = query["congestion_control"] ?? "";
return item;
} }
item.Address = url.IdnHost; public static string? ToUri(ProfileItem? item)
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
var rawUserInfo = Utils.UrlDecode(url.UserInfo);
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2)
{ {
item.Id = userInfoParts.First(); if (item == null)
item.Security = userInfoParts.Last(); {
} return null;
}
var query = Utils.ParseQueryString(url.Query); var remark = string.Empty;
ResolveStdTransport(query, ref item); if (item.Remarks.IsNotEmpty())
item.HeaderType = query["congestion_control"] ?? ""; {
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
dicQuery.Add("congestion_control", item.HeaderType);
return item; return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
} }
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
dicQuery.Add("congestion_control", item.HeaderType);
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
} }
} }

View file

@ -1,48 +1,49 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class V2rayFmt : BaseFmt
{ {
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks) public class V2rayFmt : BaseFmt
{ {
var configObjects = JsonUtils.Deserialize<object[]>(strData); public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
if (configObjects is not { Length: > 0 })
{ {
return null; var configObjects = JsonUtils.Deserialize<object[]>(strData);
} if (configObjects is not { Length: > 0 })
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var profileIt = ResolveFull(objectString, subRemarks);
if (profileIt != null)
{ {
lstResult.Add(profileIt); return null;
} }
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var profileIt = ResolveFull(objectString, subRemarks);
if (profileIt != null)
{
lstResult.Add(profileIt);
}
}
return lstResult;
} }
return lstResult; public static ProfileItem? ResolveFull(string strData, string? subRemarks)
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
var config = JsonUtils.ParseJson(strData);
if (config?["inbounds"] == null
|| config["outbounds"] == null
|| config["routing"] == null)
{ {
return null; var config = JsonUtils.ParseJson(strData);
if (config?["inbounds"] == null
|| config["outbounds"] == null
|| config["routing"] == null)
{
return null;
}
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = config?["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom"
};
return profileItem;
} }
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = config?["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom"
};
return profileItem;
} }
} }

View file

@ -1,59 +1,60 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class VLESSFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public class VLESSFmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; public static ProfileItem? Resolve(string str, out string msg)
ProfileItem item = new()
{ {
ConfigType = EConfigType.VLESS, msg = ResUI.ConfigurationFormatIncorrect;
Security = Global.None
};
var url = Utils.TryUri(str); ProfileItem item = new()
if (url == null) {
{ ConfigType = EConfigType.VLESS,
return null; Security = Global.None
};
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.Security = query["encryption"] ?? Global.None;
item.StreamSecurity = query["security"] ?? "";
_ = ResolveStdTransport(query, ref item);
return item;
} }
item.Address = url.IdnHost; public static string? ToUri(ProfileItem? item)
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.Security = query["encryption"] ?? Global.None;
item.StreamSecurity = query["security"] ?? "";
_ = ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{ {
return null; if (item == null)
} {
return null;
}
var remark = string.Empty; var remark = string.Empty;
if (item.Remarks.IsNotEmpty()) if (item.Remarks.IsNotEmpty())
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
var dicQuery = new Dictionary<string, string>(); var dicQuery = new Dictionary<string, string>();
if (item.Security.IsNotEmpty()) if (item.Security.IsNotEmpty())
{ {
dicQuery.Add("encryption", item.Security); dicQuery.Add("encryption", item.Security);
} }
else else
{ {
dicQuery.Add("encryption", Global.None); dicQuery.Add("encryption", Global.None);
} }
_ = GetStdTransport(item, Global.None, ref dicQuery); _ = GetStdTransport(item, Global.None, ref dicQuery);
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark); return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
}
} }
} }

View file

@ -1,125 +1,126 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class VmessFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public class VmessFmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; public static ProfileItem? Resolve(string str, out string msg)
ProfileItem? item;
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0)
{ {
item = ResolveStdVmess(str); msg = ResUI.ConfigurationFormatIncorrect;
} ProfileItem? item;
else if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0)
{ {
item = ResolveVmess(str, out msg); item = ResolveStdVmess(str);
} }
return item; else
} {
item = ResolveVmess(str, out msg);
public static string? ToUri(ProfileItem? item) }
{ return item;
if (item == null)
{
return null;
}
var vmessQRCode = new VmessQRCode
{
v = item.ConfigVersion,
ps = item.Remarks.TrimEx(),
add = item.Address,
port = item.Port,
id = item.Id,
aid = item.AlterId,
scy = item.Security,
net = item.Network,
type = item.HeaderType,
host = item.RequestHost,
path = item.Path,
tls = item.StreamSecurity,
sni = item.Sni,
alpn = item.Alpn,
fp = item.Fingerprint
};
var url = JsonUtils.Serialize(vmessQRCode);
url = Utils.Base64Encode(url);
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
return url;
}
private static ProfileItem? ResolveVmess(string result, out string msg)
{
msg = string.Empty;
var item = new ProfileItem
{
ConfigType = EConfigType.VMess
};
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
result = Utils.Base64Decode(result);
var vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
if (vmessQRCode == null)
{
msg = ResUI.FailedConversionConfiguration;
return null;
} }
item.Network = Global.DefaultNetwork; public static string? ToUri(ProfileItem? item)
item.HeaderType = Global.None;
item.ConfigVersion = vmessQRCode.v;
item.Remarks = Utils.ToString(vmessQRCode.ps);
item.Address = Utils.ToString(vmessQRCode.add);
item.Port = vmessQRCode.port;
item.Id = Utils.ToString(vmessQRCode.id);
item.AlterId = vmessQRCode.aid;
item.Security = Utils.ToString(vmessQRCode.scy);
item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
if (vmessQRCode.net.IsNotEmpty())
{ {
item.Network = vmessQRCode.net; if (item == null)
} {
if (vmessQRCode.type.IsNotEmpty()) return null;
{ }
item.HeaderType = vmessQRCode.type; var vmessQRCode = new VmessQRCode
{
v = item.ConfigVersion,
ps = item.Remarks.TrimEx(),
add = item.Address,
port = item.Port,
id = item.Id,
aid = item.AlterId,
scy = item.Security,
net = item.Network,
type = item.HeaderType,
host = item.RequestHost,
path = item.Path,
tls = item.StreamSecurity,
sni = item.Sni,
alpn = item.Alpn,
fp = item.Fingerprint
};
var url = JsonUtils.Serialize(vmessQRCode);
url = Utils.Base64Encode(url);
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
return url;
} }
item.RequestHost = Utils.ToString(vmessQRCode.host); private static ProfileItem? ResolveVmess(string result, out string msg)
item.Path = Utils.ToString(vmessQRCode.path);
item.StreamSecurity = Utils.ToString(vmessQRCode.tls);
item.Sni = Utils.ToString(vmessQRCode.sni);
item.Alpn = Utils.ToString(vmessQRCode.alpn);
item.Fingerprint = Utils.ToString(vmessQRCode.fp);
return item;
}
public static ProfileItem? ResolveStdVmess(string str)
{
var item = new ProfileItem
{ {
ConfigType = EConfigType.VMess, msg = string.Empty;
Security = "auto" var item = new ProfileItem
}; {
ConfigType = EConfigType.VMess
};
var url = Utils.TryUri(str); result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
if (url == null) result = Utils.Base64Decode(result);
{
return null; var vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
if (vmessQRCode == null)
{
msg = ResUI.FailedConversionConfiguration;
return null;
}
item.Network = Global.DefaultNetwork;
item.HeaderType = Global.None;
item.ConfigVersion = vmessQRCode.v;
item.Remarks = Utils.ToString(vmessQRCode.ps);
item.Address = Utils.ToString(vmessQRCode.add);
item.Port = vmessQRCode.port;
item.Id = Utils.ToString(vmessQRCode.id);
item.AlterId = vmessQRCode.aid;
item.Security = Utils.ToString(vmessQRCode.scy);
item.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
if (vmessQRCode.net.IsNotEmpty())
{
item.Network = vmessQRCode.net;
}
if (vmessQRCode.type.IsNotEmpty())
{
item.HeaderType = vmessQRCode.type;
}
item.RequestHost = Utils.ToString(vmessQRCode.host);
item.Path = Utils.ToString(vmessQRCode.path);
item.StreamSecurity = Utils.ToString(vmessQRCode.tls);
item.Sni = Utils.ToString(vmessQRCode.sni);
item.Alpn = Utils.ToString(vmessQRCode.alpn);
item.Fingerprint = Utils.ToString(vmessQRCode.fp);
return item;
} }
item.Address = url.IdnHost; public static ProfileItem? ResolveStdVmess(string str)
item.Port = url.Port; {
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); var item = new ProfileItem
item.Id = Utils.UrlDecode(url.UserInfo); {
ConfigType = EConfigType.VMess,
Security = "auto"
};
var query = Utils.ParseQueryString(url.Query); var url = Utils.TryUri(str);
ResolveStdTransport(query, ref item); if (url == null)
{
return null;
}
return item; item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
return item;
}
} }
} }

View file

@ -1,67 +1,68 @@
namespace ServiceLib.Handler.Fmt; namespace ServiceLib.Handler.Fmt
public class WireguardFmt : BaseFmt
{ {
public static ProfileItem? Resolve(string str, out string msg) public class WireguardFmt : BaseFmt
{ {
msg = ResUI.ConfigurationFormatIncorrect; public static ProfileItem? Resolve(string str, out string msg)
ProfileItem item = new()
{ {
ConfigType = EConfigType.WireGuard msg = ResUI.ConfigurationFormatIncorrect;
};
var url = Utils.TryUri(str); ProfileItem item = new()
if (url == null) {
{ ConfigType = EConfigType.WireGuard
return null; };
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.PublicKey = Utils.UrlDecode(query["publickey"] ?? "");
item.Path = Utils.UrlDecode(query["reserved"] ?? "");
item.RequestHost = Utils.UrlDecode(query["address"] ?? "");
item.ShortId = Utils.UrlDecode(query["mtu"] ?? "");
return item;
} }
item.Address = url.IdnHost; public static string? ToUri(ProfileItem? item)
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.PublicKey = Utils.UrlDecode(query["publickey"] ?? "");
item.Path = Utils.UrlDecode(query["reserved"] ?? "");
item.RequestHost = Utils.UrlDecode(query["address"] ?? "");
item.ShortId = Utils.UrlDecode(query["mtu"] ?? "");
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{ {
return null; if (item == null)
} {
return null;
}
var remark = string.Empty; var remark = string.Empty;
if (item.Remarks.IsNotEmpty()) if (item.Remarks.IsNotEmpty())
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); remark = "#" + Utils.UrlEncode(item.Remarks);
} }
var dicQuery = new Dictionary<string, string>(); var dicQuery = new Dictionary<string, string>();
if (item.PublicKey.IsNotEmpty()) if (item.PublicKey.IsNotEmpty())
{ {
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey)); dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
}
if (item.Path.IsNotEmpty())
{
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
}
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
}
if (item.ShortId.IsNotEmpty())
{
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
}
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
} }
if (item.Path.IsNotEmpty())
{
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
}
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
}
if (item.ShortId.IsNotEmpty())
{
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
}
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
} }
} }

View file

@ -1,43 +1,44 @@
using ReactiveUI; using ReactiveUI;
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public class NoticeHandler
{ {
private static readonly Lazy<NoticeHandler> _instance = new(() => new()); public class NoticeHandler
public static NoticeHandler Instance => _instance.Value;
public void Enqueue(string? content)
{ {
if (content.IsNullOrEmpty()) private static readonly Lazy<NoticeHandler> _instance = new(() => new());
public static NoticeHandler Instance => _instance.Value;
public void Enqueue(string? content)
{ {
return; if (content.IsNullOrEmpty())
{
return;
}
MessageBus.Current.SendMessage(content, EMsgCommand.SendSnackMsg.ToString());
} }
MessageBus.Current.SendMessage(content, EMsgCommand.SendSnackMsg.ToString());
}
public void SendMessage(string? content) public void SendMessage(string? content)
{
if (content.IsNullOrEmpty())
{ {
return; if (content.IsNullOrEmpty())
{
return;
}
MessageBus.Current.SendMessage(content, EMsgCommand.SendMsgView.ToString());
} }
MessageBus.Current.SendMessage(content, EMsgCommand.SendMsgView.ToString());
}
public void SendMessageEx(string? content) public void SendMessageEx(string? content)
{
if (content.IsNullOrEmpty())
{ {
return; if (content.IsNullOrEmpty())
{
return;
}
content = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss} {content}";
SendMessage(content);
} }
content = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss} {content}";
SendMessage(content);
}
public void SendMessageAndEnqueue(string? msg) public void SendMessageAndEnqueue(string? msg)
{ {
Enqueue(msg); Enqueue(msg);
SendMessage(msg); SendMessage(msg);
}
} }
} }

View file

@ -1,114 +1,115 @@
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public class PacHandler
{ {
private static string _configPath; public class PacHandler
private static int _httpPort;
private static int _pacPort;
private static TcpListener? _tcpListener;
private static byte[] _writeContent;
private static bool _isRunning;
private static bool _needRestart = true;
public static async Task Start(string configPath, int httpPort, int pacPort)
{ {
_needRestart = configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning; private static string _configPath;
private static int _httpPort;
private static int _pacPort;
private static TcpListener? _tcpListener;
private static byte[] _writeContent;
private static bool _isRunning;
private static bool _needRestart = true;
_configPath = configPath; public static async Task Start(string configPath, int httpPort, int pacPort)
_httpPort = httpPort;
_pacPort = pacPort;
await InitText();
if (_needRestart)
{ {
Stop(); _needRestart = configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning;
RunListener();
}
}
private static async Task InitText() _configPath = configPath;
{ _httpPort = httpPort;
var path = Path.Combine(_configPath, "pac.txt"); _pacPort = pacPort;
// Delete the old pac file await InitText();
if (File.Exists(path) && Utils.GetFileHash(path).Equals("b590c07280f058ef05d5394aa2f927fe"))
{
File.Delete(path);
}
if (!File.Exists(path)) if (_needRestart)
{
var pac = EmbedUtils.GetEmbedText(Global.PacFileName);
await File.AppendAllTextAsync(path, pac);
}
var pacText =
(await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
sb.AppendLine("Connection:close");
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
sb.AppendLine();
sb.Append(pacText);
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
}
private static void RunListener()
{
_tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{
while (_isRunning)
{ {
try Stop();
{ RunListener();
if (!_tcpListener.Pending())
{
await Task.Delay(10);
continue;
}
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => WriteContent(client));
}
catch
{
// ignored
}
} }
}, TaskCreationOptions.LongRunning);
}
private static void WriteContent(TcpClient client)
{
var stream = client.GetStream();
stream.Write(_writeContent, 0, _writeContent.Length);
stream.Flush();
}
public static void Stop()
{
if (_tcpListener == null)
{
return;
} }
try
private static async Task InitText()
{ {
_isRunning = false; var path = Path.Combine(_configPath, "pac.txt");
_tcpListener.Stop();
_tcpListener = null; // Delete the old pac file
if (File.Exists(path) && Utils.GetFileHash(path).Equals("b590c07280f058ef05d5394aa2f927fe"))
{
File.Delete(path);
}
if (!File.Exists(path))
{
var pac = EmbedUtils.GetEmbedText(Global.PacFileName);
await File.AppendAllTextAsync(path, pac);
}
var pacText =
(await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
sb.AppendLine("Connection:close");
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
sb.AppendLine();
sb.Append(pacText);
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
} }
catch
private static void RunListener()
{ {
// ignored _tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{
while (_isRunning)
{
try
{
if (!_tcpListener.Pending())
{
await Task.Delay(10);
continue;
}
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => WriteContent(client));
}
catch
{
// ignored
}
}
}, TaskCreationOptions.LongRunning);
}
private static void WriteContent(TcpClient client)
{
var stream = client.GetStream();
stream.Write(_writeContent, 0, _writeContent.Length);
stream.Flush();
}
public static void Stop()
{
if (_tcpListener == null)
{
return;
}
try
{
_isRunning = false;
_tcpListener.Stop();
_tcpListener = null;
}
catch
{
// ignored
}
} }
} }
} }

View file

@ -2,180 +2,181 @@ using System.Collections.Concurrent;
//using System.Reactive.Linq; //using System.Reactive.Linq;
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public class ProfileExHandler
{ {
private static readonly Lazy<ProfileExHandler> _instance = new(() => new()); public class ProfileExHandler
private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
private readonly Queue<string> _queIndexIds = new();
public static ProfileExHandler Instance => _instance.Value;
private static readonly string _tag = "ProfileExHandler";
public ProfileExHandler()
{ {
//Init(); private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
} private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
private readonly Queue<string> _queIndexIds = new();
public static ProfileExHandler Instance => _instance.Value;
private static readonly string _tag = "ProfileExHandler";
public async Task Init() public ProfileExHandler()
{
await InitData();
}
public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
{
return await Task.FromResult(_lstProfileEx);
}
private async Task InitData()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
_lstProfileEx = new(await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync());
}
private void IndexIdEnqueue(string indexId)
{
if (indexId.IsNotEmpty() && !_queIndexIds.Contains(indexId))
{ {
_queIndexIds.Enqueue(indexId); //Init();
} }
}
private async Task SaveQueueIndexIds() public async Task Init()
{
var cnt = _queIndexIds.Count;
if (cnt > 0)
{ {
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync(); await InitData();
List<ProfileExItem> lstInserts = []; }
List<ProfileExItem> lstUpdates = [];
for (var i = 0; i < cnt; i++) public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
{
return await Task.FromResult(_lstProfileEx);
}
private async Task InitData()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
_lstProfileEx = new(await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync());
}
private void IndexIdEnqueue(string indexId)
{
if (indexId.IsNotEmpty() && !_queIndexIds.Contains(indexId))
{ {
var id = _queIndexIds.Dequeue(); _queIndexIds.Enqueue(indexId);
var item = lstExists.FirstOrDefault(t => t.IndexId == id); }
var itemNew = _lstProfileEx?.FirstOrDefault(t => t.IndexId == id); }
if (itemNew is null)
private async Task SaveQueueIndexIds()
{
var cnt = _queIndexIds.Count;
if (cnt > 0)
{
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync();
List<ProfileExItem> lstInserts = [];
List<ProfileExItem> lstUpdates = [];
for (var i = 0; i < cnt; i++)
{ {
continue; var id = _queIndexIds.Dequeue();
var item = lstExists.FirstOrDefault(t => t.IndexId == id);
var itemNew = _lstProfileEx?.FirstOrDefault(t => t.IndexId == id);
if (itemNew is null)
{
continue;
}
if (item is not null)
{
lstUpdates.Add(itemNew);
}
else
{
lstInserts.Add(itemNew);
}
} }
if (item is not null) try
{ {
lstUpdates.Add(itemNew); if (lstInserts.Count > 0)
{
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
}
if (lstUpdates.Count > 0)
{
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
}
} }
else catch (Exception ex)
{ {
lstInserts.Add(itemNew); Logging.SaveLog(_tag, ex);
} }
} }
}
private ProfileExItem AddProfileEx(string indexId)
{
var profileEx = new ProfileExItem()
{
IndexId = indexId,
Delay = 0,
Speed = 0,
Sort = 0,
Message = string.Empty
};
_lstProfileEx.Add(profileEx);
IndexIdEnqueue(indexId);
return profileEx;
}
private ProfileExItem GetProfileExItem(string? indexId)
{
return _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId) ?? AddProfileEx(indexId);
}
public async Task ClearAll()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem ");
_lstProfileEx = new();
}
public async Task SaveTo()
{
try try
{ {
if (lstInserts.Count > 0) await SaveQueueIndexIds();
{
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
}
if (lstUpdates.Count > 0)
{
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
} }
} }
}
private ProfileExItem AddProfileEx(string indexId) public void SetTestDelay(string indexId, int delay)
{
var profileEx = new ProfileExItem()
{ {
IndexId = indexId, var profileEx = GetProfileExItem(indexId);
Delay = 0,
Speed = 0,
Sort = 0,
Message = string.Empty
};
_lstProfileEx.Add(profileEx);
IndexIdEnqueue(indexId);
return profileEx;
}
private ProfileExItem GetProfileExItem(string? indexId) profileEx.Delay = delay;
{ IndexIdEnqueue(indexId);
return _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId) ?? AddProfileEx(indexId);
}
public async Task ClearAll()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem ");
_lstProfileEx = new();
}
public async Task SaveTo()
{
try
{
await SaveQueueIndexIds();
} }
catch (Exception ex)
public void SetTestSpeed(string indexId, decimal speed)
{ {
Logging.SaveLog(_tag, ex); var profileEx = GetProfileExItem(indexId);
profileEx.Speed = speed;
IndexIdEnqueue(indexId);
} }
}
public void SetTestDelay(string indexId, int delay) public void SetTestMessage(string indexId, string message)
{
var profileEx = GetProfileExItem(indexId);
profileEx.Delay = delay;
IndexIdEnqueue(indexId);
}
public void SetTestSpeed(string indexId, decimal speed)
{
var profileEx = GetProfileExItem(indexId);
profileEx.Speed = speed;
IndexIdEnqueue(indexId);
}
public void SetTestMessage(string indexId, string message)
{
var profileEx = GetProfileExItem(indexId);
profileEx.Message = message;
IndexIdEnqueue(indexId);
}
public void SetSort(string indexId, int sort)
{
var profileEx = GetProfileExItem(indexId);
profileEx.Sort = sort;
IndexIdEnqueue(indexId);
}
public int GetSort(string indexId)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
if (profileEx == null)
{ {
return 0; var profileEx = GetProfileExItem(indexId);
}
return profileEx.Sort;
}
public int GetMaxSort() profileEx.Message = message;
{ IndexIdEnqueue(indexId);
if (_lstProfileEx.Count <= 0) }
{
return 0; public void SetSort(string indexId, int sort)
{
var profileEx = GetProfileExItem(indexId);
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);
} }
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
} }
} }

View file

@ -1,163 +1,164 @@
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public class StatisticsHandler
{ {
private static readonly Lazy<StatisticsHandler> instance = new(() => new()); public class StatisticsHandler
public static StatisticsHandler Instance => instance.Value;
private Config _config;
private ServerStatItem? _serverStatItem;
private List<ServerStatItem> _lstServerStat;
private Action<ServerSpeedItem>? _updateFunc;
private StatisticsXrayService? _statisticsXray;
private StatisticsSingboxService? _statisticsSingbox;
private static readonly string _tag = "StatisticsHandler";
public List<ServerStatItem> ServerStat => _lstServerStat;
public async Task Init(Config config, Action<ServerSpeedItem> updateFunc)
{ {
_config = config; private static readonly Lazy<StatisticsHandler> instance = new(() => new());
_updateFunc = updateFunc; public static StatisticsHandler Instance => instance.Value;
if (config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
{
await InitData();
_statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler); private Config _config;
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler); private ServerStatItem? _serverStatItem;
} private List<ServerStatItem> _lstServerStat;
} private Action<ServerSpeedItem>? _updateFunc;
public void Close() private StatisticsXrayService? _statisticsXray;
{ private StatisticsSingboxService? _statisticsSingbox;
try private static readonly string _tag = "StatisticsHandler";
{ public List<ServerStatItem> ServerStat => _lstServerStat;
_statisticsXray?.Close();
_statisticsSingbox?.Close();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public async Task ClearAllServerStatistics() public async Task Init(Config config, Action<ServerSpeedItem> updateFunc)
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem ");
_serverStatItem = null;
_lstServerStat = new();
}
public async Task SaveTo()
{
try
{ {
if (_lstServerStat != null) _config = config;
_updateFunc = updateFunc;
if (config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
{ {
await SQLiteHelper.Instance.UpdateAllAsync(_lstServerStat); await InitData();
_statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler);
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler);
} }
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public async Task CloneServerStatItem(string indexId, string toIndexId) public void Close()
{
if (_lstServerStat == null)
{ {
return; try
{
_statisticsXray?.Close();
_statisticsSingbox?.Close();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
} }
if (indexId == toIndexId) public async Task ClearAllServerStatistics()
{
return;
}
var stat = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
if (stat == null)
{
return;
}
var toStat = JsonUtils.DeepCopy(stat);
toStat.IndexId = toIndexId;
await SQLiteHelper.Instance.ReplaceAsync(toStat);
_lstServerStat.Add(toStat);
}
private async Task InitData()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
long ticks = DateTime.Now.Date.Ticks;
await SQLiteHelper.Instance.ExecuteAsync($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}");
_lstServerStat = await SQLiteHelper.Instance.TableAsync<ServerStatItem>().ToListAsync();
}
private void UpdateServerStatHandler(ServerSpeedItem server)
{
_ = UpdateServerStat(server);
}
private async Task UpdateServerStat(ServerSpeedItem server)
{
await GetServerStatItem(_config.IndexId);
if (_serverStatItem is null)
{
return;
}
if (server.ProxyUp != 0 || server.ProxyDown != 0)
{
_serverStatItem.TodayUp += server.ProxyUp;
_serverStatItem.TodayDown += server.ProxyDown;
_serverStatItem.TotalUp += server.ProxyUp;
_serverStatItem.TotalDown += server.ProxyDown;
}
server.IndexId = _config.IndexId;
server.TodayUp = _serverStatItem.TodayUp;
server.TodayDown = _serverStatItem.TodayDown;
server.TotalUp = _serverStatItem.TotalUp;
server.TotalDown = _serverStatItem.TotalDown;
_updateFunc?.Invoke(server);
}
private async Task GetServerStatItem(string indexId)
{
long ticks = DateTime.Now.Date.Ticks;
if (_serverStatItem != null && _serverStatItem.IndexId != indexId)
{ {
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem ");
_serverStatItem = null; _serverStatItem = null;
_lstServerStat = new();
} }
if (_serverStatItem == null) public async Task SaveTo()
{ {
_serverStatItem = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId); try
{
if (_lstServerStat != null)
{
await SQLiteHelper.Instance.UpdateAllAsync(_lstServerStat);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public async Task CloneServerStatItem(string indexId, string toIndexId)
{
if (_lstServerStat == null)
{
return;
}
if (indexId == toIndexId)
{
return;
}
var stat = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
if (stat == null)
{
return;
}
var toStat = JsonUtils.DeepCopy(stat);
toStat.IndexId = toIndexId;
await SQLiteHelper.Instance.ReplaceAsync(toStat);
_lstServerStat.Add(toStat);
}
private async Task InitData()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
long ticks = DateTime.Now.Date.Ticks;
await SQLiteHelper.Instance.ExecuteAsync($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}");
_lstServerStat = await SQLiteHelper.Instance.TableAsync<ServerStatItem>().ToListAsync();
}
private void UpdateServerStatHandler(ServerSpeedItem server)
{
_ = UpdateServerStat(server);
}
private async Task UpdateServerStat(ServerSpeedItem server)
{
await GetServerStatItem(_config.IndexId);
if (_serverStatItem is null)
{
return;
}
if (server.ProxyUp != 0 || server.ProxyDown != 0)
{
_serverStatItem.TodayUp += server.ProxyUp;
_serverStatItem.TodayDown += server.ProxyDown;
_serverStatItem.TotalUp += server.ProxyUp;
_serverStatItem.TotalDown += server.ProxyDown;
}
server.IndexId = _config.IndexId;
server.TodayUp = _serverStatItem.TodayUp;
server.TodayDown = _serverStatItem.TodayDown;
server.TotalUp = _serverStatItem.TotalUp;
server.TotalDown = _serverStatItem.TotalDown;
_updateFunc?.Invoke(server);
}
private async Task GetServerStatItem(string indexId)
{
long ticks = DateTime.Now.Date.Ticks;
if (_serverStatItem != null && _serverStatItem.IndexId != indexId)
{
_serverStatItem = null;
}
if (_serverStatItem == null) if (_serverStatItem == null)
{ {
_serverStatItem = new ServerStatItem _serverStatItem = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
if (_serverStatItem == null)
{ {
IndexId = indexId, _serverStatItem = new ServerStatItem
TotalUp = 0, {
TotalDown = 0, IndexId = indexId,
TodayUp = 0, TotalUp = 0,
TodayDown = 0, TotalDown = 0,
DateNow = ticks TodayUp = 0,
}; TodayDown = 0,
await SQLiteHelper.Instance.ReplaceAsync(_serverStatItem); DateNow = ticks
_lstServerStat.Add(_serverStatItem); };
await SQLiteHelper.Instance.ReplaceAsync(_serverStatItem);
_lstServerStat.Add(_serverStatItem);
}
}
if (_serverStatItem.DateNow != ticks)
{
_serverStatItem.TodayUp = 0;
_serverStatItem.TodayDown = 0;
_serverStatItem.DateNow = ticks;
} }
} }
if (_serverStatItem.DateNow != ticks)
{
_serverStatItem.TodayUp = 0;
_serverStatItem.TodayDown = 0;
_serverStatItem.DateNow = ticks;
}
} }
} }

View file

@ -1,32 +1,33 @@
namespace ServiceLib.Handler.SysProxy; namespace ServiceLib.Handler.SysProxy
public class ProxySettingLinux
{ {
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh"; public class ProxySettingLinux
public static async Task SetProxy(string host, int port, string exceptions)
{ {
List<string> args = ["manual", host, port.ToString(), exceptions]; private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
await ExecCmd(args);
}
public static async Task UnsetProxy() public static async Task SetProxy(string host, int port, string exceptions)
{
List<string> args = ["none"];
await ExecCmd(args);
}
private static async Task ExecCmd(List<string> args)
{
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{ {
var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName); List<string> args = ["manual", host, port.ToString(), exceptions];
await File.AppendAllTextAsync(fileName, contents); await ExecCmd(args);
await Utils.SetLinuxChmod(fileName);
} }
await Utils.GetCliWrapOutput(fileName, args); public static async Task UnsetProxy()
{
List<string> args = ["none"];
await ExecCmd(args);
}
private static async Task ExecCmd(List<string> args)
{
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{
var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName);
await File.AppendAllTextAsync(fileName, contents);
await Utils.SetLinuxChmod(fileName);
}
await Utils.GetCliWrapOutput(fileName, args);
}
} }
} }

View file

@ -1,37 +1,38 @@
namespace ServiceLib.Handler.SysProxy; namespace ServiceLib.Handler.SysProxy
public class ProxySettingOSX
{ {
private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh"; public class ProxySettingOSX
public static async Task SetProxy(string host, int port, string exceptions)
{ {
List<string> args = ["set", host, port.ToString()]; private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
if (exceptions.IsNotEmpty())
public static async Task SetProxy(string host, int port, string exceptions)
{ {
args.AddRange(exceptions.Split(',')); List<string> args = ["set", host, port.ToString()];
if (exceptions.IsNotEmpty())
{
args.AddRange(exceptions.Split(','));
}
await ExecCmd(args);
} }
await ExecCmd(args); public static async Task UnsetProxy()
}
public static async Task UnsetProxy()
{
List<string> args = ["clear"];
await ExecCmd(args);
}
private static async Task ExecCmd(List<string> args)
{
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{ {
var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName); List<string> args = ["clear"];
await File.AppendAllTextAsync(fileName, contents); await ExecCmd(args);
await Utils.SetLinuxChmod(fileName);
} }
await Utils.GetCliWrapOutput(fileName, args); private static async Task ExecCmd(List<string> args)
{
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
if (!File.Exists(fileName))
{
var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName);
await File.AppendAllTextAsync(fileName, contents);
await Utils.SetLinuxChmod(fileName);
}
await Utils.GetCliWrapOutput(fileName, args);
}
} }
} }

View file

@ -1,359 +1,360 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption; using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
namespace ServiceLib.Handler.SysProxy; namespace ServiceLib.Handler.SysProxy
public class ProxySettingWindows
{ {
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings"; public class ProxySettingWindows
private static bool SetProxyFallback(string? strProxy, string? exceptions, int type)
{ {
if (type == 1) private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
{
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
}
if (type == 2)
{
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 1);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
}
else if (type == 4)
{
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
}
return true;
}
/// <summary> private static bool SetProxyFallback(string? strProxy, string? exceptions, int type)
// set to use no proxy
/// </summary>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
public static bool UnsetProxy()
{
return SetProxy(null, null, 1);
}
/// <summary>
/// Set system proxy settings
/// </summary>
/// <param name="strProxy"> proxy address</param>
/// <param name="exceptions">exception addresses that do not use proxy</param>
/// <param name="type">type of proxy defined in PerConnFlags
/// PROXY_TYPE_DIRECT = 0x00000001, // direct connection (no proxy)
/// PROXY_TYPE_PROXY = 0x00000002, // via named proxy
/// PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy script URL
/// PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
/// </param>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
/// <returns>true: one of connection is successfully updated proxy settings</returns>
public static bool SetProxy(string? strProxy, string? exceptions, int type)
{
try
{ {
// set proxy for LAN if (type == 1)
var result = SetConnectionProxy(null, strProxy, exceptions, type);
// set proxy for dial up connections
var connections = EnumerateRasEntries();
foreach (var connection in connections)
{ {
result |= SetConnectionProxy(connection, strProxy, exceptions, type); WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
} }
return result; if (type == 2)
}
catch
{
_ = SetProxyFallback(strProxy, exceptions, type);
return false;
}
}
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
{
var list = new InternetPerConnOptionList();
var optionCount = 1;
if (type == 1) // No proxy
{
optionCount = 1;
}
else if (type is 2 or 4) // named proxy or autoproxy script URL
{
optionCount = exceptions.IsNullOrEmpty() ? 2 : 3;
}
var m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
var m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
if (type == 2) // named proxy
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
}
else if (type == 4) // autoproxy script url
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL);
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
}
var options = new InternetConnectionOption[optionCount];
// USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
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); // !! remember to deallocate memory 1
// except for these addresses ...
if (optionCount > 2)
{ {
options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS; WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 1);
options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions); // !! remember to deallocate memory 2 WindowsUtils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
}
else if (type == 4)
{
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
}
return true;
}
/// <summary>
// set to use no proxy
/// </summary>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
public static bool UnsetProxy()
{
return SetProxy(null, null, 1);
}
/// <summary>
/// Set system proxy settings
/// </summary>
/// <param name="strProxy"> proxy address</param>
/// <param name="exceptions">exception addresses that do not use proxy</param>
/// <param name="type">type of proxy defined in PerConnFlags
/// PROXY_TYPE_DIRECT = 0x00000001, // direct connection (no proxy)
/// PROXY_TYPE_PROXY = 0x00000002, // via named proxy
/// PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy script URL
/// PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
/// </param>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
/// <returns>true: one of connection is successfully updated proxy settings</returns>
public static bool SetProxy(string? strProxy, string? exceptions, int type)
{
try
{
// set proxy for LAN
var result = SetConnectionProxy(null, strProxy, exceptions, type);
// set proxy for dial up connections
var connections = EnumerateRasEntries();
foreach (var connection in connections)
{
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
}
return result;
}
catch
{
_ = SetProxyFallback(strProxy, exceptions, type);
return false;
} }
} }
// default stuff private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
list.dwSize = Marshal.SizeOf(list);
if (connectionName != null)
{ {
list.szConnection = Marshal.StringToHGlobalAuto(connectionName); // !! remember to deallocate memory 3 var list = new InternetPerConnOptionList();
}
else
{
list.szConnection = nint.Zero;
}
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
var optSize = Marshal.SizeOf(typeof(InternetConnectionOption)); var optionCount = 1;
// make a pointer out of all that ... if (type == 1) // No proxy
var optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
// copy the array over into that spot in memory ...
for (var i = 0; i < options.Length; ++i)
{
if (Environment.Is64BitOperatingSystem)
{ {
var opt = new nint(optionsPtr.ToInt64() + (i * optSize)); optionCount = 1;
Marshal.StructureToPtr(options[i], opt, false); }
else if (type is 2 or 4) // named proxy or autoproxy script URL
{
optionCount = exceptions.IsNullOrEmpty() ? 2 : 3;
}
var m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
var m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
if (type == 2) // named proxy
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
}
else if (type == 4) // autoproxy script url
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL);
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
}
var options = new InternetConnectionOption[optionCount];
// USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
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); // !! remember to deallocate memory 1
// 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); // !! remember to deallocate memory 2
}
}
// default stuff
list.dwSize = Marshal.SizeOf(list);
if (connectionName != null)
{
list.szConnection = Marshal.StringToHGlobalAuto(connectionName); // !! remember to deallocate memory 3
} }
else else
{ {
var opt = new nint(optionsPtr.ToInt32() + (i * optSize)); list.szConnection = nint.Zero;
Marshal.StructureToPtr(options[i], opt, false);
} }
} list.dwOptionCount = options.Length;
list.dwOptionError = 0;
list.options = optionsPtr; var optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
// and then make a pointer out of the whole list var optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
var ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5 // copy the array over into that spot in memory ...
Marshal.StructureToPtr(list, ipcoListPtr, false); for (var i = 0; i < options.Length; ++i)
// and finally, call the API method!
var isSuccess = NativeMethods.InternetSetOption(nint.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize);
var returnvalue = 0; // ERROR_SUCCESS
if (!isSuccess)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastPInvokeError();
}
else
{
// Notify the system that the registry settings have been changed and cause them to be refreshed
_ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
_ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
}
// FREE the data ASAP
if (list.szConnection != nint.Zero)
{
Marshal.FreeHGlobal(list.szConnection); // release mem 3
}
if (optionCount > 1)
{
Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1
if (optionCount > 2)
{ {
Marshal.FreeHGlobal(options[2].m_Value.m_StringPtr); // release mem 2 if (Environment.Is64BitOperatingSystem)
{
var opt = new nint(optionsPtr.ToInt64() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
else
{
var opt = new nint(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
} }
}
Marshal.FreeCoTaskMem(optionsPtr); // release mem 4
Marshal.FreeCoTaskMem(ipcoListPtr); // release mem 5
if (returnvalue != 0)
{
// throw the error codes, they might be helpful
throw new ApplicationException($"Set Internet Proxy failed with error code: {Marshal.GetLastWin32Error()}");
}
return true; list.options = optionsPtr;
}
/// <summary> // and then make a pointer out of the whole list
/// Retrieve list of connections including LAN and WAN to support PPPoE connection var ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
/// </summary> Marshal.StructureToPtr(list, ipcoListPtr, false);
/// <returns>A list of RAS connection names. May be empty list if no dial up connection.</returns>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
private static IEnumerable<string> EnumerateRasEntries()
{
var entries = 0;
// attempt to query with 1 entry buffer
var rasEntryNames = new RASENTRYNAME[1];
var bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
var result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries); // and finally, call the API method!
// increase buffer if the buffer is not large enough var isSuccess = NativeMethods.InternetSetOption(nint.Zero,
if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL) InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
{ ipcoListPtr, list.dwSize);
rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf(typeof(RASENTRYNAME))]; var returnvalue = 0; // ERROR_SUCCESS
for (var i = 0; i < rasEntryNames.Length; i++) if (!isSuccess)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastPInvokeError();
}
else
{ {
rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME)); // Notify the system that the registry settings have been changed and cause them to be refreshed
_ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
_ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
} }
result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries); // FREE the data ASAP
} if (list.szConnection != nint.Zero)
if (result == 0)
{
var entryNames = new List<string>();
for (var i = 0; i < entries; i++)
{ {
entryNames.Add(rasEntryNames[i].szEntryName); Marshal.FreeHGlobal(list.szConnection); // release mem 3
}
if (optionCount > 1)
{
Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1
if (optionCount > 2)
{
Marshal.FreeHGlobal(options[2].m_Value.m_StringPtr); // release mem 2
}
}
Marshal.FreeCoTaskMem(optionsPtr); // release mem 4
Marshal.FreeCoTaskMem(ipcoListPtr); // release mem 5
if (returnvalue != 0)
{
// throw the error codes, they might be helpful
throw new ApplicationException($"Set Internet Proxy failed with error code: {Marshal.GetLastWin32Error()}");
} }
return entryNames; return true;
}
throw new ApplicationException($"RasEnumEntries failed with error code: {result}");
}
#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 nint 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 nint 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));
} }
// Nested Types /// <summary>
[StructLayout(LayoutKind.Explicit)] /// Retrieve list of connections including LAN and WAN to support PPPoE connection
public struct InternetConnectionOptionValue /// </summary>
/// <returns>A list of RAS connection names. May be empty list if no dial up connection.</returns>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
private static IEnumerable<string> EnumerateRasEntries()
{ {
// Fields var entries = 0;
[FieldOffset(0)] // attempt to query with 1 entry buffer
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime; var rasEntryNames = new RASENTRYNAME[1];
var bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
[FieldOffset(0)] var result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
public int m_Int; // increase buffer if the buffer is not large enough
if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL)
{
rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf(typeof(RASENTRYNAME))];
for (var i = 0; i < rasEntryNames.Length; i++)
{
rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
}
[FieldOffset(0)] result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
public nint m_StringPtr; }
if (result == 0)
{
var entryNames = new List<string>();
for (var i = 0; i < entries; i++)
{
entryNames.Add(rasEntryNames[i].szEntryName);
}
return entryNames;
}
throw new ApplicationException($"RasEnumEntries failed with error code: {result}");
}
#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 nint 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 nint options;
} }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct RASENTRYNAME public struct InternetConnectionOption
{ {
public int dwSize; private static readonly int Size;
public PerConnOption m_Option;
public InternetConnectionOptionValue m_Value;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)] static InternetConnectionOption()
public string szEntryName; {
Size = Marshal.SizeOf(typeof(InternetConnectionOption));
}
public int dwFlags; // Nested Types
[StructLayout(LayoutKind.Explicit)]
public struct InternetConnectionOptionValue
{
// Fields
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH + 1)] [FieldOffset(0)]
public string szPhonebookPath; public int m_Int;
[FieldOffset(0)]
public nint m_StringPtr;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct RASENTRYNAME
{
public int dwSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
public string szEntryName;
public int dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH + 1)]
public string szPhonebookPath;
}
// Constants
public const int RAS_MaxEntryName = 256;
public const int MAX_PATH = 260; // Standard MAX_PATH value in Windows
} }
// Constants #endregion WinInet structures
public const int RAS_MaxEntryName = 256;
public const int MAX_PATH = 260; // Standard MAX_PATH value in Windows #region WinInet enums
}
#endregion WinInet structures //
// options manifests for Internet{Query|Set}Option
//
public enum InternetOption : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
INTERNET_OPTION_REFRESH = 37,
INTERNET_OPTION_SETTINGS_CHANGED = 39
}
#region WinInet enums //
// 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.
}
// //
// options manifests for Internet{Query|Set}Option // PER_CONN_FLAGS
// //
public enum InternetOption : uint [Flags]
{ public enum PerConnFlags
INTERNET_OPTION_PER_CONNECTION_OPTION = 75, {
INTERNET_OPTION_REFRESH = 37, PROXY_TYPE_DIRECT = 0x00000001, // direct to net
INTERNET_OPTION_SETTINGS_CHANGED = 39 PROXY_TYPE_PROXY = 0x00000002, // via named proxy
} PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
// public enum ErrorCode : uint
// Options used in INTERNET_PER_CONN_OPTON struct {
// ERROR_BUFFER_TOO_SMALL = 603,
public enum PerConnOption ERROR_INVALID_SIZE = 632
{ }
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.
}
// #endregion WinInet enums
// 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
}
public enum ErrorCode : uint internal static class NativeMethods
{ {
ERROR_BUFFER_TOO_SMALL = 603, [DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
ERROR_INVALID_SIZE = 632 [return: MarshalAs(UnmanagedType.Bool)]
} public static extern bool InternetSetOption(nint hInternet, InternetOption dwOption, nint lpBuffer, int dwBufferLength);
#endregion WinInet enums [DllImport("Rasapi32.dll", CharSet = CharSet.Auto)]
public static extern uint RasEnumEntries(
internal static class NativeMethods string? reserved, // Reserved, must be null
{ string? lpszPhonebook, // Pointer to full path and filename of phone-book file. If this parameter is NULL, the entries are enumerated from all the remote access phone-book files
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)] [In, Out] RASENTRYNAME[]? lprasentryname, // Buffer to receive RAS entry names
[return: MarshalAs(UnmanagedType.Bool)] ref int lpcb, // Size of the buffer
public static extern bool InternetSetOption(nint hInternet, InternetOption dwOption, nint lpBuffer, int dwBufferLength); ref int lpcEntries // Number of entries written to the buffer
);
[DllImport("Rasapi32.dll", CharSet = CharSet.Auto)] }
public static extern uint RasEnumEntries(
string? reserved, // Reserved, must be null
string? lpszPhonebook, // Pointer to full path and filename of phone-book file. If this parameter is NULL, the entries are enumerated from all the remote access phone-book files
[In, Out] RASENTRYNAME[]? lprasentryname, // Buffer to receive RAS entry names
ref int lpcb, // Size of the buffer
ref int lpcEntries // Number of entries written to the buffer
);
} }
} }

View file

@ -1,98 +1,99 @@
namespace ServiceLib.Handler.SysProxy; namespace ServiceLib.Handler.SysProxy
public static class SysProxyHandler
{ {
private static readonly string _tag = "SysProxyHandler"; public static class SysProxyHandler
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
{ {
var type = config.SystemProxyItem.SysProxyType; private static readonly string _tag = "SysProxyHandler";
if (forceDisable && type != ESysProxyType.Unchanged) public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
{ {
type = ESysProxyType.ForcedClear; var type = config.SystemProxyItem.SysProxyType;
}
try if (forceDisable && type != ESysProxyType.Unchanged)
{
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (port <= 0)
{ {
return false; type = ESysProxyType.ForcedClear;
} }
switch (type)
try
{ {
case ESysProxyType.ForcedChange when Utils.IsWindows(): var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
{ var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
GetWindowsProxyString(config, port, out var strProxy, out var strExceptions); if (port <= 0)
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2); {
return false;
}
switch (type)
{
case ESysProxyType.ForcedChange when Utils.IsWindows():
{
GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
break;
}
case ESysProxyType.ForcedChange when Utils.IsLinux():
await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
break; break;
}
case ESysProxyType.ForcedChange when Utils.IsLinux():
await ProxySettingLinux.SetProxy(Global.Loopback, port, exceptions);
break;
case ESysProxyType.ForcedChange when Utils.IsOSX(): case ESysProxyType.ForcedChange when Utils.IsOSX():
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions); await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
break; break;
case ESysProxyType.ForcedClear when Utils.IsWindows(): case ESysProxyType.ForcedClear when Utils.IsWindows():
ProxySettingWindows.UnsetProxy(); ProxySettingWindows.UnsetProxy();
break; break;
case ESysProxyType.ForcedClear when Utils.IsLinux(): case ESysProxyType.ForcedClear when Utils.IsLinux():
await ProxySettingLinux.UnsetProxy(); await ProxySettingLinux.UnsetProxy();
break; break;
case ESysProxyType.ForcedClear when Utils.IsOSX(): case ESysProxyType.ForcedClear when Utils.IsOSX():
await ProxySettingOSX.UnsetProxy(); await ProxySettingOSX.UnsetProxy();
break; break;
case ESysProxyType.Pac when Utils.IsWindows(): case ESysProxyType.Pac when Utils.IsWindows():
await SetWindowsProxyPac(port); await SetWindowsProxyPac(port);
break; break;
}
if (type != ESysProxyType.Pac && Utils.IsWindows())
{
PacHandler.Stop();
}
} }
catch (Exception ex)
if (type != ESysProxyType.Pac && Utils.IsWindows())
{ {
PacHandler.Stop(); Logging.SaveLog(_tag, ex);
}
return true;
}
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions)
{
strExceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (config.SystemProxyItem.NotProxyLocalAddress)
{
strExceptions = $"<local>;{strExceptions}";
}
strProxy = string.Empty;
if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty())
{
strProxy = $"{Global.Loopback}:{port}";
}
else
{
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
.Replace("{ip}", Global.Loopback)
.Replace("{http_port}", port.ToString())
.Replace("{socks_port}", port.ToString());
} }
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return true;
}
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions) private static async Task SetWindowsProxyPac(int port)
{
strExceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (config.SystemProxyItem.NotProxyLocalAddress)
{ {
strExceptions = $"<local>;{strExceptions}"; var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac);
} await PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
strProxy = string.Empty; ProxySettingWindows.SetProxy(strProxy, "", 4);
if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty())
{
strProxy = $"{Global.Loopback}:{port}";
}
else
{
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
.Replace("{ip}", Global.Loopback)
.Replace("{http_port}", port.ToString())
.Replace("{socks_port}", port.ToString());
} }
} }
}
private static async Task SetWindowsProxyPac(int port)
{
var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac);
await PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
ProxySettingWindows.SetProxy(strProxy, "", 4);
}
}

View file

@ -1,97 +1,98 @@
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public class TaskHandler
{ {
private static readonly Lazy<TaskHandler> _instance = new(() => new()); public class TaskHandler
public static TaskHandler Instance => _instance.Value;
public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
{ {
Task.Run(() => ScheduledTasks(config, updateFunc)); private static readonly Lazy<TaskHandler> _instance = new(() => new());
} public static TaskHandler Instance => _instance.Value;
private async Task ScheduledTasks(Config config, Action<bool, string> updateFunc) public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
{
Logging.SaveLog("Setup Scheduled Tasks");
var numOfExecuted = 1;
while (true)
{ {
//1 minute Task.Run(() => ScheduledTasks(config, updateFunc));
await Task.Delay(1000 * 60);
//Execute once 1 minute
await UpdateTaskRunSubscription(config, updateFunc);
//Execute once 20 minute
if (numOfExecuted % 20 == 0)
{
//Logging.SaveLog("Execute save config");
await ConfigHandler.SaveConfig(config);
await ProfileExHandler.Instance.SaveTo();
}
//Execute once 1 hour
if (numOfExecuted % 60 == 0)
{
//Logging.SaveLog("Execute delete expired files");
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
//Check once 1 hour
await UpdateTaskRunGeo(config, numOfExecuted / 60, updateFunc);
}
numOfExecuted++;
}
}
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc)
{
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = (await AppHandler.Instance.SubItems())?
.Where(t => t.AutoUpdateInterval > 0)
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
.ToList();
if (lstSubs is not { Count: > 0 })
{
return;
} }
Logging.SaveLog("Execute update subscription"); private async Task ScheduledTasks(Config config, Action<bool, string> updateFunc)
var updateHandle = new UpdateService();
foreach (var item in lstSubs)
{ {
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) => Logging.SaveLog("Setup Scheduled Tasks");
var numOfExecuted = 1;
while (true)
{ {
updateFunc?.Invoke(success, msg); //1 minute
if (success) await Task.Delay(1000 * 60);
//Execute once 1 minute
await UpdateTaskRunSubscription(config, updateFunc);
//Execute once 20 minute
if (numOfExecuted % 20 == 0)
{ {
Logging.SaveLog($"Update subscription end. {msg}"); //Logging.SaveLog("Execute save config");
await ConfigHandler.SaveConfig(config);
await ProfileExHandler.Instance.SaveTo();
} }
});
item.UpdateTime = updateTime; //Execute once 1 hour
await ConfigHandler.AddSubItem(config, item); if (numOfExecuted % 60 == 0)
await Task.Delay(1000); {
//Logging.SaveLog("Execute delete expired files");
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
//Check once 1 hour
await UpdateTaskRunGeo(config, numOfExecuted / 60, updateFunc);
}
numOfExecuted++;
}
} }
}
private async Task UpdateTaskRunGeo(Config config, int hours, Action<bool, string> updateFunc) private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc)
{
if (config.GuiItem.AutoUpdateInterval > 0 && hours > 0 && hours % config.GuiItem.AutoUpdateInterval == 0)
{ {
Logging.SaveLog("Execute update geo files"); var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = (await AppHandler.Instance.SubItems())?
.Where(t => t.AutoUpdateInterval > 0)
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
.ToList();
var updateHandle = new UpdateService(); if (lstSubs is not { Count: > 0 })
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{ {
updateFunc?.Invoke(false, msg); return;
}); }
Logging.SaveLog("Execute update subscription");
var updateHandle = new UpdateService();
foreach (var item in lstSubs)
{
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) =>
{
updateFunc?.Invoke(success, msg);
if (success)
{
Logging.SaveLog($"Update subscription end. {msg}");
}
});
item.UpdateTime = updateTime;
await ConfigHandler.AddSubItem(config, item);
await Task.Delay(1000);
}
}
private async Task UpdateTaskRunGeo(Config config, int hours, Action<bool, string> updateFunc)
{
if (config.GuiItem.AutoUpdateInterval > 0 && hours > 0 && hours % config.GuiItem.AutoUpdateInterval == 0)
{
Logging.SaveLog("Execute update geo files");
var updateHandle = new UpdateService();
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{
updateFunc?.Invoke(false, msg);
});
}
} }
} }
} }

View file

@ -1,181 +1,182 @@
using System.Net; using System.Net;
using WebDav; using WebDav;
namespace ServiceLib.Handler; namespace ServiceLib.Handler
public sealed class WebDavHandler
{ {
private static readonly Lazy<WebDavHandler> _instance = new(() => new()); public sealed class WebDavHandler
public static WebDavHandler Instance => _instance.Value;
private readonly Config? _config;
private WebDavClient? _client;
private string? _lastDescription;
private string _webDir = Global.AppName + "_backup";
private readonly string _webFileName = "backup.zip";
private readonly string _tag = "WebDav--";
public WebDavHandler()
{ {
_config = AppHandler.Instance.Config; private static readonly Lazy<WebDavHandler> _instance = new(() => new());
} public static WebDavHandler Instance => _instance.Value;
private async Task<bool> GetClient() private readonly Config? _config;
{ private WebDavClient? _client;
try private string? _lastDescription;
private string _webDir = Global.AppName + "_backup";
private readonly string _webFileName = "backup.zip";
private readonly string _tag = "WebDav--";
public WebDavHandler()
{ {
if (_config.WebDavItem.Url.IsNullOrEmpty() _config = AppHandler.Instance.Config;
|| _config.WebDavItem.UserName.IsNullOrEmpty() }
|| _config.WebDavItem.Password.IsNullOrEmpty())
private async Task<bool> GetClient()
{
try
{ {
throw new ArgumentException("webdav parameter error or null"); if (_config.WebDavItem.Url.IsNullOrEmpty()
|| _config.WebDavItem.UserName.IsNullOrEmpty()
|| _config.WebDavItem.Password.IsNullOrEmpty())
{
throw new ArgumentException("webdav parameter error or null");
}
if (_client != null)
{
_client?.Dispose();
_client = null;
}
if (_config.WebDavItem.DirName.IsNullOrEmpty())
{
_webDir = Global.AppName + "_backup";
}
else
{
_webDir = _config.WebDavItem.DirName.TrimEx();
}
var clientParams = new WebDavClientParams
{
BaseAddress = new Uri(_config.WebDavItem.Url),
Credentials = new NetworkCredential(_config.WebDavItem.UserName, _config.WebDavItem.Password)
};
_client = new WebDavClient(clientParams);
} }
if (_client != null) catch (Exception ex)
{ {
_client?.Dispose(); SaveLog(ex);
_client = null;
}
if (_config.WebDavItem.DirName.IsNullOrEmpty())
{
_webDir = Global.AppName + "_backup";
}
else
{
_webDir = _config.WebDavItem.DirName.TrimEx();
}
var clientParams = new WebDavClientParams
{
BaseAddress = new Uri(_config.WebDavItem.Url),
Credentials = new NetworkCredential(_config.WebDavItem.UserName, _config.WebDavItem.Password)
};
_client = new WebDavClient(clientParams);
}
catch (Exception ex)
{
SaveLog(ex);
return false;
}
return await Task.FromResult(true);
}
private async Task<bool> TryCreateDir()
{
if (_client is null)
{
return false;
}
try
{
var result2 = await _client.Mkcol(_webDir);
if (result2.IsSuccessful)
{
return true;
}
SaveLog(result2.Description);
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
private void SaveLog(string desc)
{
_lastDescription = desc;
Logging.SaveLog(_tag + desc);
}
private void SaveLog(Exception ex)
{
_lastDescription = ex.Message;
Logging.SaveLog(_tag, ex);
}
public async Task<bool> CheckConnection()
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
var testName = "readme_test";
var myContent = new StringContent(testName);
var result = await _client.PutFile($"{_webDir}/{testName}", myContent);
if (result.IsSuccessful)
{
await _client.Delete($"{_webDir}/{testName}");
return true;
}
else
{
SaveLog(result.Description);
}
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
public async Task<bool> PutFile(string fileName)
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
await using var fs = File.OpenRead(fileName);
var result = await _client.PutFile($"{_webDir}/{_webFileName}", fs); // upload a resource
if (result.IsSuccessful)
{
return true;
}
SaveLog(result.Description);
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
public async Task<bool> GetRawFile(string fileName)
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
var response = await _client.GetRawFile($"{_webDir}/{_webFileName}");
if (!response.IsSuccessful)
{
SaveLog(response.Description);
return false; return false;
} }
return await Task.FromResult(true);
await using var outputFileStream = new FileStream(fileName, FileMode.Create);
await response.Stream.CopyToAsync(outputFileStream);
return true;
} }
catch (Exception ex)
private async Task<bool> TryCreateDir()
{ {
SaveLog(ex); if (_client is null)
{
return false;
}
try
{
var result2 = await _client.Mkcol(_webDir);
if (result2.IsSuccessful)
{
return true;
}
SaveLog(result2.Description);
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
} }
return false;
}
public string GetLastError() => _lastDescription ?? string.Empty; private void SaveLog(string desc)
{
_lastDescription = desc;
Logging.SaveLog(_tag + desc);
}
private void SaveLog(Exception ex)
{
_lastDescription = ex.Message;
Logging.SaveLog(_tag, ex);
}
public async Task<bool> CheckConnection()
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
var testName = "readme_test";
var myContent = new StringContent(testName);
var result = await _client.PutFile($"{_webDir}/{testName}", myContent);
if (result.IsSuccessful)
{
await _client.Delete($"{_webDir}/{testName}");
return true;
}
else
{
SaveLog(result.Description);
}
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
public async Task<bool> PutFile(string fileName)
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
await using var fs = File.OpenRead(fileName);
var result = await _client.PutFile($"{_webDir}/{_webFileName}", fs); // upload a resource
if (result.IsSuccessful)
{
return true;
}
SaveLog(result.Description);
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
public async Task<bool> GetRawFile(string fileName)
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
var response = await _client.GetRawFile($"{_webDir}/{_webFileName}");
if (!response.IsSuccessful)
{
SaveLog(response.Description);
return false;
}
await using var outputFileStream = new FileStream(fileName, FileMode.Create);
await response.Stream.CopyToAsync(outputFileStream);
return true;
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
public string GetLastError() => _lastDescription ?? string.Empty;
}
} }

View file

@ -1,10 +1,11 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class CheckUpdateModel
{ {
public bool? IsSelected { get; set; } public class CheckUpdateModel
public string? CoreType { get; set; } {
public string? Remarks { get; set; } public bool? IsSelected { get; set; }
public string? FileName { get; set; } public string? CoreType { get; set; }
public bool? IsFinished { get; set; } public string? Remarks { get; set; }
} public string? FileName { get; set; }
public bool? IsFinished { get; set; }
}
}

View file

@ -1,16 +1,17 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class ClashConnectionModel
{ {
public string? Id { get; set; } public class ClashConnectionModel
public string? Network { get; set; } {
public string? Type { get; set; } public string? Id { get; set; }
public string? Host { get; set; } public string? Network { get; set; }
public ulong Upload { get; set; } public string? Type { get; set; }
public ulong Download { get; set; } public string? Host { get; set; }
public string? UploadTraffic { get; set; } public ulong Upload { get; set; }
public string? DownloadTraffic { get; set; } public ulong Download { get; set; }
public double Time { get; set; } public string? UploadTraffic { get; set; }
public string? Elapsed { get; set; } public string? DownloadTraffic { get; set; }
public string? Chain { get; set; } public double Time { get; set; }
} public string? Elapsed { get; set; }
public string? Chain { get; set; }
}
}

View file

@ -1,36 +1,37 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class ClashConnections
{ {
public ulong downloadTotal { get; set; } public class ClashConnections
public ulong uploadTotal { get; set; } {
public List<ConnectionItem>? connections { get; set; } public ulong downloadTotal { get; set; }
} public ulong uploadTotal { get; set; }
public List<ConnectionItem>? connections { get; set; }
}
public class ConnectionItem public class ConnectionItem
{ {
public string? id { get; set; } public string? id { get; set; }
public MetadataItem? metadata { get; set; } public MetadataItem? metadata { get; set; }
public ulong upload { get; set; } public ulong upload { get; set; }
public ulong download { get; set; } public ulong download { get; set; }
public DateTime start { get; set; } public DateTime start { get; set; }
public List<string>? chains { get; set; } public List<string>? chains { get; set; }
public string? rule { get; set; } public string? rule { get; set; }
public string? rulePayload { get; set; } public string? rulePayload { get; set; }
} }
public class MetadataItem public class MetadataItem
{ {
public string? network { get; set; } public string? network { get; set; }
public string? type { get; set; } public string? type { get; set; }
public string? sourceIP { get; set; } public string? sourceIP { get; set; }
public string? destinationIP { get; set; } public string? destinationIP { get; set; }
public string? sourcePort { get; set; } public string? sourcePort { get; set; }
public string? destinationPort { get; set; } public string? destinationPort { get; set; }
public string? host { get; set; } public string? host { get; set; }
public string? nsMode { get; set; } public string? nsMode { get; set; }
public object? uid { get; set; } public object? uid { get; set; }
public string? process { get; set; } public string? process { get; set; }
public string? processPath { get; set; } public string? processPath { get; set; }
public string? remoteDestination { get; set; } public string? remoteDestination { get; set; }
} }
}

View file

@ -1,16 +1,17 @@
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Models; namespace ServiceLib.Models
public class ClashProviders
{ {
public Dictionary<string, ProvidersItem>? providers { get; set; } public class ClashProviders
public class ProvidersItem
{ {
public string? name { get; set; } public Dictionary<string, ProvidersItem>? providers { get; set; }
public List<ProxiesItem>? proxies { get; set; }
public string? type { get; set; } public class ProvidersItem
public string? vehicleType { get; set; } {
public string? name { get; set; }
public List<ProxiesItem>? proxies { get; set; }
public string? type { get; set; }
public string? vehicleType { get; set; }
}
} }
} }

View file

@ -1,23 +1,24 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class ClashProxies
{ {
public Dictionary<string, ProxiesItem>? proxies { get; set; } public class ClashProxies
public class ProxiesItem
{ {
public List<string>? all { get; set; } public Dictionary<string, ProxiesItem>? proxies { get; set; }
public List<HistoryItem>? history { get; set; }
public string? name { get; set; }
public string? type { get; set; }
public bool udp { get; set; }
public string? now { get; set; }
public int delay { get; set; }
}
public class HistoryItem public class ProxiesItem
{ {
public string? time { get; set; } public List<string>? all { get; set; }
public int delay { get; set; } public List<HistoryItem>? history { get; set; }
public string? name { get; set; }
public string? type { get; set; }
public bool udp { get; set; }
public string? now { get; set; }
public int delay { get; set; }
}
public class HistoryItem
{
public string? time { get; set; }
public int delay { get; set; }
}
} }
} }

View file

@ -1,17 +1,18 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class ClashProxyModel
{ {
public string? Name { get; set; } [Serializable]
public class ClashProxyModel
{
public string? Name { get; set; }
public string? Type { get; set; } public string? Type { get; set; }
public string? Now { get; set; } public string? Now { get; set; }
public int Delay { get; set; } public int Delay { get; set; }
public string? DelayName { get; set; } public string? DelayName { get; set; }
public bool IsActive { get; set; } public bool IsActive { get; set; }
} }
}

View file

@ -1,7 +1,8 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class CmdItem
{ {
public string? Cmd { get; set; } public class CmdItem
public List<string>? Arguments { get; set; } {
} public string? Cmd { get; set; }
public List<string>? Arguments { get; set; }
}
}

View file

@ -1,14 +1,15 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class ComboItem
{ {
public string? ID public class ComboItem
{ {
get; set; public string? ID
} {
get; set;
}
public string? Text public string? Text
{ {
get; set; get; set;
}
} }
} }

View file

@ -1,56 +1,57 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
/// <summary>
/// 本软件配置文件实体类
/// </summary>
[Serializable]
public class Config
{ {
#region property /// <summary>
/// 本软件配置文件实体类
public string IndexId { get; set; } /// </summary>
public string SubIndexId { get; set; } [Serializable]
public class Config
public ECoreType RunningCoreType { get; set; }
public bool IsRunningCore(ECoreType type)
{ {
switch (type) #region property
public string IndexId { get; set; }
public string SubIndexId { get; set; }
public ECoreType RunningCoreType { get; set; }
public bool IsRunningCore(ECoreType type)
{ {
case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5: switch (type)
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo: {
return true; case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5:
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo:
return true;
default: default:
return false; return false;
}
} }
#endregion property
#region other entities
public CoreBasicItem CoreBasicItem { get; set; }
public TunModeItem TunModeItem { get; set; }
public KcpItem KcpItem { get; set; }
public GrpcItem GrpcItem { get; set; }
public RoutingBasicItem RoutingBasicItem { get; set; }
public GUIItem GuiItem { get; set; }
public MsgUIItem MsgUIItem { get; set; }
public UIItem UiItem { get; set; }
public ConstItem ConstItem { get; set; }
public SpeedTestItem SpeedTestItem { get; set; }
public Mux4RayItem Mux4RayItem { get; set; }
public Mux4SboxItem Mux4SboxItem { get; set; }
public HysteriaItem HysteriaItem { get; set; }
public ClashUIItem ClashUIItem { get; set; }
public SystemProxyItem SystemProxyItem { get; set; }
public WebDavItem WebDavItem { get; set; }
public CheckUpdateItem CheckUpdateItem { get; set; }
public Fragment4RayItem? Fragment4RayItem { get; set; }
public List<InItem> Inbound { get; set; }
public List<KeyEventItem> GlobalHotkeys { get; set; }
public List<CoreTypeItem> CoreTypeItem { get; set; }
#endregion other entities
} }
}
#endregion property
#region other entities
public CoreBasicItem CoreBasicItem { get; set; }
public TunModeItem TunModeItem { get; set; }
public KcpItem KcpItem { get; set; }
public GrpcItem GrpcItem { get; set; }
public RoutingBasicItem RoutingBasicItem { get; set; }
public GUIItem GuiItem { get; set; }
public MsgUIItem MsgUIItem { get; set; }
public UIItem UiItem { get; set; }
public ConstItem ConstItem { get; set; }
public SpeedTestItem SpeedTestItem { get; set; }
public Mux4RayItem Mux4RayItem { get; set; }
public Mux4SboxItem Mux4SboxItem { get; set; }
public HysteriaItem HysteriaItem { get; set; }
public ClashUIItem ClashUIItem { get; set; }
public SystemProxyItem SystemProxyItem { get; set; }
public WebDavItem WebDavItem { get; set; }
public CheckUpdateItem CheckUpdateItem { get; set; }
public Fragment4RayItem? Fragment4RayItem { get; set; }
public List<InItem> Inbound { get; set; }
public List<KeyEventItem> GlobalHotkeys { get; set; }
public List<CoreTypeItem> CoreTypeItem { get; set; }
#endregion other entities
}

View file

@ -1,247 +1,248 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class CoreBasicItem
{ {
public bool LogEnabled { get; set; } [Serializable]
public class CoreBasicItem
{
public bool LogEnabled { get; set; }
public string Loglevel { get; set; } public string Loglevel { get; set; }
public bool MuxEnabled { get; set; } public bool MuxEnabled { get; set; }
public bool DefAllowInsecure { get; set; } public bool DefAllowInsecure { get; set; }
public string DefFingerprint { get; set; } public string DefFingerprint { get; set; }
public string DefUserAgent { get; set; } public string DefUserAgent { get; set; }
public bool EnableFragment { get; set; } public bool EnableFragment { get; set; }
public bool EnableCacheFile4Sbox { get; set; } = true; public bool EnableCacheFile4Sbox { get; set; } = true;
} }
[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 List<string>? DestOverride { get; set; } = ["http", "tls"]; public List<string>? DestOverride { get; set; } = ["http", "tls"];
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; }
public bool SecondLocalPortEnabled { get; set; } public bool SecondLocalPortEnabled { 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? IdleTimeout { get; set; } public int? IdleTimeout { get; set; }
public int? HealthCheckTimeout { get; set; } public int? HealthCheckTimeout { get; set; }
public bool? PermitWithoutStream { get; set; } public bool? PermitWithoutStream { get; set; }
public int? InitialWindowsSize { get; set; } public int? InitialWindowsSize { 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 DisplayRealTimeSpeed { get; set; } public bool DisplayRealTimeSpeed { get; set; }
public bool KeepOlderDedupl { get; set; } public bool KeepOlderDedupl { get; set; }
public int AutoUpdateInterval { get; set; } public int AutoUpdateInterval { get; set; }
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 MsgUIItem public class MsgUIItem
{ {
public string? MainMsgFilter { get; set; } public string? MainMsgFilter { get; set; }
public bool? AutoRefresh { get; set; } public bool? AutoRefresh { get; set; }
} }
[Serializable] [Serializable]
public class UIItem public class UIItem
{ {
public bool EnableAutoAdjustMainLvColWidth { get; set; } public bool EnableAutoAdjustMainLvColWidth { get; set; }
public bool EnableUpdateSubOnlyRemarksExist { get; set; } public bool EnableUpdateSubOnlyRemarksExist { 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 EGirdOrientation MainGirdOrientation { get; set; } = EGirdOrientation.Vertical; public EGirdOrientation MainGirdOrientation { get; set; } = EGirdOrientation.Vertical;
public string? ColorPrimaryName { get; set; } public string? ColorPrimaryName { get; set; }
public string? CurrentTheme { get; set; } public string? CurrentTheme { 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; } public bool AutoHideStartup { get; set; }
public bool Hide2TrayWhenClose { get; set; } public bool Hide2TrayWhenClose { get; set; }
public List<ColumnItem> MainColumnItem { get; set; } public List<ColumnItem> MainColumnItem { get; set; }
public bool ShowInTaskbar { get; set; } public bool ShowInTaskbar { get; set; }
} }
[Serializable] [Serializable]
public class ConstItem public class ConstItem
{ {
public string? SubConvertUrl { get; set; } public string? SubConvertUrl { get; set; }
public string? GeoSourceUrl { get; set; } public string? GeoSourceUrl { get; set; }
public string? SrsSourceUrl { get; set; } public string? SrsSourceUrl { get; set; }
public string? RouteRulesTemplateSourceUrl { get; set; } public string? RouteRulesTemplateSourceUrl { get; set; }
} }
[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 int? KeyCode { get; set; } public int? 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; }
public bool EnableIPv6Address { get; set; } public bool EnableIPv6Address { get; set; }
public string? LinuxSudoPwd { get; set; } public string? LinuxSudoPwd { 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; }
public int MixedConcurrencyCount { get; set; } public int MixedConcurrencyCount { get; set; }
} }
[Serializable] [Serializable]
public class RoutingBasicItem public class RoutingBasicItem
{ {
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; }
} }
[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 Mux4RayItem public class Mux4RayItem
{ {
public int? Concurrency { get; set; } public int? Concurrency { get; set; }
public int? XudpConcurrency { get; set; } public int? XudpConcurrency { get; set; }
public string? XudpProxyUDP443 { get; set; } public string? XudpProxyUDP443 { get; set; }
} }
[Serializable] [Serializable]
public class Mux4SboxItem public class Mux4SboxItem
{ {
public string Protocol { get; set; } public string Protocol { get; set; }
public int MaxConnections { get; set; } public int MaxConnections { get; set; }
public bool? Padding { get; set; } public bool? Padding { get; set; }
} }
[Serializable] [Serializable]
public class HysteriaItem public class HysteriaItem
{ {
public int UpMbps { get; set; } public int UpMbps { get; set; }
public int DownMbps { get; set; } public int DownMbps { get; set; }
public int HopInterval { get; set; } = 30; public int HopInterval { get; set; } = 30;
} }
[Serializable] [Serializable]
public class ClashUIItem public class ClashUIItem
{ {
public ERuleMode RuleMode { get; set; } public ERuleMode RuleMode { get; set; }
public bool EnableIPv6 { get; set; } public bool EnableIPv6 { get; set; }
public bool EnableMixinContent { get; set; } public bool EnableMixinContent { get; set; }
public int ProxiesSorting { get; set; } public int ProxiesSorting { get; set; }
public bool ProxiesAutoRefresh { get; set; } public bool ProxiesAutoRefresh { get; set; }
public int ProxiesAutoDelayTestInterval { get; set; } = 10; public int ProxiesAutoDelayTestInterval { get; set; } = 10;
public bool ConnectionsAutoRefresh { get; set; } public bool ConnectionsAutoRefresh { get; set; }
public int ConnectionsRefreshInterval { get; set; } = 2; public int ConnectionsRefreshInterval { get; set; } = 2;
} }
[Serializable] [Serializable]
public class SystemProxyItem public class SystemProxyItem
{ {
public ESysProxyType SysProxyType { get; set; } public ESysProxyType SysProxyType { get; set; }
public string SystemProxyExceptions { get; set; } public string SystemProxyExceptions { get; set; }
public bool NotProxyLocalAddress { get; set; } = true; public bool NotProxyLocalAddress { get; set; } = true;
public string SystemProxyAdvancedProtocol { get; set; } public string SystemProxyAdvancedProtocol { get; set; }
} }
[Serializable] [Serializable]
public class WebDavItem public class WebDavItem
{ {
public string? Url { get; set; } public string? Url { get; set; }
public string? UserName { get; set; } public string? UserName { get; set; }
public string? Password { get; set; } public string? Password { get; set; }
public string? DirName { get; set; } public string? DirName { get; set; }
} }
[Serializable] [Serializable]
public class CheckUpdateItem public class CheckUpdateItem
{ {
public bool CheckPreReleaseUpdate { get; set; } public bool CheckPreReleaseUpdate { get; set; }
public List<string>? SelectedCoreTypes { get; set; } public List<string>? SelectedCoreTypes { get; set; }
} }
[Serializable] [Serializable]
public class Fragment4RayItem public class Fragment4RayItem
{ {
public string? Packets { get; set; } public string? Packets { get; set; }
public string? Length { get; set; } public string? Length { get; set; }
public string? Interval { get; set; } public string? Interval { get; set; }
}
} }

View file

@ -1,20 +1,21 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class CoreInfo
{ {
public ECoreType CoreType { get; set; } [Serializable]
public List<string>? CoreExes { get; set; } public class CoreInfo
public string? Arguments { get; set; } {
public string? Url { get; set; } public ECoreType CoreType { get; set; }
public string? ReleaseApiUrl { get; set; } public List<string>? CoreExes { get; set; }
public string? DownloadUrlWin64 { get; set; } public string? Arguments { get; set; }
public string? DownloadUrlWinArm64 { get; set; } public string? Url { get; set; }
public string? DownloadUrlLinux64 { get; set; } public string? ReleaseApiUrl { get; set; }
public string? DownloadUrlLinuxArm64 { get; set; } public string? DownloadUrlWin64 { get; set; }
public string? DownloadUrlOSX64 { get; set; } public string? DownloadUrlWinArm64 { get; set; }
public string? DownloadUrlOSXArm64 { get; set; } public string? DownloadUrlLinux64 { get; set; }
public string? Match { get; set; } public string? DownloadUrlLinuxArm64 { get; set; }
public string? VersionArg { get; set; } public string? DownloadUrlOSX64 { get; set; }
public bool AbsolutePath { get; set; } public string? DownloadUrlOSXArm64 { get; set; }
public string? Match { get; set; }
public string? VersionArg { get; set; }
public bool AbsolutePath { get; set; }
}
} }

View file

@ -1,19 +1,20 @@
using SQLite; using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class DNSItem
{ {
[PrimaryKey] [Serializable]
public string Id { get; set; } public class DNSItem
{
[PrimaryKey]
public string Id { get; set; }
public string Remarks { get; set; } public string Remarks { get; set; }
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public ECoreType CoreType { get; set; } public ECoreType CoreType { get; set; }
public bool UseSystemHosts { get; set; } public bool UseSystemHosts { get; set; }
public string? NormalDNS { get; set; } public string? NormalDNS { get; set; }
public string? TunDNS { get; set; } public string? TunDNS { get; set; }
public string? DomainStrategy4Freedom { get; set; } public string? DomainStrategy4Freedom { get; set; }
public string? DomainDNSAddress { get; set; } public string? DomainDNSAddress { get; set; }
} }
}

View file

@ -1,67 +1,68 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ServiceLib.Models; namespace ServiceLib.Models
public class GitHubReleaseAsset
{ {
[JsonPropertyName("url")] public string? Url { get; set; } public class GitHubReleaseAsset
{
[JsonPropertyName("url")] public string? Url { get; set; }
[JsonPropertyName("id")] public int Id { get; set; } [JsonPropertyName("id")] public int Id { get; set; }
[JsonPropertyName("node_id")] public string? NodeId { get; set; } [JsonPropertyName("node_id")] public string? NodeId { get; set; }
[JsonPropertyName("name")] public string? Name { get; set; } [JsonPropertyName("name")] public string? Name { get; set; }
[JsonPropertyName("label")] public object Label { get; set; } [JsonPropertyName("label")] public object Label { get; set; }
[JsonPropertyName("content_type")] public string? ContentType { get; set; } [JsonPropertyName("content_type")] public string? ContentType { get; set; }
[JsonPropertyName("state")] public string? State { get; set; } [JsonPropertyName("state")] public string? State { get; set; }
[JsonPropertyName("size")] public int Size { get; set; } [JsonPropertyName("size")] public int Size { get; set; }
[JsonPropertyName("download_count")] public int DownloadCount { get; set; } [JsonPropertyName("download_count")] public int DownloadCount { get; set; }
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; } [JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
[JsonPropertyName("updated_at")] public DateTime UpdatedAt { get; set; } [JsonPropertyName("updated_at")] public DateTime UpdatedAt { get; set; }
[JsonPropertyName("browser_download_url")] public string? BrowserDownloadUrl { get; set; } [JsonPropertyName("browser_download_url")] public string? BrowserDownloadUrl { get; set; }
} }
public class GitHubRelease public class GitHubRelease
{ {
[JsonPropertyName("url")] public string? Url { get; set; } [JsonPropertyName("url")] public string? Url { get; set; }
[JsonPropertyName("assets_url")] public string? AssetsUrl { get; set; } [JsonPropertyName("assets_url")] public string? AssetsUrl { get; set; }
[JsonPropertyName("upload_url")] public string? UploadUrl { get; set; } [JsonPropertyName("upload_url")] public string? UploadUrl { get; set; }
[JsonPropertyName("html_url")] public string? HtmlUrl { get; set; } [JsonPropertyName("html_url")] public string? HtmlUrl { get; set; }
[JsonPropertyName("id")] public int Id { get; set; } [JsonPropertyName("id")] public int Id { get; set; }
[JsonPropertyName("node_id")] public string? NodeId { get; set; } [JsonPropertyName("node_id")] public string? NodeId { get; set; }
[JsonPropertyName("tag_name")] public string? TagName { get; set; } [JsonPropertyName("tag_name")] public string? TagName { get; set; }
[JsonPropertyName("target_commitish")] public string? TargetCommitish { get; set; } [JsonPropertyName("target_commitish")] public string? TargetCommitish { get; set; }
[JsonPropertyName("name")] public string? Name { get; set; } [JsonPropertyName("name")] public string? Name { get; set; }
[JsonPropertyName("draft")] public bool Draft { get; set; } [JsonPropertyName("draft")] public bool Draft { get; set; }
[JsonPropertyName("prerelease")] public bool Prerelease { get; set; } [JsonPropertyName("prerelease")] public bool Prerelease { get; set; }
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; } [JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
[JsonPropertyName("published_at")] public DateTime PublishedAt { get; set; } [JsonPropertyName("published_at")] public DateTime PublishedAt { get; set; }
[JsonPropertyName("assets")] public List<GitHubReleaseAsset> Assets { get; set; } [JsonPropertyName("assets")] public List<GitHubReleaseAsset> Assets { get; set; }
[JsonPropertyName("tarball_url")] public string? TarballUrl { get; set; } [JsonPropertyName("tarball_url")] public string? TarballUrl { get; set; }
[JsonPropertyName("zipball_url")] public string? ZipballUrl { get; set; } [JsonPropertyName("zipball_url")] public string? ZipballUrl { get; set; }
[JsonPropertyName("body")] public string? Body { get; set; } [JsonPropertyName("body")] public string? Body { get; set; }
} }
}

View file

@ -1,12 +1,13 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
internal class IPAPIInfo
{ {
public string? ip { get; set; } internal class IPAPIInfo
public string? city { get; set; } {
public string? region { get; set; } public string? ip { get; set; }
public string? region_code { get; set; } public string? city { get; set; }
public string? country { get; set; } public string? region { get; set; }
public string? country_name { get; set; } public string? region_code { get; set; }
public string? country_code { get; set; } public string? country { get; set; }
} public string? country_name { get; set; }
public string? country_code { get; set; }
}
}

View file

@ -1,15 +1,16 @@
using SQLite; using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class ProfileExItem
{ {
[PrimaryKey] [Serializable]
public string IndexId { get; set; } public class ProfileExItem
{
[PrimaryKey]
public string IndexId { get; set; }
public int Delay { get; set; } public int Delay { get; set; }
public decimal Speed { get; set; } public decimal Speed { get; set; }
public int Sort { get; set; } public int Sort { get; set; }
public string? Message { get; set; } public string? Message { get; set; }
}
} }

View file

@ -1,96 +1,96 @@
using SQLite; using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class ProfileItem
{ {
public ProfileItem() [Serializable]
public class ProfileItem
{ {
IndexId = string.Empty; public ProfileItem()
ConfigType = EConfigType.VMess;
ConfigVersion = 2;
Address = string.Empty;
Port = 0;
Id = string.Empty;
AlterId = 0;
Security = string.Empty;
Network = string.Empty;
Remarks = string.Empty;
HeaderType = string.Empty;
RequestHost = string.Empty;
Path = string.Empty;
StreamSecurity = string.Empty;
AllowInsecure = string.Empty;
Subid = string.Empty;
Flow = string.Empty;
}
#region function
public string GetSummary()
{
var summary = $"[{(ConfigType).ToString()}] ";
var arrAddr = Address.Split('.');
var addr = arrAddr.Length switch
{ {
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}", IndexId = string.Empty;
> 1 => $"***{arrAddr.Last()}", ConfigType = EConfigType.VMess;
_ => Address ConfigVersion = 2;
}; Address = string.Empty;
summary += ConfigType switch Port = 0;
{ Id = string.Empty;
EConfigType.Custom => $"[{CoreType.ToString()}]{Remarks}", AlterId = 0;
_ => $"{Remarks}({addr}:{Port})" Security = string.Empty;
}; Network = string.Empty;
return summary; Remarks = string.Empty;
} HeaderType = string.Empty;
RequestHost = string.Empty;
public List<string>? GetAlpn() Path = string.Empty;
{ StreamSecurity = string.Empty;
return Alpn.IsNullOrEmpty() ? null : Utils.String2List(Alpn); AllowInsecure = string.Empty;
} Subid = string.Empty;
Flow = string.Empty;
public string GetNetwork()
{
if (Network.IsNullOrEmpty() || !Global.Networks.Contains(Network))
{
return Global.DefaultNetwork;
} }
return Network.TrimEx();
#region function
public string GetSummary()
{
var summary = $"[{(ConfigType).ToString()}] ";
var arrAddr = Address.Split('.');
var addr = arrAddr.Length switch
{
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}",
> 1 => $"***{arrAddr.Last()}",
_ => Address
};
summary += ConfigType switch
{
EConfigType.Custom => $"[{CoreType.ToString()}]{Remarks}",
_ => $"{Remarks}({addr}:{Port})"
};
return summary;
}
public List<string>? GetAlpn()
{
return Alpn.IsNullOrEmpty() ? null : Utils.String2List(Alpn);
}
public string GetNetwork()
{
if (Network.IsNullOrEmpty() || !Global.Networks.Contains(Network))
{
return Global.DefaultNetwork;
}
return Network.TrimEx();
}
#endregion function
[PrimaryKey]
public string IndexId { get; set; }
public EConfigType ConfigType { get; set; }
public int ConfigVersion { get; set; }
public string Address { get; set; }
public int Port { get; set; }
public string Ports { get; set; }
public string Id { get; set; }
public int AlterId { get; set; }
public string Security { get; set; }
public string Network { get; set; }
public string Remarks { get; set; }
public string HeaderType { get; set; }
public string RequestHost { get; set; }
public string Path { get; set; }
public string StreamSecurity { get; set; }
public string AllowInsecure { get; set; }
public string Subid { get; set; }
public bool IsSub { get; set; } = true;
public string Flow { get; set; }
public string Sni { get; set; }
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; }
public string Extra { get; set; }
} }
#endregion function
[PrimaryKey]
public string IndexId { get; set; }
public EConfigType ConfigType { get; set; }
public int ConfigVersion { get; set; }
public string Address { get; set; }
public int Port { get; set; }
public string Ports { get; set; }
public string Id { get; set; }
public int AlterId { get; set; }
public string Security { get; set; }
public string Network { get; set; }
public string Remarks { get; set; }
public string HeaderType { get; set; }
public string RequestHost { get; set; }
public string Path { get; set; }
public string StreamSecurity { get; set; }
public string AllowInsecure { get; set; }
public string Subid { get; set; }
public bool IsSub { get; set; } = true;
public string Flow { get; set; }
public string Sni { get; set; }
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; }
public string Extra { get; set; }
} }

View file

@ -1,17 +1,18 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class ProfileItemModel : ProfileItem
{ {
public bool IsActive { get; set; } [Serializable]
public string SubRemarks { get; set; } public class ProfileItemModel : ProfileItem
public int Delay { get; set; } {
public decimal Speed { get; set; } public bool IsActive { get; set; }
public int Sort { get; set; } public string SubRemarks { get; set; }
public string DelayVal { get; set; } public int Delay { get; set; }
public string SpeedVal { get; set; } public decimal Speed { get; set; }
public string TodayUp { get; set; } public int Sort { get; set; }
public string TodayDown { get; set; } public string DelayVal { get; set; }
public string TotalUp { get; set; } public string SpeedVal { get; set; }
public string TotalDown { get; set; } public string TodayUp { get; set; }
} public string TodayDown { get; set; }
public string TotalUp { get; set; }
public string TotalDown { get; set; }
}
}

View file

@ -1,26 +1,27 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class RetResult
{ {
public bool Success { get; set; } public class RetResult
public string? Msg { get; set; }
public object? Data { get; set; }
public RetResult(bool success = false)
{ {
Success = success; public bool Success { get; set; }
} public string? Msg { get; set; }
public object? Data { get; set; }
public RetResult(bool success, string? msg) public RetResult(bool success = false)
{ {
Success = success; Success = success;
Msg = msg; }
}
public RetResult(bool success, string? msg, object? data) public RetResult(bool success, string? msg)
{ {
Success = success; Success = success;
Msg = msg; Msg = msg;
Data = data; }
public RetResult(bool success, string? msg, object? data)
{
Success = success;
Msg = msg;
Data = data;
}
} }
} }

View file

@ -1,22 +1,23 @@
using SQLite; using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class RoutingItem
{ {
[PrimaryKey] [Serializable]
public string Id { get; set; } public class RoutingItem
{
[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 RuleSet { get; set; } public string RuleSet { get; set; }
public int RuleNum { get; set; } public int RuleNum { get; set; }
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public bool Locked { get; set; } public bool Locked { get; set; }
public string CustomIcon { get; set; } public string CustomIcon { get; set; }
public string CustomRulesetPath4Singbox { get; set; } public string CustomRulesetPath4Singbox { get; set; }
public string DomainStrategy { get; set; } public string DomainStrategy { get; set; }
public string DomainStrategy4Singbox { get; set; } public string DomainStrategy4Singbox { get; set; }
public int Sort { get; set; } public int Sort { get; set; }
} }
}

View file

@ -1,7 +1,8 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class RoutingItemModel : RoutingItem
{ {
public bool IsActive { get; set; } [Serializable]
} public class RoutingItemModel : RoutingItem
{
public bool IsActive { get; set; }
}
}

View file

@ -1,8 +1,9 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class RoutingTemplate
{ {
public string Version { get; set; } [Serializable]
public RoutingItem[] RoutingItems { get; set; } public class RoutingTemplate
} {
public string Version { get; set; }
public RoutingItem[] RoutingItems { get; set; }
}
}

View file

@ -1,18 +1,19 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class RulesItem
{ {
public string Id { get; set; } [Serializable]
public string? Type { get; set; } public class RulesItem
public string? Port { get; set; } {
public string? Network { get; set; } public string Id { get; set; }
public List<string>? InboundTag { get; set; } public string? Type { get; set; }
public string? OutboundTag { get; set; } public string? Port { get; set; }
public List<string>? Ip { get; set; } public string? Network { get; set; }
public List<string>? Domain { get; set; } public List<string>? InboundTag { get; set; }
public List<string>? Protocol { get; set; } public string? OutboundTag { get; set; }
public List<string>? Process { get; set; } public List<string>? Ip { get; set; }
public bool Enabled { get; set; } = true; public List<string>? Domain { get; set; }
public string? Remarks { get; set; } public List<string>? Protocol { get; set; }
} public List<string>? Process { get; set; }
public bool Enabled { get; set; } = true;
public string? Remarks { get; set; }
}
}

View file

@ -1,10 +1,11 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class RulesItemModel : RulesItem
{ {
public string InboundTags { get; set; } [Serializable]
public string Ips { get; set; } public class RulesItemModel : RulesItem
public string Domains { get; set; } {
public string Protocols { get; set; } public string InboundTags { get; set; }
} public string Ips { get; set; }
public string Domains { get; set; }
public string Protocols { get; set; }
}
}

View file

@ -1,21 +1,22 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class ServerSpeedItem : ServerStatItem
{ {
public long ProxyUp { get; set; } [Serializable]
public class ServerSpeedItem : ServerStatItem
{
public long ProxyUp { get; set; }
public long ProxyDown { get; set; } public long ProxyDown { get; set; }
public long DirectUp { get; set; } public long DirectUp { get; set; }
public long DirectDown { get; set; } public long DirectDown { get; set; }
} }
[Serializable] [Serializable]
public class TrafficItem public class TrafficItem
{ {
public ulong Up { get; set; } public ulong Up { get; set; }
public ulong Down { get; set; } public ulong Down { get; set; }
} }
}

View file

@ -1,20 +1,21 @@
using SQLite; using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class ServerStatItem
{ {
[PrimaryKey] [Serializable]
public string IndexId { get; set; } public class ServerStatItem
{
[PrimaryKey]
public string IndexId { get; set; }
public long TotalUp { get; set; } public long TotalUp { get; set; }
public long TotalDown { get; set; } public long TotalDown { get; set; }
public long TodayUp { get; set; } public long TodayUp { get; set; }
public long TodayDown { get; set; } public long TodayDown { get; set; }
public long DateNow { get; set; } public long DateNow { get; set; }
} }
}

View file

@ -1,12 +1,13 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class ServerTestItem
{ {
public string? IndexId { get; set; } [Serializable]
public string? Address { get; set; } public class ServerTestItem
public int Port { get; set; } {
public EConfigType ConfigType { get; set; } public string? IndexId { get; set; }
public bool AllowTest { get; set; } public string? Address { get; set; }
public int QueueNum { get; set; } public int Port { get; set; }
public EConfigType ConfigType { get; set; }
public bool AllowTest { get; set; }
public int QueueNum { get; set; }
}
} }

View file

@ -1,256 +1,257 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class SingboxConfig
{ {
public Log4Sbox log { get; set; } public class SingboxConfig
public Dns4Sbox? dns { get; set; } {
public List<Inbound4Sbox> inbounds { get; set; } public Log4Sbox log { get; set; }
public List<Outbound4Sbox> outbounds { get; set; } public Dns4Sbox? dns { get; set; }
public Route4Sbox route { get; set; } public List<Inbound4Sbox> inbounds { get; set; }
public Experimental4Sbox? experimental { get; set; } public List<Outbound4Sbox> outbounds { get; set; }
} public Route4Sbox route { get; set; }
public Experimental4Sbox? experimental { get; set; }
}
public class Log4Sbox public class Log4Sbox
{ {
public bool? disabled { get; set; } public bool? disabled { get; set; }
public string level { get; set; } public string level { get; set; }
public string output { get; set; } public string output { get; set; }
public bool? timestamp { get; set; } public bool? timestamp { get; set; }
} }
public class Dns4Sbox public class Dns4Sbox
{ {
public List<Server4Sbox> servers { get; set; } public List<Server4Sbox> servers { get; set; }
public List<Rule4Sbox> rules { get; set; } public List<Rule4Sbox> rules { get; set; }
public string? final { get; set; } public string? final { get; set; }
public string? strategy { get; set; } public string? strategy { get; set; }
public bool? disable_cache { get; set; } public bool? disable_cache { get; set; }
public bool? disable_expire { get; set; } public bool? disable_expire { get; set; }
public bool? independent_cache { get; set; } public bool? independent_cache { get; set; }
public bool? reverse_mapping { get; set; } public bool? reverse_mapping { get; set; }
public string? client_subnet { get; set; } public string? client_subnet { get; set; }
public Fakeip4Sbox? fakeip { get; set; } public Fakeip4Sbox? fakeip { get; set; }
} }
public class Route4Sbox public class Route4Sbox
{ {
public bool? auto_detect_interface { get; set; } public bool? auto_detect_interface { get; set; }
public List<Rule4Sbox> rules { get; set; } public List<Rule4Sbox> rules { get; set; }
public List<Ruleset4Sbox>? rule_set { get; set; } public List<Ruleset4Sbox>? rule_set { get; set; }
} }
[Serializable] [Serializable]
public class Rule4Sbox public class Rule4Sbox
{ {
public string? outbound { get; set; } public string? outbound { get; set; }
public string? server { get; set; } public string? server { get; set; }
public bool? disable_cache { get; set; } public bool? disable_cache { get; set; }
public string? type { get; set; } public string? type { get; set; }
public string? mode { get; set; } public string? mode { get; set; }
public bool? ip_is_private { get; set; } public bool? ip_is_private { get; set; }
public string? client_subnet { get; set; } public string? client_subnet { get; set; }
public bool? invert { get; set; } public bool? invert { get; set; }
public string? clash_mode { get; set; } public string? clash_mode { get; set; }
public List<string>? inbound { get; set; } public List<string>? inbound { get; set; }
public List<string>? protocol { get; set; } public List<string>? protocol { get; set; }
public List<string>? network { get; set; } public List<string>? network { get; set; }
public List<int>? port { get; set; } public List<int>? port { get; set; }
public List<string>? port_range { get; set; } public List<string>? port_range { get; set; }
public List<string>? geosite { get; set; } public List<string>? geosite { get; set; }
public List<string>? domain { get; set; } public List<string>? domain { get; set; }
public List<string>? domain_suffix { get; set; } public List<string>? domain_suffix { get; set; }
public List<string>? domain_keyword { get; set; } public List<string>? domain_keyword { get; set; }
public List<string>? domain_regex { get; set; } public List<string>? domain_regex { get; set; }
public List<string>? geoip { get; set; } public List<string>? geoip { get; set; }
public List<string>? ip_cidr { get; set; } public List<string>? ip_cidr { get; set; }
public List<string>? source_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; }
public List<string>? rule_set { get; set; } public List<string>? rule_set { get; set; }
public List<Rule4Sbox>? rules { get; set; } public List<Rule4Sbox>? rules { get; set; }
} }
[Serializable] [Serializable]
public class Inbound4Sbox public class Inbound4Sbox
{ {
public string type { get; set; } public string type { get; set; }
public string tag { get; set; } public string tag { get; set; }
public string listen { get; set; } public string listen { get; set; }
public int? listen_port { get; set; } public int? listen_port { get; set; }
public string? domain_strategy { get; set; } public string? domain_strategy { get; set; }
public string interface_name { get; set; } public string interface_name { get; set; }
public List<string>? address { get; set; } public List<string>? address { get; set; }
public int? mtu { get; set; } public int? mtu { get; set; }
public bool? auto_route { get; set; } public bool? auto_route { get; set; }
public bool? strict_route { get; set; } public bool? strict_route { get; set; }
public bool? endpoint_independent_nat { get; set; } public bool? endpoint_independent_nat { get; set; }
public string? stack { get; set; } public string? stack { get; set; }
public bool? sniff { get; set; } public bool? sniff { get; set; }
public bool? sniff_override_destination { get; set; } public bool? sniff_override_destination { get; set; }
public List<User4Sbox> users { get; set; } public List<User4Sbox> users { get; set; }
} }
public class User4Sbox public class User4Sbox
{ {
public string username { get; set; } public string username { get; set; }
public string password { get; set; } public string password { get; set; }
} }
public class Outbound4Sbox public class Outbound4Sbox
{ {
public string type { get; set; } public string type { get; set; }
public string tag { get; set; } public string tag { get; set; }
public string? server { get; set; } public string? server { get; set; }
public int? server_port { get; set; } public int? server_port { get; set; }
public List<string>? server_ports { get; set; } public List<string>? server_ports { get; set; }
public string? uuid { get; set; } public string? uuid { get; set; }
public string? security { get; set; } public string? security { get; set; }
public int? alter_id { get; set; } public int? alter_id { get; set; }
public string? flow { get; set; } public string? flow { get; set; }
public string? hop_interval { get; set; } public string? hop_interval { get; set; }
public int? up_mbps { get; set; } public int? up_mbps { get; set; }
public int? down_mbps { get; set; } public int? down_mbps { get; set; }
public string? auth_str { get; set; } public string? auth_str { get; set; }
public int? recv_window_conn { get; set; } public int? recv_window_conn { get; set; }
public int? recv_window { get; set; } public int? recv_window { get; set; }
public bool? disable_mtu_discovery { get; set; } public bool? disable_mtu_discovery { get; set; }
public string? detour { get; set; } public string? detour { get; set; }
public string? method { get; set; } public string? method { get; set; }
public string? username { get; set; } public string? username { get; set; }
public string? password { get; set; } public string? password { get; set; }
public string? congestion_control { get; set; } public string? congestion_control { get; set; }
public string? version { get; set; } public string? version { get; set; }
public string? network { get; set; } public string? network { get; set; }
public string? packet_encoding { get; set; } public string? packet_encoding { get; set; }
public List<string>? local_address { get; set; } public List<string>? local_address { get; set; }
public string? private_key { get; set; } public string? private_key { get; set; }
public string? peer_public_key { get; set; } public string? peer_public_key { get; set; }
public List<int>? reserved { get; set; } public List<int>? reserved { get; set; }
public int? mtu { get; set; } public int? mtu { get; set; }
public string? plugin { get; set; } public string? plugin { get; set; }
public string? plugin_opts { get; set; } public string? plugin_opts { get; set; }
public Tls4Sbox? tls { get; set; } public Tls4Sbox? tls { get; set; }
public Multiplex4Sbox? multiplex { get; set; } public Multiplex4Sbox? multiplex { get; set; }
public Transport4Sbox? transport { get; set; } public Transport4Sbox? transport { get; set; }
public HyObfs4Sbox? obfs { get; set; } public HyObfs4Sbox? obfs { get; set; }
public List<string>? outbounds { get; set; } public List<string>? outbounds { get; set; }
public bool? interrupt_exist_connections { get; set; } public bool? interrupt_exist_connections { get; set; }
} }
public class Tls4Sbox public class Tls4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string? server_name { get; set; } public string? server_name { get; set; }
public bool? insecure { get; set; } public bool? insecure { get; set; }
public List<string>? alpn { get; set; } public List<string>? alpn { get; set; }
public Utls4Sbox? utls { get; set; } public Utls4Sbox? utls { get; set; }
public Reality4Sbox? reality { get; set; } public Reality4Sbox? reality { get; set; }
} }
public class Multiplex4Sbox public class Multiplex4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string protocol { get; set; } public string protocol { get; set; }
public int max_connections { get; set; } public int max_connections { get; set; }
public bool? padding { get; set; } public bool? padding { get; set; }
} }
public class Utls4Sbox public class Utls4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string fingerprint { get; set; } public string fingerprint { get; set; }
} }
public class Reality4Sbox public class Reality4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string public_key { get; set; } public string public_key { get; set; }
public string short_id { get; set; } public string short_id { get; set; }
} }
public class Transport4Sbox public class Transport4Sbox
{ {
public string? type { get; set; } public string? type { get; set; }
public object? host { get; set; } public object? host { get; set; }
public string? path { get; set; } public string? path { get; set; }
public Headers4Sbox? headers { get; set; } public Headers4Sbox? headers { get; set; }
public string? service_name { get; set; } public string? service_name { get; set; }
public string? idle_timeout { get; set; } public string? idle_timeout { get; set; }
public string? ping_timeout { get; set; } public string? ping_timeout { get; set; }
public bool? permit_without_stream { 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 HyObfs4Sbox public class HyObfs4Sbox
{ {
public string? type { get; set; } public string? type { get; set; }
public string? password { get; set; } public string? password { get; set; }
} }
public class Server4Sbox public class Server4Sbox
{ {
public string? tag { get; set; } public string? tag { get; set; }
public string? address { get; set; } public string? address { get; set; }
public string? address_resolver { get; set; } public string? address_resolver { get; set; }
public string? address_strategy { get; set; } public string? address_strategy { get; set; }
public string? strategy { get; set; } public string? strategy { get; set; }
public string? detour { get; set; } public string? detour { get; set; }
public string? client_subnet { get; set; } public string? client_subnet { get; set; }
} }
public class Experimental4Sbox public class Experimental4Sbox
{ {
public CacheFile4Sbox? cache_file { get; set; } public CacheFile4Sbox? cache_file { get; set; }
public V2ray_Api4Sbox? v2ray_api { get; set; } public V2ray_Api4Sbox? v2ray_api { get; set; }
public Clash_Api4Sbox? clash_api { get; set; } public Clash_Api4Sbox? clash_api { get; set; }
} }
public class V2ray_Api4Sbox public class V2ray_Api4Sbox
{ {
public string listen { get; set; } public string listen { get; set; }
public Stats4Sbox stats { get; set; } public Stats4Sbox stats { get; set; }
} }
public class Clash_Api4Sbox public class Clash_Api4Sbox
{ {
public string? external_controller { get; set; } public string? external_controller { get; set; }
public bool? store_selected { get; set; } public bool? store_selected { get; set; }
} }
public class Stats4Sbox public class Stats4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public List<string>? inbounds { get; set; } public List<string>? inbounds { get; set; }
public List<string>? outbounds { get; set; } public List<string>? outbounds { get; set; }
public List<string>? users { get; set; } public List<string>? users { get; set; }
} }
public class Fakeip4Sbox public class Fakeip4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string inet4_range { get; set; } public string inet4_range { get; set; }
public string inet6_range { get; set; } public string inet6_range { get; set; }
} }
public class CacheFile4Sbox public class CacheFile4Sbox
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public string? path { get; set; } public string? path { get; set; }
public string? cache_id { get; set; } public string? cache_id { get; set; }
public bool? store_fakeip { get; set; } public bool? store_fakeip { get; set; }
} }
public class Ruleset4Sbox public class Ruleset4Sbox
{ {
public string? tag { get; set; } public string? tag { get; set; }
public string? type { get; set; } public string? type { get; set; }
public string? format { get; set; } public string? format { get; set; }
public string? path { get; set; } public string? path { get; set; }
public string? url { get; set; } public string? url { get; set; }
public string? download_detour { get; set; } public string? download_detour { get; set; }
public string? update_interval { get; set; } public string? update_interval { get; set; }
}
} }

View file

@ -1,11 +1,12 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class SpeedTestResult
{ {
public string? IndexId { get; set; } [Serializable]
public class SpeedTestResult
{
public string? IndexId { get; set; }
public string? Delay { get; set; } public string? Delay { get; set; }
public string? Speed { get; set; } public string? Speed { get; set; }
} }
}

View file

@ -1,17 +1,18 @@
namespace ServiceLib.Models; namespace ServiceLib.Models
public class SsSIP008
{ {
public List<SsServer>? servers { get; set; } public class SsSIP008
} {
public List<SsServer>? servers { get; set; }
}
[Serializable] [Serializable]
public class SsServer public class SsServer
{ {
public string? remarks { get; set; } public string? remarks { get; set; }
public string? server { get; set; } public string? server { get; set; }
public string? server_port { get; set; } public string? server_port { get; set; }
public string? method { get; set; } public string? method { get; set; }
public string? password { get; set; } public string? password { get; set; }
public string? plugin { get; set; } public string? plugin { get; set; }
} }
}

View file

@ -1,38 +1,39 @@
using SQLite; using SQLite;
namespace ServiceLib.Models; namespace ServiceLib.Models
[Serializable]
public class SubItem
{ {
[PrimaryKey] [Serializable]
public string Id { get; set; } public class SubItem
{
[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; }
public string? PrevProfile { get; set; } public string? PrevProfile { get; set; }
public string? NextProfile { get; set; } public string? NextProfile { get; set; }
public int? PreSocksPort { get; set; } public int? PreSocksPort { get; set; }
public string? Memo { get; set; } public string? Memo { get; set; }
} }
}

View file

@ -1,436 +1,395 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ServiceLib.Models; namespace ServiceLib.Models
public class V2rayConfig
{ {
public Log4Ray log { get; set; } public class V2rayConfig
public object dns { get; set; } {
public List<Inbounds4Ray> inbounds { get; set; } public Log4Ray log { get; set; }
public List<Outbounds4Ray> outbounds { get; set; } public object dns { get; set; }
public Routing4Ray routing { get; set; } public List<Inbounds4Ray> inbounds { get; set; }
public Metrics4Ray? metrics { get; set; } public List<Outbounds4Ray> outbounds { get; set; }
public Policy4Ray? policy { get; set; } public Routing4Ray routing { get; set; }
public Stats4Ray? stats { get; set; } public Metrics4Ray? metrics { get; set; }
public Observatory4Ray? observatory { get; set; } public Policy4Ray? policy { get; set; }
public BurstObservatory4Ray? burstObservatory { get; set; } public Stats4Ray? stats { get; set; }
public string? remarks { get; set; } public string? remarks { get; set; }
} }
public class Stats4Ray public class Stats4Ray
{ } { }
public class Metrics4Ray public class Metrics4Ray
{ {
public string tag { get; set; } public string tag { get; set; }
} }
public class Policy4Ray public class Policy4Ray
{ {
public SystemPolicy4Ray system { get; set; } public SystemPolicy4Ray system { get; set; }
} }
public class SystemPolicy4Ray public class SystemPolicy4Ray
{ {
public bool statsOutboundUplink { get; set; } public bool statsOutboundUplink { get; set; }
public bool statsOutboundDownlink { get; set; } public bool statsOutboundDownlink { get; set; }
} }
public class Log4Ray public class Log4Ray
{ {
public string? access { get; set; } public string? access { get; set; }
public string? error { get; set; } public string? error { get; set; }
public string? loglevel { get; set; } public string? loglevel { get; set; }
} }
public class Inbounds4Ray public class Inbounds4Ray
{ {
public string tag { get; set; } public string tag { get; set; }
public int port { get; set; } public int port { get; set; }
public string listen { get; set; } public string listen { get; set; }
public string protocol { get; set; } public string protocol { get; set; }
public Sniffing4Ray sniffing { get; set; } public Sniffing4Ray sniffing { get; set; }
public Inboundsettings4Ray settings { get; set; } public Inboundsettings4Ray settings { get; set; }
} }
public class Inboundsettings4Ray public class Inboundsettings4Ray
{ {
public string? auth { get; set; } public string? auth { get; set; }
public bool? udp { get; set; } public bool? udp { get; set; }
public string? ip { get; set; } public string? ip { get; set; }
public string? address { get; set; } public string? address { get; set; }
public List<UsersItem4Ray>? clients { get; set; } public List<UsersItem4Ray>? clients { get; set; }
public string? decryption { get; set; } public string? decryption { get; set; }
public bool? allowTransparent { get; set; } public bool? allowTransparent { get; set; }
public List<AccountsItem4Ray>? accounts { get; set; } public List<AccountsItem4Ray>? accounts { get; set; }
} }
public class UsersItem4Ray public class UsersItem4Ray
{ {
public string? id { get; set; } public string? id { get; set; }
public int? alterId { get; set; } public int? alterId { get; set; }
public string? email { get; set; } public string? email { get; set; }
public string? security { get; set; } public string? security { get; set; }
public string? encryption { get; set; } public string? encryption { get; set; }
public string? flow { get; set; } public string? flow { get; set; }
} }
public class Sniffing4Ray public class Sniffing4Ray
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public List<string>? destOverride { get; set; } public List<string>? destOverride { get; set; }
public bool routeOnly { get; set; } public bool routeOnly { get; set; }
} }
public class Outbounds4Ray public class Outbounds4Ray
{ {
public string tag { get; set; } public string tag { get; set; }
public string protocol { get; set; } public string protocol { get; set; }
public Outboundsettings4Ray settings { get; set; } public Outboundsettings4Ray settings { get; set; }
public StreamSettings4Ray streamSettings { get; set; } public StreamSettings4Ray streamSettings { get; set; }
public Mux4Ray mux { get; set; } public Mux4Ray mux { get; set; }
} }
public class Outboundsettings4Ray public class Outboundsettings4Ray
{ {
public List<VnextItem4Ray>? vnext { get; set; } public List<VnextItem4Ray>? vnext { get; set; }
public List<ServersItem4Ray>? servers { get; set; } public List<ServersItem4Ray>? servers { get; set; }
public Response4Ray? response { get; set; } public Response4Ray? response { get; set; }
public string domainStrategy { get; set; } public string domainStrategy { get; set; }
public int? userLevel { get; set; } public int? userLevel { get; set; }
public FragmentItem4Ray? fragment { get; set; } public FragmentItem4Ray? fragment { get; set; }
} }
public class VnextItem4Ray public class VnextItem4Ray
{ {
public string address { get; set; } public string address { get; set; }
public int port { get; set; } public int port { get; set; }
public List<UsersItem4Ray> users { get; set; } public List<UsersItem4Ray> users { get; set; }
} }
public class ServersItem4Ray public class ServersItem4Ray
{ {
public string email { get; set; } public string email { get; set; }
public string address { get; set; } public string address { get; set; }
public string? method { get; set; } public string? method { get; set; }
public bool? ota { get; set; } public bool? ota { get; set; }
public string? password { get; set; } public string? password { get; set; }
public int port { get; set; } public int port { get; set; }
public int? level { get; set; } public int? level { get; set; }
public string flow { get; set; } public string flow { get; set; }
public List<SocksUsersItem4Ray> users { get; set; } public List<SocksUsersItem4Ray> users { get; set; }
} }
public class SocksUsersItem4Ray public class SocksUsersItem4Ray
{ {
public string user { get; set; } public string user { get; set; }
public string pass { get; set; } public string pass { get; set; }
public int? level { get; set; } public int? level { get; set; }
} }
public class Mux4Ray public class Mux4Ray
{ {
public bool enabled { get; set; } public bool enabled { get; set; }
public int? concurrency { get; set; } public int? concurrency { get; set; }
public int? xudpConcurrency { get; set; } public int? xudpConcurrency { get; set; }
public string? xudpProxyUDP443 { get; set; } public string? xudpProxyUDP443 { get; set; }
} }
public class Response4Ray public class Response4Ray
{ {
public string type { get; set; } public string type { get; set; }
} }
public class Dns4Ray public class Dns4Ray
{ {
public List<string> servers { get; set; } public List<string> servers { get; set; }
} }
public class DnsServer4Ray public class DnsServer4Ray
{ {
public string? address { get; set; } public string? address { get; set; }
public List<string>? domains { get; set; } public List<string>? domains { get; set; }
} }
public class Routing4Ray public class Routing4Ray
{ {
public string domainStrategy { get; set; } public string domainStrategy { get; set; }
public string? domainMatcher { get; set; } public string? domainMatcher { get; set; }
public List<RulesItem4Ray> rules { get; set; } public List<RulesItem4Ray> rules { get; set; }
public List<BalancersItem4Ray>? balancers { get; set; } public List<BalancersItem4Ray>? balancers { get; set; }
} }
[Serializable] [Serializable]
public class RulesItem4Ray public class RulesItem4Ray
{ {
public string? type { get; set; } public string? type { get; set; }
public string? port { get; set; } public string? port { get; set; }
public string? network { get; set; } public string? network { 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 string? balancerTag { get; set; } public string? balancerTag { 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 class BalancersItem4Ray public class BalancersItem4Ray
{ {
public List<string>? selector { get; set; } public List<string>? selector { get; set; }
public BalancersStrategy4Ray? strategy { get; set; } public BalancersStrategy4Ray? strategy { get; set; }
public string? tag { get; set; } public string? tag { get; set; }
} }
public class BalancersStrategy4Ray public class BalancersStrategy4Ray
{ {
public string? type { get; set; } public string? type { get; set; }
public BalancersStrategySettings4Ray? settings { get; set; } }
}
public class StreamSettings4Ray
public class BalancersStrategySettings4Ray {
{ public string network { get; set; }
public int? expected { get; set; }
public string? maxRTT { get; set; } public string security { get; set; }
public float? tolerance { get; set; }
public List<string>? baselines { get; set; } public TlsSettings4Ray? tlsSettings { get; set; }
public List<BalancersStrategySettingsCosts4Ray>? costs { get; set; }
} public TcpSettings4Ray? tcpSettings { get; set; }
public class BalancersStrategySettingsCosts4Ray public KcpSettings4Ray? kcpSettings { get; set; }
{
public bool? regexp { get; set; } public WsSettings4Ray? wsSettings { get; set; }
public string? match { get; set; }
public float? value { get; set; } public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; }
}
public XhttpSettings4Ray? xhttpSettings { get; set; }
public class Observatory4Ray
{ public HttpSettings4Ray? httpSettings { get; set; }
public List<string>? subjectSelector { get; set; }
public string? probeUrl { get; set; } public QuicSettings4Ray? quicSettings { get; set; }
public string? probeInterval { get; set; }
public bool? enableConcurrency { get; set; } public TlsSettings4Ray? realitySettings { get; set; }
}
public GrpcSettings4Ray? grpcSettings { get; set; }
public class BurstObservatory4Ray
{ public Sockopt4Ray? sockopt { get; set; }
public List<string>? subjectSelector { get; set; } }
public BurstObservatoryPingConfig4Ray? pingConfig { get; set; }
} public class TlsSettings4Ray
{
public class BurstObservatoryPingConfig4Ray public bool? allowInsecure { get; set; }
{
public string? destination { get; set; } public string? serverName { get; set; }
public string? connectivity { get; set; }
public string? interval { get; set; } public List<string>? alpn { get; set; }
public int? sampling { get; set; }
public string? timeout { get; set; } public string? fingerprint { get; set; }
}
public bool? show { get; set; }
public class StreamSettings4Ray public string? publicKey { get; set; }
{ public string? shortId { get; set; }
public string network { get; set; } public string? spiderX { get; set; }
}
public string security { get; set; }
public class TcpSettings4Ray
public TlsSettings4Ray? tlsSettings { get; set; } {
public Header4Ray header { get; set; }
public TcpSettings4Ray? tcpSettings { get; set; } }
public KcpSettings4Ray? kcpSettings { get; set; } public class Header4Ray
{
public WsSettings4Ray? wsSettings { get; set; } public string type { get; set; }
public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; } public object request { get; set; }
public XhttpSettings4Ray? xhttpSettings { get; set; } public object response { get; set; }
public HttpSettings4Ray? httpSettings { get; set; } public string? domain { get; set; }
}
public QuicSettings4Ray? quicSettings { get; set; }
public class KcpSettings4Ray
public TlsSettings4Ray? realitySettings { get; set; } {
public int mtu { get; set; }
public GrpcSettings4Ray? grpcSettings { get; set; }
public int tti { get; set; }
public Sockopt4Ray? sockopt { get; set; }
} public int uplinkCapacity { get; set; }
public class TlsSettings4Ray public int downlinkCapacity { get; set; }
{
public bool? allowInsecure { get; set; } public bool congestion { get; set; }
public string? serverName { get; set; } public int readBufferSize { get; set; }
public List<string>? alpn { get; set; } public int writeBufferSize { get; set; }
public string? fingerprint { get; set; } public Header4Ray header { get; set; }
public bool? show { get; set; } public string seed { get; set; }
public string? publicKey { get; set; } }
public string? shortId { get; set; }
public string? spiderX { get; set; } public class WsSettings4Ray
} {
public string? path { get; set; }
public class TcpSettings4Ray public string? host { get; set; }
{
public Header4Ray header { get; set; } public Headers4Ray headers { get; set; }
} }
public class Header4Ray public class Headers4Ray
{ {
public string type { get; set; } public string Host { get; set; }
public object request { get; set; } [JsonPropertyName("User-Agent")]
public string UserAgent { get; set; }
public object response { get; set; } }
public string? domain { get; set; } public class HttpupgradeSettings4Ray
} {
public string? path { get; set; }
public class KcpSettings4Ray
{ public string? host { get; set; }
public int mtu { get; set; } }
public int tti { get; set; } public class XhttpSettings4Ray
{
public int uplinkCapacity { get; set; } public string? path { get; set; }
public string? host { get; set; }
public int downlinkCapacity { get; set; } public string? mode { get; set; }
public object? extra { get; set; }
public bool congestion { get; set; } }
public int readBufferSize { get; set; } public class HttpSettings4Ray
{
public int writeBufferSize { get; set; } public string? path { get; set; }
public Header4Ray header { get; set; } public List<string>? host { get; set; }
}
public string seed { get; set; }
} public class QuicSettings4Ray
{
public class WsSettings4Ray public string security { get; set; }
{
public string? path { get; set; } public string key { get; set; }
public string? host { get; set; }
public Header4Ray header { get; set; }
public Headers4Ray headers { get; set; } }
}
public class GrpcSettings4Ray
public class Headers4Ray {
{ public string? authority { get; set; }
public string Host { get; set; } public string? serviceName { get; set; }
public bool multiMode { get; set; }
[JsonPropertyName("User-Agent")] public int? idle_timeout { get; set; }
public string UserAgent { get; set; } public int? health_check_timeout { get; set; }
} public bool? permit_without_stream { get; set; }
public int? initial_windows_size { get; set; }
public class HttpupgradeSettings4Ray }
{
public string? path { get; set; } public class AccountsItem4Ray
{
public string? host { get; set; } public string user { get; set; }
}
public string pass { get; set; }
public class XhttpSettings4Ray }
{
public string? path { get; set; } public class Sockopt4Ray
public string? host { get; set; } {
public string? mode { get; set; } public string? dialerProxy { get; set; }
public object? extra { get; set; } }
}
public class FragmentItem4Ray
public class HttpSettings4Ray {
{ public string? packets { get; set; }
public string? path { get; set; } public string? length { get; set; }
public string? interval { get; set; }
public List<string>? host { get; set; } }
}
public class QuicSettings4Ray
{
public string security { get; set; }
public string key { get; set; }
public Header4Ray header { get; set; }
}
public class GrpcSettings4Ray
{
public string? authority { get; set; }
public string? serviceName { get; set; }
public bool multiMode { get; set; }
public int? idle_timeout { get; set; }
public int? health_check_timeout { get; set; }
public bool? permit_without_stream { get; set; }
public int? initial_windows_size { get; set; }
}
public class AccountsItem4Ray
{
public string user { get; set; }
public string pass { get; set; }
}
public class Sockopt4Ray
{
public string? dialerProxy { get; set; }
}
public class FragmentItem4Ray
{
public string? packets { get; set; }
public string? length { get; set; }
public string? interval { get; set; }
} }

Some files were not shown because too many files have changed in this diff Show more