Compare commits
320 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce22f34cd6 | ||
|
|
4c49e52e26 | ||
|
|
47318b5d70 | ||
|
|
d12297909c | ||
|
|
5fb4edae2d | ||
|
|
c4b490e46d | ||
|
|
45febe3fff | ||
|
|
689a81a985 | ||
|
|
28620b385a | ||
|
|
be13446e69 | ||
|
|
ee57d5b8e6 | ||
|
|
4eda3dd8fa | ||
|
|
911dc7f90e | ||
|
|
18005b96e8 | ||
|
|
a3e45d206e | ||
|
|
0accf262dc | ||
|
|
e0eb73bb0a | ||
|
|
258e822c13 | ||
|
|
4f05b93d63 | ||
|
|
bb661d4f50 | ||
|
|
201cfaa922 | ||
|
|
c339aa349c | ||
|
|
046421f345 | ||
|
|
3a2c9e7aaa | ||
|
|
0bb6e6bd86 | ||
|
|
693e8ab157 | ||
|
|
033c16c992 | ||
|
|
6ec61433fb | ||
|
|
072f773245 | ||
|
|
0086f65a96 | ||
|
|
281f14f47e | ||
|
|
9fd20ff001 | ||
|
|
7df90a6034 | ||
|
|
019869ec28 | ||
|
|
b7f4fd7469 | ||
|
|
1273d2aee1 | ||
|
|
88990b4828 | ||
|
|
2f02c2970c | ||
|
|
ade789c6d4 | ||
|
|
9738f90970 | ||
|
|
6ed0741339 | ||
|
|
e6b1e22245 | ||
|
|
6b922be0c6 | ||
|
|
6e35a260e8 | ||
|
|
1106fd8cf1 | ||
|
|
fb92b90d5c | ||
|
|
5effbee50b | ||
|
|
9bc50a9f34 | ||
|
|
2a5a339c27 | ||
|
|
78d182fff3 | ||
|
|
0efb0b5e3e | ||
|
|
a2e8755730 | ||
|
|
06ddedbc4c | ||
|
|
fa148cdf42 | ||
|
|
c1f71dee15 | ||
|
|
871cb81323 | ||
|
|
34f5c0f910 | ||
|
|
3fafc6de93 | ||
|
|
b013213745 | ||
|
|
40ea454a7c | ||
|
|
f0bac4b4c8 | ||
|
|
ffb38129e2 | ||
|
|
3c550c094a | ||
|
|
2d143687b8 | ||
|
|
1e7284f141 | ||
|
|
50bdf0927c | ||
|
|
10a60b26fa | ||
|
|
b3c2084b76 | ||
|
|
3bf2dc711d | ||
|
|
5a7836115e | ||
|
|
0da9cb45bd | ||
|
|
28aa954f8c | ||
|
|
4fc71fb77e | ||
|
|
5ea6b1e08a | ||
|
|
0cdfc97460 | ||
|
|
3dd54312e7 | ||
|
|
a866017b4c | ||
|
|
394b657fc9 | ||
|
|
50449df08d | ||
|
|
fe3836be14 | ||
|
|
055cd62dd8 | ||
|
|
4d1f7fa60c | ||
|
|
5c0fba8744 | ||
|
|
b74ddc0b43 | ||
|
|
d004c6860e | ||
|
|
fc3ba6c030 | ||
|
|
9c4dc185be | ||
|
|
2edbbc523a | ||
|
|
a5122b656d | ||
|
|
74f980aab1 | ||
|
|
35e5475255 | ||
|
|
fb649c04cf | ||
|
|
4ce7ca3c6f | ||
|
|
eebb6735aa | ||
|
|
e96c9abd69 | ||
|
|
fd7cf164ff | ||
|
|
24afe8bde4 | ||
|
|
9540669f56 | ||
|
|
c6caf29b5b | ||
|
|
2698137dea | ||
|
|
bdae08b13b | ||
|
|
019c69ecff | ||
|
|
6b99b7eec5 | ||
|
|
4eb443e547 | ||
|
|
e530789739 | ||
|
|
7618f9f7d4 | ||
|
|
d7bde77977 | ||
|
|
a0286ff810 | ||
|
|
b172b03927 | ||
|
|
a556bf9487 | ||
|
|
4f5362fdc4 | ||
|
|
176a91e7c5 | ||
|
|
3c45ef624a | ||
|
|
fea7c9fbd7 | ||
|
|
aa6b6e3849 | ||
|
|
f5956e7bf0 | ||
|
|
b2669103dc | ||
|
|
53e19ecb1d | ||
|
|
3e74bb65bd | ||
|
|
f60575b77c | ||
|
|
7a839063d0 | ||
|
|
4ccc7aa92d | ||
|
|
d5c6a42a9a | ||
|
|
3bdef4d6d8 | ||
|
|
f40eb724d7 | ||
|
|
4d84eede56 | ||
|
|
53a2fbd0ff | ||
|
|
409fe5290e | ||
|
|
75d86cf883 | ||
|
|
b33f536dbd | ||
|
|
1f77c56eaf | ||
|
|
fe895b1b27 | ||
|
|
20bb263b06 | ||
|
|
3ecbd3bc10 | ||
|
|
fe81b51dcb | ||
|
|
90ba80436d | ||
|
|
2bb5f6afc4 | ||
|
|
3971318ffb | ||
|
|
5fbeb4b0fb | ||
|
|
1b2b838bc0 | ||
|
|
8e28cc01b8 | ||
|
|
77a59e9107 | ||
|
|
1411643192 | ||
|
|
fde2a768cf | ||
|
|
b008a58cf8 | ||
|
|
8e02394ff4 | ||
|
|
60a75d9a31 | ||
|
|
24566901fb | ||
|
|
cc428618e5 | ||
|
|
4d330cedac | ||
|
|
2503583498 | ||
|
|
1b11916e1e | ||
|
|
3dfd557265 | ||
|
|
cd6bea28b6 | ||
|
|
054f0f2bc7 | ||
|
|
97347674e7 | ||
|
|
36d15852aa | ||
|
|
4813610492 | ||
|
|
243dbab06a | ||
|
|
073eaa9a26 | ||
|
|
cb1f936d05 | ||
|
|
a7f3a7b1a7 | ||
|
|
71eb5f0813 | ||
|
|
b4f50258a7 | ||
|
|
346a9c5fcc | ||
|
|
61635db5b5 | ||
|
|
4c0a59a715 | ||
|
|
aa829a66ea | ||
|
|
885f193a00 | ||
|
|
0a9bbf526d | ||
|
|
ff6716b39d | ||
|
|
8505f2db96 | ||
|
|
233d605256 | ||
|
|
c4e01d20a0 | ||
|
|
07156cb55c | ||
|
|
8361b4e4a0 | ||
|
|
bb90671979 | ||
|
|
beddc71ed8 | ||
|
|
d5f1cc99ac | ||
|
|
d03a86292e | ||
|
|
c061a24948 | ||
|
|
9c7446f820 | ||
|
|
8be1730719 | ||
|
|
7ceaf5c071 | ||
|
|
7901a59aa4 | ||
|
|
8343a1002f | ||
|
|
c83dce3cc0 | ||
|
|
14afeab2bb | ||
|
|
c22b57927c | ||
|
|
81b84d235c | ||
|
|
488e8aab00 | ||
|
|
fc43a9a726 | ||
|
|
31947fdcb3 | ||
|
|
34c7963d28 | ||
|
|
d91b0afb9a | ||
|
|
82eb3fd6bd | ||
|
|
3d3d3f83df | ||
|
|
6879c75bc8 | ||
|
|
b02ad6cdab | ||
|
|
f7b16952ea | ||
|
|
f0d05a7d4e | ||
|
|
d6ca317b20 | ||
|
|
2879fddfd9 | ||
|
|
b6c09470fc | ||
|
|
9fdf6c6c32 | ||
|
|
79085af994 | ||
|
|
d189f4b443 | ||
|
|
c9d65e5cd9 | ||
|
|
639d62588a | ||
|
|
6c9db51fd5 | ||
|
|
f0dbb6b22c | ||
|
|
f10f7b6268 | ||
|
|
48a5cbc552 | ||
|
|
6721d150e0 | ||
|
|
54c16cad7d | ||
|
|
ae3ab15245 | ||
|
|
d3c0f50fec | ||
|
|
43753b1b7a | ||
|
|
6f3e4b3682 | ||
|
|
b57cdd31bd | ||
|
|
b936c194e4 | ||
|
|
064431421a | ||
|
|
9d49c7aad0 | ||
|
|
a6f27e5071 | ||
|
|
8d1d10b783 | ||
|
|
61bea05f63 | ||
|
|
bbe7c7b884 | ||
|
|
a432852b78 | ||
|
|
770e8b8cfa | ||
|
|
7faabdc375 | ||
|
|
a9860418ba | ||
|
|
30ff9d0ea9 | ||
|
|
bbc2298939 | ||
|
|
3286e8e24d | ||
|
|
372f3991e1 | ||
|
|
9aa5c0d135 | ||
|
|
9e9808e489 | ||
|
|
3dd75b17cf | ||
|
|
2504b4737b | ||
|
|
8ff04dca0d | ||
|
|
d893ee4829 | ||
|
|
dfc5ec0705 | ||
|
|
8023eb74c9 | ||
|
|
bb4d3997ad | ||
|
|
7ede3af762 | ||
|
|
97ea1c7a9e | ||
|
|
43bb2c0fb8 | ||
|
|
6e7196bb27 | ||
|
|
87a71d58e8 | ||
|
|
32ffd43fe3 | ||
|
|
6500c8d85e | ||
|
|
9866d436da | ||
|
|
0f4884d9d8 | ||
|
|
5a81441351 | ||
|
|
e8721bfb6b | ||
|
|
1b09d95209 | ||
|
|
35f3b5a50e | ||
|
|
ff5203a561 | ||
|
|
c3c1ced309 | ||
|
|
08b1c8ec83 | ||
|
|
1dd3ee4e0a | ||
|
|
06d0c6517d | ||
|
|
4938ce6364 | ||
|
|
25f3fc354f | ||
|
|
ad1a1f8015 | ||
|
|
5c070e2ca8 | ||
|
|
70ea21fca2 | ||
|
|
bc3593871b | ||
|
|
355a424be2 | ||
|
|
0ffa9a0cc8 | ||
|
|
72fecb2b9a | ||
|
|
4e9dfe5478 | ||
|
|
c79e2e3ad4 | ||
|
|
7c33c1c322 | ||
|
|
ea32f75925 | ||
|
|
7eaed21b9a | ||
|
|
cfe8fcd28d | ||
|
|
9f4718af70 | ||
|
|
39faccfd0b | ||
|
|
7545763dae | ||
|
|
c2928be35d | ||
|
|
449bb40d48 | ||
|
|
4caf1a1e63 | ||
|
|
01b205e6f1 | ||
|
|
160d3843a6 | ||
|
|
5efb784115 | ||
|
|
acde5b8384 | ||
|
|
373ee6586a | ||
|
|
215308c329 | ||
|
|
478cadba5e | ||
|
|
f4af4da791 | ||
|
|
22d4f435de | ||
|
|
ccb8cd5c04 | ||
|
|
e963f9e349 | ||
|
|
e3c2a4b8da | ||
|
|
324f46cdad | ||
|
|
691b19b5fd | ||
|
|
63ed2311dc | ||
|
|
d59e59ca40 | ||
|
|
c9a278b4a2 | ||
|
|
8ee5304148 | ||
|
|
3beaa8145f | ||
|
|
e30c55a0af | ||
|
|
b583590a64 | ||
|
|
a28c63168a | ||
|
|
620422350f | ||
|
|
b77cc3c33b | ||
|
|
71bf9b4887 | ||
|
|
9039d401da | ||
|
|
74a0a93201 | ||
|
|
522571f0b3 | ||
|
|
123c49c22d | ||
|
|
1ff88d29be | ||
|
|
63d5a2a1be | ||
|
|
1e9a6cb06b | ||
|
|
31748aa660 | ||
|
|
d0c6ea6a63 | ||
|
|
7ffe286a56 | ||
|
|
b99b30163b | ||
|
|
83b4f1e660 |
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Set update schedule for GitHub Actions
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
# Check for updates to GitHub Actions every daily
|
||||
interval: "daily"
|
||||
12
v2rayN/AmazTool/AmazTool.csproj
Normal file
12
v2rayN/AmazTool/AmazTool.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
|
||||
<FileVersion>1.3.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
22
v2rayN/AmazTool/Program.cs
Normal file
22
v2rayN/AmazTool/Program.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace AmazTool
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用程序的主入口点。
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Please run it from the main application.(请从主应用运行)");
|
||||
Thread.Sleep(5000);
|
||||
return;
|
||||
}
|
||||
|
||||
var fileName = Uri.UnescapeDataString(string.Join(" ", args));
|
||||
UpgradeApp.Upgrade(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
127
v2rayN/AmazTool/UpgradeApp.cs
Normal file
127
v2rayN/AmazTool/UpgradeApp.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace AmazTool
|
||||
{
|
||||
internal class UpgradeApp
|
||||
{
|
||||
public static void Upgrade(string fileName)
|
||||
{
|
||||
Console.WriteLine(fileName);
|
||||
Console.WriteLine("In progress, please wait...(正在进行中,请等待)");
|
||||
|
||||
Thread.Sleep(9000);
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
Console.WriteLine("Upgrade Failed, File Not Exist(升级失败,文件不存在).");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Try to end the process(尝试结束进程).");
|
||||
try
|
||||
{
|
||||
var existing = Process.GetProcessesByName(V2rayN);
|
||||
foreach (var pp in existing)
|
||||
{
|
||||
pp?.Kill();
|
||||
pp?.WaitForExit(1000);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Access may be denied without admin right. The user may not be an administrator.
|
||||
Console.WriteLine("Failed to close v2rayN(关闭v2rayN失败).\n" +
|
||||
"Close it manually, or the upgrade may fail.(请手动关闭正在运行的v2rayN,否则可能升级失败。\n\n" + ex.StackTrace);
|
||||
}
|
||||
|
||||
Console.WriteLine("Start extracting files(开始解压文件).");
|
||||
StringBuilder sb = new();
|
||||
try
|
||||
{
|
||||
string thisAppOldFile = $"{GetExePath()}.tmp";
|
||||
File.Delete(thisAppOldFile);
|
||||
string splitKey = "/";
|
||||
|
||||
using ZipArchive archive = ZipFile.OpenRead(fileName);
|
||||
foreach (ZipArchiveEntry 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;
|
||||
string fullName = string.Join(splitKey, lst[1..lst.Length]);
|
||||
|
||||
if (string.Equals(GetExePath(), GetPath(fullName), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Move(GetExePath(), thisAppOldFile);
|
||||
}
|
||||
|
||||
string entryOutputPath = GetPath(fullName);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
|
||||
entry.ExtractToFile(entryOutputPath, true);
|
||||
|
||||
Console.WriteLine(entryOutputPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
sb.Append(ex.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Upgrade Failed(升级失败)." + ex.StackTrace);
|
||||
//return;
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
Console.WriteLine("Upgrade Failed(升级失败)." + sb.ToString());
|
||||
//return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Start v2rayN, please wait...(正在重启,请等待)");
|
||||
Thread.Sleep(9000);
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
UseShellExecute = true,
|
||||
FileName = V2rayN,
|
||||
WorkingDirectory = StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
}
|
||||
|
||||
private static string GetExePath()
|
||||
{
|
||||
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
|
||||
}
|
||||
|
||||
private static string StartupPath()
|
||||
{
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
}
|
||||
|
||||
private static string GetPath(string fileName)
|
||||
{
|
||||
string startupPath = StartupPath();
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return startupPath;
|
||||
}
|
||||
return Path.Combine(startupPath, fileName);
|
||||
}
|
||||
|
||||
private static string V2rayN => "v2rayN";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -77,8 +76,9 @@ public class PacHandler
|
||||
stream.Flush();
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
@@ -86,17 +86,16 @@ public class PacHandler
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
if (_tcpListener != null)
|
||||
if (_tcpListener == null) return;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
_isRunning = false;
|
||||
_tcpListener.Stop();
|
||||
_tcpListener = null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
_isRunning = false;
|
||||
_tcpListener.Stop();
|
||||
_tcpListener = null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.26.1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.62.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.63.0">
|
||||
<PackageReference Include="Google.Protobuf" Version="3.28.3" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.66.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.67.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
10
v2rayN/ServiceLib/Base/MyReactiveObject.cs
Normal file
10
v2rayN/ServiceLib/Base/MyReactiveObject.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.Base
|
||||
{
|
||||
public class MyReactiveObject : ReactiveObject
|
||||
{
|
||||
protected static Config? _config;
|
||||
protected Func<EViewAction, object?, Task<bool>>? _updateView;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using Downloader;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
internal class DownloaderHelper
|
||||
public class DownloaderHelper
|
||||
{
|
||||
private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
|
||||
public static DownloaderHelper Instance => _instance.Value;
|
||||
@@ -19,7 +18,7 @@ namespace v2rayN
|
||||
Uri uri = new(url);
|
||||
//Authorization Header
|
||||
var headers = new WebHeaderCollection();
|
||||
if (!Utils.IsNullOrEmpty(uri.UserInfo))
|
||||
if (Utils.IsNotEmpty(uri.UserInfo))
|
||||
{
|
||||
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
|
||||
}
|
||||
@@ -37,7 +36,7 @@ namespace v2rayN
|
||||
}
|
||||
};
|
||||
|
||||
using var downloader = new DownloadService(downloadOpt);
|
||||
await using var downloader = new Downloader.DownloadService(downloadOpt);
|
||||
downloader.DownloadFileCompleted += (sender, value) =>
|
||||
{
|
||||
if (value.Error != null)
|
||||
@@ -47,12 +46,12 @@ namespace v2rayN
|
||||
};
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
|
||||
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 reader.ReadToEnd();
|
||||
return await reader.ReadToEndAsync(cts.Token);
|
||||
}
|
||||
|
||||
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
|
||||
@@ -73,11 +72,11 @@ namespace v2rayN
|
||||
}
|
||||
};
|
||||
|
||||
DateTime totalDatetime = DateTime.Now;
|
||||
int totalSecond = 0;
|
||||
var totalDatetime = DateTime.Now;
|
||||
var totalSecond = 0;
|
||||
var hasValue = false;
|
||||
double maxSpeed = 0;
|
||||
using var downloader = new DownloadService(downloadOpt);
|
||||
await using var downloader = new Downloader.DownloadService(downloadOpt);
|
||||
//downloader.DownloadStarted += (sender, value) =>
|
||||
//{
|
||||
// if (progress != null)
|
||||
@@ -87,7 +86,7 @@ namespace v2rayN
|
||||
//};
|
||||
downloader.DownloadProgressChanged += (sender, value) =>
|
||||
{
|
||||
TimeSpan ts = (DateTime.Now - totalDatetime);
|
||||
var ts = (DateTime.Now - totalDatetime);
|
||||
if (progress != null && ts.Seconds > totalSecond)
|
||||
{
|
||||
hasValue = true;
|
||||
@@ -113,7 +112,7 @@ namespace v2rayN
|
||||
//progress.Report("......");
|
||||
using var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(timeout * 1000);
|
||||
using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token);
|
||||
await using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token);
|
||||
|
||||
downloadOpt = null;
|
||||
}
|
||||
@@ -146,7 +145,7 @@ namespace v2rayN
|
||||
|
||||
var progressPercentage = 0;
|
||||
var hasValue = false;
|
||||
using var downloader = new DownloadService(downloadOpt);
|
||||
await using var downloader = new Downloader.DownloadService(downloadOpt);
|
||||
downloader.DownloadStarted += (sender, value) =>
|
||||
{
|
||||
progress?.Report(0);
|
||||
@@ -169,11 +168,15 @@ namespace v2rayN
|
||||
{
|
||||
progress.Report(101);
|
||||
}
|
||||
else if (value.Error != null)
|
||||
{
|
||||
throw value.Error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
|
||||
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token);
|
||||
|
||||
downloadOpt = null;
|
||||
}
|
||||
198
v2rayN/ServiceLib/Common/FileManager.cs
Normal file
198
v2rayN/ServiceLib/Common/FileManager.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System.Formats.Tar;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public static class FileManager
|
||||
{
|
||||
public static bool ByteArrayToFile(string fileName, byte[] content)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllBytes(fileName, content);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, 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(ex.Message, 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(ex.Message, 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(ex.Message, 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(ex.Message, 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 (Utils.IsNotEmpty(ignoredName) && entry.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, 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(ex.Message, 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(ex.Message, ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, string? ignoredName)
|
||||
{
|
||||
// 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 (Utils.IsNotEmpty(ignoredName) && file.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (file.Extension == file.Name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var targetFilePath = Path.Combine(destinationDir, file.Name);
|
||||
file.CopyTo(targetFilePath, true);
|
||||
}
|
||||
|
||||
// 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, ignoredName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
@@ -21,6 +20,22 @@ namespace v2rayN
|
||||
|
||||
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
|
||||
|
||||
public async Task<string?> TryGetAsync(string url)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetAsync(url);
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string?> GetAsync(string url)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url)) return null;
|
||||
@@ -41,6 +56,21 @@ namespace v2rayN
|
||||
var result = await httpClient.PutAsync(url, content);
|
||||
}
|
||||
|
||||
public async Task PatchAsync(string url, Dictionary<string, string> headers)
|
||||
{
|
||||
var myContent = JsonUtils.Serialize(headers);
|
||||
var buffer = System.Text.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);
|
||||
@@ -54,8 +84,8 @@ namespace v2rayN
|
||||
var total = response.Content.Headers.ContentLength ?? -1L;
|
||||
var canReportProgress = total != -1 && progress != null;
|
||||
|
||||
using var stream = await response.Content.ReadAsStreamAsync(token);
|
||||
using var file = File.Create(fileName);
|
||||
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;
|
||||
@@ -68,7 +98,7 @@ namespace v2rayN
|
||||
totalRead += read;
|
||||
|
||||
if (read == 0) break;
|
||||
file.Write(buffer, 0, read);
|
||||
await file.WriteAsync(buffer.AsMemory(0, read), token);
|
||||
|
||||
if (canReportProgress)
|
||||
{
|
||||
@@ -103,13 +133,13 @@ namespace v2rayN
|
||||
//var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
|
||||
//var canReportProgress = total != -1 && progress != null;
|
||||
|
||||
using var stream = await response.Content.ReadAsStreamAsync(token);
|
||||
await using var stream = await response.Content.ReadAsStreamAsync(token);
|
||||
var totalRead = 0L;
|
||||
var buffer = new byte[1024 * 64];
|
||||
var isMoreToRead = true;
|
||||
string progressSpeed = string.Empty;
|
||||
DateTime totalDatetime = DateTime.Now;
|
||||
int totalSecond = 0;
|
||||
var progressSpeed = string.Empty;
|
||||
var totalDatetime = DateTime.Now;
|
||||
var totalSecond = 0;
|
||||
|
||||
do
|
||||
{
|
||||
@@ -138,7 +168,7 @@ namespace v2rayN
|
||||
|
||||
totalRead += read;
|
||||
|
||||
TimeSpan ts = (DateTime.Now - totalDatetime);
|
||||
var ts = (DateTime.Now - totalDatetime);
|
||||
if (progress != null && ts.Seconds > totalSecond)
|
||||
{
|
||||
totalSecond = ts.Seconds;
|
||||
@@ -1,14 +1,14 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
/*
|
||||
* See:
|
||||
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
|
||||
*/
|
||||
|
||||
public class Job : IDisposable
|
||||
public sealed class Job : IDisposable
|
||||
{
|
||||
private IntPtr handle = IntPtr.Zero;
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace v2rayN
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
@@ -1,11 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
internal class JsonUtils
|
||||
public class JsonUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// DeepCopy
|
||||
@@ -32,7 +31,11 @@ namespace v2rayN
|
||||
{
|
||||
return default;
|
||||
}
|
||||
return JsonSerializer.Deserialize<T>(strJson);
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
return JsonSerializer.Deserialize<T>(strJson, options);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -70,7 +73,7 @@ namespace v2rayN
|
||||
/// <returns></returns>
|
||||
public static string Serialize(object? obj, bool indented = true)
|
||||
{
|
||||
string result = string.Empty;
|
||||
var result = string.Empty;
|
||||
try
|
||||
{
|
||||
if (obj == null)
|
||||
@@ -105,12 +108,15 @@ namespace v2rayN
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="nullValue"></param>
|
||||
/// <returns></returns>
|
||||
public static int ToFile(object? obj, string filePath, bool nullValue = true)
|
||||
public static int ToFile(object? obj, string? filePath, bool nullValue = true)
|
||||
{
|
||||
int result;
|
||||
if (filePath is null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
try
|
||||
{
|
||||
using FileStream file = File.Create(filePath);
|
||||
using var file = File.Create(filePath);
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
@@ -119,14 +125,13 @@ namespace v2rayN
|
||||
};
|
||||
|
||||
JsonSerializer.Serialize(file, obj, options);
|
||||
result = 0;
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
result = -1;
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
using System.IO;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class Logging
|
||||
{
|
||||
@@ -38,40 +37,41 @@ namespace v2rayN
|
||||
foreach (var filePath in files)
|
||||
{
|
||||
var file = new FileInfo(filePath);
|
||||
if (file.CreationTime < now)
|
||||
if (file.CreationTime >= now) continue;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
catch { }
|
||||
file.Delete();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void SaveLog(string strContent)
|
||||
{
|
||||
if (LogManager.IsLoggingEnabled())
|
||||
{
|
||||
var logger = LogManager.GetLogger("Log1");
|
||||
logger.Info(strContent);
|
||||
}
|
||||
if (!LogManager.IsLoggingEnabled()) return;
|
||||
|
||||
LogManager.GetLogger("Log1").Info(strContent);
|
||||
}
|
||||
|
||||
public static void SaveLog(string strTitle, Exception ex)
|
||||
{
|
||||
if (LogManager.IsLoggingEnabled())
|
||||
if (!LogManager.IsLoggingEnabled()) return;
|
||||
|
||||
var logger = LogManager.GetLogger("Log2");
|
||||
logger.Debug($"{strTitle},{ex.Message}");
|
||||
logger.Debug(ex.StackTrace);
|
||||
if (ex?.InnerException != null)
|
||||
{
|
||||
var logger = LogManager.GetLogger("Log2");
|
||||
logger.Debug($"{strTitle},{ex.Message}");
|
||||
logger.Debug(ex.StackTrace);
|
||||
if (ex?.InnerException != null)
|
||||
{
|
||||
logger.Error(ex.InnerException);
|
||||
}
|
||||
logger.Error(ex.InnerException);
|
||||
}
|
||||
}
|
||||
}
|
||||
90
v2rayN/ServiceLib/Common/QRCodeHelper.cs
Normal file
90
v2rayN/ServiceLib/Common/QRCodeHelper.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using QRCoder;
|
||||
using SkiaSharp;
|
||||
using ZXing.SkiaSharp;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class QRCodeHelper
|
||||
{
|
||||
public static byte[]? GenQRCode(string? url)
|
||||
{
|
||||
using QRCodeGenerator qrGenerator = new();
|
||||
using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q);
|
||||
using PngByteQRCode qrCode = new(qrCodeData);
|
||||
return qrCode.GetGraphic(20);
|
||||
}
|
||||
|
||||
public static string? ParseBarcode(string? fileName)
|
||||
{
|
||||
if (fileName == null || !File.Exists(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var image = SKImage.FromEncodedData(fileName);
|
||||
var bitmap = SKBitmap.FromImage(image);
|
||||
|
||||
return ReaderBarcode(bitmap);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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 && Utils.IsNotEmpty(result.Text))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public static class QueryableExtension
|
||||
{
|
||||
@@ -17,7 +17,7 @@ namespace v2rayN
|
||||
|
||||
private static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc)
|
||||
{
|
||||
string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
|
||||
var methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
|
||||
|
||||
var memberProp = typeof(T).GetProperty(propertyName);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class SemanticVersion
|
||||
{
|
||||
@@ -15,19 +15,27 @@
|
||||
this.version = $"{major}.{minor}.{patch}";
|
||||
}
|
||||
|
||||
public SemanticVersion(string version)
|
||||
public SemanticVersion(string? version)
|
||||
{
|
||||
this.version = version.RemovePrefix('v');
|
||||
try
|
||||
{
|
||||
string[] parts = this.version.Split('.');
|
||||
if (version.IsNullOrEmpty())
|
||||
{
|
||||
this.major = 0;
|
||||
this.minor = 0;
|
||||
this.patch = 0;
|
||||
return;
|
||||
}
|
||||
this.version = version.RemovePrefix('v');
|
||||
|
||||
var parts = this.version.Split('.');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
this.major = int.Parse(parts[0]);
|
||||
this.minor = int.Parse(parts[1]);
|
||||
this.patch = 0;
|
||||
}
|
||||
else if (parts.Length == 3)
|
||||
else if (parts.Length is 3 or 4)
|
||||
{
|
||||
this.major = int.Parse(parts[0]);
|
||||
this.minor = int.Parse(parts[1]);
|
||||
@@ -43,7 +51,6 @@
|
||||
this.major = 0;
|
||||
this.minor = 0;
|
||||
this.patch = 0;
|
||||
//this.version = "0.0.0";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using SQLite;
|
||||
using System.Collections;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public sealed class SQLiteHelper
|
||||
{
|
||||
@@ -10,8 +10,7 @@ namespace v2rayN
|
||||
private string _connstr;
|
||||
private SQLiteConnection _db;
|
||||
private SQLiteAsyncConnection _dbAsync;
|
||||
private static readonly object objLock = new();
|
||||
public readonly string _configDB = "guiNDB.db";
|
||||
private readonly string _configDB = "guiNDB.db";
|
||||
|
||||
public SQLiteHelper()
|
||||
{
|
||||
@@ -25,17 +24,9 @@ namespace v2rayN
|
||||
return _db.CreateTable<T>();
|
||||
}
|
||||
|
||||
public int Insert(object model)
|
||||
public async Task<int> InsertAllAsync(IEnumerable models)
|
||||
{
|
||||
return _db.Insert(model);
|
||||
}
|
||||
|
||||
public int InsertAll(IEnumerable models)
|
||||
{
|
||||
lock (objLock)
|
||||
{
|
||||
return _db.InsertAll(models);
|
||||
}
|
||||
return await _dbAsync.InsertAllAsync(models);
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(object model)
|
||||
@@ -43,46 +34,19 @@ namespace v2rayN
|
||||
return await _dbAsync.InsertAsync(model);
|
||||
}
|
||||
|
||||
public int Replace(object model)
|
||||
{
|
||||
lock (objLock)
|
||||
{
|
||||
return _db.InsertOrReplace(model);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> ReplaceAsync(object model)
|
||||
{
|
||||
return await _dbAsync.InsertOrReplaceAsync(model);
|
||||
}
|
||||
|
||||
public int Update(object model)
|
||||
{
|
||||
lock (objLock)
|
||||
{
|
||||
return _db.Update(model);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<int> UpdateAsync(object model)
|
||||
{
|
||||
return await _dbAsync.UpdateAsync(model);
|
||||
}
|
||||
|
||||
public int UpdateAll(IEnumerable models)
|
||||
public async Task<int> UpdateAllAsync(IEnumerable models)
|
||||
{
|
||||
lock (objLock)
|
||||
{
|
||||
return _db.UpdateAll(models);
|
||||
}
|
||||
}
|
||||
|
||||
public int Delete(object model)
|
||||
{
|
||||
lock (objLock)
|
||||
{
|
||||
return _db.Delete(model);
|
||||
}
|
||||
return await _dbAsync.UpdateAllAsync(models);
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAsync(object model)
|
||||
@@ -90,19 +54,9 @@ namespace v2rayN
|
||||
return await _dbAsync.DeleteAsync(model);
|
||||
}
|
||||
|
||||
public List<T> Query<T>(string sql) where T : new()
|
||||
public async Task<int> DeleteAllAsync<T>()
|
||||
{
|
||||
return _db.Query<T>(sql);
|
||||
}
|
||||
|
||||
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
|
||||
{
|
||||
return await _dbAsync.QueryAsync<T>(sql);
|
||||
}
|
||||
|
||||
public int Execute(string sql)
|
||||
{
|
||||
return _db.Execute(sql);
|
||||
return await _dbAsync.DeleteAllAsync<T>();
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteAsync(string sql)
|
||||
@@ -110,9 +64,9 @@ namespace v2rayN
|
||||
return await _dbAsync.ExecuteAsync(sql);
|
||||
}
|
||||
|
||||
public TableQuery<T> Table<T>() where T : new()
|
||||
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
|
||||
{
|
||||
return _db.Table<T>();
|
||||
return await _dbAsync.QueryAsync<T>(sql);
|
||||
}
|
||||
|
||||
public AsyncTableQuery<T> TableAsync<T>() where T : new()
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
internal static class StringEx
|
||||
public static class StringEx
|
||||
{
|
||||
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
|
||||
{
|
||||
@@ -15,27 +14,25 @@ namespace v2rayN
|
||||
return string.IsNullOrWhiteSpace(value);
|
||||
}
|
||||
|
||||
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
|
||||
{
|
||||
return !string.IsNullOrEmpty(value);
|
||||
}
|
||||
|
||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||
{
|
||||
if (s.IsNullOrEmpty()) return false;
|
||||
return chars.Contains(s[0]);
|
||||
}
|
||||
|
||||
public static bool IsWhiteSpace(this string value)
|
||||
private static bool IsWhiteSpace(this string value)
|
||||
{
|
||||
foreach (char c in value)
|
||||
{
|
||||
if (char.IsWhiteSpace(c)) continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return value.All(char.IsWhiteSpace);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
|
||||
{
|
||||
string? line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
while (reader.ReadLine() is { } line)
|
||||
{
|
||||
if (line.IsWhiteSpace()) continue;
|
||||
yield return line;
|
||||
@@ -49,26 +46,12 @@ namespace v2rayN
|
||||
|
||||
public static string RemovePrefix(this string value, char prefix)
|
||||
{
|
||||
if (value.StartsWith(prefix))
|
||||
{
|
||||
return value.Substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return value.StartsWith(prefix) ? value[1..] : value;
|
||||
}
|
||||
|
||||
public static string RemovePrefix(this string value, string prefix)
|
||||
{
|
||||
if (value.StartsWith(prefix))
|
||||
{
|
||||
return value.Substring(prefix.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return value.StartsWith(prefix) ? value[prefix.Length..] : value;
|
||||
}
|
||||
|
||||
public static string UpperFirstChar(this string value)
|
||||
@@ -78,17 +61,12 @@ namespace v2rayN
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return char.ToUpper(value[0]) + value.Substring(1);
|
||||
return char.ToUpper(value[0]) + value[1..];
|
||||
}
|
||||
|
||||
public static string AppendQuotes(this string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return $"\"{value}\"";
|
||||
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
875
v2rayN/ServiceLib/Common/Utils.cs
Normal file
875
v2rayN/ServiceLib/Common/Utils.cs
Normal file
@@ -0,0 +1,875 @@
|
||||
using CliWrap;
|
||||
using CliWrap.Buffered;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class Utils
|
||||
{
|
||||
#region 资源操作
|
||||
|
||||
/// <summary>
|
||||
/// 获取嵌入文本资源
|
||||
/// </summary>
|
||||
/// <param name="res"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetEmbedText(string res)
|
||||
{
|
||||
var result = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
using var stream = assembly.GetManifestResourceStream(res);
|
||||
ArgumentNullException.ThrowIfNull(stream);
|
||||
using StreamReader reader = new(stream);
|
||||
result = reader.ReadToEnd();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得存储资源
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string? LoadResource(string? res)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(res))
|
||||
{
|
||||
return File.ReadAllText(res);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion 资源操作
|
||||
|
||||
#region 转换函数
|
||||
|
||||
/// <summary>
|
||||
/// 转逗号分隔的字符串
|
||||
/// </summary>
|
||||
/// <param name="lst"></param>
|
||||
/// <param name="wrap"></param>
|
||||
/// <returns></returns>
|
||||
public static string List2String(List<string>? lst, bool wrap = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (lst == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
if (wrap)
|
||||
{
|
||||
return string.Join("," + Environment.NewLine, lst);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Join(",", lst);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 逗号分隔的字符串
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string>? String2List(string? str)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
str = str.Replace(Environment.NewLine, "");
|
||||
return new List<string>(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 逗号分隔的字符串,先排序后转List
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string>? String2ListSorted(string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
str = str.Replace(Environment.NewLine, "");
|
||||
List<string> list = new(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
|
||||
list.Sort();
|
||||
return list;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base64编码
|
||||
/// </summary>
|
||||
/// <param name="plainText"></param>
|
||||
/// <returns></returns>
|
||||
public static string Base64Encode(string plainText)
|
||||
{
|
||||
try
|
||||
{
|
||||
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
return Convert.ToBase64String(plainTextBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("Base64Encode", ex);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base64解码
|
||||
/// </summary>
|
||||
/// <param name="plainText"></param>
|
||||
/// <returns></returns>
|
||||
public static string Base64Decode(string? plainText)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (plainText.IsNullOrEmpty()) return "";
|
||||
plainText = plainText.Trim()
|
||||
.Replace(Environment.NewLine, "")
|
||||
.Replace("\n", "")
|
||||
.Replace("\r", "")
|
||||
.Replace('_', '/')
|
||||
.Replace('-', '+')
|
||||
.Replace(" ", "");
|
||||
|
||||
if (plainText.Length % 4 > 0)
|
||||
{
|
||||
plainText = plainText.PadRight(plainText.Length + 4 - (plainText.Length % 4), '=');
|
||||
}
|
||||
|
||||
var data = Convert.FromBase64String(plainText);
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("Base64Decode", ex);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static int ToInt(object? obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Convert.ToInt32(obj ?? string.Empty);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ToBool(object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Convert.ToBoolean(obj);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToString(object? obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return obj?.ToString() ?? string.Empty;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToHumanReadable(long amount, out double result, out string unit)
|
||||
{
|
||||
var factor = 1024u;
|
||||
//long KBs = amount / factor;
|
||||
var KBs = amount;
|
||||
if (KBs > 0)
|
||||
{
|
||||
// multi KB
|
||||
var MBs = KBs / factor;
|
||||
if (MBs > 0)
|
||||
{
|
||||
// multi MB
|
||||
var GBs = MBs / factor;
|
||||
if (GBs > 0)
|
||||
{
|
||||
// multi GB
|
||||
var TBs = GBs / factor;
|
||||
if (TBs > 0)
|
||||
{
|
||||
result = TBs + ((GBs % factor) / (factor + 0.0));
|
||||
unit = "TB";
|
||||
return;
|
||||
}
|
||||
|
||||
result = GBs + ((MBs % factor) / (factor + 0.0));
|
||||
unit = "GB";
|
||||
return;
|
||||
}
|
||||
|
||||
result = MBs + ((KBs % factor) / (factor + 0.0));
|
||||
unit = "MB";
|
||||
return;
|
||||
}
|
||||
|
||||
result = KBs + ((amount % factor) / (factor + 0.0));
|
||||
unit = "KB";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = amount;
|
||||
unit = "B";
|
||||
}
|
||||
}
|
||||
|
||||
public static string HumanFy(long amount)
|
||||
{
|
||||
ToHumanReadable(amount, out var result, out var unit);
|
||||
return $"{result:f1} {unit}";
|
||||
}
|
||||
|
||||
public static string UrlEncode(string url)
|
||||
{
|
||||
return Uri.EscapeDataString(url);
|
||||
}
|
||||
|
||||
public static string UrlDecode(string url)
|
||||
{
|
||||
return Uri.UnescapeDataString(url);
|
||||
}
|
||||
|
||||
public static NameValueCollection ParseQueryString(string query)
|
||||
{
|
||||
var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
|
||||
if (IsNullOrEmpty(query))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var parts = query[1..].Split('&', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var keyValue = part.Split('=');
|
||||
if (keyValue.Length != 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = Uri.UnescapeDataString(keyValue[0]);
|
||||
var val = Uri.UnescapeDataString(keyValue[1]);
|
||||
|
||||
if (result[key] is null)
|
||||
{
|
||||
result.Add(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetMd5(string str)
|
||||
{
|
||||
var byteOld = Encoding.UTF8.GetBytes(str);
|
||||
var byteNew = MD5.HashData(byteOld);
|
||||
StringBuilder sb = new(32);
|
||||
foreach (var b in byteNew)
|
||||
{
|
||||
sb.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// idn to idc
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetPunycode(string url)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Uri uri = new(url);
|
||||
if (uri.Host == uri.IdnHost || uri.Host == $"[{uri.IdnHost}]")
|
||||
{
|
||||
return url;
|
||||
}
|
||||
else
|
||||
{
|
||||
return url.Replace(uri.Host, uri.IdnHost);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsBase64String(string? plainText)
|
||||
{
|
||||
if (plainText.IsNullOrEmpty()) return false;
|
||||
var buffer = new Span<byte>(new byte[plainText.Length]);
|
||||
return Convert.TryFromBase64String(plainText, buffer, out var _);
|
||||
}
|
||||
|
||||
public static string Convert2Comma(string text)
|
||||
{
|
||||
if (IsNullOrEmpty(text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.Replace(",", ",").Replace(Environment.NewLine, ",");
|
||||
}
|
||||
|
||||
#endregion 转换函数
|
||||
|
||||
#region 数据检查
|
||||
|
||||
/// <summary>
|
||||
/// 判断输入的是否是数字
|
||||
/// </summary>
|
||||
/// <param name="oText"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsNumeric(string oText)
|
||||
{
|
||||
return oText.All(char.IsNumber);
|
||||
}
|
||||
|
||||
public static bool IsNullOrEmpty(string? text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return text == "null";
|
||||
}
|
||||
|
||||
public static bool IsNotEmpty(string? text)
|
||||
{
|
||||
return !string.IsNullOrEmpty(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证Domain地址是否合法
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
public static bool IsDomain(string? domain)
|
||||
{
|
||||
if (IsNullOrEmpty(domain))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Uri.CheckHostName(domain) == UriHostNameType.Dns;
|
||||
}
|
||||
|
||||
public static bool IsIpv6(string ip)
|
||||
{
|
||||
if (IPAddress.TryParse(ip, out var address))
|
||||
{
|
||||
return address.AddressFamily switch
|
||||
{
|
||||
AddressFamily.InterNetwork => false,
|
||||
AddressFamily.InterNetworkV6 => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Uri? TryUri(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Uri(url);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPrivateNetwork(string ip)
|
||||
{
|
||||
if (IPAddress.TryParse(ip, out var address))
|
||||
{
|
||||
var ipBytes = address.GetAddressBytes();
|
||||
if (ipBytes[0] == 10) return true;
|
||||
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) return true;
|
||||
if (ipBytes[0] == 192 && ipBytes[1] == 168) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion 数据检查
|
||||
|
||||
#region 测速
|
||||
|
||||
private static bool PortInUse(int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
var ipEndPoints = ipProperties.GetActiveTcpListeners();
|
||||
//var lstIpEndPoints = new List<IPEndPoint>(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
|
||||
return ipEndPoints.Any(endPoint => endPoint.Port == port);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int GetFreePort(int defaultPort = 9090)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Utils.PortInUse(defaultPort))
|
||||
{
|
||||
return defaultPort;
|
||||
}
|
||||
|
||||
TcpListener l = new(IPAddress.Loopback, 0);
|
||||
l.Start();
|
||||
var port = ((IPEndPoint)l.LocalEndpoint).Port;
|
||||
l.Stop();
|
||||
return port;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return 59090;
|
||||
}
|
||||
|
||||
#endregion 测速
|
||||
|
||||
#region 杂项
|
||||
|
||||
public static bool UpgradeAppExists(out string fileName)
|
||||
{
|
||||
fileName = Path.Combine(Utils.StartupPath(), GetExeName("AmazTool"));
|
||||
return File.Exists(fileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得版本
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetVersion(bool blFull = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (blFull)
|
||||
{
|
||||
return
|
||||
$"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {File.GetLastWriteTime(GetExePath()):yyyy/MM/dd}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"{Global.AppName}/{GetVersionInfo()}";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return Global.AppName;
|
||||
}
|
||||
|
||||
public static string GetVersionInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString(3) ?? "0.0";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return "0.0";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得GUID
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetGuid(bool full = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (full)
|
||||
{
|
||||
return Guid.NewGuid().ToString("D");
|
||||
}
|
||||
else
|
||||
{
|
||||
return BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0).ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static bool IsGuidByParse(string strSrc)
|
||||
{
|
||||
return Guid.TryParse(strSrc, out _);
|
||||
}
|
||||
|
||||
public static void ProcessStart(string? fileName, string arguments = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> GetSystemHosts()
|
||||
{
|
||||
var systemHosts = new Dictionary<string, string>();
|
||||
var hostFile = @"C:\Windows\System32\drivers\etc\hosts";
|
||||
try
|
||||
{
|
||||
if (File.Exists(hostFile))
|
||||
{
|
||||
var hosts = File.ReadAllText(hostFile).Replace("\r", "");
|
||||
var hostsList = hosts.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var host in hostsList)
|
||||
{
|
||||
if (host.StartsWith("#")) continue;
|
||||
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (hostItem.Length < 2) continue;
|
||||
systemHosts.Add(hostItem[1], hostItem[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
|
||||
return systemHosts;
|
||||
}
|
||||
|
||||
public static async Task<string?> GetCliWrapOutput(string filePath, string? arg)
|
||||
{
|
||||
return await GetCliWrapOutput(filePath, arg != null ? new List<string>() { arg } : null);
|
||||
}
|
||||
|
||||
public static async Task<string?> GetCliWrapOutput(string filePath, IEnumerable<string>? args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cmd = Cli.Wrap(filePath);
|
||||
if (args != null)
|
||||
{
|
||||
if (args.Count() == 1)
|
||||
{
|
||||
cmd = cmd.WithArguments(args.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = cmd.WithArguments(args);
|
||||
}
|
||||
}
|
||||
|
||||
var result = await cmd.ExecuteBufferedAsync();
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
return result.StandardOutput.ToString();
|
||||
}
|
||||
|
||||
Logging.SaveLog(result.ToString() ?? "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GetCliWrapOutput", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion 杂项
|
||||
|
||||
#region TempPath
|
||||
|
||||
public static string GetPath(string fileName)
|
||||
{
|
||||
var startupPath = StartupPath();
|
||||
if (IsNullOrEmpty(fileName))
|
||||
{
|
||||
return startupPath;
|
||||
}
|
||||
|
||||
return Path.Combine(startupPath, fileName);
|
||||
}
|
||||
|
||||
public static string GetExePath()
|
||||
{
|
||||
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
|
||||
}
|
||||
|
||||
public static string StartupPath()
|
||||
{
|
||||
return AppDomain.CurrentDomain.BaseDirectory;
|
||||
}
|
||||
|
||||
public static string GetTempPath(string filename = "")
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiTemps");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetBackupPath(string filename)
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiBackups");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
|
||||
public static string GetConfigPath(string filename = "")
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiConfigs");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetBinPath(string filename, string? coreType = null)
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "bin");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (coreType != null)
|
||||
{
|
||||
tempPath = Path.Combine(tempPath, coreType.ToLower().ToString());
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetLogPath(string filename = "")
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiLogs");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFontsPath(string filename = "")
|
||||
{
|
||||
var tempPath = Path.Combine(StartupPath(), "guiFonts");
|
||||
if (!Directory.Exists(tempPath))
|
||||
{
|
||||
Directory.CreateDirectory(tempPath);
|
||||
}
|
||||
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return tempPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(tempPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion TempPath
|
||||
|
||||
#region Platform
|
||||
|
||||
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||
|
||||
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||
|
||||
public static string GetExeName(string name)
|
||||
{
|
||||
return IsWindows() ? $"{name}.exe" : name;
|
||||
}
|
||||
|
||||
public static bool IsAdministrator()
|
||||
{
|
||||
if (IsWindows())
|
||||
{
|
||||
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = GetLinuxUserId().Result ?? "1000";
|
||||
if (int.TryParse(id, out var userId))
|
||||
{
|
||||
return userId == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string?> GetLinuxUserId()
|
||||
{
|
||||
var arg = new List<string>() { "-c", "id -u" };
|
||||
return await GetCliWrapOutput("/bin/bash", arg);
|
||||
}
|
||||
|
||||
public static async Task<string?> SetLinuxChmod(string? fileName)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty()) return null;
|
||||
var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
|
||||
return await GetCliWrapOutput("/bin/bash", arg);
|
||||
}
|
||||
|
||||
public static async Task<string?> GetLinuxFontFamily(string lang)
|
||||
{
|
||||
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
|
||||
var arg = new List<string>() { "-c", $"fc-list : family" };
|
||||
return await GetCliWrapOutput("/bin/bash", arg);
|
||||
}
|
||||
|
||||
public static string? GetHomePath()
|
||||
{
|
||||
return IsWindows()
|
||||
? Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%")
|
||||
: Environment.GetEnvironmentVariable("HOME");
|
||||
}
|
||||
|
||||
#endregion Platform
|
||||
}
|
||||
}
|
||||
52
v2rayN/ServiceLib/Common/WindowsUtils.cs
Normal file
52
v2rayN/ServiceLib/Common/WindowsUtils.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
internal static class WindowsUtils
|
||||
{
|
||||
public static string? RegReadValue(string path, string name, string def)
|
||||
{
|
||||
RegistryKey? regKey = null;
|
||||
try
|
||||
{
|
||||
regKey = Registry.CurrentUser.OpenSubKey(path, false);
|
||||
var value = regKey?.GetValue(name) as string;
|
||||
return Utils.IsNullOrEmpty(value) ? def : value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
regKey?.Close();
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public static void RegWriteValue(string path, string name, object value)
|
||||
{
|
||||
RegistryKey? regKey = null;
|
||||
try
|
||||
{
|
||||
regKey = Registry.CurrentUser.CreateSubKey(path);
|
||||
if (Utils.IsNullOrEmpty(value.ToString()))
|
||||
{
|
||||
regKey?.DeleteValue(name, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
regKey?.SetValue(name, value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
regKey?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
81
v2rayN/ServiceLib/Common/YamlUtils.cs
Normal file
81
v2rayN/ServiceLib/Common/YamlUtils.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class YamlUtils
|
||||
{
|
||||
#region YAML
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化成对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static T FromYaml<T>(string str)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
var obj = deserializer.Deserialize<T>(str);
|
||||
return obj;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("FromYaml", ex);
|
||||
return deserializer.Deserialize<T>("");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToYaml(object? obj)
|
||||
{
|
||||
var result = string.Empty;
|
||||
if (obj == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
var serializer = new SerializerBuilder()
|
||||
.WithNamingConvention(HyphenatedNamingConvention.Instance)
|
||||
.Build();
|
||||
|
||||
try
|
||||
{
|
||||
result = serializer.Serialize(obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string? PreprocessYaml(string str)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
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("PreprocessYaml", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion YAML
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EConfigType
|
||||
{
|
||||
VMess = 1,
|
||||
Custom = 2,
|
||||
Shadowsocks = 3,
|
||||
Socks = 4,
|
||||
SOCKS = 4,
|
||||
VLESS = 5,
|
||||
Trojan = 6,
|
||||
Hysteria2 = 7,
|
||||
Tuic = 8,
|
||||
Wireguard = 9,
|
||||
Http = 10
|
||||
TUIC = 8,
|
||||
WireGuard = 9,
|
||||
HTTP = 10
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ECoreType
|
||||
{
|
||||
v2fly = 1,
|
||||
Xray = 2,
|
||||
SagerNet = 3,
|
||||
|
||||
//SagerNet = 3,
|
||||
v2fly_v5 = 4,
|
||||
clash = 11,
|
||||
clash_meta = 12,
|
||||
|
||||
//clash = 11,
|
||||
//clash_meta = 12,
|
||||
mihomo = 13,
|
||||
|
||||
hysteria = 21,
|
||||
naiveproxy = 22,
|
||||
tuic = 23,
|
||||
9
v2rayN/ServiceLib/Enums/EGirdOrientation.cs
Normal file
9
v2rayN/ServiceLib/Enums/EGirdOrientation.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EGirdOrientation
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Tab,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EGlobalHotkey
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EInboundProtocol
|
||||
{
|
||||
@@ -8,6 +8,7 @@
|
||||
http2,
|
||||
pac,
|
||||
api,
|
||||
api2,
|
||||
speedtest = 21
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EMove
|
||||
{
|
||||
11
v2rayN/ServiceLib/Enums/EMsgCommand.cs
Normal file
11
v2rayN/ServiceLib/Enums/EMsgCommand.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EMsgCommand
|
||||
{
|
||||
ClearMsg,
|
||||
SendMsgView,
|
||||
SendSnackMsg,
|
||||
RefreshProfiles,
|
||||
StopSpeedtest
|
||||
}
|
||||
}
|
||||
8
v2rayN/ServiceLib/Enums/EPresetType.cs
Normal file
8
v2rayN/ServiceLib/Enums/EPresetType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EPresetType
|
||||
{
|
||||
Default = 0,
|
||||
Russia = 1,
|
||||
}
|
||||
}
|
||||
10
v2rayN/ServiceLib/Enums/ERuleMode.cs
Normal file
10
v2rayN/ServiceLib/Enums/ERuleMode.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ERuleMode
|
||||
{
|
||||
Rule = 0,
|
||||
Global = 1,
|
||||
Direct = 2,
|
||||
Unchanged = 3
|
||||
}
|
||||
}
|
||||
21
v2rayN/ServiceLib/Enums/EServerColName.cs
Normal file
21
v2rayN/ServiceLib/Enums/EServerColName.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EServerColName
|
||||
{
|
||||
Def = 0,
|
||||
ConfigType,
|
||||
Remarks,
|
||||
Address,
|
||||
Port,
|
||||
Network,
|
||||
StreamSecurity,
|
||||
SubRemarks,
|
||||
DelayVal,
|
||||
SpeedVal,
|
||||
|
||||
TodayDown,
|
||||
TodayUp,
|
||||
TotalDown,
|
||||
TotalUp
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ESpeedActionType
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ESysProxyType
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ETransport
|
||||
{
|
||||
@@ -6,6 +6,7 @@
|
||||
kcp,
|
||||
ws,
|
||||
httpupgrade,
|
||||
splithttp,
|
||||
h2,
|
||||
http,
|
||||
quic,
|
||||
46
v2rayN/ServiceLib/Enums/EViewAction.cs
Normal file
46
v2rayN/ServiceLib/Enums/EViewAction.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EViewAction
|
||||
{
|
||||
CloseWindow,
|
||||
ShowYesNo,
|
||||
SaveFileDialog,
|
||||
AddBatchRoutingRulesYesNo,
|
||||
AdjustMainLvColWidth,
|
||||
SetClipboardData,
|
||||
AddServerViaClipboard,
|
||||
ImportRulesFromClipboard,
|
||||
ProfilesFocus,
|
||||
ShareSub,
|
||||
ShareServer,
|
||||
ShowHideWindow,
|
||||
ScanScreenTask,
|
||||
ScanImageTask,
|
||||
Shutdown,
|
||||
BrowseServer,
|
||||
ImportRulesFromFile,
|
||||
InitSettingFont,
|
||||
SubEditWindow,
|
||||
RoutingRuleSettingWindow,
|
||||
RoutingRuleDetailsWindow,
|
||||
AddServerWindow,
|
||||
AddServer2Window,
|
||||
DNSSettingWindow,
|
||||
RoutingSettingWindow,
|
||||
OptionSettingWindow,
|
||||
GlobalHotkeySettingWindow,
|
||||
SubSettingWindow,
|
||||
DispatcherSpeedTest,
|
||||
DispatcherRefreshConnections,
|
||||
DispatcherRefreshProxyGroups,
|
||||
DispatcherProxiesDelayTest,
|
||||
DispatcherStatistics,
|
||||
DispatcherServerAvailability,
|
||||
DispatcherReload,
|
||||
DispatcherRefreshServersBiz,
|
||||
DispatcherRefreshIcon,
|
||||
DispatcherCheckUpdate,
|
||||
DispatcherCheckUpdateFinished,
|
||||
DispatcherShowMsg,
|
||||
}
|
||||
}
|
||||
3
v2rayN/ServiceLib/FodyWeavers.xml
Normal file
3
v2rayN/ServiceLib/FodyWeavers.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ReactiveUI />
|
||||
</Weavers>
|
||||
@@ -1,19 +1,15 @@
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib
|
||||
{
|
||||
internal class Global
|
||||
public class Global
|
||||
{
|
||||
#region const
|
||||
|
||||
public const string AppName = "v2rayN";
|
||||
public const string GithubUrl = "https://github.com";
|
||||
public const string GithubApiUrl = "https://api.github.com/repos";
|
||||
public const string V2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases";
|
||||
public const string XrayCoreUrl = "https://github.com/XTLS/Xray-core/releases";
|
||||
public const string SagerNetCoreUrl = "https://github.com/SagerNet/v2ray-core/releases";
|
||||
public const string NUrl = @"https://github.com/2dust/v2rayN/releases";
|
||||
public const string ClashCoreUrl = "https://github.com/Dreamacro/clash/releases";
|
||||
public const string ClashMetaCoreUrl = "https://github.com/MetaCubeX/Clash.Meta/releases";
|
||||
public const string MihomoCoreUrl = "https://github.com/MetaCubeX/mihomo/releases";
|
||||
public const string HysteriaCoreUrl = "https://github.com/apernet/hysteria/releases";
|
||||
public const string NaiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases";
|
||||
@@ -23,26 +19,33 @@ namespace v2rayN
|
||||
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
|
||||
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
|
||||
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
|
||||
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/SagerNet/sing-{0}/rule-set/{1}.srs";
|
||||
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
|
||||
|
||||
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
|
||||
public const string ConfigFileName = "guiNConfig.json";
|
||||
public const string CoreConfigFileName = "config.json";
|
||||
public const string CorePreConfigFileName = "configPre.json";
|
||||
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
|
||||
public const string V2raySampleClient = "v2rayN.Sample.SampleClientConfig";
|
||||
public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig";
|
||||
public const string V2raySampleHttpRequestFileName = "v2rayN.Sample.SampleHttpRequest";
|
||||
public const string V2raySampleHttpResponseFileName = "v2rayN.Sample.SampleHttpResponse";
|
||||
public const string V2raySampleInbound = "v2rayN.Sample.SampleInbound";
|
||||
public const string V2raySampleOutbound = "v2rayN.Sample.SampleOutbound";
|
||||
public const string SingboxSampleOutbound = "v2rayN.Sample.SingboxSampleOutbound";
|
||||
public const string CustomRoutingFileName = "v2rayN.Sample.custom_routing_";
|
||||
public const string TunSingboxDNSFileName = "v2rayN.Sample.tun_singbox_dns";
|
||||
public const string TunSingboxInboundFileName = "v2rayN.Sample.tun_singbox_inbound";
|
||||
public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules";
|
||||
public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal";
|
||||
public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal";
|
||||
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
|
||||
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
||||
|
||||
public const string NamespaceSample = "ServiceLib.Sample.";
|
||||
public const string V2raySampleClient = NamespaceSample + "SampleClientConfig";
|
||||
public const string SingboxSampleClient = NamespaceSample + "SingboxSampleClientConfig";
|
||||
public const string V2raySampleHttpRequestFileName = NamespaceSample + "SampleHttpRequest";
|
||||
public const string V2raySampleHttpResponseFileName = NamespaceSample + "SampleHttpResponse";
|
||||
public const string V2raySampleInbound = NamespaceSample + "SampleInbound";
|
||||
public const string V2raySampleOutbound = NamespaceSample + "SampleOutbound";
|
||||
public const string SingboxSampleOutbound = NamespaceSample + "SingboxSampleOutbound";
|
||||
public const string CustomRoutingFileName = NamespaceSample + "custom_routing_";
|
||||
public const string TunSingboxDNSFileName = NamespaceSample + "tun_singbox_dns";
|
||||
public const string TunSingboxInboundFileName = NamespaceSample + "tun_singbox_inbound";
|
||||
public const string TunSingboxRulesFileName = NamespaceSample + "tun_singbox_rules";
|
||||
public const string DNSV2rayNormalFileName = NamespaceSample + "dns_v2ray_normal";
|
||||
public const string DNSSingboxNormalFileName = NamespaceSample + "dns_singbox_normal";
|
||||
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
|
||||
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
|
||||
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
|
||||
|
||||
public const string DefaultSecurity = "auto";
|
||||
public const string DefaultNetwork = "tcp";
|
||||
@@ -57,6 +60,7 @@ namespace v2rayN
|
||||
public const string InboundAPIProtocol = "dokodemo-door";
|
||||
public const string HttpProtocol = "http://";
|
||||
public const string HttpsProtocol = "https://";
|
||||
public const string SocksProtocol = "socks://";
|
||||
|
||||
public const string UserEMail = "t@t.tt";
|
||||
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
|
||||
@@ -67,9 +71,6 @@ namespace v2rayN
|
||||
public const string GrpcGunMode = "gun";
|
||||
public const string GrpcMultiMode = "multi";
|
||||
public const int MaxPort = 65536;
|
||||
public const string CommandClearMsg = "CommandClearMsg";
|
||||
public const string CommandSendMsgView = "CommandSendMsgView";
|
||||
public const string CommandStopSpeedTest = "CommandStopSpeedTest";
|
||||
public const string DelayUnit = "";
|
||||
public const string SpeedUnit = "";
|
||||
public const int MinFontSize = 10;
|
||||
@@ -104,18 +105,38 @@ namespace v2rayN
|
||||
|
||||
public static readonly List<string> SpeedTestUrls = new() {
|
||||
@"https://speed.cloudflare.com/__down?bytes=100000000",
|
||||
@"https://speed.cloudflare.com/__down?bytes=50000000",
|
||||
@"https://speed.cloudflare.com/__down?bytes=10000000",
|
||||
@"http://cachefly.cachefly.net/50mb.test",
|
||||
@"http://cachefly.cachefly.net/100mb.test",
|
||||
@"http://cachefly.cachefly.net/10mb.test"
|
||||
@"https://cachefly.cachefly.net/50mb.test",
|
||||
};
|
||||
|
||||
public static readonly List<string> SpeedPingTestUrls = new() {
|
||||
@"https://www.google.com/generate_204",
|
||||
@"https://www.gstatic.com/generate_204",
|
||||
@"https://www.apple.com/library/test/success.html",
|
||||
@"http://www.msftconnecttest.com/connecttest.txt",
|
||||
};
|
||||
|
||||
public static readonly List<string> GeoFilesSources = new() {
|
||||
"",
|
||||
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
|
||||
};
|
||||
|
||||
public static readonly List<string> SingboxRulesetSources = new() {
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs",
|
||||
};
|
||||
|
||||
public static readonly List<string> RoutingRulesSources = new() {
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json",
|
||||
};
|
||||
|
||||
public static readonly List<string> DNSTemplateSources = new() {
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/",
|
||||
};
|
||||
|
||||
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" },
|
||||
@@ -131,25 +152,25 @@ namespace v2rayN
|
||||
{
|
||||
{EConfigType.VMess,"vmess://"},
|
||||
{EConfigType.Shadowsocks,"ss://"},
|
||||
{EConfigType.Socks,"socks://"},
|
||||
{EConfigType.SOCKS,"socks://"},
|
||||
{EConfigType.VLESS,"vless://"},
|
||||
{EConfigType.Trojan,"trojan://"},
|
||||
{EConfigType.Hysteria2,"hysteria2://"},
|
||||
{EConfigType.Tuic,"tuic://"},
|
||||
{EConfigType.Wireguard,"wireguard://"}
|
||||
{EConfigType.TUIC,"tuic://"},
|
||||
{EConfigType.WireGuard,"wireguard://"}
|
||||
};
|
||||
|
||||
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
|
||||
{
|
||||
{EConfigType.VMess,"vmess"},
|
||||
{EConfigType.Shadowsocks,"shadowsocks"},
|
||||
{EConfigType.Socks,"socks"},
|
||||
{EConfigType.Http,"http"},
|
||||
{EConfigType.SOCKS,"socks"},
|
||||
{EConfigType.HTTP,"http"},
|
||||
{EConfigType.VLESS,"vless"},
|
||||
{EConfigType.Trojan,"trojan"},
|
||||
{EConfigType.Hysteria2,"hysteria2"},
|
||||
{EConfigType.Tuic,"tuic"},
|
||||
{EConfigType.Wireguard,"wireguard"}
|
||||
{EConfigType.TUIC,"tuic"},
|
||||
{EConfigType.WireGuard,"wireguard"}
|
||||
};
|
||||
|
||||
public static readonly List<string> VmessSecurities = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" };
|
||||
@@ -158,9 +179,9 @@ namespace v2rayN
|
||||
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
|
||||
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
|
||||
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
|
||||
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "h2", "quic", "grpc" };
|
||||
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "splithttp", "h2", "quic", "grpc" };
|
||||
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
|
||||
public static readonly List<string> CoreTypes = new() { "v2fly", "SagerNet", "Xray", "sing_box" };
|
||||
public static readonly List<string> CoreTypes = new() { "v2fly", "Xray", "sing_box" };
|
||||
public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" };
|
||||
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
|
||||
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
|
||||
@@ -170,11 +191,15 @@ namespace v2rayN
|
||||
|
||||
public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
|
||||
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
|
||||
public static readonly List<string> SingboxDomainStrategy4Out = new() { "ipv4_only", "prefer_ipv4", "prefer_ipv6", "ipv6_only", "" };
|
||||
public static readonly List<string> DomainDNSAddress = ["223.5.5.5", "223.6.6.6", "localhost"];
|
||||
public static readonly List<string> SingboxDomainDNSAddress = ["223.5.5.5", "223.6.6.6", "dhcp://auto"];
|
||||
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
|
||||
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2,http/1.1", "h3,h2", "h2,http/1.1", "" };
|
||||
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
|
||||
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
|
||||
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
|
||||
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
|
||||
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
|
||||
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
|
||||
public static readonly List<string> TunMtus = new() { "1280", "1408", "1500", "9000" };
|
||||
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
|
||||
@@ -182,6 +207,10 @@ namespace v2rayN
|
||||
public static readonly List<string> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
|
||||
public static readonly List<string> TuicCongestionControls = new() { "cubic", "new_reno", "bbr" };
|
||||
|
||||
public static readonly List<string> allowSelectType = new List<string> { "selector", "urltest", "loadbalance", "fallback" };
|
||||
public static readonly List<string> notAllowTestType = new List<string> { "selector", "urltest", "direct", "reject", "compatible", "pass", "loadbalance", "fallback" };
|
||||
public static readonly List<string> proxyVehicleType = new List<string> { "file", "http" };
|
||||
|
||||
#endregion const
|
||||
}
|
||||
}
|
||||
11
v2rayN/ServiceLib/GlobalUsings.cs
Normal file
11
v2rayN/ServiceLib/GlobalUsings.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Common;
|
||||
global using ServiceLib.Enums;
|
||||
global using ServiceLib.Handler;
|
||||
global using ServiceLib.Handler.Fmt;
|
||||
global using ServiceLib.Services;
|
||||
global using ServiceLib.Services.Statistics;
|
||||
global using ServiceLib.Services.CoreConfig;
|
||||
global using ServiceLib.Models;
|
||||
global using ServiceLib.Resx;
|
||||
global using ServiceLib.Handler.SysProxy;
|
||||
274
v2rayN/ServiceLib/Handler/AppHandler.cs
Normal file
274
v2rayN/ServiceLib/Handler/AppHandler.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
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
|
||||
{
|
||||
get
|
||||
{
|
||||
_statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
|
||||
return _statePort.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public int StatePort2
|
||||
{
|
||||
get
|
||||
{
|
||||
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
|
||||
return _statePort2.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAdministrator
|
||||
{
|
||||
get
|
||||
{
|
||||
_isAdministrator ??= Utils.IsAdministrator();
|
||||
return _isAdministrator.Value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Property
|
||||
|
||||
#region Init
|
||||
|
||||
public bool InitApp()
|
||||
{
|
||||
_config = ConfigHandler.LoadConfig();
|
||||
if (_config == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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.Setup();
|
||||
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
|
||||
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
|
||||
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
||||
Logging.ClearLogs();
|
||||
|
||||
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();
|
||||
_processJob?.AddProcess(processHandle);
|
||||
}
|
||||
}
|
||||
|
||||
#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 (Utils.IsNullOrEmpty(subid))
|
||||
{
|
||||
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
|
||||
from ProfileItem a
|
||||
left join SubItem b on a.subid = b.id
|
||||
where 1=1 ";
|
||||
if (Utils.IsNotEmpty(subid))
|
||||
{
|
||||
sql += $" and a.subid = '{subid}'";
|
||||
}
|
||||
if (Utils.IsNotEmpty(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<List<ProfileItemModel>?> ProfileItemsEx(string subid, string filter)
|
||||
{
|
||||
var lstModel = await ProfileItems(_config.SubIndexId, filter);
|
||||
|
||||
await ConfigHandler.SetDefaultServer(_config, lstModel);
|
||||
|
||||
var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
|
||||
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
|
||||
lstModel = (from t in lstModel
|
||||
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
|
||||
from t22 in t2b.DefaultIfEmpty()
|
||||
join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b
|
||||
from t33 in t3b.DefaultIfEmpty()
|
||||
select new ProfileItemModel
|
||||
{
|
||||
IndexId = t.IndexId,
|
||||
ConfigType = t.ConfigType,
|
||||
Remarks = t.Remarks,
|
||||
Address = t.Address,
|
||||
Port = t.Port,
|
||||
Security = t.Security,
|
||||
Network = t.Network,
|
||||
StreamSecurity = t.StreamSecurity,
|
||||
Subid = t.Subid,
|
||||
SubRemarks = t.SubRemarks,
|
||||
IsActive = t.IndexId == _config.IndexId,
|
||||
Sort = t33 == null ? 0 : t33.Sort,
|
||||
Delay = t33 == null ? 0 : t33.Delay,
|
||||
DelayVal = t33?.Delay != 0 ? $"{t33?.Delay} {Global.DelayUnit}" : string.Empty,
|
||||
SpeedVal = t33?.Speed != 0 ? $"{t33?.Speed} {Global.SpeedUnit}" : string.Empty,
|
||||
TodayDown = t22 == null ? "" : Utils.HumanFy(t22.TodayDown),
|
||||
TodayUp = t22 == null ? "" : Utils.HumanFy(t22.TodayUp),
|
||||
TotalDown = t22 == null ? "" : Utils.HumanFy(t22.TotalDown),
|
||||
TotalUp = t22 == null ? "" : Utils.HumanFy(t22.TotalUp)
|
||||
}).OrderBy(t => t.Sort).ToList();
|
||||
|
||||
return lstModel;
|
||||
}
|
||||
|
||||
public async Task<ProfileItem?> GetProfileItem(string indexId)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(indexId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
|
||||
}
|
||||
|
||||
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(remarks))
|
||||
{
|
||||
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>().Where(it => it.Locked == false).OrderBy(t => t.Sort).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<RoutingItem?> GetRoutingItem(string id)
|
||||
{
|
||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Locked == false && 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.SsSecuritiesInSagerNet;
|
||||
}
|
||||
|
||||
public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType)
|
||||
{
|
||||
if (profileItem?.CoreType != null)
|
||||
{
|
||||
return (ECoreType)profileItem.CoreType;
|
||||
}
|
||||
|
||||
if (_config.CoreTypeItem == null)
|
||||
{
|
||||
return ECoreType.Xray;
|
||||
}
|
||||
var item = _config.CoreTypeItem.FirstOrDefault(it => it.ConfigType == eConfigType);
|
||||
if (item == null)
|
||||
{
|
||||
return ECoreType.Xray;
|
||||
}
|
||||
return item.CoreType;
|
||||
}
|
||||
|
||||
#endregion Core Type
|
||||
}
|
||||
}
|
||||
155
v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
Normal file
155
v2rayN/ServiceLib/Handler/AutoStartupHandler.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System.Security.Principal;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public static class AutoStartupHandler
|
||||
{
|
||||
public static async Task<bool> UpdateTask(Config config)
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
await ClearTaskWindows();
|
||||
|
||||
if (config.GuiItem.AutoRun)
|
||||
{
|
||||
await SetTaskWindows();
|
||||
}
|
||||
}
|
||||
else if (Utils.IsLinux())
|
||||
{
|
||||
await ClearTaskLinux();
|
||||
|
||||
if (config.GuiItem.AutoRun)
|
||||
{
|
||||
await SetTaskLinux();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Windows
|
||||
|
||||
private static async Task ClearTaskWindows()
|
||||
{
|
||||
var autoRunName = GetAutoRunNameWindows();
|
||||
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, "");
|
||||
if (Utils.IsAdministrator())
|
||||
{
|
||||
AutoStartTaskService(autoRunName, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SetTaskWindows()
|
||||
{
|
||||
try
|
||||
{
|
||||
var autoRunName = GetAutoRunNameWindows();
|
||||
var exePath = Utils.GetExePath();
|
||||
if (Utils.IsAdministrator())
|
||||
{
|
||||
AutoStartTaskService(autoRunName, exePath, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath.AppendQuotes());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 (Utils.IsNullOrEmpty(taskName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var logonUser = WindowsIdentity.GetCurrent().Name;
|
||||
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
|
||||
var tasks = taskService.RootFolder.GetTasks(new Regex(taskName));
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
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(10) });
|
||||
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(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task SetTaskLinux()
|
||||
{
|
||||
try
|
||||
{
|
||||
var linuxConfig = Utils.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(ex.Message, 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
|
||||
}
|
||||
}
|
||||
196
v2rayN/ServiceLib/Handler/ClashApiHandler.cs
Normal file
196
v2rayN/ServiceLib/Handler/ClashApiHandler.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
using static ServiceLib.Models.ClashProxies;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class ClashApiHandler
|
||||
{
|
||||
private static readonly Lazy<ClashApiHandler> instance = new(() => new());
|
||||
public static ClashApiHandler Instance => instance.Value;
|
||||
|
||||
private Dictionary<string, ProxiesItem>? _proxies;
|
||||
public Dictionary<string, object> ProfileContent { get; set; }
|
||||
|
||||
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config)
|
||||
{
|
||||
for (var i = 0; i < 5; 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)
|
||||
{
|
||||
_proxies = clashProxies?.proxies;
|
||||
return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders);
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (blAll)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (_proxies != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Task.Delay(5000).Wait();
|
||||
}
|
||||
if (_proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lstProxy = new List<ClashProxyModel>();
|
||||
foreach (KeyValuePair<string, ProxiesItem> 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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (lstProxy == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var urlBase = $"{GetApiUrl()}/proxies";
|
||||
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
|
||||
|
||||
List<Task> 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);
|
||||
}));
|
||||
}
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
|
||||
Task.Delay(1000).Wait();
|
||||
updateFunc?.Invoke(null, "");
|
||||
});
|
||||
}
|
||||
|
||||
public List<ProxiesItem>? GetClashProxyGroups()
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileContent = ProfileContent;
|
||||
if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GetClashProxyGroups", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClashSetActiveProxy(string name, string nameNode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/proxies/{name}";
|
||||
Dictionary<string, string> headers = new Dictionary<string, string>();
|
||||
headers.Add("name", nameNode);
|
||||
await HttpClientHelper.Instance.PutAsync(url, headers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, 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";
|
||||
Dictionary<string, string> headers = new Dictionary<string, string>();
|
||||
headers.Add("path", filePath);
|
||||
await HttpClientHelper.Instance.PutAsync(url, headers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ClashConnections?> GetClashConnectionsAsync(Config config)
|
||||
{
|
||||
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(ex.Message, 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(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetApiUrl()
|
||||
{
|
||||
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
|
||||
}
|
||||
}
|
||||
}
|
||||
1833
v2rayN/ServiceLib/Handler/ConfigHandler.cs
Normal file
1833
v2rayN/ServiceLib/Handler/ConfigHandler.cs
Normal file
File diff suppressed because it is too large
Load Diff
130
v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
Normal file
130
v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
/// </summary>
|
||||
public class CoreConfigHandler
|
||||
{
|
||||
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var config = AppHandler.Instance.Config;
|
||||
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),
|
||||
_ => await GenerateClientCustomConfig(node, fileName)
|
||||
};
|
||||
}
|
||||
else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node);
|
||||
}
|
||||
if (result.Success != true)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (Utils.IsNotEmpty(fileName) && result.Data != null)
|
||||
{
|
||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
try
|
||||
{
|
||||
if (node == null || fileName is null)
|
||||
{
|
||||
ret.Msg = ResUI.CheckServerSettings;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
string addressFileName = node.Address;
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Utils.GetConfigPath(addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
File.Copy(addressFileName, fileName);
|
||||
File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file.
|
||||
|
||||
//check again
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
ret.Success = true;
|
||||
return ret;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GenerateClientCustomConfig", ex);
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
|
||||
{
|
||||
var result = new RetResult();
|
||||
if (coreType == ECoreType.sing_box)
|
||||
{
|
||||
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds);
|
||||
}
|
||||
else if (coreType == ECoreType.Xray)
|
||||
{
|
||||
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds);
|
||||
}
|
||||
if (result.Success != true)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
await File.WriteAllTextAsync(fileName, result.Data.ToString());
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<RetResult> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,138 +1,125 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using v2rayN.Models;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
/// <summary>
|
||||
/// Core process processing class
|
||||
/// </summary>
|
||||
internal class CoreHandler
|
||||
public class CoreHandler
|
||||
{
|
||||
private static readonly Lazy<CoreHandler> _instance = new(() => new());
|
||||
public static CoreHandler Instance => _instance.Value;
|
||||
private Config _config;
|
||||
private Process? _process;
|
||||
private Process? _processPre;
|
||||
private Action<bool, string> _updateFunc;
|
||||
private Action<bool, string>? _updateFunc;
|
||||
|
||||
public CoreHandler(Config config, Action<bool, string> update)
|
||||
public async Task Init(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
_updateFunc = updateFunc;
|
||||
|
||||
Environment.SetEnvironmentVariable("v2ray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
|
||||
if (Utils.IsLinux())
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
|
||||
foreach (var it in coreInfo)
|
||||
{
|
||||
if (it.CoreType == ECoreType.v2rayN)
|
||||
{
|
||||
if (Utils.UpgradeAppExists(out var fileName))
|
||||
{
|
||||
await Utils.SetLinuxChmod(fileName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var name in it.CoreExes)
|
||||
{
|
||||
var exe = Utils.GetBinPath(Utils.GetExeName(name), it.CoreType.ToString());
|
||||
if (File.Exists(exe))
|
||||
{
|
||||
await Utils.SetLinuxChmod(exe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadCore()
|
||||
public async Task LoadCore(ProfileItem? node)
|
||||
{
|
||||
var node = ConfigHandler.GetDefaultServer(_config);
|
||||
if (node == null)
|
||||
{
|
||||
ShowMsg(false, ResUI.CheckServerSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
string fileName = Utils.GetConfigPath(Global.CoreConfigFileName);
|
||||
if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0)
|
||||
var fileName = Utils.GetConfigPath(Global.CoreConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
|
||||
ShowMsg(false, result.Msg);
|
||||
if (result.Success != true)
|
||||
{
|
||||
ShowMsg(false, msg);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowMsg(false, msg);
|
||||
ShowMsg(true, $"{node.GetSummary()}");
|
||||
CoreStop();
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
Utils.RemoveTunDevice();
|
||||
}
|
||||
|
||||
CoreStart(node);
|
||||
await CoreStop();
|
||||
await CoreStart(node);
|
||||
|
||||
//In tun mode, do a delay check and restart the core
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
Observable.Range(1, 1)
|
||||
.Delay(TimeSpan.FromSeconds(15))
|
||||
.Subscribe(x =>
|
||||
{
|
||||
{
|
||||
if (_process == null || _process.HasExited)
|
||||
{
|
||||
CoreStart(node);
|
||||
ShowMsg(false, "Tun mode restart the core once");
|
||||
Logging.SaveLog("Tun mode restart the core once");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//if (_config.tunModeItem.enableTun)
|
||||
//{
|
||||
// Observable.Range(1, 1)
|
||||
// .Delay(TimeSpan.FromSeconds(15))
|
||||
// .Subscribe(x =>
|
||||
// {
|
||||
// {
|
||||
// if (_process == null || _process.HasExited)
|
||||
// {
|
||||
// CoreStart(node);
|
||||
// ShowMsg(false, "Tun mode restart the core once");
|
||||
// Logging.SaveLog("Tun mode restart the core once");
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
public int LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
||||
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
||||
{
|
||||
int pid = -1;
|
||||
var coreType = selecteds.Exists(t => t.configType == EConfigType.Hysteria2 || t.configType == EConfigType.Tuic || t.configType == EConfigType.Wireguard) ? ECoreType.sing_box : ECoreType.Xray;
|
||||
string configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
||||
if (CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType, out string msg) != 0)
|
||||
var pid = -1;
|
||||
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
||||
ShowMsg(false, result.Msg);
|
||||
if (result.Success)
|
||||
{
|
||||
ShowMsg(false, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowMsg(false, msg);
|
||||
pid = CoreStartSpeedtest(configPath, coreType);
|
||||
pid = await CoreStartSpeedtest(configPath, coreType);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
public void CoreStop()
|
||||
public async Task CoreStop()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool hasProc = false;
|
||||
if (_process != null)
|
||||
{
|
||||
KillProcess(_process);
|
||||
await KillProcess(_process);
|
||||
_process.Dispose();
|
||||
_process = null;
|
||||
hasProc = true;
|
||||
}
|
||||
|
||||
if (_processPre != null)
|
||||
{
|
||||
KillProcess(_processPre);
|
||||
await KillProcess(_processPre);
|
||||
_processPre.Dispose();
|
||||
_processPre = null;
|
||||
hasProc = true;
|
||||
}
|
||||
|
||||
if (!hasProc)
|
||||
{
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo();
|
||||
foreach (var it in coreInfo)
|
||||
{
|
||||
if (it.coreType == ECoreType.v2rayN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (string vName in it.coreExes)
|
||||
{
|
||||
var existing = Process.GetProcessesByName(vName);
|
||||
foreach (Process p in existing)
|
||||
{
|
||||
string? path = p.MainModule?.FileName;
|
||||
if (path == $"{Utils.GetBinPath(vName, it.coreType.ToString())}.exe")
|
||||
{
|
||||
KillProcess(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -141,12 +128,12 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
public void CoreStopPid(int pid)
|
||||
public async Task CoreStopPid(int pid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var _p = Process.GetProcessById(pid);
|
||||
KillProcess(_p);
|
||||
await KillProcess(_p);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -159,10 +146,9 @@ namespace v2rayN.Handler
|
||||
private string CoreFindExe(CoreInfo coreInfo)
|
||||
{
|
||||
string fileName = string.Empty;
|
||||
foreach (string name in coreInfo.coreExes)
|
||||
foreach (var name in coreInfo.CoreExes)
|
||||
{
|
||||
string vName = $"{name}.exe";
|
||||
vName = Utils.GetBinPath(vName, coreInfo.coreType.ToString());
|
||||
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
|
||||
if (File.Exists(vName))
|
||||
{
|
||||
fileName = vName;
|
||||
@@ -171,57 +157,75 @@ namespace v2rayN.Handler
|
||||
}
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
string msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.coreType.ToString()), string.Join(", ", coreInfo.coreExes.ToArray()), coreInfo.coreUrl);
|
||||
string msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.CoreType.ToString()), string.Join(", ", coreInfo.CoreExes.ToArray()), coreInfo.Url);
|
||||
Logging.SaveLog(msg);
|
||||
ShowMsg(false, msg);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private void CoreStart(ProfileItem node)
|
||||
private async Task CoreStart(ProfileItem node)
|
||||
{
|
||||
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
||||
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||
|
||||
ECoreType coreType;
|
||||
if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
|
||||
{
|
||||
coreType = ECoreType.sing_box;
|
||||
}
|
||||
else
|
||||
{
|
||||
coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
|
||||
}
|
||||
_config.runningCoreType = coreType;
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType);
|
||||
//ECoreType coreType;
|
||||
//if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun)
|
||||
//{
|
||||
// coreType = ECoreType.sing_box;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
|
||||
//}
|
||||
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||
_config.RunningCoreType = coreType;
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
|
||||
var displayLog = node.configType != EConfigType.Custom || node.displayLog;
|
||||
var proc = RunProcess(node, coreInfo, "", displayLog);
|
||||
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
|
||||
var proc = await RunProcess(node, coreInfo, "", displayLog);
|
||||
if (proc is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_process = proc;
|
||||
|
||||
//start a socks service
|
||||
//start a pre service
|
||||
if (_process != null && !_process.HasExited)
|
||||
{
|
||||
if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
|
||||
ProfileItem? itemSocks = null;
|
||||
var preCoreType = ECoreType.sing_box;
|
||||
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && _config.TunModeItem.EnableTun)
|
||||
{
|
||||
var preCoreType = _config.tunModeItem.enableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var itemSocks = new ProfileItem()
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
coreType = preCoreType,
|
||||
configType = EConfigType.Socks,
|
||||
address = Global.Loopback,
|
||||
port = node.preSocksPort
|
||||
CoreType = preCoreType,
|
||||
ConfigType = EConfigType.SOCKS,
|
||||
Address = Global.Loopback,
|
||||
Sni = node.Address, //Tun2SocksAddress
|
||||
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||
};
|
||||
_config.runningCoreType = preCoreType;
|
||||
string fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName);
|
||||
if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0)
|
||||
}
|
||||
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
|
||||
{
|
||||
preCoreType = _config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
var coreInfo2 = LazyConfig.Instance.GetCoreInfo(preCoreType);
|
||||
var proc2 = RunProcess(node, coreInfo2, $" -c {Global.CorePreConfigFileName}", true);
|
||||
CoreType = preCoreType,
|
||||
ConfigType = EConfigType.SOCKS,
|
||||
Address = Global.Loopback,
|
||||
Port = node.PreSocksPort.Value,
|
||||
};
|
||||
_config.RunningCoreType = preCoreType;
|
||||
}
|
||||
if (itemSocks != null)
|
||||
{
|
||||
string fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName);
|
||||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2);
|
||||
if (result.Success)
|
||||
{
|
||||
var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
|
||||
var proc2 = await RunProcess(node, coreInfo2, $" -c {Global.CorePreConfigFileName}", true);
|
||||
if (proc2 is not null)
|
||||
{
|
||||
_processPre = proc2;
|
||||
@@ -231,15 +235,15 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private int CoreStartSpeedtest(string configPath, ECoreType coreType)
|
||||
private async Task<int> CoreStartSpeedtest(string configPath, ECoreType coreType)
|
||||
{
|
||||
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||
|
||||
ShowMsg(false, configPath);
|
||||
try
|
||||
{
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType);
|
||||
var proc = RunProcess(new(), coreInfo, $" -c {Global.CoreSpeedtestConfigFileName}", true);
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
var proc = await RunProcess(new(), coreInfo, $" -c {Global.CoreSpeedtestConfigFileName}", true);
|
||||
if (proc is null)
|
||||
{
|
||||
return -1;
|
||||
@@ -258,14 +262,14 @@ namespace v2rayN.Handler
|
||||
|
||||
private void ShowMsg(bool notify, string msg)
|
||||
{
|
||||
_updateFunc(notify, msg);
|
||||
_updateFunc?.Invoke(notify, msg);
|
||||
}
|
||||
|
||||
#endregion Private
|
||||
|
||||
#region Process
|
||||
|
||||
private Process? RunProcess(ProfileItem node, CoreInfo coreInfo, string configPath, bool displayLog)
|
||||
private async Task<Process?> RunProcess(ProfileItem node, CoreInfo coreInfo, string configPath, bool displayLog)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -279,7 +283,7 @@ namespace v2rayN.Handler
|
||||
StartInfo = new()
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = string.Format(coreInfo.arguments, configPath),
|
||||
Arguments = string.Format(coreInfo.Arguments, configPath),
|
||||
WorkingDirectory = Utils.GetConfigPath(),
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = displayLog,
|
||||
@@ -295,7 +299,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
proc.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(e.Data))
|
||||
if (Utils.IsNotEmpty(e.Data))
|
||||
{
|
||||
string msg = e.Data + Environment.NewLine;
|
||||
ShowMsg(false, msg);
|
||||
@@ -303,7 +307,7 @@ namespace v2rayN.Handler
|
||||
};
|
||||
proc.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(e.Data))
|
||||
if (Utils.IsNotEmpty(e.Data))
|
||||
{
|
||||
string msg = e.Data + Environment.NewLine;
|
||||
ShowMsg(false, msg);
|
||||
@@ -332,7 +336,7 @@ namespace v2rayN.Handler
|
||||
startUpSuccessful = true;
|
||||
}
|
||||
|
||||
LazyConfig.Instance.AddProcess(proc.Handle);
|
||||
AppHandler.Instance.AddProcess(proc.Handle);
|
||||
return proc;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -344,7 +348,7 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private void KillProcess(Process? proc)
|
||||
private async Task KillProcess(Process? proc)
|
||||
{
|
||||
if (proc is null)
|
||||
{
|
||||
173
v2rayN/ServiceLib/Handler/CoreInfoHandler.cs
Normal file
173
v2rayN/ServiceLib/Handler/CoreInfoHandler.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
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()
|
||||
{
|
||||
InitCoreInfo();
|
||||
}
|
||||
|
||||
public CoreInfo? GetCoreInfo(ECoreType coreType)
|
||||
{
|
||||
if (_coreInfo == null)
|
||||
{
|
||||
InitCoreInfo();
|
||||
}
|
||||
return _coreInfo?.FirstOrDefault(t => t.CoreType == coreType);
|
||||
}
|
||||
|
||||
public List<CoreInfo> GetCoreInfo()
|
||||
{
|
||||
if (_coreInfo == null)
|
||||
{
|
||||
InitCoreInfo();
|
||||
}
|
||||
return _coreInfo ?? [];
|
||||
}
|
||||
|
||||
private void InitCoreInfo()
|
||||
{
|
||||
_coreInfo = [];
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.v2rayN,
|
||||
Url = Global.NUrl,
|
||||
ReleaseApiUrl = Global.NUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
DownloadUrlWin64 = Global.NUrl + "/download/{0}/v2rayN-windows-64.zip",
|
||||
DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip",
|
||||
DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
|
||||
DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.v2fly,
|
||||
CoreExes = new List<string> { "wv2ray", "v2ray" },
|
||||
Arguments = "",
|
||||
Url = Global.V2flyCoreUrl,
|
||||
ReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
Match = "V2Ray",
|
||||
VersionArg = "-version",
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.v2fly_v5,
|
||||
CoreExes = new List<string> { "v2ray" },
|
||||
Arguments = "run -c config.json -format jsonv5",
|
||||
Url = Global.V2flyCoreUrl,
|
||||
ReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
Match = "V2Ray",
|
||||
VersionArg = "version",
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.Xray,
|
||||
CoreExes = new List<string> { "xray", "wxray" },
|
||||
Arguments = "run {0}",
|
||||
Url = Global.XrayCoreUrl,
|
||||
ReleaseApiUrl = Global.XrayCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
DownloadUrlWin64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-64.zip",
|
||||
DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
|
||||
DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
|
||||
DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
|
||||
Match = "Xray",
|
||||
VersionArg = "-version",
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.mihomo,
|
||||
CoreExes = new List<string> { $"mihomo-windows-amd64{(Avx2.X64.IsSupported ? "" : "-compatible")}", "mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-windows-386", "mihomo", "clash" },
|
||||
Arguments = "-f config.json" + PortableMode(),
|
||||
Url = Global.MihomoCoreUrl,
|
||||
ReleaseApiUrl = Global.MihomoCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
DownloadUrlWin64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip",
|
||||
DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
|
||||
DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
|
||||
DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz",
|
||||
Match = "Mihomo",
|
||||
VersionArg = "-v",
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.hysteria,
|
||||
CoreExes = new List<string> { "hysteria-windows-amd64", "hysteria-windows-386", "hysteria" },
|
||||
Arguments = "",
|
||||
Url = Global.HysteriaCoreUrl,
|
||||
ReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.naiveproxy,
|
||||
CoreExes = new List<string> { "naiveproxy", "naive" },
|
||||
Arguments = "config.json",
|
||||
Url = Global.NaiveproxyCoreUrl,
|
||||
RedirectInfo = false,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.tuic,
|
||||
CoreExes = new List<string> { "tuic-client", "tuic" },
|
||||
Arguments = "-c config.json",
|
||||
Url = Global.TuicCoreUrl,
|
||||
RedirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
CoreExes = new List<string> { "sing-box-client", "sing-box" },
|
||||
Arguments = "run {0} --disable-color",
|
||||
Url = Global.SingboxCoreUrl,
|
||||
RedirectInfo = true,
|
||||
ReleaseApiUrl = Global.SingboxCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
DownloadUrlWin64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip",
|
||||
DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
|
||||
DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
|
||||
DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
|
||||
Match = "sing-box",
|
||||
VersionArg = "version",
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.juicity,
|
||||
CoreExes = new List<string> { "juicity-client", "juicity" },
|
||||
Arguments = "run -c config.json",
|
||||
Url = Global.JuicityCoreUrl
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.hysteria2,
|
||||
CoreExes = new List<string> { "hysteria-windows-amd64", "hysteria-windows-386", "hysteria" },
|
||||
Arguments = "",
|
||||
Url = Global.HysteriaCoreUrl,
|
||||
ReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
RedirectInfo = true,
|
||||
});
|
||||
}
|
||||
|
||||
private string PortableMode()
|
||||
{
|
||||
return $" -d \"{Utils.GetBinPath("")}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
215
v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
Normal file
215
v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class BaseFmt
|
||||
{
|
||||
protected static string GetIpv6(string address)
|
||||
{
|
||||
if (Utils.IsIpv6(address))
|
||||
{
|
||||
// 检查地址是否已经被方括号包围,如果没有,则添加方括号
|
||||
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
|
||||
}
|
||||
return address; // 如果不是IPv6地址,直接返回原地址
|
||||
}
|
||||
|
||||
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
||||
{
|
||||
if (Utils.IsNotEmpty(item.Flow))
|
||||
{
|
||||
dicQuery.Add("flow", item.Flow);
|
||||
}
|
||||
|
||||
if (Utils.IsNotEmpty(item.StreamSecurity))
|
||||
{
|
||||
dicQuery.Add("security", item.StreamSecurity);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (securityDef != null)
|
||||
{
|
||||
dicQuery.Add("security", securityDef);
|
||||
}
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.Sni);
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Fingerprint))
|
||||
{
|
||||
dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.PublicKey))
|
||||
{
|
||||
dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.ShortId))
|
||||
{
|
||||
dicQuery.Add("sid", Utils.UrlEncode(item.ShortId));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.SpiderX))
|
||||
{
|
||||
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
|
||||
}
|
||||
if (item.AllowInsecure.Equals("true"))
|
||||
{
|
||||
dicQuery.Add("allowInsecure", "1");
|
||||
}
|
||||
|
||||
dicQuery.Add("type", Utils.IsNotEmpty(item.Network) ? item.Network : nameof(ETransport.tcp));
|
||||
|
||||
switch (item.Network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("seed", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
case nameof(ETransport.splithttp):
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
dicQuery["type"] = nameof(ETransport.http);
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
|
||||
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
|
||||
dicQuery.Add("key", Utils.UrlEncode(item.Path));
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
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):
|
||||
case nameof(ETransport.splithttp):
|
||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
item.Network = nameof(ETransport.h2);
|
||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
item.HeaderType = query["headerType"] ?? Global.None;
|
||||
item.RequestHost = query["quicSecurity"] ?? Global.None;
|
||||
item.Path = Utils.UrlDecode(query["key"] ?? "");
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
item.RequestHost = Utils.UrlDecode(query["authority"] ?? "");
|
||||
item.Path = Utils.UrlDecode(query["serviceName"] ?? "");
|
||||
item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected static bool Contains(string str, params string[] s)
|
||||
{
|
||||
foreach (var item in s)
|
||||
{
|
||||
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static string WriteAllText(string strData, string ext = "json")
|
||||
{
|
||||
var fileName = Utils.GetTempPath($"{Utils.GetGuid(false)}.{ext}");
|
||||
File.WriteAllText(fileName, strData);
|
||||
return fileName;
|
||||
}
|
||||
|
||||
protected static string ToUri(EConfigType eConfigType, string address, object port, string userInfo, Dictionary<string, string>? dicQuery, string? remark)
|
||||
{
|
||||
var query = dicQuery != null
|
||||
? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()))
|
||||
: string.Empty;
|
||||
|
||||
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
|
||||
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
|
||||
}
|
||||
}
|
||||
}
|
||||
23
v2rayN/ServiceLib/Handler/Fmt/ClashFmt.cs
Normal file
23
v2rayN/ServiceLib/Handler/Fmt/ClashFmt.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class ClashFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "port", "socks-port", "proxies"))
|
||||
{
|
||||
var fileName = WriteAllText(strData, "yaml");
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.mihomo,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "clash_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
Normal file
90
v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class FmtHandler
|
||||
{
|
||||
public static string? GetShareUri(ProfileItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = item.ConfigType switch
|
||||
{
|
||||
EConfigType.VMess => VmessFmt.ToUri(item),
|
||||
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
|
||||
EConfigType.SOCKS => SocksFmt.ToUri(item),
|
||||
EConfigType.Trojan => TrojanFmt.ToUri(item),
|
||||
EConfigType.VLESS => VLESSFmt.ToUri(item),
|
||||
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
|
||||
EConfigType.TUIC => TuicFmt.ToUri(item),
|
||||
EConfigType.WireGuard => WireguardFmt.ToUri(item),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
return url;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveConfig(string config, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
try
|
||||
{
|
||||
string str = config.TrimEx();
|
||||
if (Utils.IsNullOrEmpty(str))
|
||||
{
|
||||
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)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
msg = ResUI.Incorrectconfiguration;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
Normal file
94
v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class Hysteria2Fmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.Hysteria2
|
||||
};
|
||||
|
||||
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";
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (Utils.IsNotEmpty(item.Sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.Sni);
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("obfs", "salamander");
|
||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0");
|
||||
|
||||
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.hysteria,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
v2rayN/ServiceLib/Handler/Fmt/NaiveproxyFmt.cs
Normal file
23
v2rayN/ServiceLib/Handler/Fmt/NaiveproxyFmt.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class NaiveproxyFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.naiveproxy,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "naiveproxy_custom"
|
||||
};
|
||||
return profileItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
172
v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
Normal file
172
v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class ShadowsocksFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
|
||||
item = ResolveSSLegacy(str) ?? ResolveSip002(str);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (item.Address.Length == 0 || item.Port == 0 || item.Security.Length == 0 || item.Id.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.ConfigType = EConfigType.Shadowsocks;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
||||
// item.security,
|
||||
// item.id,
|
||||
// item.address,
|
||||
// item.port);
|
||||
//url = Utile.Base64Encode(url);
|
||||
//new Sip002
|
||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
|
||||
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
|
||||
}
|
||||
|
||||
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static ProfileItem? ResolveSSLegacy(string result)
|
||||
{
|
||||
var match = UrlFinder.Match(result);
|
||||
if (!match.Success)
|
||||
return null;
|
||||
|
||||
ProfileItem item = new();
|
||||
var base64 = match.Groups["base64"].Value.TrimEnd('/');
|
||||
var tag = match.Groups["tag"].Value;
|
||||
if (Utils.IsNotEmpty(tag))
|
||||
{
|
||||
item.Remarks = Utils.UrlDecode(tag);
|
||||
}
|
||||
Match details;
|
||||
try
|
||||
{
|
||||
details = DetailsParser.Match(Utils.Base64Decode(base64));
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!details.Success)
|
||||
return null;
|
||||
item.Security = details.Groups["method"].Value;
|
||||
item.Id = details.Groups["password"].Value;
|
||||
item.Address = details.Groups["hostname"].Value;
|
||||
item.Port = Utils.ToInt(details.Groups["port"].Value);
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSip002(string result)
|
||||
{
|
||||
var parsedUrl = Utils.TryUri(result);
|
||||
if (parsedUrl == null) return null;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
Address = parsedUrl.IdnHost,
|
||||
Port = parsedUrl.Port,
|
||||
};
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
//2022-blake3
|
||||
if (rawUserInfo.Contains(':'))
|
||||
{
|
||||
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Security = userInfoParts[0];
|
||||
item.Id = Utils.UrlDecode(userInfoParts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// parse base64 UserInfo
|
||||
string userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Security = userInfoParts[0];
|
||||
item.Id = userInfoParts[1];
|
||||
}
|
||||
|
||||
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") && Utils.IsNotEmpty(obfsHost))
|
||||
{
|
||||
obfsHost = obfsHost?.Replace("obfs-host=", "");
|
||||
item.Network = Global.DefaultNetwork;
|
||||
item.HeaderType = Global.TcpHeaderHttp;
|
||||
item.RequestHost = obfsHost ?? "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static List<ProfileItem>? ResolveSip008(string result)
|
||||
{
|
||||
//SsSIP008
|
||||
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(result);
|
||||
if (lstSsServer?.Count <= 0)
|
||||
{
|
||||
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(result);
|
||||
if (ssSIP008?.servers?.Count > 0)
|
||||
{
|
||||
lstSsServer = ssSIP008.servers;
|
||||
}
|
||||
}
|
||||
|
||||
if (lstSsServer?.Count > 0)
|
||||
{
|
||||
List<ProfileItem> lst = [];
|
||||
foreach (var it in lstSsServer)
|
||||
{
|
||||
var ssItem = new ProfileItem()
|
||||
{
|
||||
Remarks = it.remarks,
|
||||
Security = it.method,
|
||||
Id = it.password,
|
||||
Address = it.server,
|
||||
Port = Utils.ToInt(it.server_port)
|
||||
};
|
||||
lst.Add(ssItem);
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
v2rayN/ServiceLib/Handler/Fmt/SingboxFmt.cs
Normal file
55
v2rayN/ServiceLib/Handler/Fmt/SingboxFmt.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class SingboxFmt : BaseFmt
|
||||
{
|
||||
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
|
||||
{
|
||||
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
|
||||
if (configObjects != null && configObjects.Length > 0)
|
||||
{
|
||||
List<ProfileItem> lstResult = [];
|
||||
foreach (var configObject in configObjects)
|
||||
{
|
||||
var objectString = JsonUtils.Serialize(configObject);
|
||||
var singboxCon = JsonUtils.Deserialize<SingboxConfig>(objectString);
|
||||
if (singboxCon?.inbounds?.Count > 0
|
||||
&& singboxCon.outbounds?.Count > 0
|
||||
&& singboxCon.route != null)
|
||||
{
|
||||
var fileName = WriteAllText(objectString);
|
||||
|
||||
var profileIt = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "singbox_custom",
|
||||
};
|
||||
lstResult.Add(profileIt);
|
||||
}
|
||||
}
|
||||
return lstResult;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(strData);
|
||||
if (singboxConfig?.inbounds?.Count > 0
|
||||
&& singboxConfig.outbounds?.Count > 0
|
||||
&& singboxConfig.route != null)
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.sing_box,
|
||||
Address = fileName,
|
||||
Remarks = subRemarks ?? "singbox_custom"
|
||||
};
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
Normal file
119
v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class SocksFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
|
||||
item = ResolveSocksNew(str) ?? ResolveSocks(str);
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (item.Address.Length == 0 || item.Port == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item.ConfigType = EConfigType.SOCKS;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
||||
// item.security,
|
||||
// item.id,
|
||||
// item.address,
|
||||
// item.port);
|
||||
//url = Utile.Base64Encode(url);
|
||||
//new
|
||||
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
|
||||
int indexRemark = result.IndexOf("#");
|
||||
if (indexRemark > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
item.Remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
|
||||
}
|
||||
catch { }
|
||||
result = result[..indexRemark];
|
||||
}
|
||||
//part decode
|
||||
int indexS = result.IndexOf("@");
|
||||
if (indexS > 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Utils.Base64Decode(result);
|
||||
}
|
||||
|
||||
string[] arr1 = result.Split('@');
|
||||
if (arr1.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string[] arr21 = arr1[0].Split(':');
|
||||
//string[] arr22 = arr1[1].Split(':');
|
||||
int indexPort = arr1[1].LastIndexOf(":");
|
||||
if (arr21.Length != 2 || indexPort < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
item.Address = arr1[1][..indexPort];
|
||||
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
|
||||
item.Security = arr21[0];
|
||||
item.Id = arr21[1];
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static ProfileItem? ResolveSocksNew(string result)
|
||||
{
|
||||
var parsedUrl = Utils.TryUri(result);
|
||||
if (parsedUrl == null) return null;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
Address = parsedUrl.IdnHost,
|
||||
Port = parsedUrl.Port,
|
||||
};
|
||||
|
||||
// parse base64 UserInfo
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
var userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
var userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.Security = userInfoParts[0];
|
||||
item.Id = userInfoParts[1];
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs
Normal file
44
v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class TrojanFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.Trojan
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
Normal file
59
v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class TuicFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.TUIC
|
||||
};
|
||||
|
||||
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[0];
|
||||
item.Security = userInfoParts[1];
|
||||
}
|
||||
|
||||
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;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (Utils.IsNotEmpty(item.Sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.Sni);
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Alpn))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
v2rayN/ServiceLib/Handler/Fmt/V2rayFmt.cs
Normal file
56
v2rayN/ServiceLib/Handler/Fmt/V2rayFmt.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class V2rayFmt : BaseFmt
|
||||
{
|
||||
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
|
||||
{
|
||||
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
|
||||
if (configObjects != null && configObjects.Length > 0)
|
||||
{
|
||||
List<ProfileItem> lstResult = [];
|
||||
foreach (var configObject in configObjects)
|
||||
{
|
||||
var objectString = JsonUtils.Serialize(configObject);
|
||||
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString);
|
||||
if (v2rayCon?.inbounds?.Count > 0
|
||||
&& v2rayCon.outbounds?.Count > 0
|
||||
&& v2rayCon.routing != null)
|
||||
{
|
||||
var fileName = WriteAllText(objectString);
|
||||
|
||||
var profileIt = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.Xray,
|
||||
Address = fileName,
|
||||
Remarks = v2rayCon.remarks ?? subRemarks ?? "v2ray_custom",
|
||||
};
|
||||
lstResult.Add(profileIt);
|
||||
}
|
||||
}
|
||||
return lstResult;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
|
||||
{
|
||||
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(strData);
|
||||
if (v2rayConfig?.inbounds?.Count > 0
|
||||
&& v2rayConfig.outbounds?.Count > 0
|
||||
&& v2rayConfig.routing != null)
|
||||
{
|
||||
var fileName = WriteAllText(strData);
|
||||
|
||||
var profileItem = new ProfileItem
|
||||
{
|
||||
CoreType = ECoreType.Xray,
|
||||
Address = fileName,
|
||||
Remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom"
|
||||
};
|
||||
|
||||
return profileItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
Normal file
55
v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class VLESSFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.VLESS,
|
||||
Security = Global.None
|
||||
};
|
||||
|
||||
var url = Utils.TryUri(str);
|
||||
if (url == null) return null;
|
||||
|
||||
item.Address = url.IdnHost;
|
||||
item.Port = url.Port;
|
||||
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
item.Id = Utils.UrlDecode(url.UserInfo);
|
||||
|
||||
var query = Utils.ParseQueryString(url.Query);
|
||||
item.Security = query["encryption"] ?? Global.None;
|
||||
item.StreamSecurity = query["security"] ?? "";
|
||||
ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (Utils.IsNotEmpty(item.Security))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
122
v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs
Normal file
122
v2rayN/ServiceLib/Handler/Fmt/VmessFmt.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class VmessFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0)
|
||||
{
|
||||
item = ResolveStdVmess(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = ResolveVmess(str, out msg);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
VmessQRCode vmessQRCode = new()
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
|
||||
if (vmessQRCode == null)
|
||||
{
|
||||
msg = ResUI.FailedConversionConfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
item.Network = Global.DefaultNetwork;
|
||||
item.HeaderType = Global.None;
|
||||
|
||||
item.ConfigVersion = vmessQRCode.v;
|
||||
item.Remarks = Utils.ToString(vmessQRCode.ps);
|
||||
item.Address = Utils.ToString(vmessQRCode.add);
|
||||
item.Port = vmessQRCode.port;
|
||||
item.Id = Utils.ToString(vmessQRCode.id);
|
||||
item.AlterId = vmessQRCode.aid;
|
||||
item.Security = Utils.ToString(vmessQRCode.scy);
|
||||
|
||||
item.Security = Utils.IsNotEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (Utils.IsNotEmpty(vmessQRCode.net))
|
||||
{
|
||||
item.Network = vmessQRCode.net;
|
||||
}
|
||||
if (Utils.IsNotEmpty(vmessQRCode.type))
|
||||
{
|
||||
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)
|
||||
{
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.VMess,
|
||||
Security = "auto"
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
Normal file
63
v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
public class WireguardFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.WireGuard
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
if (item == null) return null;
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (Utils.IsNotEmpty(item.Remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (Utils.IsNotEmpty(item.PublicKey))
|
||||
{
|
||||
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.Path))
|
||||
{
|
||||
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.RequestHost))
|
||||
{
|
||||
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
|
||||
}
|
||||
if (Utils.IsNotEmpty(item.ShortId))
|
||||
{
|
||||
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
|
||||
}
|
||||
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
v2rayN/ServiceLib/Handler/NoticeHandler.cs
Normal file
44
v2rayN/ServiceLib/Handler/NoticeHandler.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public class NoticeHandler
|
||||
{
|
||||
private static readonly Lazy<NoticeHandler> _instance = new(() => new());
|
||||
public static NoticeHandler Instance => _instance.Value;
|
||||
|
||||
public void Enqueue(string? content)
|
||||
{
|
||||
if (content.IsNullOrEmpty())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,68 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive.Linq;
|
||||
using v2rayN.Models;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
//using System.Reactive.Linq;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
internal class ProfileExHandler
|
||||
public class ProfileExHandler
|
||||
{
|
||||
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
|
||||
private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
|
||||
private Queue<string> _queIndexIds = new();
|
||||
public ConcurrentBag<ProfileExItem> ProfileExs => _lstProfileEx;
|
||||
public static ProfileExHandler Instance => _instance.Value;
|
||||
|
||||
public ProfileExHandler()
|
||||
{
|
||||
Init();
|
||||
//Init();
|
||||
}
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
await InitData();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
SaveQueueIndexIds();
|
||||
await Task.Delay(1000 * 600);
|
||||
await SaveQueueIndexIds();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void Init()
|
||||
public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
|
||||
{
|
||||
SQLiteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
|
||||
return _lstProfileEx;
|
||||
}
|
||||
|
||||
_lstProfileEx = new(SQLiteHelper.Instance.Table<ProfileExItem>());
|
||||
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 (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
if (Utils.IsNotEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
{
|
||||
_queIndexIds.Enqueue(indexId);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveQueueIndexIds()
|
||||
private async Task SaveQueueIndexIds()
|
||||
{
|
||||
var cnt = _queIndexIds.Count;
|
||||
if (cnt > 0)
|
||||
{
|
||||
var lstExists = SQLiteHelper.Instance.Table<ProfileExItem>();
|
||||
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync();
|
||||
List<ProfileExItem> lstInserts = [];
|
||||
List<ProfileExItem> lstUpdates = [];
|
||||
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
var id = _queIndexIds.Dequeue();
|
||||
var item = lstExists.FirstOrDefault(t => t.indexId == id);
|
||||
var itemNew = _lstProfileEx?.FirstOrDefault(t => t.indexId == id);
|
||||
var item = lstExists.FirstOrDefault(t => t.IndexId == id);
|
||||
var itemNew = _lstProfileEx?.FirstOrDefault(t => t.IndexId == id);
|
||||
if (itemNew is null)
|
||||
{
|
||||
continue;
|
||||
@@ -72,10 +80,10 @@ namespace v2rayN.Handler
|
||||
try
|
||||
{
|
||||
if (lstInserts.Count() > 0)
|
||||
SQLiteHelper.Instance.InsertAll(lstInserts);
|
||||
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
|
||||
|
||||
if (lstUpdates.Count() > 0)
|
||||
SQLiteHelper.Instance.UpdateAll(lstUpdates);
|
||||
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -88,26 +96,26 @@ namespace v2rayN.Handler
|
||||
{
|
||||
profileEx = new()
|
||||
{
|
||||
indexId = indexId,
|
||||
delay = 0,
|
||||
speed = 0,
|
||||
sort = 0
|
||||
IndexId = indexId,
|
||||
Delay = 0,
|
||||
Speed = 0,
|
||||
Sort = 0
|
||||
};
|
||||
_lstProfileEx.Add(profileEx);
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
public async Task ClearAll()
|
||||
{
|
||||
SQLiteHelper.Instance.Execute($"delete from ProfileExItem ");
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem ");
|
||||
_lstProfileEx = new();
|
||||
}
|
||||
|
||||
public void SaveTo()
|
||||
public async Task SaveTo()
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveQueueIndexIds();
|
||||
await SaveQueueIndexIds();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -117,49 +125,49 @@ namespace v2rayN.Handler
|
||||
|
||||
public void SetTestDelay(string indexId, string delayVal)
|
||||
{
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (profileEx == null)
|
||||
{
|
||||
AddProfileEx(indexId, ref profileEx);
|
||||
}
|
||||
|
||||
int.TryParse(delayVal, out int delay);
|
||||
profileEx.delay = delay;
|
||||
profileEx.Delay = delay;
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public void SetTestSpeed(string indexId, string speedVal)
|
||||
{
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (profileEx == null)
|
||||
{
|
||||
AddProfileEx(indexId, ref profileEx);
|
||||
}
|
||||
|
||||
decimal.TryParse(speedVal, out decimal speed);
|
||||
profileEx.speed = speed;
|
||||
profileEx.Speed = speed;
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public void SetSort(string indexId, int sort)
|
||||
{
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (profileEx == null)
|
||||
{
|
||||
AddProfileEx(indexId, ref profileEx);
|
||||
}
|
||||
profileEx.sort = sort;
|
||||
profileEx.Sort = sort;
|
||||
IndexIdEnqueue(indexId);
|
||||
}
|
||||
|
||||
public int GetSort(string indexId)
|
||||
{
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.indexId == indexId);
|
||||
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
|
||||
if (profileEx == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return profileEx.sort;
|
||||
return profileEx.Sort;
|
||||
}
|
||||
|
||||
public int GetMaxSort()
|
||||
@@ -168,7 +176,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return _lstProfileEx.Max(t => t == null ? 0 : t.sort);
|
||||
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
|
||||
}
|
||||
}
|
||||
}
|
||||
141
v2rayN/ServiceLib/Handler/StatisticsHandler.cs
Normal file
141
v2rayN/ServiceLib/Handler/StatisticsHandler.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
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 StatisticsV2rayService? _statisticsV2Ray;
|
||||
private StatisticsSingboxService? _statisticsSingbox;
|
||||
|
||||
public List<ServerStatItem> ServerStat => _lstServerStat;
|
||||
|
||||
public async Task Init(Config config, Action<ServerSpeedItem> updateFunc)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = updateFunc;
|
||||
if (!config.GuiItem.EnableStatistics)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await InitData();
|
||||
|
||||
_statisticsV2Ray = new StatisticsV2rayService(config, UpdateServerStatHandler);
|
||||
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
try
|
||||
{
|
||||
_statisticsV2Ray?.Close();
|
||||
_statisticsSingbox?.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, 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(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
152
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs
Normal file
152
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
namespace ServiceLib.Handler.SysProxy
|
||||
{
|
||||
public class ProxySettingLinux
|
||||
{
|
||||
public static async Task SetProxy(string host, int port)
|
||||
{
|
||||
var lstCmd = GetSetCmds(host, port);
|
||||
|
||||
await ExecCmd(lstCmd);
|
||||
}
|
||||
|
||||
public static async Task UnsetProxy()
|
||||
{
|
||||
var lstCmd = GetUnsetCmds();
|
||||
|
||||
await ExecCmd(lstCmd);
|
||||
}
|
||||
|
||||
private static async Task ExecCmd(List<CmdItem> lstCmd)
|
||||
{
|
||||
foreach (var cmd in lstCmd)
|
||||
{
|
||||
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
await Task.Delay(10);
|
||||
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetSetCmds(string host, int port)
|
||||
{
|
||||
var isKde = IsKde(out var configDir);
|
||||
List<string> lstType = ["", "http", "https", "socks", "ftp"];
|
||||
List<CmdItem> lstCmd = [];
|
||||
|
||||
if (isKde)
|
||||
{
|
||||
foreach (var type in lstType)
|
||||
{
|
||||
lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var type in lstType)
|
||||
{
|
||||
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port));
|
||||
}
|
||||
}
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetUnsetCmds()
|
||||
{
|
||||
var isKde = IsKde(out var configDir);
|
||||
List<CmdItem> lstCmd = [];
|
||||
|
||||
if (isKde)
|
||||
{
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "kwriteconfig5",
|
||||
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"]
|
||||
});
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir)
|
||||
{
|
||||
List<CmdItem> lstCmd = [];
|
||||
|
||||
if (type.IsNullOrEmpty())
|
||||
{
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = "kwriteconfig5",
|
||||
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"]
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var type2 = type.Equals("https") ? "http" : type;
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "kwriteconfig5",
|
||||
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"]
|
||||
});
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetSetCmd4Gnome(string type, string host, int port)
|
||||
{
|
||||
List<CmdItem> lstCmd = [];
|
||||
|
||||
if (type.IsNullOrEmpty())
|
||||
{
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"]
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", $"org.gnome.system.proxy.{type}", "host", host]
|
||||
});
|
||||
|
||||
lstCmd.Add(new()
|
||||
{
|
||||
Cmd = "gsettings",
|
||||
Arguments = ["set", $"org.gnome.system.proxy.{type}", "port", $"{port}"]
|
||||
});
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static bool IsKde(out string configDir)
|
||||
{
|
||||
configDir = "/home";
|
||||
var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
|
||||
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase);
|
||||
if (isKde)
|
||||
{
|
||||
var homeDir = Environment.GetEnvironmentVariable("HOME");
|
||||
if (homeDir != null)
|
||||
{
|
||||
configDir = Path.Combine(homeDir, ".config");
|
||||
}
|
||||
}
|
||||
|
||||
return isKde;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs
Normal file
82
v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
namespace ServiceLib.Handler.SysProxy
|
||||
{
|
||||
public class ProxySettingOSX
|
||||
{
|
||||
/*
|
||||
* 仅测试了,MacOS 13.7.1 x86 版本,其他版本有待确认
|
||||
*/
|
||||
/// <summary>
|
||||
/// 应用接口类型
|
||||
/// </summary>
|
||||
private static readonly List<string> LstInterface = ["Ethernet", "Wi-Fi", "Thunderbolt Bridge"];
|
||||
|
||||
/// <summary>
|
||||
/// 代理类型,对应 http,https,socks
|
||||
/// </summary>
|
||||
private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"];
|
||||
|
||||
public static async Task SetProxy(string host, int port)
|
||||
{
|
||||
var lstCmd = GetSetCmds(host, port);
|
||||
await ExecCmd(lstCmd);
|
||||
}
|
||||
|
||||
|
||||
public static async Task UnsetProxy()
|
||||
{
|
||||
var lstCmd = GetUnsetCmds();
|
||||
await ExecCmd(lstCmd);
|
||||
}
|
||||
|
||||
|
||||
private static async Task ExecCmd(List<CmdItem> lstCmd)
|
||||
{
|
||||
foreach (var cmd in lstCmd)
|
||||
{
|
||||
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await Task.Delay(10);
|
||||
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetSetCmds(string host, int port)
|
||||
{
|
||||
List<CmdItem> lstCmd = [];
|
||||
foreach (var interf in LstInterface)
|
||||
{
|
||||
foreach (var type in LstTypes)
|
||||
{
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "networksetup",
|
||||
Arguments = [$"-{type}", interf, host, (type.Contains("socks") ? (port - 1) : port).ToString()]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
|
||||
private static List<CmdItem> GetUnsetCmds()
|
||||
{
|
||||
List<CmdItem> lstCmd = [];
|
||||
foreach (var interf in LstInterface)
|
||||
{
|
||||
foreach (var type in LstTypes)
|
||||
{
|
||||
lstCmd.Add(new CmdItem()
|
||||
{
|
||||
Cmd = "networksetup",
|
||||
Arguments = [$"-{type}state", interf, "off"]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return lstCmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,38 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using static v2rayN.Handler.ProxySetting.InternetConnectionOption;
|
||||
using System.Runtime.InteropServices;
|
||||
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler.SysProxy
|
||||
{
|
||||
internal class ProxySetting
|
||||
public class ProxySettingWindows
|
||||
{
|
||||
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
|
||||
|
||||
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, "ProxyOverride", string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
|
||||
}
|
||||
if (type == 2)
|
||||
{
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 1);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
|
||||
}
|
||||
else if (type == 4)
|
||||
{
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
|
||||
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
// set to use no proxy
|
||||
/// </summary>
|
||||
@@ -29,15 +57,24 @@ namespace v2rayN.Handler
|
||||
/// <returns>true: one of connection is successfully updated proxy settings</returns>
|
||||
public static bool SetProxy(string? strProxy, string? exceptions, int type)
|
||||
{
|
||||
// set proxy for LAN
|
||||
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
|
||||
// set proxy for dial up connections
|
||||
var connections = EnumerateRasEntries();
|
||||
foreach (var connection in connections)
|
||||
try
|
||||
{
|
||||
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
|
||||
// set proxy for LAN
|
||||
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
|
||||
// set proxy for dial up connections
|
||||
var connections = EnumerateRasEntries();
|
||||
foreach (var connection in connections)
|
||||
{
|
||||
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetProxyFallback(strProxy, exceptions, type);
|
||||
//Logging.SaveLog(ex.Message, ex);
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
|
||||
@@ -51,7 +88,7 @@ namespace v2rayN.Handler
|
||||
}
|
||||
else if (type is 2 or 4) // named proxy or autoproxy script URL
|
||||
{
|
||||
optionCount = Utils.IsNullOrEmpty(exceptions) ? 2 : 3;
|
||||
optionCount = string.IsNullOrEmpty(exceptions) ? 2 : 3;
|
||||
}
|
||||
|
||||
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
|
||||
@@ -94,25 +131,25 @@ namespace v2rayN.Handler
|
||||
}
|
||||
else
|
||||
{
|
||||
list.szConnection = IntPtr.Zero;
|
||||
list.szConnection = nint.Zero;
|
||||
}
|
||||
list.dwOptionCount = options.Length;
|
||||
list.dwOptionError = 0;
|
||||
|
||||
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
|
||||
// make a pointer out of all that ...
|
||||
IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
|
||||
nint optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
|
||||
// copy the array over into that spot in memory ...
|
||||
for (int i = 0; i < options.Length; ++i)
|
||||
{
|
||||
if (Environment.Is64BitOperatingSystem)
|
||||
{
|
||||
IntPtr opt = new(optionsPtr.ToInt64() + (i * optSize));
|
||||
nint opt = new(optionsPtr.ToInt64() + (i * optSize));
|
||||
Marshal.StructureToPtr(options[i], opt, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
IntPtr opt = new(optionsPtr.ToInt32() + (i * optSize));
|
||||
nint opt = new(optionsPtr.ToInt32() + (i * optSize));
|
||||
Marshal.StructureToPtr(options[i], opt, false);
|
||||
}
|
||||
}
|
||||
@@ -120,11 +157,11 @@ namespace v2rayN.Handler
|
||||
list.options = optionsPtr;
|
||||
|
||||
// and then make a pointer out of the whole list
|
||||
IntPtr ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
|
||||
nint ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
|
||||
Marshal.StructureToPtr(list, ipcoListPtr, false);
|
||||
|
||||
// and finally, call the API method!
|
||||
bool isSuccess = NativeMethods.InternetSetOption(IntPtr.Zero,
|
||||
bool isSuccess = NativeMethods.InternetSetOption(nint.Zero,
|
||||
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
|
||||
ipcoListPtr, list.dwSize);
|
||||
int returnvalue = 0; // ERROR_SUCCESS
|
||||
@@ -135,12 +172,12 @@ namespace v2rayN.Handler
|
||||
else
|
||||
{
|
||||
// Notify the system that the registry settings have been changed and cause them to be refreshed
|
||||
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
|
||||
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
|
||||
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 != IntPtr.Zero) Marshal.FreeHGlobal(list.szConnection); // release mem 3
|
||||
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
|
||||
@@ -204,13 +241,13 @@ namespace v2rayN.Handler
|
||||
public struct InternetPerConnOptionList
|
||||
{
|
||||
public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
|
||||
public IntPtr szConnection; // connection name to set/query options
|
||||
public 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 IntPtr options;
|
||||
};
|
||||
public nint options;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct InternetConnectionOption
|
||||
@@ -236,7 +273,7 @@ namespace v2rayN.Handler
|
||||
public int m_Int;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public IntPtr m_StringPtr;
|
||||
public nint m_StringPtr;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
@@ -308,7 +345,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength);
|
||||
public static extern bool InternetSetOption(nint hInternet, InternetOption dwOption, nint lpBuffer, int dwBufferLength);
|
||||
|
||||
[DllImport("Rasapi32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern uint RasEnumEntries(
|
||||
106
v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs
Normal file
106
v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using PacLib;
|
||||
|
||||
namespace ServiceLib.Handler.SysProxy
|
||||
{
|
||||
public static class SysProxyHandler
|
||||
{
|
||||
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
|
||||
{
|
||||
var type = config.SystemProxyItem.SysProxyType;
|
||||
|
||||
if (forceDisable && type != ESysProxyType.Unchanged)
|
||||
{
|
||||
type = ESysProxyType.ForcedClear;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.http);
|
||||
var portSocks = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
if (port <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case ESysProxyType.ForcedChange when Utils.IsWindows():
|
||||
{
|
||||
GetWindowsProxyString(config, port, portSocks, out var strProxy, out var strExceptions);
|
||||
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
|
||||
break;
|
||||
}
|
||||
case ESysProxyType.ForcedChange when Utils.IsLinux():
|
||||
await ProxySettingLinux.SetProxy(Global.Loopback, port);
|
||||
break;
|
||||
|
||||
case ESysProxyType.ForcedChange:
|
||||
{
|
||||
if (Utils.IsOSX())
|
||||
{
|
||||
await ProxySettingOSX.SetProxy(Global.Loopback, port);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ESysProxyType.ForcedClear when Utils.IsWindows():
|
||||
ProxySettingWindows.UnsetProxy();
|
||||
break;
|
||||
|
||||
case ESysProxyType.ForcedClear when Utils.IsLinux():
|
||||
await ProxySettingLinux.UnsetProxy();
|
||||
break;
|
||||
|
||||
case ESysProxyType.ForcedClear:
|
||||
{
|
||||
if (Utils.IsOSX())
|
||||
{
|
||||
await ProxySettingOSX.UnsetProxy();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ESysProxyType.Pac when Utils.IsWindows():
|
||||
{
|
||||
var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac);
|
||||
PacHandler.Start(Utils.GetConfigPath(), port, portPac);
|
||||
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
|
||||
ProxySettingWindows.SetProxy(strProxy, "", 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type != ESysProxyType.Pac && Utils.IsWindows())
|
||||
{
|
||||
PacHandler.Stop();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void GetWindowsProxyString(Config config, int port, int portSocks, out string strProxy, out string strExceptions)
|
||||
{
|
||||
strExceptions = "";
|
||||
if (config.SystemProxyItem.NotProxyLocalAddress)
|
||||
{
|
||||
strExceptions = $"<local>;{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
|
||||
}
|
||||
|
||||
strProxy = string.Empty;
|
||||
if (Utils.IsNullOrEmpty(config.SystemProxyItem.SystemProxyAdvancedProtocol))
|
||||
{
|
||||
strProxy = $"{Global.Loopback}:{port}";
|
||||
}
|
||||
else
|
||||
{
|
||||
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
|
||||
.Replace("{ip}", Global.Loopback)
|
||||
.Replace("{http_port}", port.ToString())
|
||||
.Replace("{socks_port}", portSocks.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
72
v2rayN/ServiceLib/Handler/TaskHandler.cs
Normal file
72
v2rayN/ServiceLib/Handler/TaskHandler.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
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)
|
||||
{
|
||||
Task.Run(() => UpdateTaskRunSubscription(config, updateFunc));
|
||||
Task.Run(() => UpdateTaskRunGeo(config, updateFunc));
|
||||
}
|
||||
|
||||
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
await Task.Delay(60000);
|
||||
Logging.SaveLog("UpdateTaskRunSubscription");
|
||||
|
||||
var updateHandle = new UpdateService();
|
||||
while (true)
|
||||
{
|
||||
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();
|
||||
|
||||
foreach (var item in lstSubs)
|
||||
{
|
||||
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) =>
|
||||
{
|
||||
updateFunc?.Invoke(success, msg);
|
||||
if (success)
|
||||
Logging.SaveLog("subscription" + msg);
|
||||
});
|
||||
item.UpdateTime = updateTime;
|
||||
await ConfigHandler.AddSubItem(config, item);
|
||||
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
await Task.Delay(60000);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
var autoUpdateGeoTime = DateTime.Now;
|
||||
|
||||
//await Task.Delay(1000 * 120);
|
||||
Logging.SaveLog("UpdateTaskRunGeo");
|
||||
|
||||
var updateHandle = new UpdateService();
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(1000 * 3600);
|
||||
|
||||
var dtNow = DateTime.Now;
|
||||
if (config.GuiItem.AutoUpdateInterval > 0)
|
||||
{
|
||||
if ((dtNow - autoUpdateGeoTime).Hours % config.GuiItem.AutoUpdateInterval == 0)
|
||||
{
|
||||
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
|
||||
{
|
||||
updateFunc?.Invoke(false, msg);
|
||||
});
|
||||
autoUpdateGeoTime = dtNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
179
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
179
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using System.Net;
|
||||
using WebDav;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class WebDavHandler
|
||||
{
|
||||
private static readonly Lazy<WebDavHandler> _instance = new(() => new());
|
||||
public static WebDavHandler Instance => _instance.Value;
|
||||
|
||||
private Config? _config;
|
||||
private WebDavClient? _client;
|
||||
private string? _lastDescription;
|
||||
private string _webDir = Global.AppName + "_backup";
|
||||
private readonly string _webFileName = "backup.zip";
|
||||
private string _logTitle = "WebDav--";
|
||||
|
||||
public WebDavHandler()
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
}
|
||||
|
||||
private async Task<bool> GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
private void SaveLog(string desc)
|
||||
{
|
||||
_lastDescription = desc;
|
||||
Logging.SaveLog(_logTitle + desc);
|
||||
}
|
||||
|
||||
private void SaveLog(Exception ex)
|
||||
{
|
||||
_lastDescription = ex.Message;
|
||||
Logging.SaveLog(_logTitle, ex);
|
||||
}
|
||||
|
||||
public async Task<bool> CheckConnection()
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
var testName = "readme_test";
|
||||
var myContent = new StringContent(testName);
|
||||
var result = await _client.PutFile($"{_webDir}/{testName}", myContent);
|
||||
if (result.IsSuccessful)
|
||||
{
|
||||
await _client.Delete($"{_webDir}/{testName}");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveLog(result.Description);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> PutFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
await using var fs = File.OpenRead(fileName);
|
||||
var result = await _client.PutFile($"{_webDir}/{_webFileName}", fs); // upload a resource
|
||||
if (result.IsSuccessful)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SaveLog(result.Description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> GetRawFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _client.GetRawFile($"{_webDir}/{_webFileName}");
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
SaveLog(response.Description);
|
||||
return false;
|
||||
}
|
||||
|
||||
await using var outputFileStream = new FileStream(fileName, FileMode.Create);
|
||||
await response.Stream.CopyToAsync(outputFileStream);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetLastError() => _lastDescription ?? string.Empty;
|
||||
}
|
||||
}
|
||||
11
v2rayN/ServiceLib/Models/CheckUpdateModel.cs
Normal file
11
v2rayN/ServiceLib/Models/CheckUpdateModel.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class CheckUpdateModel
|
||||
{
|
||||
public bool? IsSelected { get; set; }
|
||||
public string? CoreType { get; set; }
|
||||
public string? Remarks { get; set; }
|
||||
public string? FileName { get; set; }
|
||||
public bool? IsFinished { get; set; }
|
||||
}
|
||||
}
|
||||
17
v2rayN/ServiceLib/Models/ClashConnectionModel.cs
Normal file
17
v2rayN/ServiceLib/Models/ClashConnectionModel.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ClashConnectionModel
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? Network { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Host { get; set; }
|
||||
public ulong Upload { get; set; }
|
||||
public ulong Download { get; set; }
|
||||
public string? UploadTraffic { get; set; }
|
||||
public string? DownloadTraffic { get; set; }
|
||||
public double Time { get; set; }
|
||||
public string? Elapsed { get; set; }
|
||||
public string? Chain { get; set; }
|
||||
}
|
||||
}
|
||||
37
v2rayN/ServiceLib/Models/ClashConnections.cs
Normal file
37
v2rayN/ServiceLib/Models/ClashConnections.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ClashConnections
|
||||
{
|
||||
public ulong downloadTotal { get; set; }
|
||||
public ulong uploadTotal { get; set; }
|
||||
public List<ConnectionItem>? connections { get; set; }
|
||||
}
|
||||
|
||||
public class ConnectionItem
|
||||
{
|
||||
public string? id { get; set; }
|
||||
public MetadataItem? metadata { get; set; }
|
||||
public ulong upload { get; set; }
|
||||
public ulong download { get; set; }
|
||||
public DateTime start { get; set; }
|
||||
public List<string>? chains { get; set; }
|
||||
public string? rule { get; set; }
|
||||
public string? rulePayload { get; set; }
|
||||
}
|
||||
|
||||
public class MetadataItem
|
||||
{
|
||||
public string? network { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? sourceIP { get; set; }
|
||||
public string? destinationIP { get; set; }
|
||||
public string? sourcePort { get; set; }
|
||||
public string? destinationPort { get; set; }
|
||||
public string? host { get; set; }
|
||||
public string? nsMode { get; set; }
|
||||
public object uid { get; set; }
|
||||
public string? process { get; set; }
|
||||
public string? processPath { get; set; }
|
||||
public string? remoteDestination { get; set; }
|
||||
}
|
||||
}
|
||||
17
v2rayN/ServiceLib/Models/ClashProviders.cs
Normal file
17
v2rayN/ServiceLib/Models/ClashProviders.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using static ServiceLib.Models.ClashProxies;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ClashProviders
|
||||
{
|
||||
public Dictionary<string, ProvidersItem>? providers { get; set; }
|
||||
|
||||
public class ProvidersItem
|
||||
{
|
||||
public string? name { get; set; }
|
||||
public List<ProxiesItem>? proxies { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? vehicleType { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
24
v2rayN/ServiceLib/Models/ClashProxies.cs
Normal file
24
v2rayN/ServiceLib/Models/ClashProxies.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ClashProxies
|
||||
{
|
||||
public Dictionary<string, ProxiesItem>? proxies { get; set; }
|
||||
|
||||
public class ProxiesItem
|
||||
{
|
||||
public List<string>? all { get; set; }
|
||||
public List<HistoryItem>? history { get; set; }
|
||||
public string? name { get; set; }
|
||||
public string? type { get; set; }
|
||||
public bool udp { get; set; }
|
||||
public string? now { get; set; }
|
||||
public int delay { get; set; }
|
||||
}
|
||||
|
||||
public class HistoryItem
|
||||
{
|
||||
public string? time { get; set; }
|
||||
public int delay { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
18
v2rayN/ServiceLib/Models/ClashProxyModel.cs
Normal file
18
v2rayN/ServiceLib/Models/ClashProxyModel.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ClashProxyModel
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Type { get; set; }
|
||||
|
||||
public string? Now { get; set; }
|
||||
|
||||
public int Delay { get; set; }
|
||||
|
||||
public string? DelayName { get; set; }
|
||||
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
8
v2rayN/ServiceLib/Models/CmdItem.cs
Normal file
8
v2rayN/ServiceLib/Models/CmdItem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class CmdItem
|
||||
{
|
||||
public string? Cmd { get; set; }
|
||||
public List<string>? Arguments { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ComboItem
|
||||
{
|
||||
public string ID
|
||||
public string? ID
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string Text
|
||||
public string? Text
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
55
v2rayN/ServiceLib/Models/Config.cs
Normal file
55
v2rayN/ServiceLib/Models/Config.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 本软件配置文件实体类
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Config
|
||||
{
|
||||
#region property
|
||||
|
||||
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:
|
||||
return true;
|
||||
default:
|
||||
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 List<InItem> Inbound { get; set; }
|
||||
public List<KeyEventItem> GlobalHotkeys { get; set; }
|
||||
public List<CoreTypeItem> CoreTypeItem { get; set; }
|
||||
|
||||
#endregion other entities
|
||||
}
|
||||
}
|
||||
253
v2rayN/ServiceLib/Models/ConfigItems.cs
Normal file
253
v2rayN/ServiceLib/Models/ConfigItems.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class CoreBasicItem
|
||||
{
|
||||
public bool LogEnabled { get; set; }
|
||||
|
||||
public string Loglevel { get; set; }
|
||||
|
||||
public bool MuxEnabled { get; set; }
|
||||
|
||||
public bool DefAllowInsecure { get; set; }
|
||||
|
||||
public string DefFingerprint { get; set; }
|
||||
|
||||
public string DefUserAgent { get; set; }
|
||||
|
||||
public bool EnableFragment { get; set; }
|
||||
|
||||
public bool EnableCacheFile4Sbox { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class InItem
|
||||
{
|
||||
public int LocalPort { get; set; }
|
||||
|
||||
public string Protocol { get; set; }
|
||||
|
||||
public bool UdpEnabled { get; set; }
|
||||
|
||||
public bool SniffingEnabled { get; set; } = true;
|
||||
public List<string>? DestOverride { get; set; } = ["http", "tls"];
|
||||
public bool RouteOnly { get; set; }
|
||||
public bool AllowLANConn { get; set; }
|
||||
|
||||
public bool NewPort4LAN { get; set; }
|
||||
|
||||
public string User { get; set; }
|
||||
|
||||
public string Pass { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KcpItem
|
||||
{
|
||||
public int Mtu { get; set; }
|
||||
|
||||
public int Tti { get; set; }
|
||||
|
||||
public int UplinkCapacity { get; set; }
|
||||
|
||||
public int DownlinkCapacity { get; set; }
|
||||
|
||||
public bool Congestion { get; set; }
|
||||
|
||||
public int ReadBufferSize { get; set; }
|
||||
|
||||
public int WriteBufferSize { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GrpcItem
|
||||
{
|
||||
public int? IdleTimeout { get; set; }
|
||||
public int? HealthCheckTimeout { get; set; }
|
||||
public bool? PermitWithoutStream { get; set; }
|
||||
public int? InitialWindowsSize { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GUIItem
|
||||
{
|
||||
public bool AutoRun { get; set; }
|
||||
|
||||
public bool EnableStatistics { get; set; }
|
||||
|
||||
public bool KeepOlderDedupl { get; set; }
|
||||
|
||||
public bool IgnoreGeoUpdateCore { get; set; } = true;
|
||||
|
||||
public int AutoUpdateInterval { get; set; }
|
||||
|
||||
public bool EnableSecurityProtocolTls13 { get; set; }
|
||||
|
||||
public int TrayMenuServersLimit { get; set; } = 20;
|
||||
|
||||
public bool EnableHWA { get; set; } = false;
|
||||
|
||||
public bool EnableLog { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MsgUIItem
|
||||
{
|
||||
public string? MainMsgFilter { get; set; }
|
||||
public bool? AutoRefresh { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UIItem
|
||||
{
|
||||
public bool EnableAutoAdjustMainLvColWidth { get; set; }
|
||||
public bool EnableUpdateSubOnlyRemarksExist { get; set; }
|
||||
public double MainWidth { get; set; }
|
||||
public double MainHeight { get; set; }
|
||||
public double MainGirdHeight1 { get; set; }
|
||||
public double MainGirdHeight2 { get; set; }
|
||||
public EGirdOrientation MainGirdOrientation { get; set; } = EGirdOrientation.Vertical;
|
||||
public bool ColorModeDark { get; set; }
|
||||
public bool FollowSystemTheme { get; set; }
|
||||
public string? ColorPrimaryName { get; set; }
|
||||
public string CurrentLanguage { get; set; }
|
||||
public string CurrentFontFamily { get; set; }
|
||||
public int CurrentFontSize { get; set; }
|
||||
public bool EnableDragDropSort { get; set; }
|
||||
public bool DoubleClick2Activate { get; set; }
|
||||
public bool AutoHideStartup { get; set; }
|
||||
public List<ColumnItem> MainColumnItem { get; set; }
|
||||
public bool ShowInTaskbar { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ConstItem
|
||||
{
|
||||
public string DefIEProxyExceptions { get; set; }
|
||||
public string SubConvertUrl { get; set; } = string.Empty;
|
||||
public string? GeoSourceUrl { get; set; }
|
||||
public string? SrsSourceUrl { get; set; }
|
||||
public string? RouteRulesTemplateSourceUrl { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class KeyEventItem
|
||||
{
|
||||
public EGlobalHotkey EGlobalHotkey { get; set; }
|
||||
|
||||
public bool Alt { get; set; }
|
||||
|
||||
public bool Control { get; set; }
|
||||
|
||||
public bool Shift { get; set; }
|
||||
|
||||
public int? KeyCode { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CoreTypeItem
|
||||
{
|
||||
public EConfigType ConfigType { get; set; }
|
||||
|
||||
public ECoreType CoreType { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TunModeItem
|
||||
{
|
||||
public bool EnableTun { get; set; }
|
||||
public bool StrictRoute { get; set; } = true;
|
||||
public string Stack { get; set; }
|
||||
public int Mtu { get; set; }
|
||||
public bool EnableExInbound { get; set; }
|
||||
public bool EnableIPv6Address { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SpeedTestItem
|
||||
{
|
||||
public int SpeedTestTimeout { get; set; }
|
||||
public string SpeedTestUrl { get; set; }
|
||||
public string SpeedPingTestUrl { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RoutingBasicItem
|
||||
{
|
||||
public string DomainStrategy { get; set; }
|
||||
public string DomainStrategy4Singbox { get; set; }
|
||||
public string DomainMatcher { get; set; }
|
||||
public string RoutingIndexId { get; set; }
|
||||
public bool EnableRoutingAdvanced { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ColumnItem
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Mux4RayItem
|
||||
{
|
||||
public int? Concurrency { get; set; }
|
||||
public int? XudpConcurrency { get; set; }
|
||||
public string? XudpProxyUDP443 { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Mux4SboxItem
|
||||
{
|
||||
public string Protocol { get; set; }
|
||||
public int MaxConnections { get; set; }
|
||||
public bool? Padding { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HysteriaItem
|
||||
{
|
||||
public int UpMbps { get; set; }
|
||||
public int DownMbps { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ClashUIItem
|
||||
{
|
||||
public ERuleMode RuleMode { get; set; }
|
||||
public bool EnableIPv6 { get; set; }
|
||||
public bool EnableMixinContent { get; set; }
|
||||
public int ProxiesSorting { get; set; }
|
||||
public bool ProxiesAutoRefresh { get; set; }
|
||||
public int ProxiesAutoDelayTestInterval { get; set; } = 10;
|
||||
public int ConnectionsSorting { get; set; }
|
||||
public bool ConnectionsAutoRefresh { get; set; }
|
||||
public int ConnectionsRefreshInterval { get; set; } = 2;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SystemProxyItem
|
||||
{
|
||||
public ESysProxyType SysProxyType { get; set; }
|
||||
public string SystemProxyExceptions { get; set; }
|
||||
public bool NotProxyLocalAddress { get; set; } = true;
|
||||
public string SystemProxyAdvancedProtocol { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WebDavItem
|
||||
{
|
||||
public string? Url { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? Password { get; set; }
|
||||
public string? DirName { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CheckUpdateItem
|
||||
{
|
||||
public bool CheckPreReleaseUpdate { get; set; }
|
||||
public List<string>? SelectedCoreTypes { get; set; }
|
||||
}
|
||||
}
|
||||
19
v2rayN/ServiceLib/Models/CoreInfo.cs
Normal file
19
v2rayN/ServiceLib/Models/CoreInfo.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class CoreInfo
|
||||
{
|
||||
public ECoreType CoreType { get; set; }
|
||||
public List<string>? CoreExes { get; set; }
|
||||
public string? Arguments { get; set; }
|
||||
public string? Url { get; set; }
|
||||
public string? ReleaseApiUrl { get; set; }
|
||||
public string? DownloadUrlWin64 { get; set; }
|
||||
public string? DownloadUrlWinArm64 { get; set; }
|
||||
public string? DownloadUrlLinux64 { get; set; }
|
||||
public string? DownloadUrlLinuxArm64 { get; set; }
|
||||
public string? Match { get; set; }
|
||||
public string? VersionArg { get; set; }
|
||||
public bool RedirectInfo { get; set; }
|
||||
}
|
||||
}
|
||||
20
v2rayN/ServiceLib/Models/DNSItem.cs
Normal file
20
v2rayN/ServiceLib/Models/DNSItem.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class DNSItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Remarks { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public ECoreType CoreType { get; set; }
|
||||
public bool UseSystemHosts { get; set; }
|
||||
public string? NormalDNS { get; set; }
|
||||
public string? TunDNS { get; set; }
|
||||
public string? DomainStrategy4Freedom { get; set; }
|
||||
public string? DomainDNSAddress { get; set; }
|
||||
}
|
||||
}
|
||||
68
v2rayN/ServiceLib/Models/GitHubRelease.cs
Normal file
68
v2rayN/ServiceLib/Models/GitHubRelease.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class GitHubReleaseAsset
|
||||
{
|
||||
[JsonPropertyName("url")] public string? Url { get; set; }
|
||||
|
||||
[JsonPropertyName("id")] public int Id { get; set; }
|
||||
|
||||
[JsonPropertyName("node_id")] public string? NodeId { get; set; }
|
||||
|
||||
[JsonPropertyName("name")] public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("label")] public object Label { get; set; }
|
||||
|
||||
[JsonPropertyName("content_type")] public string? ContentType { get; set; }
|
||||
|
||||
[JsonPropertyName("state")] public string? State { get; set; }
|
||||
|
||||
[JsonPropertyName("size")] public int Size { get; set; }
|
||||
|
||||
[JsonPropertyName("download_count")] public int DownloadCount { get; set; }
|
||||
|
||||
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("updated_at")] public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("browser_download_url")] public string? BrowserDownloadUrl { get; set; }
|
||||
}
|
||||
|
||||
public class GitHubRelease
|
||||
{
|
||||
[JsonPropertyName("url")] public string? Url { get; set; }
|
||||
|
||||
[JsonPropertyName("assets_url")] public string? AssetsUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("upload_url")] public string? UploadUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("html_url")] public string? HtmlUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("id")] public int Id { get; set; }
|
||||
|
||||
[JsonPropertyName("node_id")] public string? NodeId { get; set; }
|
||||
|
||||
[JsonPropertyName("tag_name")] public string? TagName { get; set; }
|
||||
|
||||
[JsonPropertyName("target_commitish")] public string? TargetCommitish { get; set; }
|
||||
|
||||
[JsonPropertyName("name")] public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("draft")] public bool Draft { get; set; }
|
||||
|
||||
[JsonPropertyName("prerelease")] public bool Prerelease { get; set; }
|
||||
|
||||
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("published_at")] public DateTime PublishedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("assets")] public List<GitHubReleaseAsset> Assets { get; set; }
|
||||
|
||||
[JsonPropertyName("tarball_url")] public string? TarballUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("zipball_url")] public string? ZipballUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("body")] public string? Body { get; set; }
|
||||
}
|
||||
}
|
||||
15
v2rayN/ServiceLib/Models/ProfileExItem.cs
Normal file
15
v2rayN/ServiceLib/Models/ProfileExItem.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ProfileExItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string IndexId { get; set; }
|
||||
|
||||
public int Delay { get; set; }
|
||||
public decimal Speed { get; set; }
|
||||
public int Sort { get; set; }
|
||||
}
|
||||
}
|
||||
114
v2rayN/ServiceLib/Models/ProfileItem.cs
Normal file
114
v2rayN/ServiceLib/Models/ProfileItem.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ProfileItem
|
||||
{
|
||||
public ProfileItem()
|
||||
{
|
||||
IndexId = string.Empty;
|
||||
ConfigType = EConfigType.VMess;
|
||||
ConfigVersion = 2;
|
||||
Address = string.Empty;
|
||||
Port = 0;
|
||||
Id = string.Empty;
|
||||
AlterId = 0;
|
||||
Security = string.Empty;
|
||||
Network = string.Empty;
|
||||
Remarks = string.Empty;
|
||||
HeaderType = string.Empty;
|
||||
RequestHost = string.Empty;
|
||||
Path = string.Empty;
|
||||
StreamSecurity = string.Empty;
|
||||
AllowInsecure = string.Empty;
|
||||
Subid = string.Empty;
|
||||
Flow = string.Empty;
|
||||
}
|
||||
|
||||
#region function
|
||||
|
||||
public string GetSummary()
|
||||
{
|
||||
string summary = string.Format("[{0}] ", (ConfigType).ToString());
|
||||
string[] arrAddr = Address.Split('.');
|
||||
string addr;
|
||||
if (arrAddr.Length > 2)
|
||||
{
|
||||
addr = $"{arrAddr.First()}***{arrAddr.Last()}";
|
||||
}
|
||||
else if (arrAddr.Length > 1)
|
||||
{
|
||||
addr = $"***{arrAddr.Last()}";
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = Address;
|
||||
}
|
||||
switch (ConfigType)
|
||||
{
|
||||
case EConfigType.Custom:
|
||||
summary += string.Format("[{1}]{0}", Remarks, CoreType.ToString());
|
||||
break;
|
||||
|
||||
default:
|
||||
summary += string.Format("{0}({1}:{2})", Remarks, addr, Port);
|
||||
break;
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
public List<string>? GetAlpn()
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(Alpn))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Utils.String2List(Alpn);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetNetwork()
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(Network) || !Global.Networks.Contains(Network))
|
||||
{
|
||||
return Global.DefaultNetwork;
|
||||
}
|
||||
return Network.TrimEx();
|
||||
}
|
||||
|
||||
#endregion function
|
||||
|
||||
[PrimaryKey]
|
||||
public string IndexId { get; set; }
|
||||
|
||||
public EConfigType ConfigType { get; set; }
|
||||
public int ConfigVersion { get; set; }
|
||||
public string Address { get; set; }
|
||||
public int Port { 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; }
|
||||
}
|
||||
}
|
||||
18
v2rayN/ServiceLib/Models/ProfileItemModel.cs
Normal file
18
v2rayN/ServiceLib/Models/ProfileItemModel.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ProfileItemModel : ProfileItem
|
||||
{
|
||||
public bool IsActive { get; set; }
|
||||
public string SubRemarks { get; set; }
|
||||
public int Delay { get; set; }
|
||||
public decimal Speed { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public string DelayVal { get; set; }
|
||||
public string SpeedVal { get; set; }
|
||||
public string TodayUp { get; set; }
|
||||
public string TodayDown { get; set; }
|
||||
public string TotalUp { get; set; }
|
||||
public string TotalDown { get; set; }
|
||||
}
|
||||
}
|
||||
27
v2rayN/ServiceLib/Models/RetResult.cs
Normal file
27
v2rayN/ServiceLib/Models/RetResult.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class RetResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
v2rayN/ServiceLib/Models/RoutingItem.cs
Normal file
23
v2rayN/ServiceLib/Models/RoutingItem.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class RoutingItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Remarks { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string RuleSet { get; set; }
|
||||
public int RuleNum { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public bool Locked { get; set; }
|
||||
public string CustomIcon { get; set; }
|
||||
public string CustomRulesetPath4Singbox { get; set; }
|
||||
public string DomainStrategy { get; set; }
|
||||
public string DomainStrategy4Singbox { get; set; }
|
||||
public int Sort { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class RoutingItemModel : RoutingItem
|
||||
{
|
||||
public bool isActive { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
}
|
||||
9
v2rayN/ServiceLib/Models/RoutingTemplate.cs
Normal file
9
v2rayN/ServiceLib/Models/RoutingTemplate.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class RoutingTemplate
|
||||
{
|
||||
public string Version { get; set; }
|
||||
public RoutingItem[] RoutingItems { get; set; }
|
||||
}
|
||||
}
|
||||
19
v2rayN/ServiceLib/Models/RulesItem.cs
Normal file
19
v2rayN/ServiceLib/Models/RulesItem.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class RulesItem
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public string? Port { get; set; }
|
||||
public string? Network { get; set; }
|
||||
public List<string>? InboundTag { get; set; }
|
||||
public string? OutboundTag { get; set; }
|
||||
public List<string>? Ip { get; set; }
|
||||
public List<string>? Domain { get; set; }
|
||||
public List<string>? Protocol { get; set; }
|
||||
public List<string>? Process { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public string? Remarks { get; set; }
|
||||
}
|
||||
}
|
||||
11
v2rayN/ServiceLib/Models/RulesItemModel.cs
Normal file
11
v2rayN/ServiceLib/Models/RulesItemModel.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class RulesItemModel : RulesItem
|
||||
{
|
||||
public string InboundTags { get; set; }
|
||||
public string Ips { get; set; }
|
||||
public string Domains { get; set; }
|
||||
public string Protocols { get; set; }
|
||||
}
|
||||
}
|
||||
22
v2rayN/ServiceLib/Models/ServerSpeedItem.cs
Normal file
22
v2rayN/ServiceLib/Models/ServerSpeedItem.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ServerSpeedItem : ServerStatItem
|
||||
{
|
||||
public long ProxyUp { get; set; }
|
||||
|
||||
public long ProxyDown { get; set; }
|
||||
|
||||
public long DirectUp { get; set; }
|
||||
|
||||
public long DirectDown { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TrafficItem
|
||||
{
|
||||
public ulong Up { get; set; }
|
||||
|
||||
public ulong Down { get; set; }
|
||||
}
|
||||
}
|
||||
21
v2rayN/ServiceLib/Models/ServerStatItem.cs
Normal file
21
v2rayN/ServiceLib/Models/ServerStatItem.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ServerStatItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string IndexId { get; set; }
|
||||
|
||||
public long TotalUp { get; set; }
|
||||
|
||||
public long TotalDown { get; set; }
|
||||
|
||||
public long TodayUp { get; set; }
|
||||
|
||||
public long TodayDown { get; set; }
|
||||
|
||||
public long DateNow { get; set; }
|
||||
}
|
||||
}
|
||||
13
v2rayN/ServiceLib/Models/ServerTestItem.cs
Normal file
13
v2rayN/ServiceLib/Models/ServerTestItem.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ServerTestItem
|
||||
{
|
||||
public string? IndexId { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public int Port { get; set; }
|
||||
public EConfigType ConfigType { get; set; }
|
||||
public bool AllowTest { get; set; }
|
||||
public int Delay { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class SingboxConfig
|
||||
{
|
||||
@@ -28,6 +28,7 @@
|
||||
public bool? disable_expire { get; set; }
|
||||
public bool? independent_cache { get; set; }
|
||||
public bool? reverse_mapping { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
public Fakeip4Sbox? fakeip { get; set; }
|
||||
}
|
||||
|
||||
@@ -44,12 +45,15 @@
|
||||
public string? outbound { get; set; }
|
||||
public string? server { get; set; }
|
||||
public bool? disable_cache { get; set; }
|
||||
public List<string>? inbound { get; set; }
|
||||
public List<string>? protocol { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? mode { get; set; }
|
||||
public string? network { get; set; }
|
||||
public bool? ip_is_private { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
public bool? invert { get; set; }
|
||||
public string? clash_mode { get; set; }
|
||||
public List<string>? inbound { get; set; }
|
||||
public List<string>? protocol { get; set; }
|
||||
public List<string>? network { get; set; }
|
||||
public List<int>? port { get; set; }
|
||||
public List<string>? port_range { get; set; }
|
||||
public List<string>? geosite { get; set; }
|
||||
@@ -62,6 +66,7 @@
|
||||
public List<string>? source_ip_cidr { get; set; }
|
||||
public List<string>? process_name { get; set; }
|
||||
public List<string>? rule_set { get; set; }
|
||||
public List<Rule4Sbox>? rules { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -115,10 +120,10 @@
|
||||
public string? version { get; set; }
|
||||
public string? network { get; set; }
|
||||
public string? packet_encoding { get; set; }
|
||||
public string[]? local_address { get; set; }
|
||||
public List<string>? local_address { get; set; }
|
||||
public string? private_key { get; set; }
|
||||
public string? peer_public_key { get; set; }
|
||||
public int[]? reserved { get; set; }
|
||||
public List<int>? reserved { get; set; }
|
||||
public int? mtu { get; set; }
|
||||
public string? plugin { get; set; }
|
||||
public string? plugin_opts { get; set; }
|
||||
@@ -126,16 +131,18 @@
|
||||
public Multiplex4Sbox? multiplex { get; set; }
|
||||
public Transport4Sbox? transport { get; set; }
|
||||
public HyObfs4Sbox? obfs { get; set; }
|
||||
public List<string>? outbounds { get; set; }
|
||||
public bool? interrupt_exist_connections { get; set; }
|
||||
}
|
||||
|
||||
public class Tls4Sbox
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
public string server_name { get; set; }
|
||||
public string? server_name { get; set; }
|
||||
public bool? insecure { get; set; }
|
||||
public List<string> alpn { get; set; }
|
||||
public Utls4Sbox utls { get; set; }
|
||||
public Reality4Sbox reality { get; set; }
|
||||
public List<string>? alpn { get; set; }
|
||||
public Utls4Sbox? utls { get; set; }
|
||||
public Reality4Sbox? reality { get; set; }
|
||||
}
|
||||
|
||||
public class Multiplex4Sbox
|
||||
@@ -143,6 +150,7 @@
|
||||
public bool enabled { get; set; }
|
||||
public string protocol { get; set; }
|
||||
public int max_connections { get; set; }
|
||||
public bool? padding { get; set; }
|
||||
}
|
||||
|
||||
public class Utls4Sbox
|
||||
@@ -184,11 +192,13 @@
|
||||
|
||||
public class Server4Sbox
|
||||
{
|
||||
public string tag { get; set; }
|
||||
public string address { get; set; }
|
||||
public string address_resolver { get; set; }
|
||||
public string strategy { get; set; }
|
||||
public string? tag { get; set; }
|
||||
public string? address { get; set; }
|
||||
public string? address_resolver { get; set; }
|
||||
public string? address_strategy { get; set; }
|
||||
public string? strategy { get; set; }
|
||||
public string? detour { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
}
|
||||
|
||||
public class Experimental4Sbox
|
||||
@@ -241,6 +251,6 @@
|
||||
public string? path { get; set; }
|
||||
public string? url { get; set; }
|
||||
public string? download_detour { get; set; }
|
||||
public string? update_interval { get; set; }
|
||||
public string? update_interval { get; set; }
|
||||
}
|
||||
}
|
||||
12
v2rayN/ServiceLib/Models/SpeedTestResult.cs
Normal file
12
v2rayN/ServiceLib/Models/SpeedTestResult.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class SpeedTestResult
|
||||
{
|
||||
public string? IndexId { get; set; }
|
||||
|
||||
public string? Delay { get; set; }
|
||||
|
||||
public string? Speed { get; set; }
|
||||
}
|
||||
}
|
||||
18
v2rayN/ServiceLib/Models/SsSIP008.cs
Normal file
18
v2rayN/ServiceLib/Models/SsSIP008.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class SsSIP008
|
||||
{
|
||||
public List<SsServer>? servers { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SsServer
|
||||
{
|
||||
public string? remarks { get; set; }
|
||||
public string? server { get; set; }
|
||||
public string? server_port { get; set; }
|
||||
public string? method { get; set; }
|
||||
public string? password { get; set; }
|
||||
public string? plugin { get; set; }
|
||||
}
|
||||
}
|
||||
39
v2rayN/ServiceLib/Models/SubItem.cs
Normal file
39
v2rayN/ServiceLib/Models/SubItem.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class SubItem
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Remarks { get; set; }
|
||||
|
||||
public string Url { get; set; }
|
||||
|
||||
public string MoreUrl { get; set; }
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
public string UserAgent { get; set; } = string.Empty;
|
||||
|
||||
public int Sort { get; set; }
|
||||
|
||||
public string? Filter { get; set; }
|
||||
|
||||
public int AutoUpdateInterval { get; set; }
|
||||
|
||||
public long UpdateTime { get; set; }
|
||||
|
||||
public string? ConvertTarget { get; set; }
|
||||
|
||||
public string? PrevProfile { get; set; }
|
||||
|
||||
public string? NextProfile { get; set; }
|
||||
|
||||
public int? PreSocksPort { get; set; }
|
||||
|
||||
public string? Memo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// v2ray配置文件实体类 例子SampleConfig.txt
|
||||
@@ -50,7 +50,7 @@ namespace v2rayN.Models
|
||||
}
|
||||
|
||||
public class Stats4Ray
|
||||
{ };
|
||||
{ }
|
||||
|
||||
public class API4Ray
|
||||
{
|
||||
@@ -343,15 +343,10 @@ namespace v2rayN.Models
|
||||
|
||||
public class Mux4Ray
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int concurrency { get; set; }
|
||||
public int? concurrency { get; set; }
|
||||
public int? xudpConcurrency { get; set; }
|
||||
public string? xudpProxyUDP443 { get; set; }
|
||||
}
|
||||
|
||||
public class Response4Ray
|
||||
@@ -370,6 +365,12 @@ namespace v2rayN.Models
|
||||
public List<string> servers { get; set; }
|
||||
}
|
||||
|
||||
public class DnsServer4Ray
|
||||
{
|
||||
public string? address { get; set; }
|
||||
public List<string>? domains { get; set; }
|
||||
}
|
||||
|
||||
public class Routing4Ray
|
||||
{
|
||||
/// <summary>
|
||||
@@ -380,12 +381,14 @@ namespace v2rayN.Models
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string domainMatcher { get; set; }
|
||||
public string? domainMatcher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<RulesItem4Ray> rules { get; set; }
|
||||
|
||||
public List<BalancersItem4Ray>? balancers { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -394,11 +397,14 @@ namespace v2rayN.Models
|
||||
public string? type { get; set; }
|
||||
|
||||
public string? port { get; set; }
|
||||
public string? network { get; set; }
|
||||
|
||||
public List<string>? inboundTag { get; set; }
|
||||
|
||||
public string? outboundTag { get; set; }
|
||||
|
||||
public string? balancerTag { get; set; }
|
||||
|
||||
public List<string>? ip { get; set; }
|
||||
|
||||
public List<string>? domain { get; set; }
|
||||
@@ -406,6 +412,18 @@ namespace v2rayN.Models
|
||||
public List<string>? protocol { get; set; }
|
||||
}
|
||||
|
||||
public class BalancersItem4Ray
|
||||
{
|
||||
public List<string>? selector { get; set; }
|
||||
public BalancersStrategy4Ray? strategy { get; set; }
|
||||
public string? tag { get; set; }
|
||||
}
|
||||
|
||||
public class BalancersStrategy4Ray
|
||||
{
|
||||
public string? type { get; set; }
|
||||
}
|
||||
|
||||
public class StreamSettings4Ray
|
||||
{
|
||||
/// <summary>
|
||||
@@ -421,47 +439,52 @@ namespace v2rayN.Models
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public TlsSettings4Ray tlsSettings { get; set; }
|
||||
public TlsSettings4Ray? tlsSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tcp传输额外设置
|
||||
/// </summary>
|
||||
public TcpSettings4Ray tcpSettings { get; set; }
|
||||
public TcpSettings4Ray? tcpSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Kcp传输额外设置
|
||||
/// </summary>
|
||||
public KcpSettings4Ray kcpSettings { get; set; }
|
||||
public KcpSettings4Ray? kcpSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ws传输额外设置
|
||||
/// </summary>
|
||||
public WsSettings4Ray wsSettings { get; set; }
|
||||
public WsSettings4Ray? wsSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public HttpupgradeSettings4Ray? httpupgradeSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public SplithttpSettings4Ray? splithttpSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// h2传输额外设置
|
||||
/// </summary>
|
||||
public HttpSettings4Ray httpSettings { get; set; }
|
||||
public HttpSettings4Ray? httpSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// QUIC
|
||||
/// </summary>
|
||||
public QuicSettings4Ray quicSettings { get; set; }
|
||||
public QuicSettings4Ray? quicSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// VLESS only
|
||||
/// </summary>
|
||||
public TlsSettings4Ray realitySettings { get; set; }
|
||||
public TlsSettings4Ray? realitySettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// grpc
|
||||
/// </summary>
|
||||
public GrpcSettings4Ray grpcSettings { get; set; }
|
||||
public GrpcSettings4Ray? grpcSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// sockopt
|
||||
@@ -488,7 +511,7 @@ namespace v2rayN.Models
|
||||
|
||||
public string? fingerprint { get; set; }
|
||||
|
||||
public bool? show { get; set; } = false;
|
||||
public bool? show { get; set; }
|
||||
public string? publicKey { get; set; }
|
||||
public string? shortId { get; set; }
|
||||
public string? spiderX { get; set; }
|
||||
@@ -608,17 +631,28 @@ namespace v2rayN.Models
|
||||
public string? host { get; set; }
|
||||
}
|
||||
|
||||
public class SplithttpSettings4Ray
|
||||
{
|
||||
public string? path { get; set; }
|
||||
|
||||
public string? host { get; set; }
|
||||
|
||||
public int? maxUploadSize { get; set; }
|
||||
|
||||
public int? maxConcurrentUploads { get; set; }
|
||||
}
|
||||
|
||||
public class HttpSettings4Ray
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string path { get; set; }
|
||||
public string? path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<string> host { get; set; }
|
||||
public List<string>? host { get; set; }
|
||||
}
|
||||
|
||||
public class QuicSettings4Ray
|
||||
@@ -644,10 +678,10 @@ namespace v2rayN.Models
|
||||
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 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
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Models
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Tcp伪装http的Request,只要Host
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user