Compare commits

...

13 commits

Author SHA1 Message Date
2dust
24ccfb8077 up 7.11.0
Some checks failed
release Linux / build (Release) (push) Has been cancelled
release macOS / build (Release) (push) Has been cancelled
release Windows desktop (Avalonia UI) / build (Release) (push) Has been cancelled
release Windows / build (Release) (push) Has been cancelled
2025-04-03 14:24:20 +08:00
2dust
204451db6c Bug fix
https://github.com/2dust/v2rayN/issues/7058
2025-04-03 14:22:12 +08:00
Pk-web6936
f553bbc41e
Update Persian Translation (#7053) 2025-04-03 10:19:09 +08:00
2dust
8cb4f2f961 Adjusted the server configuration right-click menu 2025-04-02 15:53:28 +08:00
2dust
4d3db56065 csharp_style_namespace_declarations = file_scoped 2025-04-02 11:44:23 +08:00
NeonSweet
d92540121f
Update proxy_set_linux_sh (#7042)
Co-authored-by: neonsweet <neonsweet@126.com>
2025-04-02 09:39:51 +08:00
2dust
17d586ea26 Update Directory.Packages.props 2025-03-31 15:05:07 +08:00
2dust
9a096d31fc Remove ads rules from default routing rules and DNS 2025-03-30 11:07:59 +08:00
2dust
bf83dbdfea Global setting ScrollViewer AllowAutoHide = False for desktop 2025-03-29 20:42:02 +08:00
2dust
e31cd0e199 Update Directory.Packages.props 2025-03-29 20:40:47 +08:00
2dust
1e11477e27 When add a new routing rule, add it to the top 2025-03-29 19:48:15 +08:00
2dust
e0750df96c Update v2rayN.sln 2025-03-29 19:40:32 +08:00
DHR60
e3580b05f7
add xray core leastPing support (#7023)
* add xray core leastPing support

* Refactor multi-server configuration UI logic

* Remove unused functions
2025-03-29 16:44:42 +08:00
204 changed files with 24422 additions and 24339 deletions

View file

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

View file

@ -2,116 +2,115 @@ using System.Diagnostics;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
namespace AmazTool namespace AmazTool;
internal class UpgradeApp
{ {
internal class UpgradeApp public static void Upgrade(string fileName)
{ {
public static void Upgrade(string fileName) Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}");
Utils.Waiting(5);
if (!File.Exists(fileName))
{ {
Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}"); Console.WriteLine(Resx.Resource.UpgradeFileNotFound);
return;
}
Utils.Waiting(5); Console.WriteLine(Resx.Resource.TryTerminateProcess);
try
if (!File.Exists(fileName)) {
var existing = Process.GetProcessesByName(Utils.V2rayN);
foreach (var pp in existing)
{ {
Console.WriteLine(Resx.Resource.UpgradeFileNotFound); var path = pp.MainModule?.FileName ?? "";
return; if (path.StartsWith(Utils.GetPath(Utils.V2rayN)))
}
Console.WriteLine(Resx.Resource.TryTerminateProcess);
try
{
var existing = Process.GetProcessesByName(Utils.V2rayN);
foreach (var pp in existing)
{ {
var path = pp.MainModule?.FileName ?? ""; pp?.Kill();
if (path.StartsWith(Utils.GetPath(Utils.V2rayN))) pp?.WaitForExit(1000);
{
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); // 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); Console.WriteLine(Resx.Resource.StartUnzipping);
StringBuilder sb = new(); StringBuilder sb = new();
try try
{ {
var thisAppOldFile = $"{Utils.GetExePath()}.tmp"; var thisAppOldFile = $"{Utils.GetExePath()}.tmp";
File.Delete(thisAppOldFile); File.Delete(thisAppOldFile);
var splitKey = "/"; var splitKey = "/";
using var archive = ZipFile.OpenRead(fileName); using var archive = ZipFile.OpenRead(fileName);
foreach (var entry in archive.Entries) foreach (var entry in archive.Entries)
{
try
{ {
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
{ {
if (entry.Length == 0) entry.ExtractToFile(entryOutputPath, true);
{
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
{
entry.ExtractToFile(entryOutputPath, true);
}
catch
{
Thread.Sleep(1000);
entry.ExtractToFile(entryOutputPath, true);
}
Console.WriteLine(entryOutputPath);
} }
catch (Exception ex) catch
{ {
sb.Append(ex.StackTrace); Thread.Sleep(1000);
entry.ExtractToFile(entryOutputPath, true);
} }
Console.WriteLine(entryOutputPath);
}
catch (Exception ex)
{
sb.Append(ex.StackTrace);
} }
} }
catch (Exception ex)
{
Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace);
//return;
}
if (sb.Length > 0)
{
Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString());
//return;
}
Console.WriteLine(Resx.Resource.Restartv2rayN);
Utils.Waiting(2);
Utils.StartV2RayN();
} }
catch (Exception ex)
{
Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace);
//return;
}
if (sb.Length > 0)
{
Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString());
//return;
}
Console.WriteLine(Resx.Resource.Restartv2rayN);
Utils.Waiting(2);
Utils.StartV2RayN();
} }
} }

View file

@ -1,52 +1,51 @@
using System.Diagnostics; using System.Diagnostics;
namespace AmazTool namespace AmazTool;
internal class Utils
{ {
internal class Utils public static string GetExePath()
{ {
public static string GetExePath() return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
{ }
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
}
public static string StartupPath() public static string StartupPath()
{ {
return AppDomain.CurrentDomain.BaseDirectory; return AppDomain.CurrentDomain.BaseDirectory;
} }
public static string GetPath(string fileName) public static string GetPath(string fileName)
{
var startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{ {
var startupPath = StartupPath(); return startupPath;
if (string.IsNullOrEmpty(fileName)) }
return Path.Combine(startupPath, fileName);
}
public static string V2rayN => "v2rayN";
public static void StartV2RayN()
{
Process process = new()
{
StartInfo = new()
{ {
return startupPath; UseShellExecute = true,
FileName = V2rayN,
WorkingDirectory = StartupPath()
} }
return Path.Combine(startupPath, fileName); };
} process.Start();
}
public static string V2rayN => "v2rayN"; public static void Waiting(int second)
{
public static void StartV2RayN() for (var i = second; i > 0; i--)
{ {
Process process = new() Console.WriteLine(i);
{ 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.10.5</Version> <Version>7.11.0</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</NoWarn> <NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058</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.5" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.6" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.5" /> <PackageVersion Include="Avalonia.Desktop" Version="11.2.6" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.5" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.2.6" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.5" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.6" />
<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.5" /> <PackageVersion Include="Semi.Avalonia" Version="11.2.1.6" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.5" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.6" />
<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.8.0" /> <PackageVersion Include="WebDav.Client" Version="2.9.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,10 +1,9 @@
using ReactiveUI; using ReactiveUI;
namespace ServiceLib.Base namespace ServiceLib.Base;
public class MyReactiveObject : ReactiveObject
{ {
public class MyReactiveObject : ReactiveObject protected static Config? _config;
{ protected Func<EViewAction, object?, Task<bool>>? _updateView;
protected static Config? _config; }
protected Func<EViewAction, object?, Task<bool>>? _updateView;
}
}

View file

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

View file

@ -1,75 +1,74 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace ServiceLib.Common namespace ServiceLib.Common;
public class DesUtils
{ {
public class DesUtils /// <summary>
/// Encrypt
/// </summary>
/// <param name="text"></param>
/// /// <param name="key"></param>
/// <returns></returns>
public static string Encrypt(string? text, string? key = null)
{ {
/// <summary> if (text.IsNullOrEmpty())
/// Encrypt
/// </summary>
/// <param name="text"></param>
/// /// <param name="key"></param>
/// <returns></returns>
public static string Encrypt(string? text, string? key = null)
{ {
if (text.IsNullOrEmpty()) return string.Empty;
{
return string.Empty;
}
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
var dsp = DES.Create();
using var memStream = new MemoryStream();
using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
using var sWriter = new StreamWriter(cryStream);
sWriter.Write(text);
sWriter.Flush();
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));
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8));
}
private static string GetDefaultKey()
{
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
} }
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
var dsp = DES.Create();
using var memStream = new MemoryStream();
using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
using var sWriter = new StreamWriter(cryStream);
sWriter.Write(text);
sWriter.Flush();
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));
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8));
}
private static string GetDefaultKey()
{
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
}
}

View file

@ -1,181 +1,180 @@
using System.Net; using System.Net;
using Downloader; using Downloader;
namespace ServiceLib.Common namespace ServiceLib.Common;
public class DownloaderHelper
{ {
public class DownloaderHelper private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
public static DownloaderHelper Instance => _instance.Value;
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{ {
private static readonly Lazy<DownloaderHelper> _instance = new(() => new()); if (url.IsNullOrEmpty())
public static DownloaderHelper Instance => _instance.Value;
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{ {
if (url.IsNullOrEmpty()) return null;
{ }
return null;
}
Uri uri = new(url); Uri uri = new(url);
//Authorization Header //Authorization Header
var headers = new WebHeaderCollection(); var headers = new WebHeaderCollection();
if (uri.UserInfo.IsNotEmpty()) if (uri.UserInfo.IsNotEmpty())
{ {
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo)); headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
} }
var downloadOpt = new DownloadConfiguration() var downloadOpt = new DownloadConfiguration()
{ {
Timeout = timeout * 1000, Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2, MaxTryAgainOnFailover = 2,
RequestConfiguration = 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)
{ {
if (value.Error != null) 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));
}
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));
} }
};
var downloadOpt = new DownloadConfiguration() using var cts = new CancellationTokenSource();
{ await downloader.DownloadFileTaskAsync(url, fileName, cts.Token);
Timeout = timeout * 1000,
MaxTryAgainOnFailover = 2,
RequestConfiguration =
{
Timeout= timeout * 1000,
Proxy = webProxy
}
};
var totalDatetime = DateTime.Now; downloadOpt = null;
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,226 +2,225 @@ 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 bool ByteArrayToFile(string fileName, byte[] content) public static class FileManager
{
private static readonly string _tag = "FileManager";
public static bool ByteArrayToFile(string fileName, byte[] content)
{
try
{ {
try 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)
{ {
File.WriteAllBytes(fileName, content); if (entry.Length == 0)
return true; {
} continue;
catch (Exception ex) }
{ try
Logging.SaveLog(_tag, ex); {
if (ignoredName.IsNotEmpty() && entry.Name.Contains(ignoredName))
{
continue;
}
entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
}
catch (IOException ex)
{
Logging.SaveLog(_tag, ex);
}
} }
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return false; return false;
} }
return true;
}
public static void DecompressFile(string fileName, byte[] content) public static List<string>? GetFilesFromZip(string fileName)
{
if (!File.Exists(fileName))
{ {
try 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))
{ {
using var fs = File.Create(fileName); File.Delete(destinationArchiveFileName);
using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false);
input.CopyTo(fs);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
} }
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.SmallestSize, true);
}
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)
{
// 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}");
} }
public static void DecompressFile(string fileName, string toPath, string? toName) // 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())
{ {
try if (ignoredName.IsNotEmpty() && file.Name.Contains(ignoredName))
{ {
FileInfo fileInfo = new(fileName); continue;
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) if (file.Extension == file.Name)
{ {
Logging.SaveLog(_tag, ex); continue;
} }
var targetFilePath = Path.Combine(destinationDir, file.Name);
if (!overwrite && File.Exists(targetFilePath))
{
continue;
}
_ = file.CopyTo(targetFilePath, overwrite);
} }
public static void DecompressTarFile(string fileName, string toPath) // If recursive and copying subdirectories, recursively call this method
if (recursive)
{ {
try foreach (var subDir in dirs)
{ {
using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); var newDestinationDir = Path.Combine(destinationDir, subDir.Name);
using var gz = new GZipStream(fs, CompressionMode.Decompress, leaveOpen: true); CopyDirectory(subDir.FullName, newDestinationDir, true, overwrite, ignoredName);
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)
{
continue;
}
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 (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);
}
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.SmallestSize, true);
}
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)
{
// 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}");
}
// 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 (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;
}
file.Delete();
}
}
catch
{
// ignored
} }
} }
} }
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;
}
file.Delete();
}
}
catch
{
// ignored
}
}
} }

View file

@ -2,205 +2,204 @@ 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
{ {
/// <summary> private static readonly Lazy<HttpClientHelper> _instance = new(() =>
/// </summary>
public class HttpClientHelper
{ {
private static readonly Lazy<HttpClientHelper> _instance = new(() => SocketsHttpHandler handler = new() { UseCookies = false };
{ HttpClientHelper helper = new(new HttpClient(handler));
SocketsHttpHandler handler = new() { UseCookies = false }; return helper;
HttpClientHelper helper = new(new HttpClient(handler)); });
return helper;
});
public static HttpClientHelper Instance => _instance.Value; public static HttpClientHelper Instance => _instance.Value;
private readonly HttpClient httpClient; private readonly HttpClient httpClient;
private HttpClientHelper(HttpClient httpClient) private HttpClientHelper(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task<string?> TryGetAsync(string url)
{
if (url.IsNullOrEmpty())
{ {
this.httpClient = httpClient; return null;
} }
public async Task<string?> TryGetAsync(string url) try
{ {
if (url.IsNullOrEmpty()) var response = await httpClient.GetAsync(url);
{ return await response.Content.ReadAsStringAsync();
return null; }
} catch
{
return null;
}
}
try public async Task<string?> GetAsync(string url)
{ {
var response = await httpClient.GetAsync(url); if (url.IsNullOrEmpty())
return await response.Content.ReadAsStringAsync(); {
} return null;
catch }
{ return await httpClient.GetStringAsync(url);
return null; }
}
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);
} }
public async Task<string?> GetAsync(string url) using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode)
{ {
if (url.IsNullOrEmpty()) throw new Exception(response.StatusCode.ToString());
{
return null;
}
return await httpClient.GetStringAsync(url);
} }
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default) 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)
{ {
if (url.IsNullOrEmpty()) token.ThrowIfCancellationRequested();
var read = await stream.ReadAsync(buffer, token);
totalRead += read;
if (read == 0)
{ {
return null; break;
} }
return await client.GetStringAsync(url, token); await file.WriteAsync(buffer.AsMemory(0, read), 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;
}
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) if (canReportProgress)
{ {
progress?.Report(101); var percent = (int)(100.0 * totalRead / total);
//if (progressPercentage != percent && percent % 10 == 0)
{
progressPercentage = percent;
progress?.Report(percent);
}
} }
} }
if (canReportProgress)
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{ {
if (url.IsNullOrEmpty()) 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)
{ {
throw new ArgumentNullException(nameof(url)); if (totalRead > 0)
}
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.StatusCode.ToString());
}
//var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
//var canReportProgress = total != -1 && progress != null;
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;
{
return;
}
else
{
token.ThrowIfCancellationRequested();
}
}
var read = await stream.ReadAsync(buffer, token);
if (read == 0)
{
isMoreToRead = false;
} }
else else
{ {
var data = new byte[read]; token.ThrowIfCancellationRequested();
buffer.ToList().CopyTo(0, data, 0, read); }
}
totalRead += read; var read = await stream.ReadAsync(buffer, token);
var ts = DateTime.Now - totalDatetime; if (read == 0)
if (progress != null && ts.Seconds > totalSecond) {
isMoreToRead = false;
}
else
{
var data = new byte[read];
buffer.ToList().CopyTo(0, data, 0, read);
totalRead += read;
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)
{ {
totalSecond = ts.Seconds; progressSpeed = speed;
var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0"); progress.Report(speed);
if (progressSpeed != speed)
{
progressSpeed = speed;
progress.Report(speed);
}
} }
} }
} while (isMoreToRead); }
} } while (isMoreToRead);
} }
} }

View file

@ -1,8 +1,7 @@
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
@ -178,4 +177,4 @@ namespace ServiceLib.Common
} }
#endregion Helper classes #endregion Helper classes
}

View file

@ -1,131 +1,130 @@
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
{ {
public class JsonUtils private static readonly string _tag = "JsonUtils";
/// <summary>
/// DeepCopy
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T DeepCopy<T>(T obj)
{ {
private static readonly string _tag = "JsonUtils"; return Deserialize<T>(Serialize(obj, false))!;
}
/// <summary> /// <summary>
/// DeepCopy /// Deserialize to object
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="obj"></param> /// <param name="strJson"></param>
/// <returns></returns> /// <returns></returns>
public static T DeepCopy<T>(T obj) public static T? Deserialize<T>(string? strJson)
{
try
{ {
return Deserialize<T>(Serialize(obj, false))!; if (string.IsNullOrWhiteSpace(strJson))
}
/// <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
/// <summary>
/// parse
/// </summary>
/// <param name="strJson"></param>
/// <returns></returns>
public static JsonNode? ParseJson(string strJson)
{ {
try return default;
}
}
/// <summary>
/// parse
/// </summary>
/// <param name="strJson"></param>
/// <returns></returns>
public static JsonNode? ParseJson(string strJson)
{
try
{
if (string.IsNullOrWhiteSpace(strJson))
{ {
if (string.IsNullOrWhiteSpace(strJson))
{
return null;
}
return JsonNode.Parse(strJson);
}
catch
{
//SaveLog(ex.Message, ex);
return null; return null;
} }
return JsonNode.Parse(strJson);
} }
catch
/// <summary>
/// Serialize Object to Json string
/// </summary>
/// <param name="obj"></param>
/// <param name="indented"></param>
/// <param name="nullValue"></param>
/// <returns></returns>
public static string Serialize(object? obj, bool indented = true, bool nullValue = false)
{ {
var result = string.Empty; //SaveLog(ex.Message, ex);
try return null;
{
if (obj == null)
{
return result;
}
var options = new JsonSerializerOptions
{
WriteIndented = indented,
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);
}
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);
} }
}
/// <summary>
/// Serialize Object to Json string
/// </summary>
/// <param name="obj"></param>
/// <param name="indented"></param>
/// <param name="nullValue"></param>
/// <returns></returns>
public static string Serialize(object? obj, bool indented = true, bool nullValue = false)
{
var result = string.Empty;
try
{
if (obj == null)
{
return result;
}
var options = new JsonSerializerOptions
{
WriteIndented = indented,
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);
}
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,53 +2,54 @@ using NLog;
using NLog.Config; using NLog.Config;
using NLog.Targets; using NLog.Targets;
namespace ServiceLib.Common namespace ServiceLib.Common;
public class Logging
{ {
public class Logging private static readonly Logger _logger1 = LogManager.GetLogger("Log1");
private static readonly Logger _logger2 = LogManager.GetLogger("Log2");
public static void Setup()
{ {
public static void Setup() LoggingConfiguration config = new();
FileTarget fileTarget = new();
config.AddTarget("file", fileTarget);
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
LogManager.Configuration = config;
}
public static void LoggingEnabled(bool enable)
{
if (!enable)
{ {
LoggingConfiguration config = new(); LogManager.SuspendLogging();
FileTarget fileTarget = new(); }
config.AddTarget("file", fileTarget); }
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt"); public static void SaveLog(string strContent)
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget)); {
LogManager.Configuration = config; if (!LogManager.IsLoggingEnabled())
{
return;
} }
public static void LoggingEnabled(bool enable) _logger1.Info(strContent);
}
public static void SaveLog(string strTitle, Exception ex)
{
if (!LogManager.IsLoggingEnabled())
{ {
if (!enable) return;
{
LogManager.SuspendLogging();
}
} }
public static void SaveLog(string strContent) _logger2.Debug($"{strTitle},{ex.Message}");
_logger2.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{ {
if (!LogManager.IsLoggingEnabled()) _logger2.Error(ex.InnerException);
{
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,90 +1,89 @@
using QRCoder; using QRCoder;
using SkiaSharp; using SkiaSharp;
using ZXing.SkiaSharp; using ZXing.SkiaSharp;
namespace ServiceLib.Common namespace ServiceLib.Common;
public class QRCodeHelper
{ {
public class QRCodeHelper public static byte[]? GenQRCode(string? url)
{ {
public static byte[]? GenQRCode(string? url) using QRCodeGenerator qrGenerator = new();
{ using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q);
using QRCodeGenerator qrGenerator = new(); using PngByteQRCode qrCode = new(qrCodeData);
using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q); return qrCode.GetGraphic(20);
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;
}
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;
}
} }
}
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;
}
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,187 +1,186 @@
namespace ServiceLib.Common namespace ServiceLib.Common;
public class SemanticVersion
{ {
public class SemanticVersion private readonly int major;
private readonly int minor;
private readonly int patch;
private readonly string version;
public SemanticVersion(int major, int minor, int patch)
{ {
private readonly int major; this.major = major;
private readonly int minor; this.minor = minor;
private readonly int patch; this.patch = patch;
private readonly string version; version = $"{major}.{minor}.{patch}";
}
public SemanticVersion(int major, int minor, int patch) public SemanticVersion(string? version)
{
try
{ {
this.major = major; if (string.IsNullOrEmpty(version))
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');
public override bool Equals(object? obj) var parts = this.version.Split('.');
{ if (parts.Length == 2)
if (obj is SemanticVersion other)
{ {
return major == other.major && minor == other.minor && patch == other.patch; 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 else
{ {
return false; throw new ArgumentException("Invalid version string");
} }
} }
catch
public override int GetHashCode()
{ {
return major.GetHashCode() ^ minor.GetHashCode() ^ patch.GetHashCode(); major = 0;
minor = 0;
patch = 0;
} }
}
/// <summary> public override bool Equals(object? obj)
/// Use ToVersionString(string? prefix) instead if possible. {
/// </summary> if (obj is SemanticVersion other)
/// <returns>major.minor.patch</returns> {
public override string ToString() return major == other.major && minor == other.minor && patch == other.patch;
}
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)
{ {
if (prefix == null) return $"{prefix}{version}";
{
return version;
}
else
{
return $"{prefix}{version}";
}
} }
public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
{ return v1.Equals(v2); }
public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
{ return !v1.Equals(v2); }
public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
{ return v1.GreaterEquals(v2); }
public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
{ return v1.LessEquals(v2); }
#region Private
private bool GreaterEquals(SemanticVersion other)
{
if (major < other.major)
{
return false;
}
else if (major > other.major)
{
return true;
}
else
{
if (minor < other.minor)
{
return false;
}
else if (minor > other.minor)
{
return true;
}
else
{
if (patch < other.patch)
{
return false;
}
else if (patch > other.patch)
{
return true;
}
else
{
return true;
}
}
}
}
private bool LessEquals(SemanticVersion other)
{
if (major < other.major)
{
return true;
}
else if (major > other.major)
{
return false;
}
else
{
if (minor < other.minor)
{
return true;
}
else if (minor > other.minor)
{
return false;
}
else
{
if (patch < other.patch)
{
return true;
}
else if (patch > other.patch)
{
return false;
}
else
{
return true;
}
}
}
}
#endregion Private
} }
public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
{ return v1.Equals(v2); }
public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
{ return !v1.Equals(v2); }
public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
{ return v1.GreaterEquals(v2); }
public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
{ return v1.LessEquals(v2); }
#region Private
private bool GreaterEquals(SemanticVersion other)
{
if (major < other.major)
{
return false;
}
else if (major > other.major)
{
return true;
}
else
{
if (minor < other.minor)
{
return false;
}
else if (minor > other.minor)
{
return true;
}
else
{
if (patch < other.patch)
{
return false;
}
else if (patch > other.patch)
{
return true;
}
else
{
return true;
}
}
}
}
private bool LessEquals(SemanticVersion other)
{
if (major < other.major)
{
return true;
}
else if (major > other.major)
{
return false;
}
else
{
if (minor < other.minor)
{
return true;
}
else if (minor > other.minor)
{
return false;
}
else
{
if (patch < other.patch)
{
return true;
}
else if (patch > other.patch)
{
return false;
}
else
{
return true;
}
}
}
}
#endregion Private
} }

View file

@ -1,91 +1,90 @@
using System.Collections; using System.Collections;
using SQLite; using SQLite;
namespace ServiceLib.Common namespace ServiceLib.Common;
public sealed class SQLiteHelper
{ {
public sealed class SQLiteHelper private static readonly Lazy<SQLiteHelper> _instance = new(() => new());
public static SQLiteHelper Instance => _instance.Value;
private readonly string _connstr;
private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync;
private readonly string _configDB = "guiNDB.db";
public SQLiteHelper()
{ {
private static readonly Lazy<SQLiteHelper> _instance = new(() => new()); _connstr = Utils.GetConfigPath(_configDB);
public static SQLiteHelper Instance => _instance.Value; _db = new SQLiteConnection(_connstr, false);
private readonly string _connstr; _dbAsync = new SQLiteAsyncConnection(_connstr, false);
private SQLiteConnection _db; }
private SQLiteAsyncConnection _dbAsync;
private readonly string _configDB = "guiNDB.db";
public SQLiteHelper() public CreateTableResult CreateTable<T>()
{
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(() =>
{ {
_connstr = Utils.GetConfigPath(_configDB); _db?.Close();
_db = new SQLiteConnection(_connstr, false); _db?.Dispose();
_dbAsync = new SQLiteAsyncConnection(_connstr, false); _db = null;
}
public CreateTableResult CreateTable<T>() _dbAsync?.GetConnection()?.Close();
{ _dbAsync?.GetConnection()?.Dispose();
return _db.CreateTable<T>(); _dbAsync = null;
} });
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,83 +1,82 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace ServiceLib.Common namespace ServiceLib.Common;
public static class StringEx
{ {
public static class StringEx public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
{ {
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value) return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value);
{ }
return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value);
}
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value) public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
{ {
return string.IsNullOrWhiteSpace(value); return string.IsNullOrWhiteSpace(value);
} }
public static bool IsNotEmpty([NotNullWhen(false)] this string? value) public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
{ {
return !string.IsNullOrEmpty(value); return !string.IsNullOrEmpty(value);
} }
public static bool BeginWithAny(this string s, IEnumerable<char> chars) public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{
if (s.IsNullOrEmpty())
{ {
if (s.IsNullOrEmpty()) return false;
}
return chars.Contains(s.First());
}
private static bool IsWhiteSpace(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 false; continue;
} }
return chars.Contains(s.First()); yield return line;
}
private static bool IsWhiteSpace(this string value)
{
return value.All(char.IsWhiteSpace);
}
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
{
while (reader.ReadLine() is { } line)
{
if (line.IsWhiteSpace())
{
continue;
}
yield return line;
}
}
public static string TrimEx(this string? value)
{
return value == null ? string.Empty : value.Trim();
}
public static string RemovePrefix(this string value, char prefix)
{
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;
} }
} }
public static string TrimEx(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 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,73 +2,72 @@ 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
{ {
internal static class WindowsUtils private static readonly string _tag = "WindowsUtils";
public static string? RegReadValue(string path, string name, string def)
{ {
private static readonly string _tag = "WindowsUtils"; RegistryKey? regKey = null;
try
public static string? RegReadValue(string path, string name, string def)
{ {
RegistryKey? regKey = null; regKey = Registry.CurrentUser.OpenSubKey(path, false);
try var value = regKey?.GetValue(name) as string;
{ return value.IsNullOrEmpty() ? def : value;
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;
} }
catch (Exception ex)
public static void RegWriteValue(string path, string name, object value)
{ {
RegistryKey? regKey = null; Logging.SaveLog(_tag, ex);
try }
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 = Registry.CurrentUser.CreateSubKey(path); regKey?.DeleteValue(name, false);
if (value.ToString().IsNullOrEmpty())
{
regKey?.DeleteValue(name, false);
}
else
{
regKey?.SetValue(name, value);
}
} }
catch (Exception ex) else
{ {
Logging.SaveLog(_tag, ex); regKey?.SetValue(name, value);
}
finally
{
regKey?.Close();
} }
} }
catch (Exception ex)
public static async Task RemoveTunDevice()
{ {
try Logging.SaveLog(_tag, ex);
{ }
var sum = MD5.HashData(Encoding.UTF8.GetBytes("wintunsingbox_tun")); finally
var guid = new Guid(sum); {
var pnpUtilPath = @"C:\Windows\System32\pnputil.exe"; regKey?.Close();
var arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """; }
}
// Try to remove the device public static async Task RemoveTunDevice()
_ = await Utils.GetCliWrapOutput(pnpUtilPath, arg); {
} try
catch (Exception ex) {
{ var sum = MD5.HashData(Encoding.UTF8.GetBytes("wintunsingbox_tun"));
Logging.SaveLog(_tag, ex); var guid = new Guid(sum);
} var pnpUtilPath = @"C:\Windows\System32\pnputil.exe";
var arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """;
// Try to remove the device
_ = await Utils.GetCliWrapOutput(pnpUtilPath, arg);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
} }
} }
} }

View file

@ -2,79 +2,78 @@ 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
{ {
public class YamlUtils private static readonly string _tag = "YamlUtils";
#region YAML
/// <summary>
/// 反序列化成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
/// <returns></returns>
public static T FromYaml<T>(string str)
{ {
private static readonly string _tag = "YamlUtils"; var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
#region YAML .Build();
try
/// <summary>
/// 反序列化成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
/// <returns></returns>
public static T FromYaml<T>(string str)
{ {
var deserializer = new DeserializerBuilder() var obj = deserializer.Deserialize<T>(str);
.WithNamingConvention(PascalCaseNamingConvention.Instance) return obj;
.Build();
try
{
var obj = deserializer.Deserialize<T>(str);
return obj;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return deserializer.Deserialize<T>("");
}
} }
catch (Exception ex)
/// <summary>
/// 序列化
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToYaml(object? obj)
{ {
var result = string.Empty; Logging.SaveLog(_tag, ex);
if (obj == null) return deserializer.Deserialize<T>("");
{ }
return result; }
}
var serializer = new SerializerBuilder()
.WithNamingConvention(HyphenatedNamingConvention.Instance)
.Build();
try /// <summary>
{ /// 序列化
result = serializer.Serialize(obj); /// </summary>
} /// <param name="obj"></param>
catch (Exception ex) /// <returns></returns>
{ public static string ToYaml(object? obj)
Logging.SaveLog(_tag, ex); {
} var result = string.Empty;
if (obj == null)
{
return result; return result;
} }
var serializer = new SerializerBuilder()
.WithNamingConvention(HyphenatedNamingConvention.Instance)
.Build();
public static string? PreprocessYaml(string str) try
{ {
try result = serializer.Serialize(obj);
{
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,16 +1,15 @@
namespace ServiceLib.Enums namespace ServiceLib.Enums;
public enum EConfigType
{ {
public enum EConfigType VMess = 1,
{ Custom = 2,
VMess = 1, Shadowsocks = 3,
Custom = 2, SOCKS = 4,
Shadowsocks = 3, VLESS = 5,
SOCKS = 4, Trojan = 6,
VLESS = 5, Hysteria2 = 7,
Trojan = 6, TUIC = 8,
Hysteria2 = 7, WireGuard = 9,
TUIC = 8, HTTP = 10
WireGuard = 9, }
HTTP = 10
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,155 +1,155 @@
namespace ServiceLib namespace ServiceLib;
public class Global
{ {
public class Global #region const
{
#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 @@ namespace ServiceLib
{"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 @@ namespace ServiceLib
{ 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 @@ namespace ServiceLib
{ 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 @@ namespace ServiceLib
"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 @@ namespace ServiceLib
"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 @@ namespace ServiceLib
"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 @@ namespace ServiceLib
"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 @@ namespace ServiceLib
"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,13 +512,12 @@ namespace ServiceLib
{ 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,244 +1,247 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
public sealed class AppHandler
{ {
public sealed class AppHandler #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
{ {
#region Property get
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 _statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
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;
} }
} }
}
public int StatePort2 #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())
{ {
get return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync();
{
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
}
} }
else
public bool IsAdministrator
{ {
get return await SQLiteHelper.Instance.TableAsync<ProfileItem>().Where(t => t.Subid == subid).ToListAsync();
{
_isAdministrator ??= Utils.IsAdministrator();
return _isAdministrator.Value;
}
} }
}
#endregion Property public async Task<List<string>?> ProfileItemIndexes(string subid)
{
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
}
#region Init public async Task<List<ProfileItemModel>?> ProfileItems(string subid, string filter)
{
public bool InitApp() var sql = @$"select a.*
{
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('\''))
{ {
sql += $" and a.subid = '{subid}'"; filter = filter.Replace("'", "");
} }
if (filter.IsNotEmpty()) sql += string.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter);
{
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);
} }
public async Task<ProfileItem?> GetProfileItem(string indexId) return await SQLiteHelper.Instance.QueryAsync<ProfileItemModel>(sql);
{
if (indexId.IsNullOrEmpty())
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
}
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
{
if (remarks.IsNullOrEmpty())
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
}
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 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
} }
public async Task<ProfileItem?> GetProfileItem(string indexId)
{
if (indexId.IsNullOrEmpty())
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
}
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
{
if (remarks.IsNullOrEmpty())
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
}
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 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
{ {
public static class AutoStartupHandler private static readonly string _tag = "AutoStartupHandler";
public static async Task<bool> UpdateTask(Config config)
{ {
private static readonly string _tag = "AutoStartupHandler"; if (Utils.IsWindows())
public static async Task<bool> UpdateTask(Config config)
{ {
if (Utils.IsWindows()) await ClearTaskWindows();
if (config.GuiItem.AutoRun)
{ {
await ClearTaskWindows(); await SetTaskWindows();
if (config.GuiItem.AutoRun)
{
await SetTaskWindows();
}
} }
else if (Utils.IsLinux()) }
else if (Utils.IsLinux())
{
await ClearTaskLinux();
if (config.GuiItem.AutoRun)
{ {
await ClearTaskLinux(); await SetTaskLinux();
if (config.GuiItem.AutoRun)
{
await SetTaskLinux();
}
} }
else if (Utils.IsOSX()) }
else if (Utils.IsOSX())
{
await ClearTaskOSX();
if (config.GuiItem.AutoRun)
{ {
await ClearTaskOSX(); await SetTaskOSX();
if (config.GuiItem.AutoRun)
{
await SetTaskOSX();
}
} }
return true;
} }
#region Windows return true;
}
private static async Task ClearTaskWindows() #region Windows
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();
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, ""); var exePath = Utils.GetExePath();
if (Utils.IsAdministrator()) if (Utils.IsAdministrator())
{ {
AutoStartTaskService(autoRunName, "", ""); AutoStartTaskService(autoRunName, exePath, "");
} }
else
await Task.CompletedTask;
}
private static async Task SetTaskWindows()
{
try
{ {
var autoRunName = GetAutoRunNameWindows(); WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath.AppendQuotes());
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)
private static string GetHomePathLinux()
{ {
var homePath = Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop"); Logging.SaveLog(_tag, ex);
Directory.CreateDirectory(Path.GetDirectoryName(homePath)); }
return homePath; 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;
} }
#endregion Linux var logonUser = WindowsIdentity.GetCurrent().Name;
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
#region macOS var tasks = taskService.RootFolder.GetTasks(new Regex(taskName));
if (fileName.IsNullOrEmpty())
private static async Task ClearTaskOSX()
{ {
try foreach (var t in tasks)
{ {
var launchAgentPath = GetLaunchAgentPathMacOS(); taskService.RootFolder.DeleteTask(t.Name);
if (File.Exists(launchAgentPath))
{
var args = new[] { "-c", $"launchctl unload -w \"{launchAgentPath}\"" };
await Utils.GetCliWrapOutput(Global.LinuxBash, args);
File.Delete(launchAgentPath);
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
} }
return;
} }
private static async Task SetTaskOSX() var task = taskService.NewTask();
{ task.RegistrationInfo.Description = description;
try task.Settings.DisallowStartIfOnBatteries = false;
{ task.Settings.StopIfGoingOnBatteries = false;
var plistContent = GenerateLaunchAgentPlist(); task.Settings.RunOnlyIfIdle = false;
var launchAgentPath = GetLaunchAgentPathMacOS(); task.Settings.IdleSettings.StopOnIdleEnd = false;
await File.WriteAllTextAsync(launchAgentPath, plistContent); 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)));
var args = new[] { "-c", $"launchctl load -w \"{launchAgentPath}\"" }; 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);
}
}
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}\"" };
await Utils.GetCliWrapOutput(Global.LinuxBash, args); await Utils.GetCliWrapOutput(Global.LinuxBash, args);
}
catch (Exception ex) File.Delete(launchAgentPath);
{
Logging.SaveLog(_tag, ex);
} }
} }
catch (Exception ex)
private static string GetLaunchAgentPathMacOS()
{ {
var homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); Logging.SaveLog(_tag, ex);
var launchAgentPath = Path.Combine(homePath, "Library", "LaunchAgents", $"{Global.AppName}-LaunchAgent.plist");
Directory.CreateDirectory(Path.GetDirectoryName(launchAgentPath));
return launchAgentPath;
} }
}
private static string GenerateLaunchAgentPlist() private static async Task SetTaskOSX()
{
try
{ {
var exePath = Utils.GetExePath(); var plistContent = GenerateLaunchAgentPlist();
var appName = Path.GetFileNameWithoutExtension(exePath); var launchAgentPath = GetLaunchAgentPathMacOS();
return $@"<?xml version=""1.0"" encoding=""UTF-8""?> await File.WriteAllTextAsync(launchAgentPath, plistContent);
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,8 +235,7 @@ namespace ServiceLib.Handler
<false/> <false/>
</dict> </dict>
</plist>"; </plist>";
}
#endregion macOS
} }
#endregion macOS
} }

View file

@ -1,188 +1,187 @@
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
public sealed class ClashApiHandler
{ {
public sealed class ClashApiHandler 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()
{ {
private static readonly Lazy<ClashApiHandler> instance = new(() => new()); for (var i = 0; i < 3; i++)
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++) var url = $"{GetApiUrl()}/proxies";
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)
{ {
var url = $"{GetApiUrl()}/proxies"; _proxies = clashProxies?.proxies;
var result = await HttpClientHelper.Instance.TryGetAsync(url); return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders);
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);
} }
return null; await Task.Delay(2000);
} }
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc) return null;
}
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
{
Task.Run(async () =>
{ {
Task.Run(async () => if (blAll)
{ {
if (blAll) if (_proxies == null)
{ {
if (_proxies == null) await GetClashProxiesAsync();
{
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>();
if (lstProxy is not { Count: > 0 }) foreach (var kv in _proxies ?? [])
{ {
return; if (Global.notAllowTestType.Contains(kv.Value.type?.ToLower()))
}
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;
} }
var name = it.Name; lstProxy.Add(new ClashProxyModel()
var url = string.Format(urlBase, name);
tasks.Add(Task.Run(async () =>
{ {
var result = await HttpClientHelper.Instance.TryGetAsync(url); Name = kv.Value.name,
updateFunc?.Invoke(it, result); Type = kv.Value.type?.ToLower(),
})); });
} }
await Task.WhenAll(tasks); }
await Task.Delay(1000);
updateFunc?.Invoke(null, "");
});
}
public List<ProxiesItem>? GetClashProxyGroups() if (lstProxy is not { Count: > 0 })
{
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 urlBase = $"{GetApiUrl()}/configs"; var tasks = new List<Task>();
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, "");
});
}
await HttpClientHelper.Instance.PatchAsync(urlBase, headers); public List<ProxiesItem>? GetClashProxyGroups()
} {
try
public async Task ClashConfigReload(string filePath)
{ {
await ClashConnectionClose(""); var fileContent = ProfileContent;
try if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
{ {
var url = $"{GetApiUrl()}/configs?force=true"; return null;
var headers = new Dictionary<string, string>();
headers.Add("path", filePath);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
} }
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
} }
catch (Exception ex)
public async Task<ClashConnections?> GetClashConnectionsAsync()
{ {
try Logging.SaveLog(_tag, ex);
{
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 ClashConnectionClose(string id) public async Task ClashSetActiveProxy(string name, string nameNode)
{
try
{ {
try var url = $"{GetApiUrl()}/proxies/{name}";
{ var headers = new Dictionary<string, string>();
var url = $"{GetApiUrl()}/connections/{id}"; headers.Add("name", nameNode);
await HttpClientHelper.Instance.DeleteAsync(url); await HttpClientHelper.Instance.PutAsync(url, headers);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
} }
catch (Exception ex)
private string GetApiUrl()
{ {
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}"; Logging.SaveLog(_tag, ex);
} }
} }
public async Task ClashConfigUpdate(Dictionary<string, string> headers)
{
if (_proxies == null)
{
return;
}
var urlBase = $"{GetApiUrl()}/configs";
await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
}
public async Task ClashConfigReload(string filePath)
{
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;
}
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,156 +1,155 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
/// <summary>
/// Core configuration file processing class
/// </summary>
public class CoreConfigHandler
{ {
/// <summary> private static readonly string _tag = "CoreConfigHandler";
/// Core configuration file processing class
/// </summary> public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
public class CoreConfigHandler
{ {
private static readonly string _tag = "CoreConfigHandler"; var config = AppHandler.Instance.Config;
var result = new RetResult();
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName) if (node.ConfigType == EConfigType.Custom)
{ {
var config = AppHandler.Instance.Config; result = node.CoreType switch
var result = new RetResult();
if (node.ConfigType == EConfigType.Custom)
{ {
result = node.CoreType switch ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
{ ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName),
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName), _ => await 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)
} {
else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
{ }
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node); else
} {
else result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node);
{ }
result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node); if (result.Success != true)
} {
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(); await File.WriteAllTextAsync(fileName, result.Data.ToString());
try }
return result;
}
private static async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
{
var ret = new RetResult();
try
{
if (node == null || fileName is null)
{ {
if (node == null || fileName is null) ret.Msg = ResUI.CheckServerSettings;
{ return ret;
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)
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))
{ {
Logging.SaveLog(_tag, ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration; ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret; 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)
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
{ {
var result = new RetResult(); Logging.SaveLog(_tag, ex);
if (coreType == ECoreType.sing_box) ret.Msg = ResUI.FailedGenDefaultConfiguration;
{ return ret;
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;
}
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;
} }
} }
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;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
{
var result = new RetResult();
if (coreType == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
}
else
{
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
} }

View file

@ -1,410 +1,409 @@
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
{ {
/// <summary> private static readonly Lazy<CoreHandler> _instance = new(() => new());
/// Core process processing class public static CoreHandler Instance => _instance.Value;
/// </summary> private Config _config;
public class CoreHandler private Process? _process;
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)
{ {
private static readonly Lazy<CoreHandler> _instance = new(() => new()); _config = config;
public static CoreHandler Instance => _instance.Value; _updateFunc = updateFunc;
private Config _config;
private Process? _process;
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) Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
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")
{ {
_config = config; var fromPath = Utils.GetBaseDirectory("bin");
_updateFunc = updateFunc; var toPath = Utils.GetBinPath("");
if (fromPath != toPath)
Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
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"); FileManager.CopyDirectory(fromPath, toPath, true, false);
var toPath = Utils.GetBinPath("");
if (fromPath != toPath)
{
FileManager.CopyDirectory(fromPath, toPath, true, false);
}
} }
}
if (Utils.IsNonWindows()) if (Utils.IsNonWindows())
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo)
{ {
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(); if (it.CoreType == ECoreType.v2rayN)
foreach (var it in coreInfo)
{ {
if (it.CoreType == ECoreType.v2rayN) if (Utils.UpgradeAppExists(out var upgradeFileName))
{ {
if (Utils.UpgradeAppExists(out var upgradeFileName)) await Utils.SetLinuxChmod(upgradeFileName);
{
await Utils.SetLinuxChmod(upgradeFileName);
}
continue;
} }
continue;
}
foreach (var name in it.CoreExes) foreach (var name in it.CoreExes)
{
var exe = Utils.GetBinPath(Utils.GetExeName(name), it.CoreType.ToString());
if (File.Exists(exe))
{ {
var exe = Utils.GetBinPath(Utils.GetExeName(name), it.CoreType.ToString()); await Utils.SetLinuxChmod(exe);
if (File.Exists(exe))
{
await Utils.SetLinuxChmod(exe);
}
} }
} }
} }
} }
}
public async Task LoadCore(ProfileItem? node) public async Task LoadCore(ProfileItem? node)
{
if (node == null)
{ {
if (node == null) UpdateFunc(false, ResUI.CheckServerSettings);
{ 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();
}
if (Utils.IsWindows() && _config.TunModeItem.EnableTun) await CoreStart(node);
{ await CoreStartPreService(node);
await Task.Delay(100); if (_process != null)
await WindowsUtils.RemoveTunDevice(); {
} UpdateFunc(true, $"{node.GetSummary()}");
}
}
await CoreStart(node); public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
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)
{ {
UpdateFunc(true, $"{node.GetSummary()}"); await ProcUtils.ProcessKill(_process, true);
_process = null;
} }
if (_processPre != null)
{
await ProcUtils.ProcessKill(_processPre, true);
_processPre = null;
}
if (_linuxSudoPid > 0)
{
await KillProcessAsLinuxSudo();
}
_linuxSudoPid = -1;
} }
catch (Exception ex)
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; Logging.SaveLog(_tag, ex);
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) #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)
{ {
var node = await AppHandler.Instance.GetProfileItem(testItem.IndexId); return;
if (node is null) }
{ _process = proc;
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;
}
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 coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
var proc = await RunProcess(coreInfo, fileName, true, false); if (itemSocks != null)
if (proc is null)
{ {
return -1; var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
} var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
return proc.Id; if (result.Success)
}
public async Task CoreStop()
{
try
{
if (_process != null)
{ {
await ProcUtils.ProcessKill(_process, true); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
_process = null; var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
} 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)
{ {
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); return;
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?.Invoke(notify, msg); UpdateFunc(false, msg);
return null;
} }
private bool IsNeedSudo(ECoreType eCoreType) try
{ {
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 = shFilePath, FileName = fileName,
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,
StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
RedirectStandardInput = true 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(); proc.Start();
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty()) if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{ {
try var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
{ await Task.Delay(10);
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd); await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10); await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd); await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10); }
await proc.StandardInput.WriteLineAsync(pwd); if (isNeedSudo)
} _linuxSudoPid = proc.Id;
catch (Exception)
{ if (displayLog)
// ignored {
} proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
} }
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); await Task.Delay(500);
await proc.WaitForExitAsync(timeout.Token); AppHandler.Instance.AddProcess(proc.Handle);
await Task.Delay(3000); if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
return proc;
} }
catch (Exception ex)
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{ {
//Shell scripts Logging.SaveLog(_tag, ex);
var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName); UpdateFunc(mayNeedSudo, ex.Message);
File.Delete(shFilePath); return null;
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()); #endregion Process
await Utils.SetLinuxChmod(shFilePath);
Logging.SaveLog(shFilePath);
return shFilePath; #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
}
} }
#endregion Linux 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());
private List<CoreInfo>? _coreInfo;
public static CoreInfoHandler Instance => _instance.Value;
public CoreInfoHandler() public sealed class CoreInfoHandler
{
private static readonly Lazy<CoreInfoHandler> _instance = new(() => new());
private List<CoreInfo>? _coreInfo;
public static CoreInfoHandler Instance => _instance.Value;
public CoreInfoHandler()
{
InitCoreInfo();
}
public CoreInfo? GetCoreInfo(ECoreType coreType)
{
if (_coreInfo == null)
{ {
InitCoreInfo(); InitCoreInfo();
} }
return _coreInfo?.FirstOrDefault(t => t.CoreType == coreType);
}
public CoreInfo? GetCoreInfo(ECoreType coreType) public List<CoreInfo> GetCoreInfo()
{
if (_coreInfo == null)
{ {
if (_coreInfo == null) InitCoreInfo();
{
InitCoreInfo();
}
return _coreInfo?.FirstOrDefault(t => t.CoreType == coreType);
} }
return _coreInfo ?? [];
}
public List<CoreInfo> GetCoreInfo() public string GetCoreExecFile(CoreInfo? coreInfo, out string msg)
{
var fileName = string.Empty;
msg = string.Empty;
foreach (var name in coreInfo?.CoreExes)
{ {
if (_coreInfo == null) var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
if (File.Exists(vName))
{ {
InitCoreInfo(); fileName = vName;
break;
} }
return _coreInfo ?? [];
} }
if (fileName.IsNullOrEmpty())
public string GetCoreExecFile(CoreInfo? coreInfo, out string msg)
{ {
var fileName = string.Empty; msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo?.CoreType.ToString()), coreInfo?.CoreExes?.LastOrDefault(), coreInfo?.Url);
msg = string.Empty; Logging.SaveLog(msg);
foreach (var name in coreInfo?.CoreExes)
{
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;
} }
return fileName;
}
private void InitCoreInfo() private void InitCoreInfo()
{ {
var urlN = GetCoreUrl(ECoreType.v2rayN); var urlN = GetCoreUrl(ECoreType.v2rayN);
var urlXray = GetCoreUrl(ECoreType.Xray); var urlXray = GetCoreUrl(ECoreType.Xray);
var urlMihomo = GetCoreUrl(ECoreType.mihomo); var urlMihomo = GetCoreUrl(ECoreType.mihomo);
var urlSingbox = GetCoreUrl(ECoreType.sing_box); var urlSingbox = GetCoreUrl(ECoreType.sing_box);
_coreInfo = _coreInfo =
[ [
new CoreInfo new CoreInfo
{ {
CoreType = ECoreType.v2rayN, CoreType = ECoreType.v2rayN,
Url = GetCoreUrl(ECoreType.v2rayN), Url = GetCoreUrl(ECoreType.v2rayN),
@ -202,17 +202,16 @@ namespace ServiceLib.Handler
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,242 +1,241 @@
using System.Collections.Specialized; using System.Collections.Specialized;
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
public class BaseFmt
{ {
public class BaseFmt protected static string GetIpv6(string address)
{ {
protected static string GetIpv6(string address) if (Utils.IsIpv6(address))
{ {
if (Utils.IsIpv6(address)) // 检查地址是否已经被方括号包围,如果没有,则添加方括号
{ return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
// 检查地址是否已经被方括号包围,如果没有,则添加方括号 }
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]"; return address; // 如果不是IPv6地址直接返回原地址
} }
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);
} }
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery) if (item.StreamSecurity.IsNotEmpty())
{ {
if (item.Flow.IsNotEmpty()) dicQuery.Add("security", item.StreamSecurity);
}
else
{
if (securityDef != null)
{ {
dicQuery.Add("flow", item.Flow); dicQuery.Add("security", securityDef);
} }
}
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");
}
if (item.StreamSecurity.IsNotEmpty()) dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
{
dicQuery.Add("security", item.StreamSecurity); switch (item.Network)
} {
else case nameof(ETransport.tcp):
{ dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (securityDef != null) if (item.RequestHost.IsNotEmpty())
{ {
dicQuery.Add("security", securityDef); dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
} }
} 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");
}
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp)); case nameof(ETransport.kcp):
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (item.Path.IsNotEmpty())
{
dicQuery.Add("seed", Utils.UrlEncode(item.Path));
}
break;
switch (item.Network) case nameof(ETransport.ws):
{ case nameof(ETransport.httpupgrade):
case nameof(ETransport.tcp): if (item.RequestHost.IsNotEmpty())
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None); {
if (item.RequestHost.IsNotEmpty()) dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
{ }
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); if (item.Path.IsNotEmpty())
} {
break; dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.kcp): case nameof(ETransport.xhttp):
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("seed", Utils.UrlEncode(item.Path)); }
} if (item.Path.IsNotEmpty())
break; {
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
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.ws): case nameof(ETransport.http):
case nameof(ETransport.httpupgrade): case nameof(ETransport.h2):
if (item.RequestHost.IsNotEmpty()) dicQuery["type"] = nameof(ETransport.http);
{ if (item.RequestHost.IsNotEmpty())
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); {
} dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
if (item.Path.IsNotEmpty()) }
{ if (item.Path.IsNotEmpty())
dicQuery.Add("path", Utils.UrlEncode(item.Path)); {
} dicQuery.Add("path", Utils.UrlEncode(item.Path));
break; }
break;
case nameof(ETransport.xhttp): case nameof(ETransport.quic):
if (item.RequestHost.IsNotEmpty()) dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
{ dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost)); dicQuery.Add("key", Utils.UrlEncode(item.Path));
} break;
if (item.Path.IsNotEmpty())
{ case nameof(ETransport.grpc):
dicQuery.Add("path", Utils.UrlEncode(item.Path)); if (item.Path.IsNotEmpty())
} {
if (item.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(item.HeaderType)) dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
if (item.HeaderType is Global.GrpcGunMode or Global.GrpcMultiMode)
{ {
dicQuery.Add("mode", Utils.UrlEncode(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.h2):
dicQuery["type"] = nameof(ETransport.http);
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.quic):
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("key", Utils.UrlEncode(item.Path));
break;
case nameof(ETransport.grpc):
if (item.Path.IsNotEmpty())
{
dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
if (item.HeaderType is Global.GrpcGunMode or Global.GrpcMultiMode)
{
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
}
}
break;
}
return 0;
}
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
{
item.Flow = query["flow"] ?? "";
item.StreamSecurity = query["security"] ?? "";
item.Sni = query["sni"] ?? "";
item.Alpn = Utils.UrlDecode(query["alpn"] ?? "");
item.Fingerprint = Utils.UrlDecode(query["fp"] ?? "");
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? "");
item.ShortId = Utils.UrlDecode(query["sid"] ?? "");
item.SpiderX = Utils.UrlDecode(query["spx"] ?? "");
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : "";
item.Network = query["type"] ?? nameof(ETransport.tcp);
switch (item.Network)
{
case nameof(ETransport.tcp):
item.HeaderType = query["headerType"] ?? Global.None;
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
break;
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;
} }
break;
}
return 0;
}
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
{
item.Flow = query["flow"] ?? "";
item.StreamSecurity = query["security"] ?? "";
item.Sni = query["sni"] ?? "";
item.Alpn = Utils.UrlDecode(query["alpn"] ?? "");
item.Fingerprint = Utils.UrlDecode(query["fp"] ?? "");
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? "");
item.ShortId = Utils.UrlDecode(query["sid"] ?? "");
item.SpiderX = Utils.UrlDecode(query["spx"] ?? "");
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : "";
item.Network = query["type"] ?? nameof(ETransport.tcp);
switch (item.Network)
{
case nameof(ETransport.tcp):
item.HeaderType = query["headerType"] ?? Global.None;
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
break;
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;
} }
return false;
}
protected static string WriteAllText(string strData, string ext = "json") protected static string WriteAllText(string strData, string ext = "json")
{ {
var fileName = Utils.GetTempPath($"{Utils.GetGuid(false)}.{ext}"); var fileName = Utils.GetTempPath($"{Utils.GetGuid(false)}.{ext}");
File.WriteAllText(fileName, strData); File.WriteAllText(fileName, strData);
return fileName; return fileName;
} }
protected static string ToUri(EConfigType eConfigType, string address, object port, string userInfo, Dictionary<string, string>? dicQuery, string? remark) protected static string ToUri(EConfigType eConfigType, string address, object port, string userInfo, Dictionary<string, string>? dicQuery, string? remark)
{ {
var query = dicQuery != null var query = dicQuery != null
? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray())) ? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()))
: string.Empty; : string.Empty;
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}"; var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}"; return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
}
} }
} }

View file

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

View file

@ -1,92 +1,91 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
public class FmtHandler
{ {
public class FmtHandler private static readonly string _tag = "FmtHandler";
public static string? GetShareUri(ProfileItem item)
{ {
private static readonly string _tag = "FmtHandler"; try
public static string? GetShareUri(ProfileItem item)
{ {
try var url = item.ConfigType switch
{ {
var url = item.ConfigType switch EConfigType.VMess => VmessFmt.ToUri(item),
{ EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
EConfigType.VMess => VmessFmt.ToUri(item), EConfigType.SOCKS => SocksFmt.ToUri(item),
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item), EConfigType.Trojan => TrojanFmt.ToUri(item),
EConfigType.SOCKS => SocksFmt.ToUri(item), EConfigType.VLESS => VLESSFmt.ToUri(item),
EConfigType.Trojan => TrojanFmt.ToUri(item), EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
EConfigType.VLESS => VLESSFmt.ToUri(item), EConfigType.TUIC => TuicFmt.ToUri(item),
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item), EConfigType.WireGuard => WireguardFmt.ToUri(item),
EConfigType.TUIC => TuicFmt.ToUri(item), _ => null,
EConfigType.WireGuard => WireguardFmt.ToUri(item), };
_ => null,
};
return url; return url;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return "";
}
} }
catch (Exception ex)
public static ProfileItem? ResolveConfig(string config, out string msg)
{ {
msg = ResUI.ConfigurationFormatIncorrect; Logging.SaveLog(_tag, ex);
return "";
}
}
try public static ProfileItem? ResolveConfig(string config, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
try
{
string str = config.TrimEx();
if (str.IsNullOrEmpty())
{ {
string str = config.TrimEx(); msg = ResUI.FailedReadConfiguration;
if (str.IsNullOrEmpty()) return null;
{
msg = ResUI.FailedReadConfiguration;
return null;
}
if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
{
return VmessFmt.Resolve(str, out msg);
}
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;
}
} }
catch (Exception ex)
if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
{ {
Logging.SaveLog(_tag, ex); return VmessFmt.Resolve(str, out msg);
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,102 +1,101 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
public class Hysteria2Fmt : BaseFmt
{ {
public class Hysteria2Fmt : BaseFmt public static ProfileItem? Resolve(string str, out string msg)
{ {
public static ProfileItem? Resolve(string str, out string msg) msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{ {
msg = ResUI.ConfigurationFormatIncorrect; ConfigType = EConfigType.Hysteria2
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
{ {
ConfigType = EConfigType.Hysteria2 CoreType = ECoreType.hysteria,
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;
} }
public static string? ToUri(ProfileItem? item) return null;
}
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
{
if (Contains(strData, "server", "auth", "up", "down", "listen"))
{ {
if (item == null) var fileName = WriteAllText(strData);
return null;
string url = string.Empty;
string remark = string.Empty; var profileItem = new ProfileItem
if (item.Remarks.IsNotEmpty())
{ {
remark = "#" + Utils.UrlEncode(item.Remarks); CoreType = ECoreType.hysteria2,
} Address = fileName,
var dicQuery = new Dictionary<string, string>(); Remarks = subRemarks ?? "hysteria2_custom"
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);
} }
public static ProfileItem? ResolveFull(string strData, string? subRemarks) return null;
{
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,23 +1,22 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
public class NaiveproxyFmt : BaseFmt
{ {
public class NaiveproxyFmt : BaseFmt public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
public static ProfileItem? ResolveFull(string strData, string? subRemarks) if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
{ {
if (Contains(strData, "listen", "proxy", "<html>", "<body>")) var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{ {
var fileName = WriteAllText(strData); CoreType = ECoreType.naiveproxy,
Address = fileName,
var profileItem = new ProfileItem Remarks = subRemarks ?? "naiveproxy_custom"
{ };
CoreType = ECoreType.naiveproxy, return profileItem;
Address = fileName,
Remarks = subRemarks ?? "naiveproxy_custom"
};
return profileItem;
}
return null;
} }
return null;
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -1,64 +1,63 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
public class TuicFmt : BaseFmt
{ {
public class TuicFmt : BaseFmt public static ProfileItem? Resolve(string str, out string msg)
{ {
public static ProfileItem? Resolve(string str, out string msg) msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{ {
msg = ResUI.ConfigurationFormatIncorrect; ConfigType = EConfigType.TUIC
};
ProfileItem item = new() var url = Utils.TryUri(str);
{ 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;
} }
public static string? ToUri(ProfileItem? item) 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)
{ {
if (item == null) item.Id = userInfoParts.First();
{ item.Security = userInfoParts.Last();
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);
} }
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.HeaderType = query["congestion_control"] ?? "";
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);
}
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,49 +1,48 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
public class V2rayFmt : BaseFmt
{ {
public class V2rayFmt : BaseFmt public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{ {
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks) var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects is not { Length: > 0 })
{ {
var configObjects = JsonUtils.Deserialize<object[]>(strData); return null;
if (configObjects is not { Length: > 0 })
{
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;
} }
public static ProfileItem? ResolveFull(string strData, string? subRemarks) List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{ {
var config = JsonUtils.ParseJson(strData); var objectString = JsonUtils.Serialize(configObject);
if (config?["inbounds"] == null var profileIt = ResolveFull(objectString, subRemarks);
|| config["outbounds"] == null if (profileIt != null)
|| config["routing"] == null)
{ {
return null; lstResult.Add(profileIt);
} }
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = config?["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom"
};
return profileItem;
} }
return lstResult;
}
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 fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = config?["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom"
};
return profileItem;
} }
} }

View file

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

View file

@ -1,126 +1,125 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
public class VmessFmt : BaseFmt
{ {
public class VmessFmt : BaseFmt public static ProfileItem? Resolve(string str, out string msg)
{ {
public static ProfileItem? Resolve(string str, out string msg) msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0)
{ {
msg = ResUI.ConfigurationFormatIncorrect; item = ResolveStdVmess(str);
ProfileItem? item; }
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0) else
{ {
item = ResolveStdVmess(str); item = ResolveVmess(str, out msg);
} }
else return item;
{ }
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;
} }
public static string? ToUri(ProfileItem? item) 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())
{ {
if (item == null) item.Network = vmessQRCode.net;
{ }
return null; if (vmessQRCode.type.IsNotEmpty())
} {
var vmessQRCode = new VmessQRCode item.HeaderType = vmessQRCode.type;
{
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) 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;
}
public static ProfileItem? ResolveStdVmess(string str)
{
var item = new ProfileItem
{ {
msg = string.Empty; ConfigType = EConfigType.VMess,
var item = new ProfileItem Security = "auto"
{ };
ConfigType = EConfigType.VMess
};
result = result[Global.ProtocolShares[EConfigType.VMess].Length..]; var url = Utils.TryUri(str);
result = Utils.Base64Decode(result); if (url == null)
{
var vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result); return null;
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;
} }
public static ProfileItem? ResolveStdVmess(string str) item.Address = url.IdnHost;
{ item.Port = url.Port;
var item = new ProfileItem item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
{ item.Id = Utils.UrlDecode(url.UserInfo);
ConfigType = EConfigType.VMess,
Security = "auto"
};
var url = Utils.TryUri(str); var query = Utils.ParseQueryString(url.Query);
if (url == null) ResolveStdTransport(query, ref item);
{
return null;
}
item.Address = url.IdnHost; return 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;
}
} }
} }

View file

@ -1,68 +1,67 @@
namespace ServiceLib.Handler.Fmt namespace ServiceLib.Handler.Fmt;
public class WireguardFmt : BaseFmt
{ {
public class WireguardFmt : BaseFmt public static ProfileItem? Resolve(string str, out string msg)
{ {
public static ProfileItem? Resolve(string str, out string msg) msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{ {
msg = ResUI.ConfigurationFormatIncorrect; ConfigType = EConfigType.WireGuard
};
ProfileItem item = new() var url = Utils.TryUri(str);
{ 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;
} }
public static string? ToUri(ProfileItem? 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);
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)
{ {
if (item == null) return null;
{
return null;
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (item.PublicKey.IsNotEmpty())
{
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);
} }
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (item.PublicKey.IsNotEmpty())
{
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);
} }
} }

View file

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

View file

@ -1,115 +1,114 @@
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
public class PacHandler
{ {
public class PacHandler 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;
public static async Task Start(string configPath, int httpPort, int pacPort)
{ {
private static string _configPath; _needRestart = configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning;
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) _configPath = configPath;
_httpPort = httpPort;
_pacPort = pacPort;
await InitText();
if (_needRestart)
{ {
_needRestart = configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning; Stop();
RunListener();
}
}
_configPath = configPath; private static async Task InitText()
_httpPort = httpPort; {
_pacPort = pacPort; var path = Path.Combine(_configPath, "pac.txt");
await InitText(); // Delete the old pac file
if (File.Exists(path) && Utils.GetFileHash(path).Equals("b590c07280f058ef05d5394aa2f927fe"))
if (_needRestart) {
{ File.Delete(path);
Stop();
RunListener();
}
} }
private static async Task InitText() if (!File.Exists(path))
{ {
var path = Path.Combine(_configPath, "pac.txt"); var pac = EmbedUtils.GetEmbedText(Global.PacFileName);
await File.AppendAllTextAsync(path, pac);
// 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());
} }
private static void RunListener() 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 () =>
{ {
_tcpListener = TcpListener.Create(_pacPort); while (_isRunning)
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{ {
while (_isRunning) try
{ {
try if (!_tcpListener.Pending())
{ {
if (!_tcpListener.Pending()) await Task.Delay(10);
{ continue;
await Task.Delay(10); }
continue;
}
var client = await _tcpListener.AcceptTcpClientAsync(); var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => WriteContent(client)); await Task.Run(() => WriteContent(client));
}
catch
{
// ignored
}
} }
}, TaskCreationOptions.LongRunning); catch
} {
// ignored
}
}
}, TaskCreationOptions.LongRunning);
}
private static void WriteContent(TcpClient client) private static void WriteContent(TcpClient client)
{ {
var stream = client.GetStream(); var stream = client.GetStream();
stream.Write(_writeContent, 0, _writeContent.Length); stream.Write(_writeContent, 0, _writeContent.Length);
stream.Flush(); stream.Flush();
} }
public static void Stop() public static void Stop()
{
if (_tcpListener == null)
{ {
if (_tcpListener == null) return;
{ }
return; try
} {
try _isRunning = false;
{ _tcpListener.Stop();
_isRunning = false; _tcpListener = null;
_tcpListener.Stop(); }
_tcpListener = null; catch
} {
catch // ignored
{
// ignored
}
} }
} }
} }

View file

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

View file

@ -1,164 +1,163 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
public class StatisticsHandler
{ {
public class StatisticsHandler private static readonly Lazy<StatisticsHandler> instance = new(() => new());
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)
{ {
private static readonly Lazy<StatisticsHandler> instance = new(() => new()); _config = config;
public static StatisticsHandler Instance => instance.Value; _updateFunc = updateFunc;
if (config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
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; await InitData();
_updateFunc = updateFunc;
if (config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
{
await InitData();
_statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler); _statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler);
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler); _statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler);
}
}
public void Close()
{
try
{
_statisticsXray?.Close();
_statisticsSingbox?.Close();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public async Task ClearAllServerStatistics()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem ");
_serverStatItem = null;
_lstServerStat = new();
}
public async Task SaveTo()
{
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)
{
_serverStatItem = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
if (_serverStatItem == null)
{
_serverStatItem = new ServerStatItem
{
IndexId = indexId,
TotalUp = 0,
TotalDown = 0,
TodayUp = 0,
TodayDown = 0,
DateNow = ticks
};
await SQLiteHelper.Instance.ReplaceAsync(_serverStatItem);
_lstServerStat.Add(_serverStatItem);
}
}
if (_serverStatItem.DateNow != ticks)
{
_serverStatItem.TodayUp = 0;
_serverStatItem.TodayDown = 0;
_serverStatItem.DateNow = ticks;
}
} }
} }
}
public void Close()
{
try
{
_statisticsXray?.Close();
_statisticsSingbox?.Close();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
public async Task ClearAllServerStatistics()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem ");
_serverStatItem = null;
_lstServerStat = new();
}
public async Task SaveTo()
{
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)
{
_serverStatItem = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
if (_serverStatItem == null)
{
_serverStatItem = new ServerStatItem
{
IndexId = indexId,
TotalUp = 0,
TotalDown = 0,
TodayUp = 0,
TodayDown = 0,
DateNow = ticks
};
await SQLiteHelper.Instance.ReplaceAsync(_serverStatItem);
_lstServerStat.Add(_serverStatItem);
}
}
if (_serverStatItem.DateNow != ticks)
{
_serverStatItem.TodayUp = 0;
_serverStatItem.TodayDown = 0;
_serverStatItem.DateNow = ticks;
}
}
}

View file

@ -1,33 +1,32 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
public class ProxySettingLinux
{ {
public class ProxySettingLinux private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
public static async Task SetProxy(string host, int port, string exceptions)
{ {
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh"; List<string> args = ["manual", host, port.ToString(), exceptions];
await ExecCmd(args);
}
public static async Task SetProxy(string host, int port, string exceptions) 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))
{ {
List<string> args = ["manual", host, port.ToString(), exceptions]; var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName);
await ExecCmd(args); await File.AppendAllTextAsync(fileName, contents);
await Utils.SetLinuxChmod(fileName);
} }
public static async Task UnsetProxy() await Utils.GetCliWrapOutput(fileName, args);
{
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,38 +1,37 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
public class ProxySettingOSX
{ {
public class ProxySettingOSX private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
public static async Task SetProxy(string host, int port, string exceptions)
{ {
private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh"; List<string> args = ["set", host, port.ToString()];
if (exceptions.IsNotEmpty())
public static async Task SetProxy(string host, int port, string exceptions)
{ {
List<string> args = ["set", host, port.ToString()]; args.AddRange(exceptions.Split(','));
if (exceptions.IsNotEmpty())
{
args.AddRange(exceptions.Split(','));
}
await ExecCmd(args);
} }
public static async Task UnsetProxy() await ExecCmd(args);
}
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))
{ {
List<string> args = ["clear"]; var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName);
await ExecCmd(args); await File.AppendAllTextAsync(fileName, contents);
await Utils.SetLinuxChmod(fileName);
} }
private static async Task ExecCmd(List<string> args) await Utils.GetCliWrapOutput(fileName, 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,360 +1,359 @@
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
{ {
public class ProxySettingWindows private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
private static bool SetProxyFallback(string? strProxy, string? exceptions, int type)
{ {
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings"; if (type == 1)
private static bool SetProxyFallback(string? strProxy, string? exceptions, int type)
{ {
if (type == 1) WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
{ WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0); WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty); WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", 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;
} }
if (type == 2)
/// <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); 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)
/// <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 WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
{ WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
// set proxy for LAN WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
var result = SetConnectionProxy(null, strProxy, exceptions, type); WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
// 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;
}
} }
return true;
}
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type) /// <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
{ {
var list = new InternetPerConnOptionList(); // set proxy for LAN
var result = SetConnectionProxy(null, strProxy, exceptions, type);
var optionCount = 1; // set proxy for dial up connections
if (type == 1) // No proxy var connections = EnumerateRasEntries();
foreach (var connection in connections)
{ {
optionCount = 1; result |= SetConnectionProxy(connection, strProxy, exceptions, type);
} }
else if (type is 2 or 4) // named proxy or autoproxy script URL return result;
{
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
{
list.szConnection = nint.Zero;
}
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
var optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
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));
Marshal.StructureToPtr(options[i], opt, false);
}
else
{
var opt = new nint(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
}
list.options = optionsPtr;
// and then make a pointer out of the whole list
var ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
Marshal.StructureToPtr(list, ipcoListPtr, false);
// 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
}
}
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;
} }
catch
/// <summary>
/// Retrieve list of connections including LAN and WAN to support PPPoE connection
/// </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()
{ {
var entries = 0; _ = SetProxyFallback(strProxy, exceptions, type);
// attempt to query with 1 entry buffer return false;
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);
// 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));
}
result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
}
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)]
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
[StructLayout(LayoutKind.Explicit)]
public struct InternetConnectionOptionValue
{
// Fields
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
[FieldOffset(0)]
public int m_Int;
[FieldOffset(0)]
public 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
}
#endregion WinInet structures
#region WinInet enums
//
// 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
}
//
// Options used in INTERNET_PER_CONN_OPTON struct
//
public enum PerConnOption
{
INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags
INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers.
INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server.
INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script.
}
//
// PER_CONN_FLAGS
//
[Flags]
public enum PerConnFlags
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
public enum ErrorCode : uint
{
ERROR_BUFFER_TOO_SMALL = 603,
ERROR_INVALID_SIZE = 632
}
#endregion WinInet enums
internal static class NativeMethods
{
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(nint hInternet, InternetOption dwOption, nint lpBuffer, int dwBufferLength);
[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
);
} }
} }
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;
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
{
list.szConnection = nint.Zero;
}
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
var optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
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));
Marshal.StructureToPtr(options[i], opt, false);
}
else
{
var opt = new nint(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
}
list.options = optionsPtr;
// and then make a pointer out of the whole list
var ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
Marshal.StructureToPtr(list, ipcoListPtr, false);
// 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
}
}
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;
}
/// <summary>
/// Retrieve list of connections including LAN and WAN to support PPPoE connection
/// </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()
{
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);
// 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));
}
result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
}
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)]
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
[StructLayout(LayoutKind.Explicit)]
public struct InternetConnectionOptionValue
{
// Fields
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
[FieldOffset(0)]
public int m_Int;
[FieldOffset(0)]
public 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
}
#endregion WinInet structures
#region WinInet enums
//
// 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
}
//
// Options used in INTERNET_PER_CONN_OPTON struct
//
public enum PerConnOption
{
INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags
INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers.
INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server.
INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script.
}
//
// PER_CONN_FLAGS
//
[Flags]
public enum PerConnFlags
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
public enum ErrorCode : uint
{
ERROR_BUFFER_TOO_SMALL = 603,
ERROR_INVALID_SIZE = 632
}
#endregion WinInet enums
internal static class NativeMethods
{
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(nint hInternet, InternetOption dwOption, nint lpBuffer, int dwBufferLength);
[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,99 +1,98 @@
namespace ServiceLib.Handler.SysProxy namespace ServiceLib.Handler.SysProxy;
public static class SysProxyHandler
{ {
public static class SysProxyHandler private static readonly string _tag = "SysProxyHandler";
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
{ {
private static readonly string _tag = "SysProxyHandler"; var type = config.SystemProxyItem.SysProxyType;
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable) if (forceDisable && type != ESysProxyType.Unchanged)
{ {
var type = config.SystemProxyItem.SysProxyType; type = ESysProxyType.ForcedClear;
if (forceDisable && type != ESysProxyType.Unchanged)
{
type = ESysProxyType.ForcedClear;
}
try
{
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (port <= 0)
{
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;
case ESysProxyType.ForcedChange when Utils.IsOSX():
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
break;
case ESysProxyType.ForcedClear when Utils.IsWindows():
ProxySettingWindows.UnsetProxy();
break;
case ESysProxyType.ForcedClear when Utils.IsLinux():
await ProxySettingLinux.UnsetProxy();
break;
case ESysProxyType.ForcedClear when Utils.IsOSX():
await ProxySettingOSX.UnsetProxy();
break;
case ESysProxyType.Pac when Utils.IsWindows():
await SetWindowsProxyPac(port);
break;
}
if (type != ESysProxyType.Pac && Utils.IsWindows())
{
PacHandler.Stop();
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return true;
} }
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions) try
{ {
strExceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", ""); var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
if (config.SystemProxyItem.NotProxyLocalAddress) var exceptions = config.SystemProxyItem.SystemProxyExceptions.Replace(" ", "");
if (port <= 0)
{ {
strExceptions = $"<local>;{strExceptions}"; 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;
case ESysProxyType.ForcedChange when Utils.IsOSX():
await ProxySettingOSX.SetProxy(Global.Loopback, port, exceptions);
break;
case ESysProxyType.ForcedClear when Utils.IsWindows():
ProxySettingWindows.UnsetProxy();
break;
case ESysProxyType.ForcedClear when Utils.IsLinux():
await ProxySettingLinux.UnsetProxy();
break;
case ESysProxyType.ForcedClear when Utils.IsOSX():
await ProxySettingOSX.UnsetProxy();
break;
case ESysProxyType.Pac when Utils.IsWindows():
await SetWindowsProxyPac(port);
break;
} }
strProxy = string.Empty; if (type != ESysProxyType.Pac && Utils.IsWindows())
if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty())
{ {
strProxy = $"{Global.Loopback}:{port}"; PacHandler.Stop();
}
else
{
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
.Replace("{ip}", Global.Loopback)
.Replace("{http_port}", port.ToString())
.Replace("{socks_port}", port.ToString());
} }
} }
catch (Exception ex)
private static async Task SetWindowsProxyPac(int port)
{ {
var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac); Logging.SaveLog(_tag, ex);
await PacHandler.Start(Utils.GetConfigPath(), port, portPac); }
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}"; return true;
ProxySettingWindows.SetProxy(strProxy, "", 4); }
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());
} }
} }
}
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,98 +1,97 @@
namespace ServiceLib.Handler namespace ServiceLib.Handler;
public class TaskHandler
{ {
public class TaskHandler private static readonly Lazy<TaskHandler> _instance = new(() => new());
public static TaskHandler Instance => _instance.Value;
public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
{ {
private static readonly Lazy<TaskHandler> _instance = new(() => new()); Task.Run(() => ScheduledTasks(config, updateFunc));
public static TaskHandler Instance => _instance.Value; }
public void RegUpdateTask(Config config, Action<bool, string> updateFunc) private async Task ScheduledTasks(Config config, Action<bool, string> updateFunc)
{
Logging.SaveLog("Setup Scheduled Tasks");
var numOfExecuted = 1;
while (true)
{ {
Task.Run(() => ScheduledTasks(config, updateFunc)); //1 minute
} await Task.Delay(1000 * 60);
private async Task ScheduledTasks(Config config, Action<bool, string> updateFunc) //Execute once 1 minute
{ await UpdateTaskRunSubscription(config, updateFunc);
Logging.SaveLog("Setup Scheduled Tasks");
var numOfExecuted = 1; //Execute once 20 minute
while (true) if (numOfExecuted % 20 == 0)
{ {
//1 minute //Logging.SaveLog("Execute save config");
await Task.Delay(1000 * 60);
//Execute once 1 minute await ConfigHandler.SaveConfig(config);
await UpdateTaskRunSubscription(config, updateFunc); await ProfileExHandler.Instance.SaveTo();
//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"); //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");
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(); var updateHandle = new UpdateService();
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
foreach (var item in lstSubs)
{ {
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) => updateFunc?.Invoke(false, 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,182 +1,181 @@
using System.Net; using System.Net;
using WebDav; using WebDav;
namespace ServiceLib.Handler namespace ServiceLib.Handler;
public sealed class WebDavHandler
{ {
public sealed class WebDavHandler private static readonly Lazy<WebDavHandler> _instance = new(() => new());
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()
{ {
private static readonly Lazy<WebDavHandler> _instance = new(() => new()); _config = AppHandler.Instance.Config;
public static WebDavHandler Instance => _instance.Value; }
private readonly Config? _config; private async Task<bool> GetClient()
private WebDavClient? _client; {
private string? _lastDescription; try
private string _webDir = Global.AppName + "_backup";
private readonly string _webFileName = "backup.zip";
private readonly string _tag = "WebDav--";
public WebDavHandler()
{ {
_config = AppHandler.Instance.Config; 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);
} }
catch (Exception ex)
private async Task<bool> GetClient()
{ {
try SaveLog(ex);
{
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);
}
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; return false;
} }
return await Task.FromResult(true);
}
private void SaveLog(string desc) private async Task<bool> TryCreateDir()
{
if (_client is null)
{ {
_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; return false;
} }
try
public async Task<bool> PutFile(string fileName)
{ {
if (await GetClient() == false) var result2 = await _client.Mkcol(_webDir);
if (result2.IsSuccessful)
{ {
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; return true;
} }
catch (Exception ex) SaveLog(result2.Description);
{ }
SaveLog(ex); 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; return false;
} }
await TryCreateDir();
public string GetLastError() => _lastDescription ?? string.Empty; 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,11 +1,10 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
public class CheckUpdateModel
{ {
public class CheckUpdateModel public bool? IsSelected { get; set; }
{ public string? CoreType { get; set; }
public bool? IsSelected { get; set; } public string? Remarks { get; set; }
public string? CoreType { get; set; } public string? FileName { get; set; }
public string? Remarks { get; set; } public bool? IsFinished { get; set; }
public string? FileName { get; set; } }
public bool? IsFinished { get; set; }
}
}

View file

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

View file

@ -1,37 +1,36 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
public class ClashConnections
{ {
public class ClashConnections public ulong downloadTotal { get; set; }
{ public ulong uploadTotal { get; set; }
public ulong downloadTotal { get; set; } public List<ConnectionItem>? connections { 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,17 +1,16 @@
using static ServiceLib.Models.ClashProxies; using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Models namespace ServiceLib.Models;
public class ClashProviders
{ {
public class ClashProviders public Dictionary<string, ProvidersItem>? providers { get; set; }
{
public Dictionary<string, ProvidersItem>? providers { get; set; }
public class ProvidersItem public class ProvidersItem
{ {
public string? name { get; set; } public string? name { get; set; }
public List<ProxiesItem>? proxies { get; set; } public List<ProxiesItem>? proxies { get; set; }
public string? type { get; set; } public string? type { get; set; }
public string? vehicleType { get; set; } public string? vehicleType { get; set; }
}
} }
} }

View file

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

View file

@ -1,18 +1,17 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class ClashProxyModel
{ {
[Serializable] public string? Name { get; set; }
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,8 +1,7 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
public class CmdItem
{ {
public class CmdItem public string? Cmd { get; set; }
{ public List<string>? Arguments { get; set; }
public string? Cmd { get; set; } }
public List<string>? Arguments { get; set; }
}
}

View file

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

View file

@ -1,57 +1,56 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
/// <summary>
/// 本软件配置文件实体类
/// </summary>
[Serializable]
public class Config
{ {
/// <summary> #region property
/// 本软件配置文件实体类
/// </summary> public string IndexId { get; set; }
[Serializable] public string SubIndexId { get; set; }
public class Config
public ECoreType RunningCoreType { get; set; }
public bool IsRunningCore(ECoreType type)
{ {
#region property switch (type)
public string IndexId { get; set; }
public string SubIndexId { get; set; }
public ECoreType RunningCoreType { get; set; }
public bool IsRunningCore(ECoreType type)
{ {
switch (type) 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:
case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5: return true;
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,248 +1,247 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class CoreBasicItem
{ {
[Serializable] public bool LogEnabled { get; set; }
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,21 +1,20 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class CoreInfo
{ {
[Serializable] public ECoreType CoreType { get; set; }
public class CoreInfo public List<string>? CoreExes { get; set; }
{ public string? Arguments { get; set; }
public ECoreType CoreType { get; set; } public string? Url { get; set; }
public List<string>? CoreExes { get; set; } public string? ReleaseApiUrl { get; set; }
public string? Arguments { get; set; } public string? DownloadUrlWin64 { get; set; }
public string? Url { get; set; } public string? DownloadUrlWinArm64 { get; set; }
public string? ReleaseApiUrl { get; set; } public string? DownloadUrlLinux64 { get; set; }
public string? DownloadUrlWin64 { get; set; } public string? DownloadUrlLinuxArm64 { get; set; }
public string? DownloadUrlWinArm64 { get; set; } public string? DownloadUrlOSX64 { get; set; }
public string? DownloadUrlLinux64 { get; set; } public string? DownloadUrlOSXArm64 { get; set; }
public string? DownloadUrlLinuxArm64 { get; set; } public string? Match { get; set; }
public string? DownloadUrlOSX64 { get; set; } public string? VersionArg { get; set; }
public string? DownloadUrlOSXArm64 { get; set; } public bool AbsolutePath { get; set; }
public string? Match { get; set; }
public string? VersionArg { get; set; }
public bool AbsolutePath { get; set; }
}
} }

View file

@ -1,20 +1,19 @@
using SQLite; using SQLite;
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class DNSItem
{ {
[Serializable] [PrimaryKey]
public class DNSItem public string Id { get; set; }
{
[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,68 +1,67 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ServiceLib.Models namespace ServiceLib.Models;
public class GitHubReleaseAsset
{ {
public class GitHubReleaseAsset [JsonPropertyName("url")] public string? Url { get; set; }
{
[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,13 +1,12 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
internal class IPAPIInfo
{ {
internal class IPAPIInfo public string? ip { get; set; }
{ public string? city { get; set; }
public string? ip { get; set; } public string? region { get; set; }
public string? city { get; set; } public string? region_code { get; set; }
public string? region { get; set; } public string? country { get; set; }
public string? region_code { get; set; } public string? country_name { get; set; }
public string? country { get; set; } public string? country_code { get; set; }
public string? country_name { get; set; } }
public string? country_code { get; set; }
}
}

View file

@ -1,16 +1,15 @@
using SQLite; using SQLite;
namespace ServiceLib.Models namespace ServiceLib.Models;
{
[Serializable]
public class ProfileExItem
{
[PrimaryKey]
public string IndexId { get; set; }
public int Delay { get; set; } [Serializable]
public decimal Speed { get; set; } public class ProfileExItem
public int Sort { get; set; } {
public string? Message { get; set; } [PrimaryKey]
} public string IndexId { get; set; }
public int Delay { get; set; }
public decimal Speed { get; set; }
public int Sort { 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
{ {
[Serializable] public ProfileItem()
public class ProfileItem
{ {
public ProfileItem() IndexId = string.Empty;
{ ConfigType = EConfigType.VMess;
IndexId = string.Empty; ConfigVersion = 2;
ConfigType = EConfigType.VMess; Address = string.Empty;
ConfigVersion = 2; Port = 0;
Address = string.Empty; Id = string.Empty;
Port = 0; AlterId = 0;
Id = string.Empty; Security = string.Empty;
AlterId = 0; Network = string.Empty;
Security = string.Empty; Remarks = string.Empty;
Network = string.Empty; HeaderType = string.Empty;
Remarks = string.Empty; RequestHost = string.Empty;
HeaderType = string.Empty; Path = string.Empty;
RequestHost = string.Empty; StreamSecurity = string.Empty;
Path = string.Empty; AllowInsecure = string.Empty;
StreamSecurity = string.Empty; Subid = string.Empty;
AllowInsecure = string.Empty; Flow = string.Empty;
Subid = string.Empty;
Flow = string.Empty;
}
#region function
public string GetSummary()
{
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; }
} }
#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; }
} }

View file

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

View file

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

View file

@ -1,23 +1,22 @@
using SQLite; using SQLite;
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class RoutingItem
{ {
[Serializable] [PrimaryKey]
public class RoutingItem public string Id { get; set; }
{
[PrimaryKey]
public string Id { get; set; }
public string Remarks { get; set; } public string Remarks { get; set; }
public string Url { get; set; } public string Url { get; set; }
public string 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,8 +1,7 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class RoutingItemModel : RoutingItem
{ {
[Serializable] public bool IsActive { get; set; }
public class RoutingItemModel : RoutingItem }
{
public bool IsActive { get; set; }
}
}

View file

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

View file

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

View file

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

View file

@ -1,22 +1,21 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class ServerSpeedItem : ServerStatItem
{ {
[Serializable] public long ProxyUp { get; set; }
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,21 +1,20 @@
using SQLite; using SQLite;
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class ServerStatItem
{ {
[Serializable] [PrimaryKey]
public class ServerStatItem public string IndexId { get; set; }
{
[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,13 +1,12 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class ServerTestItem
{ {
[Serializable] public string? IndexId { get; set; }
public class ServerTestItem public string? Address { get; set; }
{ public int Port { get; set; }
public string? IndexId { get; set; } public EConfigType ConfigType { get; set; }
public string? Address { get; set; } public bool AllowTest { get; set; }
public int Port { get; set; } public int QueueNum { get; set; }
public EConfigType ConfigType { get; set; }
public bool AllowTest { get; set; }
public int QueueNum { get; set; }
}
} }

View file

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

View file

@ -1,12 +1,11 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class SpeedTestResult
{ {
[Serializable] public string? IndexId { get; set; }
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,18 +1,17 @@
namespace ServiceLib.Models namespace ServiceLib.Models;
{
public class SsSIP008
{
public List<SsServer>? servers { get; set; }
}
[Serializable] public class SsSIP008
public class SsServer {
{ public List<SsServer>? servers { get; set; }
public string? remarks { get; set; } }
public string? server { get; set; }
public string? server_port { get; set; } [Serializable]
public string? method { get; set; } public class SsServer
public string? password { get; set; } {
public string? plugin { get; set; } public string? remarks { get; set; }
} public string? server { get; set; }
} public string? server_port { get; set; }
public string? method { get; set; }
public string? password { get; set; }
public string? plugin { get; set; }
}

View file

@ -1,39 +1,38 @@
using SQLite; using SQLite;
namespace ServiceLib.Models namespace ServiceLib.Models;
[Serializable]
public class SubItem
{ {
[Serializable] [PrimaryKey]
public class SubItem public string Id { get; set; }
{
[PrimaryKey]
public string Id { get; set; }
public string Remarks { get; set; } public string Remarks { get; set; }
public string Url { get; set; } public string Url { get; set; }
public string MoreUrl { get; set; } public string MoreUrl { get; set; }
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public string UserAgent { get; set; } = string.Empty; public string UserAgent { get; set; } = string.Empty;
public int Sort { get; set; } public int Sort { get; set; }
public string? Filter { get; set; } public string? Filter { get; set; }
public int AutoUpdateInterval { get; set; } public int AutoUpdateInterval { get; set; }
public long UpdateTime { get; set; } public long UpdateTime { get; set; }
public string? ConvertTarget { get; set; } public string? ConvertTarget { get; set; }
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,395 +1,436 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace ServiceLib.Models namespace ServiceLib.Models;
public class V2rayConfig
{ {
public class V2rayConfig public Log4Ray log { get; set; }
{ public object dns { get; set; }
public Log4Ray log { get; set; } public List<Inbounds4Ray> inbounds { get; set; }
public object dns { get; set; } public List<Outbounds4Ray> outbounds { get; set; }
public List<Inbounds4Ray> inbounds { get; set; } public Routing4Ray routing { get; set; }
public List<Outbounds4Ray> outbounds { get; set; } public Metrics4Ray? metrics { get; set; }
public Routing4Ray routing { get; set; } public Policy4Ray? policy { get; set; }
public Metrics4Ray? metrics { get; set; } public Stats4Ray? stats { get; set; }
public Policy4Ray? policy { get; set; } public Observatory4Ray? observatory { get; set; }
public Stats4Ray? stats { get; set; } public BurstObservatory4Ray? burstObservatory { 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 security { get; set; } public string? maxRTT { get; set; }
public float? tolerance { get; set; }
public TlsSettings4Ray? tlsSettings { get; set; } public List<string>? baselines { get; set; }
public List<BalancersStrategySettingsCosts4Ray>? costs { get; set; }
public TcpSettings4Ray? tcpSettings { get; set; } }
public KcpSettings4Ray? kcpSettings { get; set; } public class BalancersStrategySettingsCosts4Ray
{
public WsSettings4Ray? wsSettings { get; set; } public bool? regexp { get; set; }
public string? match { get; set; }
public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; } public float? value { get; set; }
}
public XhttpSettings4Ray? xhttpSettings { get; set; }
public class Observatory4Ray
public HttpSettings4Ray? httpSettings { get; set; } {
public List<string>? subjectSelector { get; set; }
public QuicSettings4Ray? quicSettings { get; set; } public string? probeUrl { get; set; }
public string? probeInterval { get; set; }
public TlsSettings4Ray? realitySettings { get; set; } public bool? enableConcurrency { 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 bool? allowInsecure { get; set; } public class BurstObservatoryPingConfig4Ray
{
public string? serverName { get; set; } public string? destination { get; set; }
public string? connectivity { get; set; }
public List<string>? alpn { get; set; } public string? interval { get; set; }
public int? sampling { get; set; }
public string? fingerprint { get; set; } public string? timeout { get; set; }
}
public bool? show { get; set; }
public string? publicKey { get; set; } public class StreamSettings4Ray
public string? shortId { get; set; } {
public string? spiderX { get; set; } public string network { 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 class Header4Ray public KcpSettings4Ray? kcpSettings { get; set; }
{
public string type { get; set; } public WsSettings4Ray? wsSettings { get; set; }
public object request { get; set; } public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; }
public object response { get; set; } public XhttpSettings4Ray? xhttpSettings { get; set; }
public string? domain { get; set; } public HttpSettings4Ray? httpSettings { 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 int downlinkCapacity { get; set; } public class TlsSettings4Ray
{
public bool congestion { get; set; } public bool? allowInsecure { get; set; }
public int readBufferSize { get; set; } public string? serverName { get; set; }
public int writeBufferSize { get; set; } public List<string>? alpn { get; set; }
public Header4Ray header { get; set; } public string? fingerprint { get; set; }
public string seed { get; set; } public bool? show { get; set; }
} public string? publicKey { get; set; }
public string? shortId { get; set; }
public class WsSettings4Ray public string? spiderX { get; set; }
{ }
public string? path { get; set; }
public string? host { get; set; } public class TcpSettings4Ray
{
public Headers4Ray headers { get; set; } public Header4Ray header { get; set; }
} }
public class Headers4Ray public class Header4Ray
{ {
public string Host { get; set; } public string type { get; set; }
[JsonPropertyName("User-Agent")] public object request { get; set; }
public string UserAgent { get; set; }
} public object response { get; set; }
public class HttpupgradeSettings4Ray public string? domain { get; set; }
{ }
public string? path { get; set; }
public class KcpSettings4Ray
public string? host { get; set; } {
} public int mtu { get; set; }
public class XhttpSettings4Ray public int tti { get; set; }
{
public string? path { get; set; } public int uplinkCapacity { get; set; }
public string? host { get; set; }
public string? mode { get; set; } public int downlinkCapacity { get; set; }
public object? extra { get; set; }
} public bool congestion { get; set; }
public class HttpSettings4Ray public int readBufferSize { get; set; }
{
public string? path { get; set; } public int writeBufferSize { get; set; }
public List<string>? host { get; set; } public Header4Ray header { get; set; }
}
public string seed { get; set; }
public class QuicSettings4Ray }
{
public string security { get; set; } public class WsSettings4Ray
{
public string key { get; set; } public string? path { 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? serviceName { get; set; } public string Host { get; set; }
public bool multiMode { get; set; }
public int? idle_timeout { get; set; } [JsonPropertyName("User-Agent")]
public int? health_check_timeout { get; set; } public string UserAgent { get; set; }
public bool? permit_without_stream { get; set; } }
public int? initial_windows_size { get; set; }
} public class HttpupgradeSettings4Ray
{
public class AccountsItem4Ray public string? path { get; set; }
{
public string user { get; set; } public string? host { get; set; }
}
public string pass { get; set; }
} public class XhttpSettings4Ray
{
public class Sockopt4Ray public string? path { get; set; }
{ public string? host { get; set; }
public string? dialerProxy { get; set; } public string? mode { get; set; }
} public object? extra { get; set; }
}
public class FragmentItem4Ray
{ public class HttpSettings4Ray
public string? packets { get; set; } {
public string? length { get; set; } public string? path { 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