Compare commits
200 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
b57ba6a98b | ||
|
|
d748e6eff4 | ||
|
|
315d4b75b2 | ||
|
|
31267cbc33 | ||
|
|
c23379b3b6 | ||
|
|
568144d6a2 | ||
|
|
5857042963 | ||
|
|
28e41dc621 | ||
|
|
baef3b364b | ||
|
|
4b9ddb803f | ||
|
|
c07c7ad82f | ||
|
|
e223b80b95 | ||
|
|
5fe468fa1b | ||
|
|
60068d8d16 | ||
|
|
b6c5b46afe | ||
|
|
124cbfadb4 | ||
|
|
8d21f9b900 | ||
|
|
398dbbd2e5 | ||
|
|
e9b392d1c0 | ||
|
|
9d7c7e3225 | ||
|
|
855fd4f0b7 | ||
|
|
807839929d | ||
|
|
870955fee1 | ||
|
|
e0cea929ea | ||
|
|
315f4c35f0 | ||
|
|
d961ea22a6 | ||
|
|
5c0c07c78f | ||
|
|
bba93a0fb7 | ||
|
|
5683df2fc0 | ||
|
|
b5cb9ce67e | ||
|
|
06a32dc895 | ||
|
|
dff12a8a3a | ||
|
|
dc3f07ee84 | ||
|
|
1aef49ee11 | ||
|
|
ee3159b00e | ||
|
|
86d2c307f1 | ||
|
|
7823a217b4 | ||
|
|
2004df8337 | ||
|
|
13bc2d0340 | ||
|
|
edbc850346 | ||
|
|
901d3c85ca | ||
|
|
abbe83f3c2 | ||
|
|
bb3a04a9c8 | ||
|
|
bac13e8b71 | ||
|
|
3871681de3 | ||
|
|
ae6f2b6df6 | ||
|
|
f59bbc9981 | ||
|
|
eac0c84e11 | ||
|
|
f814cc443d | ||
|
|
262466303b | ||
|
|
3b4cc2c5f2 | ||
|
|
023d3fbf6f | ||
|
|
fe01f290bc |
File diff suppressed because it is too large
Load Diff
@@ -9,9 +9,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.25.3" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.61.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.62.0">
|
||||
<PackageReference Include="Google.Protobuf" Version="3.28.0" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.65.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.66.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
11
v2rayN/ServiceLib/Base/MyReactiveObject.cs
Normal file
11
v2rayN/ServiceLib/Base/MyReactiveObject.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.Base
|
||||
{
|
||||
public class MyReactiveObject : ReactiveObject
|
||||
{
|
||||
protected static Config? _config;
|
||||
protected Func<EViewAction, object?, Task<bool>>? _updateView;
|
||||
protected NoticeHandler? _noticeHandler;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
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;
|
||||
|
||||
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -19,9 +18,9 @@ namespace v2rayN
|
||||
Uri uri = new(url);
|
||||
//Authorization Header
|
||||
var headers = new WebHeaderCollection();
|
||||
if (!Utile.IsNullOrEmpty(uri.UserInfo))
|
||||
if (!Utils.IsNullOrEmpty(uri.UserInfo))
|
||||
{
|
||||
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utile.Base64Encode(uri.UserInfo));
|
||||
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
|
||||
}
|
||||
|
||||
var downloadOpt = new DownloadConfiguration()
|
||||
@@ -57,7 +56,7 @@ namespace v2rayN
|
||||
|
||||
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
}
|
||||
@@ -120,11 +119,11 @@ namespace v2rayN
|
||||
|
||||
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
}
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileName));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
161
v2rayN/ServiceLib/Common/FileManager.cs
Normal file
161
v2rayN/ServiceLib/Common/FileManager.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
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 UncompressedFile(string fileName, byte[] content)
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileStream 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 UncompressedFile(string fileName, string toPath, string? toName)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInfo fileInfo = new(fileName);
|
||||
using FileStream originalFileStream = fileInfo.OpenRead();
|
||||
using FileStream 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 string NonExclusiveReadAllText(string path)
|
||||
{
|
||||
return NonExclusiveReadAllText(path, Encoding.Default);
|
||||
}
|
||||
|
||||
public 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 ZipArchive archive = ZipFile.OpenRead(fileName);
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
if (entry.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(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 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
|
||||
DirectoryInfo[] dirs = dir.GetDirectories();
|
||||
|
||||
// Create the destination directory
|
||||
Directory.CreateDirectory(destinationDir);
|
||||
|
||||
// Get the files in the source directory and copy to the destination directory
|
||||
foreach (FileInfo file in dir.GetFiles())
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(ignoredName) && file.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string targetFilePath = Path.Combine(destinationDir, file.Name);
|
||||
file.CopyTo(targetFilePath);
|
||||
}
|
||||
|
||||
// If recursive and copying subdirectories, recursively call this method
|
||||
if (recursive)
|
||||
{
|
||||
foreach (DirectoryInfo subDir in dirs)
|
||||
{
|
||||
string 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,26 +20,57 @@ namespace v2rayN
|
||||
|
||||
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
|
||||
|
||||
public async Task<string?> TryGetAsync(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await httpClient.GetAsync(url);
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string?> GetAsync(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url)) return null;
|
||||
if (Utils.IsNullOrEmpty(url)) return null;
|
||||
return await httpClient.GetStringAsync(url);
|
||||
}
|
||||
|
||||
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url)) return null;
|
||||
if (Utils.IsNullOrEmpty(url)) return null;
|
||||
return await client.GetStringAsync(url, token);
|
||||
}
|
||||
|
||||
public async Task PutAsync(string url, Dictionary<string, string> headers)
|
||||
{
|
||||
var jsonContent = JsonUtile.Serialize(headers);
|
||||
var jsonContent = JsonUtils.Serialize(headers);
|
||||
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||
|
||||
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);
|
||||
@@ -76,19 +106,19 @@ namespace v2rayN
|
||||
//if (progressPercentage != percent && percent % 10 == 0)
|
||||
{
|
||||
progressPercentage = percent;
|
||||
progress!.Report(percent);
|
||||
progress?.Report(percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canReportProgress)
|
||||
{
|
||||
progress!.Report(101);
|
||||
progress?.Report(101);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
/*
|
||||
* See:
|
||||
@@ -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 JsonUtile
|
||||
public class JsonUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// DeepCopy
|
||||
@@ -28,7 +27,7 @@ namespace v2rayN
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(strJson))
|
||||
if (string.IsNullOrWhiteSpace(strJson))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
@@ -49,7 +48,7 @@ namespace v2rayN
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(strJson))
|
||||
if (string.IsNullOrWhiteSpace(strJson))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -105,9 +104,12 @@ 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);
|
||||
@@ -119,14 +121,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
|
||||
{
|
||||
@@ -13,7 +12,7 @@ namespace v2rayN
|
||||
FileTarget fileTarget = new();
|
||||
config.AddTarget("file", fileTarget);
|
||||
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
|
||||
fileTarget.FileName = Utile.GetLogPath("${shortdate}.txt");
|
||||
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
|
||||
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
|
||||
LogManager.Configuration = config;
|
||||
}
|
||||
@@ -33,7 +32,7 @@ namespace v2rayN
|
||||
try
|
||||
{
|
||||
var now = DateTime.Now.AddMonths(-1);
|
||||
var dir = Utile.GetLogPath();
|
||||
var dir = Utils.GetLogPath();
|
||||
var files = Directory.GetFiles(dir, "*.txt");
|
||||
foreach (var filePath in files)
|
||||
{
|
||||
15
v2rayN/ServiceLib/Common/QRCodeHelper.cs
Normal file
15
v2rayN/ServiceLib/Common/QRCodeHelper.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using QRCoder;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class QRCodeHelper
|
||||
{
|
||||
public static byte[]? GenQRCode(string? url)
|
||||
{
|
||||
using QRCodeGenerator qrGenerator = new();
|
||||
using QRCodeData qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q);
|
||||
using PngByteQRCode qrCode = new(qrCodeData);
|
||||
return qrCode.GetGraphic(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public static class QueryableExtension
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public class SemanticVersion
|
||||
{
|
||||
@@ -27,7 +27,7 @@
|
||||
this.minor = int.Parse(parts[1]);
|
||||
this.patch = 0;
|
||||
}
|
||||
else if (parts.Length == 3)
|
||||
else if (parts.Length == 3 || parts.Length == 4)
|
||||
{
|
||||
this.major = int.Parse(parts[0]);
|
||||
this.minor = int.Parse(parts[1]);
|
||||
@@ -1,7 +1,7 @@
|
||||
using SQLite;
|
||||
using System.Collections;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
public sealed class SQLiteHelper
|
||||
{
|
||||
@@ -15,7 +15,7 @@ namespace v2rayN
|
||||
|
||||
public SQLiteHelper()
|
||||
{
|
||||
_connstr = Utile.GetConfigPath(_configDB);
|
||||
_connstr = Utils.GetConfigPath(_configDB);
|
||||
_db = new SQLiteConnection(_connstr, false);
|
||||
_dbAsync = new SQLiteAsyncConnection(_connstr, false);
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
@@ -1,9 +1,5 @@
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.Win32.TaskScheduler;
|
||||
using System.Collections.Specialized;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
@@ -11,21 +7,12 @@ using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ZXing;
|
||||
using ZXing.Common;
|
||||
using ZXing.QrCode;
|
||||
using ZXing.Windows.Compatibility;
|
||||
|
||||
namespace v2rayN
|
||||
namespace ServiceLib.Common
|
||||
{
|
||||
internal class Utile
|
||||
public class Utils
|
||||
{
|
||||
#region 资源Json操作
|
||||
|
||||
@@ -57,11 +44,14 @@ namespace v2rayN
|
||||
/// 取得存储资源
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string? LoadResource(string res)
|
||||
public static string? LoadResource(string? res)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(res)) return null;
|
||||
if (!File.Exists(res))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return File.ReadAllText(res);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -80,7 +70,7 @@ namespace v2rayN
|
||||
/// </summary>
|
||||
/// <param name="lst"></param>
|
||||
/// <returns></returns>
|
||||
public static string List2String(List<string> lst, bool wrap = false)
|
||||
public static string List2String(List<string>? lst, bool wrap = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -346,14 +336,6 @@ namespace v2rayN
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static ImageSource IconToImageSource(Icon icon)
|
||||
{
|
||||
return Imaging.CreateBitmapSourceFromHIcon(
|
||||
icon.Handle,
|
||||
new System.Windows.Int32Rect(0, 0, icon.Width, icon.Height),
|
||||
BitmapSizeOptions.FromEmptyOptions());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// idn to idc
|
||||
/// </summary>
|
||||
@@ -361,7 +343,7 @@ namespace v2rayN
|
||||
/// <returns></returns>
|
||||
public static string GetPunycode(string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
{
|
||||
return url;
|
||||
}
|
||||
@@ -385,13 +367,14 @@ namespace v2rayN
|
||||
|
||||
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 int _);
|
||||
}
|
||||
|
||||
public static string Convert2Comma(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
if (Utils.IsNullOrEmpty(text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
@@ -421,14 +404,9 @@ namespace v2rayN
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文本
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsNullOrEmpty(string? text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -478,7 +456,7 @@ namespace v2rayN
|
||||
/// 验证Domain地址是否合法
|
||||
/// </summary>
|
||||
/// <param name="domain"></param>
|
||||
public static bool IsDomain(string domain)
|
||||
public static bool IsDomain(string? domain)
|
||||
{
|
||||
//如果为空
|
||||
if (IsNullOrEmpty(domain))
|
||||
@@ -517,29 +495,6 @@ namespace v2rayN
|
||||
|
||||
#region 测速
|
||||
|
||||
/// <summary>
|
||||
/// 取得本机 IP Address
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
//public static List<string> GetHostIPAddress()
|
||||
//{
|
||||
// List<string> lstIPAddress = new List<string>();
|
||||
// try
|
||||
// {
|
||||
// IPHostEntry IpEntry = Dns.GetHostEntry(Dns.GetHostName());
|
||||
// foreach (IPAddress ipa in IpEntry.AddressList)
|
||||
// {
|
||||
// if (ipa.AddressFamily == AddressFamily.InterNetwork)
|
||||
// lstIPAddress.Add(ipa.ToString());
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// SaveLog(ex.Message, ex);
|
||||
// }
|
||||
// return lstIPAddress;
|
||||
//}
|
||||
|
||||
public static void SetSecurityProtocol(bool enableSecurityProtocolTls13)
|
||||
{
|
||||
if (enableSecurityProtocolTls13)
|
||||
@@ -583,7 +538,7 @@ namespace v2rayN
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Utile.PortInUse(defaultPort))
|
||||
if (!Utils.PortInUse(defaultPort))
|
||||
{
|
||||
return defaultPort;
|
||||
}
|
||||
@@ -615,58 +570,35 @@ namespace v2rayN
|
||||
string location = GetExePath();
|
||||
if (blFull)
|
||||
{
|
||||
return string.Format("v2rayN - V{0} - {1}",
|
||||
FileVersionInfo.GetVersionInfo(location).FileVersion?.ToString(),
|
||||
return string.Format("{0} - V{1} - {2}",
|
||||
Global.AppName,
|
||||
GetVersionInfo(),
|
||||
File.GetLastWriteTime(location).ToString("yyyy/MM/dd"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Format("v2rayN/{0}",
|
||||
FileVersionInfo.GetVersionInfo(location).FileVersion?.ToString());
|
||||
return string.Format("{0}/{1}",
|
||||
Global.AppName,
|
||||
GetVersionInfo());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return string.Empty;
|
||||
return Global.AppName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取剪贴板数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string? GetClipboardData()
|
||||
public static string GetVersionInfo()
|
||||
{
|
||||
string? strData = string.Empty;
|
||||
try
|
||||
{
|
||||
IDataObject data = Clipboard.GetDataObject();
|
||||
if (data.GetDataPresent(DataFormats.UnicodeText))
|
||||
{
|
||||
strData = data.GetData(DataFormats.UnicodeText)?.ToString();
|
||||
}
|
||||
return strData;
|
||||
return Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString() ?? "0.0";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return strData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拷贝至剪贴板
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static void SetClipboardData(string strData)
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetText(strData);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "0.0";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,26 +626,6 @@ namespace v2rayN
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IsAdministrator
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsAdministrator()
|
||||
{
|
||||
try
|
||||
{
|
||||
WindowsIdentity current = WindowsIdentity.GetCurrent();
|
||||
WindowsPrincipal windowsPrincipal = new WindowsPrincipal(current);
|
||||
//WindowsBuiltInRole可以枚举出很多权限,例如系统用户、User、Guest等等
|
||||
return windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDownloadFileName(string url)
|
||||
{
|
||||
var fileName = Path.GetFileName(url);
|
||||
@@ -741,11 +653,11 @@ namespace v2rayN
|
||||
return Guid.TryParse(strSrc, out Guid g);
|
||||
}
|
||||
|
||||
public static void ProcessStart(string fileName)
|
||||
public static void ProcessStart(string fileName, string arguments = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -753,23 +665,6 @@ namespace v2rayN
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetDarkBorder(System.Windows.Window window, bool dark)
|
||||
{
|
||||
// Make sure the handle is created before the window is shown
|
||||
IntPtr hWnd = new System.Windows.Interop.WindowInteropHelper(window).EnsureHandle();
|
||||
int attribute = dark ? 1 : 0;
|
||||
uint attributeSize = (uint)Marshal.SizeOf(attribute);
|
||||
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, ref attribute, attributeSize);
|
||||
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize);
|
||||
}
|
||||
|
||||
public static bool IsLightTheme()
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
|
||||
var value = key?.GetValue("AppsUseLightTheme");
|
||||
return value is int i && i > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统hosts
|
||||
/// </summary>
|
||||
@@ -801,6 +696,24 @@ namespace v2rayN
|
||||
return systemHosts;
|
||||
}
|
||||
|
||||
public static string GetExeName(string name)
|
||||
{
|
||||
if (IsWindows())
|
||||
{
|
||||
return $"{name}.exe";
|
||||
}
|
||||
else
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||
|
||||
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||
|
||||
#endregion 杂项
|
||||
|
||||
#region TempPath
|
||||
@@ -840,7 +753,7 @@ namespace v2rayN
|
||||
{
|
||||
Directory.CreateDirectory(_tempPath);
|
||||
}
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return _tempPath;
|
||||
}
|
||||
@@ -876,7 +789,7 @@ namespace v2rayN
|
||||
{
|
||||
Directory.CreateDirectory(_tempPath);
|
||||
}
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return _tempPath;
|
||||
}
|
||||
@@ -901,7 +814,7 @@ namespace v2rayN
|
||||
Directory.CreateDirectory(_tempPath);
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return _tempPath;
|
||||
}
|
||||
@@ -918,7 +831,7 @@ namespace v2rayN
|
||||
{
|
||||
Directory.CreateDirectory(_tempPath);
|
||||
}
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return _tempPath;
|
||||
}
|
||||
@@ -935,7 +848,7 @@ namespace v2rayN
|
||||
{
|
||||
Directory.CreateDirectory(_tempPath);
|
||||
}
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (Utils.IsNullOrEmpty(filename))
|
||||
{
|
||||
return _tempPath;
|
||||
}
|
||||
@@ -946,244 +859,5 @@ namespace v2rayN
|
||||
}
|
||||
|
||||
#endregion TempPath
|
||||
|
||||
#region scan screen
|
||||
|
||||
public static string ScanScreen(float dpiX, float dpiY)
|
||||
{
|
||||
try
|
||||
{
|
||||
var left = (int)(SystemParameters.WorkArea.Left);
|
||||
var top = (int)(SystemParameters.WorkArea.Top);
|
||||
var width = (int)(SystemParameters.WorkArea.Width / dpiX);
|
||||
var height = (int)(SystemParameters.WorkArea.Height / dpiY);
|
||||
|
||||
using Bitmap fullImage = new Bitmap(width, height);
|
||||
using (Graphics g = Graphics.FromImage(fullImage))
|
||||
{
|
||||
g.CopyFromScreen(left, top, 0, 0, fullImage.Size, CopyPixelOperation.SourceCopy);
|
||||
}
|
||||
int maxTry = 10;
|
||||
for (int i = 0; i < maxTry; i++)
|
||||
{
|
||||
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
|
||||
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
|
||||
Rectangle cropRect = new(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
|
||||
Bitmap target = new(width, height);
|
||||
|
||||
double imageScale = (double)width / (double)cropRect.Width;
|
||||
using (Graphics g = Graphics.FromImage(target))
|
||||
{
|
||||
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
|
||||
cropRect,
|
||||
GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
BitmapLuminanceSource source = new(target);
|
||||
BinaryBitmap bitmap = new(new HybridBinarizer(source));
|
||||
QRCodeReader reader = new();
|
||||
Result result = reader.decode(bitmap);
|
||||
if (result != null)
|
||||
{
|
||||
string ret = result.Text;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static Tuple<float, float> GetDpiXY(Window window)
|
||||
{
|
||||
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
|
||||
Graphics g = Graphics.FromHwnd(hWnd);
|
||||
|
||||
return new(96 / g.DpiX, 96 / g.DpiY);
|
||||
}
|
||||
|
||||
#endregion scan screen
|
||||
|
||||
#region 开机自动启动等
|
||||
|
||||
/// <summary>
|
||||
/// 开机自动启动
|
||||
/// </summary>
|
||||
/// <param name="run"></param>
|
||||
/// <returns></returns>
|
||||
public static void SetAutoRun(string AutoRunRegPath, string AutoRunName, bool run)
|
||||
{
|
||||
try
|
||||
{
|
||||
var autoRunName = $"{AutoRunName}_{GetMD5(StartupPath())}";
|
||||
|
||||
//delete first
|
||||
RegWriteValue(AutoRunRegPath, autoRunName, "");
|
||||
if (IsAdministrator())
|
||||
{
|
||||
AutoStart(autoRunName, "", "");
|
||||
}
|
||||
|
||||
if (run)
|
||||
{
|
||||
string exePath = $"\"{GetExePath()}\"";
|
||||
if (IsAdministrator())
|
||||
{
|
||||
AutoStart(autoRunName, exePath, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
RegWriteValue(AutoRunRegPath, autoRunName, exePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static string? RegReadValue(string path, string name, string def)
|
||||
{
|
||||
RegistryKey? regKey = null;
|
||||
try
|
||||
{
|
||||
regKey = Registry.CurrentUser.OpenSubKey(path, false);
|
||||
string? value = regKey?.GetValue(name) as string;
|
||||
if (IsNullOrEmpty(value))
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 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 (IsNullOrEmpty(value.ToString()))
|
||||
{
|
||||
regKey?.DeleteValue(name, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
regKey?.SetValue(name, value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
regKey?.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 AutoStart(string taskName, string fileName, string description)
|
||||
{
|
||||
if (string.IsNullOrEmpty(taskName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
string TaskName = taskName;
|
||||
var logonUser = WindowsIdentity.GetCurrent().Name;
|
||||
string taskDescription = description;
|
||||
string deamonFileName = fileName;
|
||||
|
||||
using var taskService = new TaskService();
|
||||
var tasks = taskService.RootFolder.GetTasks(new Regex(TaskName));
|
||||
foreach (var t in tasks)
|
||||
{
|
||||
taskService.RootFolder.DeleteTask(t.Name);
|
||||
}
|
||||
if (string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var task = taskService.NewTask();
|
||||
task.RegistrationInfo.Description = taskDescription;
|
||||
task.Settings.DisallowStartIfOnBatteries = false;
|
||||
task.Settings.StopIfGoingOnBatteries = false;
|
||||
task.Settings.RunOnlyIfIdle = false;
|
||||
task.Settings.IdleSettings.StopOnIdleEnd = false;
|
||||
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
|
||||
task.Triggers.Add(new LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) });
|
||||
task.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
task.Actions.Add(new ExecAction(deamonFileName));
|
||||
|
||||
taskService.RootFolder.RegisterTaskDefinition(TaskName, task);
|
||||
}
|
||||
|
||||
public static void RemoveTunDevice()
|
||||
{
|
||||
try
|
||||
{
|
||||
string pnputilPath = @"C:\Windows\System32\pnputil.exe";
|
||||
string arg = $" /remove-device /deviceid \"wintun\"";
|
||||
|
||||
// Try to remove the device
|
||||
Process proc = new()
|
||||
{
|
||||
StartInfo = new()
|
||||
{
|
||||
FileName = pnputilPath,
|
||||
Arguments = arg,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
|
||||
proc.Start();
|
||||
var output = proc.StandardOutput.ReadToEnd();
|
||||
proc.WaitForExit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endregion 开机自动启动等
|
||||
|
||||
#region Windows API
|
||||
|
||||
[Flags]
|
||||
public enum DWMWINDOWATTRIBUTE : uint
|
||||
{
|
||||
DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19,
|
||||
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
|
||||
}
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
|
||||
|
||||
#endregion Windows API
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
T 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)
|
||||
{
|
||||
string 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,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EConfigType
|
||||
{
|
||||
@@ -10,6 +10,7 @@
|
||||
Trojan = 6,
|
||||
Hysteria2 = 7,
|
||||
Tuic = 8,
|
||||
Wireguard = 9
|
||||
Wireguard = 9,
|
||||
Http = 10
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ECoreType
|
||||
{
|
||||
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.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EGlobalHotkey
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EInboundProtocol
|
||||
{
|
||||
@@ -8,6 +8,7 @@
|
||||
http2,
|
||||
pac,
|
||||
api,
|
||||
api2,
|
||||
speedtest = 21
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EMove
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EServerColName
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ESpeedActionType
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ESysProxyType
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum ETransport
|
||||
{
|
||||
@@ -6,6 +6,7 @@
|
||||
kcp,
|
||||
ws,
|
||||
httpupgrade,
|
||||
splithttp,
|
||||
h2,
|
||||
http,
|
||||
quic,
|
||||
45
v2rayN/ServiceLib/Enums/EViewAction.cs
Normal file
45
v2rayN/ServiceLib/Enums/EViewAction.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace ServiceLib.Enums
|
||||
{
|
||||
public enum EViewAction
|
||||
{
|
||||
CloseWindow,
|
||||
ShowYesNo,
|
||||
SaveFileDialog,
|
||||
AddBatchRoutingRulesYesNo,
|
||||
AdjustMainLvColWidth,
|
||||
UpdateSysProxy,
|
||||
SetClipboardData,
|
||||
AddServerViaClipboard,
|
||||
ImportRulesFromClipboard,
|
||||
ProfilesFocus,
|
||||
ShareSub,
|
||||
ShareServer,
|
||||
ShowHideWindow,
|
||||
ScanScreenTask,
|
||||
Shutdown,
|
||||
BrowseServer,
|
||||
ImportRulesFromFile,
|
||||
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,11 +1,10 @@
|
||||
using v2rayN.Model;
|
||||
|
||||
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";
|
||||
@@ -23,25 +22,30 @@ 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/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 V2raySampleClient = "ServiceLib.Sample.SampleClientConfig";
|
||||
public const string SingboxSampleClient = "ServiceLib.Sample.SingboxSampleClientConfig";
|
||||
public const string V2raySampleHttpRequestFileName = "ServiceLib.Sample.SampleHttpRequest";
|
||||
public const string V2raySampleHttpResponseFileName = "ServiceLib.Sample.SampleHttpResponse";
|
||||
public const string V2raySampleInbound = "ServiceLib.Sample.SampleInbound";
|
||||
public const string V2raySampleOutbound = "ServiceLib.Sample.SampleOutbound";
|
||||
public const string SingboxSampleOutbound = "ServiceLib.Sample.SingboxSampleOutbound";
|
||||
public const string CustomRoutingFileName = "ServiceLib.Sample.custom_routing_";
|
||||
public const string TunSingboxDNSFileName = "ServiceLib.Sample.tun_singbox_dns";
|
||||
public const string TunSingboxInboundFileName = "ServiceLib.Sample.tun_singbox_inbound";
|
||||
public const string TunSingboxRulesFileName = "ServiceLib.Sample.tun_singbox_rules";
|
||||
public const string DNSV2rayNormalFileName = "ServiceLib.Sample.dns_v2ray_normal";
|
||||
public const string DNSSingboxNormalFileName = "ServiceLib.Sample.dns_singbox_normal";
|
||||
public const string ClashMixinYaml = "ServiceLib.Sample.clash_mixin_yaml";
|
||||
public const string ClashTunYaml = "ServiceLib.Sample.clash_tun_yaml";
|
||||
|
||||
public const string DefaultSecurity = "auto";
|
||||
public const string DefaultNetwork = "tcp";
|
||||
@@ -56,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";
|
||||
@@ -68,7 +73,9 @@ namespace v2rayN
|
||||
public const int MaxPort = 65536;
|
||||
public const string CommandClearMsg = "CommandClearMsg";
|
||||
public const string CommandSendMsgView = "CommandSendMsgView";
|
||||
public const string CommandSendSnackMsg = "CommandSendSnackMsg";
|
||||
public const string CommandStopSpeedTest = "CommandStopSpeedTest";
|
||||
public const string CommandRefreshProfiles = "CommandRefreshProfiles";
|
||||
public const string DelayUnit = "";
|
||||
public const string SpeedUnit = "";
|
||||
public const int MinFontSize = 10;
|
||||
@@ -105,12 +112,12 @@ namespace v2rayN
|
||||
@"https://speed.cloudflare.com/__down?bytes=100000000",
|
||||
@"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"
|
||||
};
|
||||
|
||||
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",
|
||||
};
|
||||
@@ -143,6 +150,7 @@ namespace v2rayN
|
||||
{EConfigType.VMess,"vmess"},
|
||||
{EConfigType.Shadowsocks,"shadowsocks"},
|
||||
{EConfigType.Socks,"socks"},
|
||||
{EConfigType.Http,"http"},
|
||||
{EConfigType.VLESS,"vless"},
|
||||
{EConfigType.Trojan,"trojan"},
|
||||
{EConfigType.Hysteria2,"hysteria2"},
|
||||
@@ -156,7 +164,7 @@ 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> CoreTypes4VLESS = new() { "Xray", "sing_box" };
|
||||
@@ -168,17 +176,26 @@ 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" };
|
||||
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
|
||||
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
|
||||
}
|
||||
}
|
||||
9
v2rayN/ServiceLib/GlobalUsings.cs
Normal file
9
v2rayN/ServiceLib/GlobalUsings.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Common;
|
||||
global using ServiceLib.Enums;
|
||||
global using ServiceLib.Handler;
|
||||
global using ServiceLib.Handler.CoreConfig;
|
||||
global using ServiceLib.Handler.Fmt;
|
||||
global using ServiceLib.Handler.Statistics;
|
||||
global using ServiceLib.Models;
|
||||
global using ServiceLib.Resx;
|
||||
206
v2rayN/ServiceLib/Handler/ClashApiHandler.cs
Normal file
206
v2rayN/ServiceLib/Handler/ClashApiHandler.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
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 void GetClashProxies(Config config, Action<ClashProxies, ClashProviders> update)
|
||||
{
|
||||
Task.Run(() => GetClashProxiesAsync(config, update));
|
||||
}
|
||||
|
||||
private async Task GetClashProxiesAsync(Config config, Action<ClashProxies, ClashProviders> update)
|
||||
{
|
||||
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;
|
||||
update(clashProxies, clashProviders);
|
||||
return;
|
||||
}
|
||||
Task.Delay(5000).Wait();
|
||||
}
|
||||
update(null, null);
|
||||
}
|
||||
|
||||
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> update)
|
||||
{
|
||||
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=" + LazyConfig.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);
|
||||
update(it, result);
|
||||
}));
|
||||
}
|
||||
Task.WaitAll(tasks.ToArray());
|
||||
|
||||
Task.Delay(1000).Wait();
|
||||
update(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 void 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 void ClashConfigUpdate(Dictionary<string, string> headers)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
if (_proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var urlBase = $"{GetApiUrl()}/configs";
|
||||
|
||||
await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
|
||||
});
|
||||
}
|
||||
|
||||
public async void ClashConfigReload(string filePath)
|
||||
{
|
||||
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 void GetClashConnections(Config config, Action<ClashConnections> update)
|
||||
{
|
||||
Task.Run(() => GetClashConnectionsAsync(config, update));
|
||||
}
|
||||
|
||||
private async Task GetClashConnectionsAsync(Config config, Action<ClashConnections> update)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = $"{GetApiUrl()}/connections";
|
||||
var result = await HttpClientHelper.Instance.TryGetAsync(url);
|
||||
var clashConnections = JsonUtils.Deserialize<ClashConnections>(result);
|
||||
|
||||
update(clashConnections);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async void 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}:{LazyConfig.Instance.StatePort2}";
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
271
v2rayN/ServiceLib/Handler/CoreConfig/CoreConfigClash.cs
Normal file
271
v2rayN/ServiceLib/Handler/CoreConfig/CoreConfigClash.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
/// </summary>
|
||||
public class CoreConfigClash
|
||||
{
|
||||
private Config _config;
|
||||
|
||||
public CoreConfigClash(Config config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成配置文件
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <returns></returns>
|
||||
public int GenerateClientCustomConfig(ProfileItem node, string? fileName, out string msg)
|
||||
{
|
||||
if (node == null || fileName is null)
|
||||
{
|
||||
msg = ResUI.CheckServerSettings;
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = ResUI.InitialConfiguration;
|
||||
|
||||
try
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
msg = ResUI.CheckServerSettings;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
string addressFileName = node.address;
|
||||
if (string.IsNullOrEmpty(addressFileName))
|
||||
{
|
||||
msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return -1;
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Path.Combine(Utils.GetConfigPath(), addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
msg = ResUI.FailedReadConfiguration + "1";
|
||||
return -1;
|
||||
}
|
||||
|
||||
string tagYamlStr1 = "!<str>";
|
||||
string tagYamlStr2 = "__strn__";
|
||||
string tagYamlStr3 = "!!str";
|
||||
var txtFile = File.ReadAllText(addressFileName);
|
||||
txtFile = txtFile.Replace(tagYamlStr1, tagYamlStr2);
|
||||
|
||||
//YAML anchors
|
||||
if (txtFile.Contains("<<:") && txtFile.Contains("*") && txtFile.Contains("&"))
|
||||
{
|
||||
txtFile = YamlUtils.PreprocessYaml(txtFile);
|
||||
}
|
||||
|
||||
var fileContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
|
||||
if (fileContent == null)
|
||||
{
|
||||
msg = ResUI.FailedConversionConfiguration;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//port
|
||||
fileContent["port"] = LazyConfig.Instance.GetLocalPort(EInboundProtocol.http);
|
||||
//socks-port
|
||||
fileContent["socks-port"] = LazyConfig.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
//log-level
|
||||
fileContent["log-level"] = GetLogLevel(_config.coreBasicItem.loglevel);
|
||||
|
||||
//external-controller
|
||||
fileContent["external-controller"] = $"{Global.Loopback}:{LazyConfig.Instance.StatePort2}";
|
||||
//allow-lan
|
||||
if (_config.inbound[0].allowLANConn)
|
||||
{
|
||||
fileContent["allow-lan"] = "true";
|
||||
fileContent["bind-address"] = "*";
|
||||
}
|
||||
else
|
||||
{
|
||||
fileContent["allow-lan"] = "false";
|
||||
}
|
||||
|
||||
//ipv6
|
||||
fileContent["ipv6"] = _config.clashUIItem.enableIPv6;
|
||||
|
||||
//mode
|
||||
if (!fileContent.ContainsKey("mode"))
|
||||
{
|
||||
fileContent["mode"] = ERuleMode.Rule.ToString().ToLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_config.clashUIItem.ruleMode != ERuleMode.Unchanged)
|
||||
{
|
||||
fileContent["mode"] = _config.clashUIItem.ruleMode.ToString().ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
//enable tun mode
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
string tun = Utils.GetEmbedText(Global.ClashTunYaml);
|
||||
if (!string.IsNullOrEmpty(tun))
|
||||
{
|
||||
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun);
|
||||
if (tunContent != null)
|
||||
fileContent["tun"] = tunContent["tun"];
|
||||
}
|
||||
}
|
||||
|
||||
//Mixin
|
||||
try
|
||||
{
|
||||
MixinContent(fileContent, node);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GenerateClientConfigClash-Mixin", ex);
|
||||
}
|
||||
|
||||
var txtFileNew = YamlUtils.ToYaml(fileContent).Replace(tagYamlStr2, tagYamlStr3);
|
||||
File.WriteAllText(fileName, txtFileNew);
|
||||
//check again
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
msg = ResUI.FailedReadConfiguration + "2";
|
||||
return -1;
|
||||
}
|
||||
|
||||
ClashApiHandler.Instance.ProfileContent = fileContent;
|
||||
|
||||
msg = string.Format(ResUI.SuccessfulConfiguration, $"{node.GetSummary()}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("GenerateClientConfigClash", ex);
|
||||
msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void MixinContent(Dictionary<string, object> fileContent, ProfileItem node)
|
||||
{
|
||||
//if (!_config.clashUIItem.enableMixinContent)
|
||||
//{
|
||||
// return;
|
||||
//}
|
||||
|
||||
var path = Utils.GetConfigPath(Global.ClashMixinConfigFileName);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var txtFile = File.ReadAllText(Utils.GetConfigPath(Global.ClashMixinConfigFileName));
|
||||
|
||||
var mixinContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
|
||||
if (mixinContent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var item in mixinContent)
|
||||
{
|
||||
if (!_config.tunModeItem.enableTun && item.Key == "tun")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.Key.StartsWith("prepend-")
|
||||
|| item.Key.StartsWith("append-")
|
||||
|| item.Key.StartsWith("removed-"))
|
||||
{
|
||||
ModifyContentMerge(fileContent, item.Key, item.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileContent[item.Key] = item.Value;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void ModifyContentMerge(Dictionary<string, object> fileContent, string key, object value)
|
||||
{
|
||||
bool blPrepend = false;
|
||||
bool blRemoved = false;
|
||||
if (key.StartsWith("prepend-"))
|
||||
{
|
||||
blPrepend = true;
|
||||
key = key.Replace("prepend-", "");
|
||||
}
|
||||
else if (key.StartsWith("append-"))
|
||||
{
|
||||
blPrepend = false;
|
||||
key = key.Replace("append-", "");
|
||||
}
|
||||
else if (key.StartsWith("removed-"))
|
||||
{
|
||||
blRemoved = true;
|
||||
key = key.Replace("removed-", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blRemoved && !fileContent.ContainsKey(key))
|
||||
{
|
||||
fileContent.Add(key, value);
|
||||
return;
|
||||
}
|
||||
var lstOri = (List<object>)fileContent[key];
|
||||
var lstValue = (List<object>)value;
|
||||
|
||||
if (blRemoved)
|
||||
{
|
||||
foreach (var item in lstValue)
|
||||
{
|
||||
lstOri.RemoveAll(t => t.ToString().StartsWith(item.ToString()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (blPrepend)
|
||||
{
|
||||
lstValue.Reverse();
|
||||
foreach (var item in lstValue)
|
||||
{
|
||||
lstOri.Insert(0, item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in lstValue)
|
||||
{
|
||||
lstOri.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetLogLevel(string level)
|
||||
{
|
||||
if (level == "none")
|
||||
{
|
||||
return "silent";
|
||||
}
|
||||
else
|
||||
{
|
||||
return level;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
using System.IO;
|
||||
using v2rayN.Model;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
/// </summary>
|
||||
internal class CoreConfigHandler
|
||||
public class CoreConfigHandler
|
||||
{
|
||||
public static int GenerateClientConfig(ProfileItem node, string? fileName, out string msg, out string content)
|
||||
{
|
||||
@@ -19,27 +15,40 @@ namespace v2rayN.Handler
|
||||
msg = ResUI.CheckServerSettings;
|
||||
return -1;
|
||||
}
|
||||
var config = LazyConfig.Instance.GetConfig();
|
||||
var config = LazyConfig.Instance.Config;
|
||||
|
||||
msg = ResUI.InitialConfiguration;
|
||||
if (node.configType == EConfigType.Custom)
|
||||
{
|
||||
return GenerateClientCustomConfig(node, fileName, out msg);
|
||||
if (node.coreType is ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo)
|
||||
{
|
||||
var configGenClash = new CoreConfigClash(config);
|
||||
return configGenClash.GenerateClientCustomConfig(node, fileName, out msg);
|
||||
}
|
||||
if (node.coreType is ECoreType.sing_box)
|
||||
{
|
||||
var configGenSingbox = new CoreConfigSingbox(config);
|
||||
return configGenSingbox.GenerateClientCustomConfig(node, fileName, out msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GenerateClientCustomConfig(node, fileName, out msg);
|
||||
}
|
||||
}
|
||||
else if (config.tunModeItem.enableTun || LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
|
||||
else if (LazyConfig.Instance.GetCoreType(node, node.configType) == ECoreType.sing_box)
|
||||
{
|
||||
var configGenSingbox = new CoreConfigSingbox(config);
|
||||
if (configGenSingbox.GenerateClientConfigContent(node, out SingboxConfig? singboxConfig, out msg) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (Utile.IsNullOrEmpty(fileName))
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
content = JsonUtile.Serialize(singboxConfig);
|
||||
content = JsonUtils.Serialize(singboxConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonUtile.ToFile(singboxConfig, fileName, false);
|
||||
JsonUtils.ToFile(singboxConfig, fileName, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -49,13 +58,13 @@ namespace v2rayN.Handler
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (Utile.IsNullOrEmpty(fileName))
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
content = JsonUtile.Serialize(v2rayConfig);
|
||||
content = JsonUtils.Serialize(v2rayConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonUtile.ToFile(v2rayConfig, fileName, false);
|
||||
JsonUtils.ToFile(v2rayConfig, fileName, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +96,7 @@ namespace v2rayN.Handler
|
||||
string addressFileName = node.address;
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
addressFileName = Utile.GetConfigPath(addressFileName);
|
||||
addressFileName = Utils.GetConfigPath(addressFileName);
|
||||
}
|
||||
if (!File.Exists(addressFileName))
|
||||
{
|
||||
@@ -104,41 +113,6 @@ namespace v2rayN.Handler
|
||||
return -1;
|
||||
}
|
||||
|
||||
//overwrite port
|
||||
if (node.preSocksPort <= 0)
|
||||
{
|
||||
var fileContent = File.ReadAllLines(fileName).ToList();
|
||||
var coreType = LazyConfig.Instance.GetCoreType(node, node.configType);
|
||||
switch (coreType)
|
||||
{
|
||||
case ECoreType.v2fly:
|
||||
case ECoreType.SagerNet:
|
||||
case ECoreType.Xray:
|
||||
case ECoreType.v2fly_v5:
|
||||
break;
|
||||
|
||||
case ECoreType.clash:
|
||||
case ECoreType.clash_meta:
|
||||
case ECoreType.mihomo:
|
||||
//remove the original
|
||||
var indexPort = fileContent.FindIndex(t => t.Contains("port:"));
|
||||
if (indexPort >= 0)
|
||||
{
|
||||
fileContent.RemoveAt(indexPort);
|
||||
}
|
||||
indexPort = fileContent.FindIndex(t => t.Contains("socks-port:"));
|
||||
if (indexPort >= 0)
|
||||
{
|
||||
fileContent.RemoveAt(indexPort);
|
||||
}
|
||||
|
||||
fileContent.Add($"port: {LazyConfig.Instance.GetLocalPort(EInboundProtocol.http)}");
|
||||
fileContent.Add($"socks-port: {LazyConfig.Instance.GetLocalPort(EInboundProtocol.socks)}");
|
||||
break;
|
||||
}
|
||||
File.WriteAllLines(fileName, fileContent);
|
||||
}
|
||||
|
||||
msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -154,21 +128,44 @@ namespace v2rayN.Handler
|
||||
{
|
||||
if (coreType == ECoreType.sing_box)
|
||||
{
|
||||
if ((new CoreConfigSingbox(config)).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
|
||||
if (new CoreConfigSingbox(config).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
JsonUtile.ToFile(singboxConfig, fileName, false);
|
||||
JsonUtils.ToFile(singboxConfig, fileName, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((new CoreConfigV2ray(config)).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
|
||||
if (new CoreConfigV2ray(config).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
JsonUtile.ToFile(v2rayConfig, fileName, false);
|
||||
JsonUtils.ToFile(v2rayConfig, fileName, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, out string msg)
|
||||
{
|
||||
msg = ResUI.CheckServerSettings;
|
||||
if (coreType == ECoreType.sing_box)
|
||||
{
|
||||
if (new CoreConfigSingbox(config).GenerateClientMultipleLoadConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
JsonUtils.ToFile(singboxConfig, fileName, false);
|
||||
}
|
||||
else if (coreType == ECoreType.Xray)
|
||||
{
|
||||
if (new CoreConfigV2ray(config).GenerateClientMultipleLoadConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
JsonUtils.ToFile(v2rayConfig, fileName, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,12 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using v2rayN.Model;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
/// <summary>
|
||||
/// Core process processing class
|
||||
/// </summary>
|
||||
internal class CoreHandler
|
||||
public class CoreHandler
|
||||
{
|
||||
private Config _config;
|
||||
private Process? _process;
|
||||
@@ -22,54 +18,48 @@ namespace v2rayN.Handler
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
|
||||
Environment.SetEnvironmentVariable("v2ray.location.asset", Utile.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
Environment.SetEnvironmentVariable("xray.location.asset", Utile.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
Environment.SetEnvironmentVariable("v2ray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||
}
|
||||
|
||||
public void LoadCore()
|
||||
public void LoadCore(ProfileItem? node)
|
||||
{
|
||||
var node = ConfigHandler.GetDefaultServer(_config);
|
||||
if (node == null)
|
||||
{
|
||||
ShowMsg(false, ResUI.CheckServerSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
string fileName = Utile.GetConfigPath(Global.CoreConfigFileName);
|
||||
string fileName = Utils.GetConfigPath(Global.CoreConfigFileName);
|
||||
if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0)
|
||||
{
|
||||
ShowMsg(false, msg);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowMsg(false, msg);
|
||||
ShowMsg(true, $"{node.GetSummary()}");
|
||||
CoreStop();
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
Utile.RemoveTunDevice();
|
||||
}
|
||||
|
||||
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");
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +67,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
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 = Utile.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
||||
string configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
||||
if (CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType, out string msg) != 0)
|
||||
{
|
||||
ShowMsg(false, msg);
|
||||
@@ -113,7 +103,7 @@ namespace v2rayN.Handler
|
||||
|
||||
if (!hasProc)
|
||||
{
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo();
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
|
||||
foreach (var it in coreInfo)
|
||||
{
|
||||
if (it.coreType == ECoreType.v2rayN)
|
||||
@@ -126,7 +116,7 @@ namespace v2rayN.Handler
|
||||
foreach (Process p in existing)
|
||||
{
|
||||
string? path = p.MainModule?.FileName;
|
||||
if (path == $"{Utile.GetBinPath(vName, it.coreType.ToString())}.exe")
|
||||
if (path == Utils.GetExeName(Utils.GetBinPath(vName, it.coreType.ToString())))
|
||||
{
|
||||
KillProcess(p);
|
||||
}
|
||||
@@ -161,17 +151,17 @@ namespace v2rayN.Handler
|
||||
string fileName = string.Empty;
|
||||
foreach (string name in coreInfo.coreExes)
|
||||
{
|
||||
string vName = $"{name}.exe";
|
||||
vName = Utile.GetBinPath(vName, coreInfo.coreType.ToString());
|
||||
string vName = Utils.GetExeName(name);
|
||||
vName = Utils.GetBinPath(vName, coreInfo.coreType.ToString());
|
||||
if (File.Exists(vName))
|
||||
{
|
||||
fileName = vName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Utile.IsNullOrEmpty(fileName))
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
string msg = string.Format(ResUI.NotFoundCore, Utile.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.coreUrl);
|
||||
Logging.SaveLog(msg);
|
||||
ShowMsg(false, msg);
|
||||
}
|
||||
@@ -183,16 +173,18 @@ namespace v2rayN.Handler
|
||||
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);
|
||||
}
|
||||
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 = LazyConfig.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);
|
||||
@@ -202,22 +194,40 @@ namespace v2rayN.Handler
|
||||
}
|
||||
_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 itemSocks = new ProfileItem()
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
coreType = ECoreType.sing_box,
|
||||
coreType = preCoreType,
|
||||
configType = EConfigType.Socks,
|
||||
address = Global.Loopback,
|
||||
port = node.preSocksPort
|
||||
sni = node.address, //Tun2SocksAddress
|
||||
port = LazyConfig.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||
};
|
||||
string fileName2 = Utile.GetConfigPath(Global.CorePreConfigFileName);
|
||||
}
|
||||
else if ((node.configType == EConfigType.Custom && node.preSocksPort > 0))
|
||||
{
|
||||
preCoreType = _config.tunModeItem.enableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||
itemSocks = new ProfileItem()
|
||||
{
|
||||
coreType = preCoreType,
|
||||
configType = EConfigType.Socks,
|
||||
address = Global.Loopback,
|
||||
port = node.preSocksPort.Value,
|
||||
};
|
||||
_config.runningCoreType = preCoreType;
|
||||
}
|
||||
if (itemSocks != null)
|
||||
{
|
||||
string fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName);
|
||||
if (CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2, out string msg2, out string configStr) == 0)
|
||||
{
|
||||
var coreInfo2 = LazyConfig.Instance.GetCoreInfo(ECoreType.sing_box);
|
||||
var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
|
||||
var proc2 = RunProcess(node, coreInfo2, $" -c {Global.CorePreConfigFileName}", true);
|
||||
if (proc2 is not null)
|
||||
{
|
||||
@@ -235,7 +245,7 @@ namespace v2rayN.Handler
|
||||
ShowMsg(false, configPath);
|
||||
try
|
||||
{
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo(coreType);
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||
var proc = RunProcess(new(), coreInfo, $" -c {Global.CoreSpeedtestConfigFileName}", true);
|
||||
if (proc is null)
|
||||
{
|
||||
@@ -267,7 +277,7 @@ namespace v2rayN.Handler
|
||||
try
|
||||
{
|
||||
string fileName = CoreFindExe(coreInfo);
|
||||
if (Utile.IsNullOrEmpty(fileName))
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -277,7 +287,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
FileName = fileName,
|
||||
Arguments = string.Format(coreInfo.arguments, configPath),
|
||||
WorkingDirectory = Utile.GetConfigPath(),
|
||||
WorkingDirectory = Utils.GetConfigPath(),
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = displayLog,
|
||||
RedirectStandardError = displayLog,
|
||||
@@ -292,7 +302,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
proc.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
if (!Utils.IsNullOrEmpty(e.Data))
|
||||
{
|
||||
string msg = e.Data + Environment.NewLine;
|
||||
ShowMsg(false, msg);
|
||||
@@ -300,7 +310,7 @@ namespace v2rayN.Handler
|
||||
};
|
||||
proc.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
if (!Utils.IsNullOrEmpty(e.Data))
|
||||
{
|
||||
string msg = e.Data + Environment.NewLine;
|
||||
ShowMsg(false, msg);
|
||||
217
v2rayN/ServiceLib/Handler/CoreInfoHandler.cs
Normal file
217
v2rayN/ServiceLib/Handler/CoreInfoHandler.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
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,
|
||||
coreUrl = Global.NUrl,
|
||||
coreReleaseApiUrl = Global.NUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
coreDownloadUrl32 = Global.NUrl + "/download/{0}/v2rayN-32.zip",
|
||||
coreDownloadUrl64 = Global.NUrl + "/download/{0}/v2rayN.zip",
|
||||
coreDownloadUrlArm64 = Global.NUrl + "/download/{0}/v2rayN-arm64.zip",
|
||||
coreDownloadUrlLinux32 = Global.NUrl + "/download/{0}/v2rayN-linux-32.zip",
|
||||
coreDownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
|
||||
coreDownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
coreType = ECoreType.v2fly,
|
||||
coreExes = new List<string> { "wv2ray", "v2ray" },
|
||||
arguments = "",
|
||||
coreUrl = Global.V2flyCoreUrl,
|
||||
coreReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
match = "V2Ray",
|
||||
versionArg = "-version",
|
||||
redirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
coreType = ECoreType.SagerNet,
|
||||
coreExes = new List<string> { "SagerNet", "v2ray" },
|
||||
arguments = "run",
|
||||
coreUrl = Global.SagerNetCoreUrl,
|
||||
coreReleaseApiUrl = Global.SagerNetCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
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",
|
||||
coreUrl = Global.V2flyCoreUrl,
|
||||
coreReleaseApiUrl = 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}",
|
||||
coreUrl = Global.XrayCoreUrl,
|
||||
coreReleaseApiUrl = Global.XrayCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
coreDownloadUrl32 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-32.zip",
|
||||
coreDownloadUrl64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-64.zip",
|
||||
coreDownloadUrlArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
|
||||
coreDownloadUrlLinux32 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-32.zip",
|
||||
coreDownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
|
||||
coreDownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
|
||||
match = "Xray",
|
||||
versionArg = "-version",
|
||||
redirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
coreType = ECoreType.clash,
|
||||
coreExes = new List<string> { "clash-windows-amd64-v3", "clash-windows-amd64", "clash-windows-386", "clash" },
|
||||
arguments = "-f config.json" + PortableMode(),
|
||||
coreUrl = Global.ClashCoreUrl,
|
||||
coreReleaseApiUrl = Global.ClashCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
match = "v",
|
||||
versionArg = "-v",
|
||||
redirectInfo = true,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
coreType = ECoreType.clash_meta,
|
||||
coreExes = new List<string> { "Clash.Meta-windows-amd64-compatible", "Clash.Meta-windows-amd64", "Clash.Meta-windows-386", "Clash.Meta", "clash" },
|
||||
arguments = "-f config.json" + PortableMode(),
|
||||
coreUrl = Global.ClashMetaCoreUrl,
|
||||
coreReleaseApiUrl = Global.ClashMetaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
match = "v",
|
||||
versionArg = "-v",
|
||||
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(),
|
||||
coreUrl = Global.MihomoCoreUrl,
|
||||
coreReleaseApiUrl = Global.MihomoCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
coreDownloadUrl32 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-386-{0}.zip",
|
||||
coreDownloadUrl64 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip",
|
||||
coreDownloadUrlArm64 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
|
||||
coreDownloadUrlLinux32 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-linux-386-{0}.gz",
|
||||
coreDownloadUrlLinux64 = Global.ClashMetaCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
|
||||
coreDownloadUrlLinuxArm64 = Global.ClashMetaCoreUrl + "/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 = "",
|
||||
coreUrl = Global.HysteriaCoreUrl,
|
||||
coreReleaseApiUrl = 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",
|
||||
coreUrl = Global.NaiveproxyCoreUrl,
|
||||
redirectInfo = false,
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
coreType = ECoreType.tuic,
|
||||
coreExes = new List<string> { "tuic-client", "tuic" },
|
||||
arguments = "-c config.json",
|
||||
coreUrl = 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",
|
||||
coreUrl = Global.SingboxCoreUrl,
|
||||
redirectInfo = true,
|
||||
coreReleaseApiUrl = Global.SingboxCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
coreDownloadUrl32 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-386.zip",
|
||||
coreDownloadUrl64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip",
|
||||
coreDownloadUrlArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
|
||||
coreDownloadUrlLinux32 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-386.tar.gz",
|
||||
coreDownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
|
||||
coreDownloadUrlLinuxArm64 = 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",
|
||||
coreUrl = Global.JuicityCoreUrl
|
||||
});
|
||||
|
||||
_coreInfo.Add(new CoreInfo
|
||||
{
|
||||
coreType = ECoreType.hysteria2,
|
||||
coreExes = new List<string> { "hysteria-windows-amd64", "hysteria-windows-386", "hysteria" },
|
||||
arguments = "",
|
||||
coreUrl = Global.HysteriaCoreUrl,
|
||||
coreReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||
redirectInfo = true,
|
||||
});
|
||||
}
|
||||
|
||||
private string PortableMode()
|
||||
{
|
||||
return $" -d \"{Utils.GetBinPath("")}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Sockets;
|
||||
using v2rayN.Model;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
/// <summary>
|
||||
///Download
|
||||
/// </summary>
|
||||
internal class DownloadHandle
|
||||
public class DownloadHandler
|
||||
{
|
||||
public event EventHandler<ResultEventArgs>? UpdateCompleted;
|
||||
|
||||
@@ -34,7 +30,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
Utile.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
||||
Utils.SetSecurityProtocol(LazyConfig.Instance.Config.guiItem.enableSecurityProtocolTls13);
|
||||
|
||||
var progress = new Progress<string>();
|
||||
progress.ProgressChanged += (sender, value) =>
|
||||
@@ -62,11 +58,11 @@ namespace v2rayN.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
public async Task DownloadFileAsync(string url, bool blProxy, int downloadTimeout)
|
||||
public async Task DownloadFileAsync(string url, string fileName, bool blProxy, int downloadTimeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
Utile.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
||||
Utils.SetSecurityProtocol(LazyConfig.Instance.Config.guiItem.enableSecurityProtocolTls13);
|
||||
UpdateCompleted?.Invoke(this, new ResultEventArgs(false, $"{ResUI.Downloading} {url}"));
|
||||
|
||||
var progress = new Progress<double>();
|
||||
@@ -78,7 +74,7 @@ namespace v2rayN.Handler
|
||||
var webProxy = GetWebProxy(blProxy);
|
||||
await DownloaderHelper.Instance.DownloadFileAsync(webProxy,
|
||||
url,
|
||||
Utile.GetTempPath(Utile.GetDownloadFileName(url)),
|
||||
fileName,
|
||||
progress,
|
||||
downloadTimeout);
|
||||
}
|
||||
@@ -96,7 +92,7 @@ namespace v2rayN.Handler
|
||||
|
||||
public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
|
||||
{
|
||||
Utile.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
||||
Utils.SetSecurityProtocol(LazyConfig.Instance.Config.guiItem.enableSecurityProtocolTls13);
|
||||
var webRequestHandler = new SocketsHttpHandler
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
@@ -121,7 +117,7 @@ namespace v2rayN.Handler
|
||||
try
|
||||
{
|
||||
var result1 = await DownloadStringAsync(url, blProxy, userAgent);
|
||||
if (!Utile.IsNullOrEmpty(result1))
|
||||
if (!Utils.IsNullOrEmpty(result1))
|
||||
{
|
||||
return result1;
|
||||
}
|
||||
@@ -139,7 +135,7 @@ namespace v2rayN.Handler
|
||||
try
|
||||
{
|
||||
var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent);
|
||||
if (!Utile.IsNullOrEmpty(result2))
|
||||
if (!Utils.IsNullOrEmpty(result2))
|
||||
{
|
||||
return result2;
|
||||
}
|
||||
@@ -159,7 +155,7 @@ namespace v2rayN.Handler
|
||||
using var wc = new WebClient();
|
||||
wc.Proxy = GetWebProxy(blProxy);
|
||||
var result3 = await wc.DownloadStringTaskAsync(url);
|
||||
if (!Utile.IsNullOrEmpty(result3))
|
||||
if (!Utils.IsNullOrEmpty(result3))
|
||||
{
|
||||
return result3;
|
||||
}
|
||||
@@ -185,7 +181,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
Utile.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
||||
Utils.SetSecurityProtocol(LazyConfig.Instance.Config.guiItem.enableSecurityProtocolTls13);
|
||||
var webProxy = GetWebProxy(blProxy);
|
||||
var client = new HttpClient(new SocketsHttpHandler()
|
||||
{
|
||||
@@ -193,17 +189,17 @@ namespace v2rayN.Handler
|
||||
UseProxy = webProxy != null
|
||||
});
|
||||
|
||||
if (Utile.IsNullOrEmpty(userAgent))
|
||||
if (Utils.IsNullOrEmpty(userAgent))
|
||||
{
|
||||
userAgent = Utile.GetVersion(false);
|
||||
userAgent = Utils.GetVersion(false);
|
||||
}
|
||||
client.DefaultRequestHeaders.UserAgent.TryParseAdd(userAgent);
|
||||
|
||||
Uri uri = new(url);
|
||||
//Authorization Header
|
||||
if (!Utile.IsNullOrEmpty(uri.UserInfo))
|
||||
if (!Utils.IsNullOrEmpty(uri.UserInfo))
|
||||
{
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utile.Base64Encode(uri.UserInfo));
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo));
|
||||
}
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
@@ -230,13 +226,13 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
Utile.SetSecurityProtocol(LazyConfig.Instance.GetConfig().guiItem.enableSecurityProtocolTls13);
|
||||
Utils.SetSecurityProtocol(LazyConfig.Instance.Config.guiItem.enableSecurityProtocolTls13);
|
||||
|
||||
var webProxy = GetWebProxy(blProxy);
|
||||
|
||||
if (Utile.IsNullOrEmpty(userAgent))
|
||||
if (Utils.IsNullOrEmpty(userAgent))
|
||||
{
|
||||
userAgent = Utile.GetVersion(false);
|
||||
userAgent = Utils.GetVersion(false);
|
||||
}
|
||||
var result = await DownloaderHelper.Instance.DownloadStringAsync(webProxy, url, userAgent, 30);
|
||||
return result;
|
||||
@@ -264,7 +260,7 @@ namespace v2rayN.Handler
|
||||
|
||||
try
|
||||
{
|
||||
var config = LazyConfig.Instance.GetConfig();
|
||||
var config = LazyConfig.Instance.Config;
|
||||
int responseTime = await GetRealPingTime(config.speedTestItem.speedPingTestUrl, webProxy, 10);
|
||||
return responseTime;
|
||||
}
|
||||
@@ -286,8 +282,6 @@ namespace v2rayN.Handler
|
||||
int responseTime = -1;
|
||||
try
|
||||
{
|
||||
Stopwatch timer = Stopwatch.StartNew();
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
|
||||
using var client = new HttpClient(new SocketsHttpHandler()
|
||||
@@ -295,9 +289,17 @@ namespace v2rayN.Handler
|
||||
Proxy = webProxy,
|
||||
UseProxy = webProxy != null
|
||||
});
|
||||
await client.GetAsync(url, cts.Token);
|
||||
|
||||
responseTime = timer.Elapsed.Milliseconds;
|
||||
List<int> oneTime = [];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
await client.GetAsync(url, cts.Token);
|
||||
timer.Stop();
|
||||
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
205
v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
Normal file
205
v2rayN/ServiceLib/Handler/Fmt/BaseFmt.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
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.IsNullOrEmpty(item.flow))
|
||||
{
|
||||
dicQuery.Add("flow", item.flow);
|
||||
}
|
||||
|
||||
if (!Utils.IsNullOrEmpty(item.streamSecurity))
|
||||
{
|
||||
dicQuery.Add("security", item.streamSecurity);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (securityDef != null)
|
||||
{
|
||||
dicQuery.Add("security", securityDef);
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.fingerprint))
|
||||
{
|
||||
dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("sid", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.spiderX))
|
||||
{
|
||||
dicQuery.Add("spx", Utils.UrlEncode(item.spiderX));
|
||||
}
|
||||
if (item.allowInsecure.Equals("true"))
|
||||
{
|
||||
dicQuery.Add("allowInsecure", "1");
|
||||
}
|
||||
|
||||
dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp));
|
||||
|
||||
switch (item.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("seed", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
case nameof(ETransport.splithttp):
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
dicQuery["type"] = nameof(ETransport.http);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(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.IsNullOrEmpty(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
Normal file
100
v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
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
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
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.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("obfs", "salamander");
|
||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.path));
|
||||
}
|
||||
dicQuery.Add("insecure", item.allowInsecure.ToLower() == "true" ? "1" : "0");
|
||||
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Hysteria2]}{url}/{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
180
v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
Normal file
180
v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
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.IsNullOrEmpty(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}");
|
||||
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
|
||||
url = $"{Global.ProtocolShares[EConfigType.Shadowsocks]}{url}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
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.IsNullOrEmpty(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)
|
||||
{
|
||||
Uri parsedUrl;
|
||||
try
|
||||
{
|
||||
parsedUrl = new Uri(result);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ProfileItem item = new()
|
||||
{
|
||||
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
address = parsedUrl.IdnHost,
|
||||
port = parsedUrl.Port,
|
||||
};
|
||||
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped);
|
||||
//2022-blake3
|
||||
if (rawUserInfo.Contains(':'))
|
||||
{
|
||||
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length != 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
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.IsNullOrEmpty(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
127
v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
Normal file
127
v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
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.IsNullOrEmpty(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}");
|
||||
url = $"{pw}@{GetIpv6(item.address)}:{item.port}";
|
||||
url = $"{Global.ProtocolShares[EConfigType.Socks]}{url}{remark}";
|
||||
return url;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Uri parsedUrl;
|
||||
try
|
||||
{
|
||||
parsedUrl = new Uri(result);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ProfileItem item = new()
|
||||
{
|
||||
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
address = parsedUrl.IdnHost,
|
||||
port = parsedUrl.Port,
|
||||
};
|
||||
|
||||
// parse base64 UserInfo
|
||||
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
|
||||
string userInfo = Utils.Base64Decode(rawUserInfo);
|
||||
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
||||
if (userInfoParts.Length == 2)
|
||||
{
|
||||
item.security = userInfoParts[0];
|
||||
item.id = userInfoParts[1];
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs
Normal file
49
v2rayN/ServiceLib/Handler/Fmt/TrojanFmt.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
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
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
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.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
GetStdTransport(item, null, ref dicQuery);
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Trojan]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
Normal file
64
v2rayN/ServiceLib/Handler/Fmt/TuicFmt.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
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
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
item.address = url.IdnHost;
|
||||
item.port = url.Port;
|
||||
item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
|
||||
var userInfoParts = url.UserInfo.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.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
dicQuery.Add("congestion_control", item.headerType);
|
||||
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
$"{item.id}:{item.security}",
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Tuic]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
Normal file
60
v2rayN/ServiceLib/Handler/Fmt/VLESSFmt.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
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
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
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.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.security))
|
||||
{
|
||||
dicQuery.Add("encryption", item.security);
|
||||
}
|
||||
else
|
||||
{
|
||||
dicQuery.Add("encryption", Global.None);
|
||||
}
|
||||
GetStdTransport(item, Global.None, ref dicQuery);
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
item.id,
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.VLESS]}{url}{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
//转成Json
|
||||
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
|
||||
if (vmessQRCode == null)
|
||||
{
|
||||
msg = ResUI.FailedConversionConfiguration;
|
||||
return null;
|
||||
}
|
||||
|
||||
item.network = Global.DefaultNetwork;
|
||||
item.headerType = Global.None;
|
||||
|
||||
item.configVersion = Utils.ToInt(vmessQRCode.v);
|
||||
item.remarks = Utils.ToString(vmessQRCode.ps);
|
||||
item.address = Utils.ToString(vmessQRCode.add);
|
||||
item.port = Utils.ToInt(vmessQRCode.port);
|
||||
item.id = Utils.ToString(vmessQRCode.id);
|
||||
item.alterId = Utils.ToInt(vmessQRCode.aid);
|
||||
item.security = Utils.ToString(vmessQRCode.scy);
|
||||
|
||||
item.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.net))
|
||||
{
|
||||
item.network = vmessQRCode.net;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(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"
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
69
v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
Normal file
69
v2rayN/ServiceLib/Handler/Fmt/WireguardFmt.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
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
|
||||
};
|
||||
|
||||
Uri url = new(str);
|
||||
|
||||
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.IsNullOrEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("reserved", Utils.UrlEncode(item.path));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("address", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("mtu", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray());
|
||||
|
||||
url = string.Format("{0}@{1}:{2}",
|
||||
Utils.UrlEncode(item.id),
|
||||
GetIpv6(item.address),
|
||||
item.port);
|
||||
url = $"{Global.ProtocolShares[EConfigType.Wireguard]}{url}/{query}{remark}";
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
242
v2rayN/ServiceLib/Handler/LazyConfig.cs
Normal file
242
v2rayN/ServiceLib/Handler/LazyConfig.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class LazyConfig
|
||||
{
|
||||
private static readonly Lazy<LazyConfig> _instance = new(() => new());
|
||||
private Config _config;
|
||||
private int? _statePort;
|
||||
private int? _statePort2;
|
||||
|
||||
public static LazyConfig 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;
|
||||
}
|
||||
}
|
||||
|
||||
private Job? _processJob;
|
||||
|
||||
public LazyConfig()
|
||||
{
|
||||
SQLiteHelper.Instance.CreateTable<SubItem>();
|
||||
SQLiteHelper.Instance.CreateTable<ProfileItem>();
|
||||
SQLiteHelper.Instance.CreateTable<ServerStatItem>();
|
||||
SQLiteHelper.Instance.CreateTable<RoutingItem>();
|
||||
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
|
||||
SQLiteHelper.Instance.CreateTable<DNSItem>();
|
||||
}
|
||||
|
||||
#region Config
|
||||
|
||||
public void SetConfig(Config config) => _config = config;
|
||||
|
||||
public 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 List<SubItem> SubItems()
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<SubItem>().ToList();
|
||||
}
|
||||
|
||||
public SubItem GetSubItem(string subid)
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<SubItem>().FirstOrDefault(t => t.id == subid);
|
||||
}
|
||||
|
||||
public List<ProfileItem> ProfileItems(string subid)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(subid))
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<ProfileItem>().ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> ProfileItemIndexes(string subid)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(subid))
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<ProfileItem>().Select(t => t.indexId).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<ProfileItem>().Where(t => t.subid == subid).Select(t => t.indexId).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ProfileItemModel> ProfileItems(string subid, string filter)
|
||||
{
|
||||
var sql = @$"select a.*
|
||||
,b.remarks subRemarks
|
||||
from ProfileItem a
|
||||
left join SubItem b on a.subid = b.id
|
||||
where 1=1 ";
|
||||
if (!Utils.IsNullOrEmpty(subid))
|
||||
{
|
||||
sql += $" and a.subid = '{subid}'";
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(filter))
|
||||
{
|
||||
if (filter.Contains('\''))
|
||||
{
|
||||
filter = filter.Replace("'", "");
|
||||
}
|
||||
sql += String.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter);
|
||||
}
|
||||
|
||||
return SQLiteHelper.Instance.Query<ProfileItemModel>(sql).ToList();
|
||||
}
|
||||
|
||||
public List<ProfileItemModel> ProfileItemsEx(string subid, string filter)
|
||||
{
|
||||
var lstModel = ProfileItems(_config.subIndexId, filter);
|
||||
|
||||
ConfigHandler.SetDefaultServer(_config, lstModel);
|
||||
|
||||
var lstServerStat = (_config.guiItem.enableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
|
||||
var lstProfileExs = ProfileExHandler.Instance.ProfileExs;
|
||||
lstModel = (from t in lstModel
|
||||
join t2 in lstServerStat on t.indexId equals t2.indexId into t2b
|
||||
from t22 in t2b.DefaultIfEmpty()
|
||||
join t3 in lstProfileExs on t.indexId equals t3.indexId into t3b
|
||||
from t33 in t3b.DefaultIfEmpty()
|
||||
select new ProfileItemModel
|
||||
{
|
||||
indexId = t.indexId,
|
||||
configType = t.configType,
|
||||
remarks = t.remarks,
|
||||
address = t.address,
|
||||
port = t.port,
|
||||
security = t.security,
|
||||
network = t.network,
|
||||
streamSecurity = t.streamSecurity,
|
||||
subid = t.subid,
|
||||
subRemarks = t.subRemarks,
|
||||
isActive = t.indexId == _config.indexId,
|
||||
sort = t33 == null ? 0 : t33.sort,
|
||||
delay = t33 == null ? 0 : t33.delay,
|
||||
delayVal = t33?.delay != 0 ? $"{t33?.delay} {Global.DelayUnit}" : string.Empty,
|
||||
speedVal = t33?.speed != 0 ? $"{t33?.speed} {Global.SpeedUnit}" : string.Empty,
|
||||
todayDown = t22 == null ? "" : Utils.HumanFy(t22.todayDown),
|
||||
todayUp = t22 == null ? "" : Utils.HumanFy(t22.todayUp),
|
||||
totalDown = t22 == null ? "" : Utils.HumanFy(t22.totalDown),
|
||||
totalUp = t22 == null ? "" : Utils.HumanFy(t22.totalUp)
|
||||
}).OrderBy(t => t.sort).ToList();
|
||||
|
||||
return lstModel;
|
||||
}
|
||||
|
||||
public ProfileItem? GetProfileItem(string indexId)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(indexId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SQLiteHelper.Instance.Table<ProfileItem>().FirstOrDefault(it => it.indexId == indexId);
|
||||
}
|
||||
|
||||
public ProfileItem? GetProfileItemViaRemarks(string remarks)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(remarks))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SQLiteHelper.Instance.Table<ProfileItem>().FirstOrDefault(it => it.remarks == remarks);
|
||||
}
|
||||
|
||||
public List<RoutingItem> RoutingItems()
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<RoutingItem>().Where(it => it.locked == false).OrderBy(t => t.sort).ToList();
|
||||
}
|
||||
|
||||
public RoutingItem GetRoutingItem(string id)
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<RoutingItem>().FirstOrDefault(it => it.locked == false && it.id == id);
|
||||
}
|
||||
|
||||
public List<DNSItem> DNSItems()
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<DNSItem>().ToList();
|
||||
}
|
||||
|
||||
public DNSItem GetDNSItem(ECoreType eCoreType)
|
||||
{
|
||||
return SQLiteHelper.Instance.Table<DNSItem>().FirstOrDefault(it => it.coreType == eCoreType);
|
||||
}
|
||||
|
||||
#endregion 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
|
||||
}
|
||||
}
|
||||
41
v2rayN/ServiceLib/Handler/NoticeHandler.cs
Normal file
41
v2rayN/ServiceLib/Handler/NoticeHandler.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public class NoticeHandler
|
||||
{
|
||||
public void Enqueue(string? content)
|
||||
{
|
||||
if (content.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
MessageBus.Current.SendMessage(content, Global.CommandSendSnackMsg);
|
||||
}
|
||||
|
||||
public void SendMessage(string? content)
|
||||
{
|
||||
if (content.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
MessageBus.Current.SendMessage(content, Global.CommandSendMsgView);
|
||||
}
|
||||
|
||||
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,13 +1,13 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive.Linq;
|
||||
using v2rayN.Model;
|
||||
|
||||
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 ConcurrentBag<ProfileExItem> _lstProfileEx = [];
|
||||
private Queue<string> _queIndexIds = new();
|
||||
public ConcurrentBag<ProfileExItem> ProfileExs => _lstProfileEx;
|
||||
public static ProfileExHandler Instance => _instance.Value;
|
||||
@@ -15,6 +15,15 @@ namespace v2rayN.Handler
|
||||
public ProfileExHandler()
|
||||
{
|
||||
Init();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
SaveQueueIndexIds();
|
||||
await Task.Delay(1000 * 600);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void Init()
|
||||
@@ -22,34 +31,59 @@ namespace v2rayN.Handler
|
||||
SQLiteHelper.Instance.Execute($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
|
||||
|
||||
_lstProfileEx = new(SQLiteHelper.Instance.Table<ProfileExItem>());
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var cnt = _queIndexIds.Count;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
var id = _queIndexIds.Dequeue();
|
||||
var item = _lstProfileEx.FirstOrDefault(t => t.indexId == id);
|
||||
if (item is not null)
|
||||
{
|
||||
SQLiteHelper.Instance.Replace(item);
|
||||
}
|
||||
}
|
||||
await Task.Delay(1000 * 60);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void IndexIdEnqueue(string indexId)
|
||||
{
|
||||
if (!Utile.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
{
|
||||
_queIndexIds.Enqueue(indexId);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveQueueIndexIds()
|
||||
{
|
||||
var cnt = _queIndexIds.Count;
|
||||
if (cnt > 0)
|
||||
{
|
||||
var lstExists = SQLiteHelper.Instance.Table<ProfileExItem>();
|
||||
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);
|
||||
if (itemNew is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
lstUpdates.Add(itemNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
lstInserts.Add(itemNew);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
if (lstInserts.Count() > 0)
|
||||
SQLiteHelper.Instance.InsertAll(lstInserts);
|
||||
|
||||
if (lstUpdates.Count() > 0)
|
||||
SQLiteHelper.Instance.UpdateAll(lstUpdates);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("ProfileExHandler", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddProfileEx(string indexId, ref ProfileExItem? profileEx)
|
||||
{
|
||||
profileEx = new()
|
||||
@@ -73,11 +107,7 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
//foreach (var item in _lstProfileEx)
|
||||
//{
|
||||
// SQLiteHelper.Instance.Replace(item);
|
||||
//}
|
||||
SQLiteHelper.Instance.UpdateAll(_lstProfileEx);
|
||||
SaveQueueIndexIds();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1,26 +1,19 @@
|
||||
using ReactiveUI;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using v2rayN.Model;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
internal class SpeedtestHandler
|
||||
public class SpeedtestHandler
|
||||
{
|
||||
private Config? _config;
|
||||
private CoreHandler _coreHandler;
|
||||
private List<ServerTestItem> _selecteds;
|
||||
private ESpeedActionType _actionType;
|
||||
private Action<string, string, string> _updateFunc;
|
||||
private Action<SpeedTestResult> _updateFunc;
|
||||
private bool _exitLoop = false;
|
||||
|
||||
public SpeedtestHandler(Config config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public SpeedtestHandler(Config config, CoreHandler coreHandler, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<string, string, string> update)
|
||||
public SpeedtestHandler(Config config, CoreHandler coreHandler, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> update)
|
||||
{
|
||||
_config = config;
|
||||
_coreHandler = coreHandler;
|
||||
@@ -90,6 +83,12 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
public void ExitLoop()
|
||||
{
|
||||
_exitLoop = true;
|
||||
UpdateFunc("", ResUI.SpeedtestingStop);
|
||||
}
|
||||
|
||||
private Task RunTcping()
|
||||
{
|
||||
try
|
||||
@@ -145,7 +144,7 @@ namespace v2rayN.Handler
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
DownloadHandle downloadHandle = new DownloadHandle();
|
||||
DownloadHandler downloadHandle = new DownloadHandler();
|
||||
|
||||
List<Task> tasks = new();
|
||||
foreach (var it in _selecteds)
|
||||
@@ -212,22 +211,11 @@ namespace v2rayN.Handler
|
||||
string url = _config.speedTestItem.speedTestUrl;
|
||||
var timeout = _config.speedTestItem.speedTestTimeout;
|
||||
|
||||
DownloadHandle downloadHandle = new();
|
||||
|
||||
var exitLoop = false;
|
||||
MessageBus.Current.Listen<string>(Global.CommandStopSpeedTest)
|
||||
.Subscribe(x =>
|
||||
{
|
||||
if (!exitLoop)
|
||||
{
|
||||
UpdateFunc("", ResUI.SpeedtestingStop);
|
||||
}
|
||||
exitLoop = true;
|
||||
});
|
||||
DownloadHandler downloadHandle = new();
|
||||
|
||||
foreach (var it in _selecteds)
|
||||
{
|
||||
if (exitLoop)
|
||||
if (_exitLoop)
|
||||
{
|
||||
UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
|
||||
continue;
|
||||
@@ -285,22 +273,11 @@ namespace v2rayN.Handler
|
||||
string url = _config.speedTestItem.speedTestUrl;
|
||||
var timeout = _config.speedTestItem.speedTestTimeout;
|
||||
|
||||
DownloadHandle downloadHandle = new();
|
||||
|
||||
var exitLoop = false;
|
||||
MessageBus.Current.Listen<string>(Global.CommandStopSpeedTest)
|
||||
.Subscribe(x =>
|
||||
{
|
||||
if (!exitLoop)
|
||||
{
|
||||
UpdateFunc("", ResUI.SpeedtestingStop);
|
||||
}
|
||||
exitLoop = true;
|
||||
});
|
||||
DownloadHandler downloadHandle = new();
|
||||
|
||||
foreach (var it in _selecteds)
|
||||
{
|
||||
if (exitLoop)
|
||||
if (_exitLoop)
|
||||
{
|
||||
UpdateFunc(it.indexId, "", ResUI.SpeedtestingSkip);
|
||||
continue;
|
||||
@@ -357,7 +334,7 @@ namespace v2rayN.Handler
|
||||
await RunSpeedTestMulti();
|
||||
}
|
||||
|
||||
private async Task<string> GetRealPingTime(DownloadHandle downloadHandle, IWebProxy webProxy)
|
||||
private async Task<string> GetRealPingTime(DownloadHandler downloadHandle, IWebProxy webProxy)
|
||||
{
|
||||
int responseTime = await downloadHandle.GetRealPingTime(_config.speedTestItem.speedPingTestUrl, webProxy, 10);
|
||||
//string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
|
||||
@@ -376,19 +353,18 @@ namespace v2rayN.Handler
|
||||
ipAddress = ipHostInfo.AddressList[0];
|
||||
}
|
||||
|
||||
Stopwatch timer = new();
|
||||
timer.Start();
|
||||
var timer = Stopwatch.StartNew();
|
||||
|
||||
IPEndPoint endPoint = new(ipAddress, port);
|
||||
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
|
||||
IAsyncResult result = clientSocket.BeginConnect(endPoint, null, null);
|
||||
var result = clientSocket.BeginConnect(endPoint, null, null);
|
||||
if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
|
||||
throw new TimeoutException("connect timeout (5s): " + url);
|
||||
clientSocket.EndConnect(result);
|
||||
|
||||
timer.Stop();
|
||||
responseTime = timer.Elapsed.Milliseconds;
|
||||
responseTime = (int)timer.Elapsed.TotalMilliseconds;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -408,7 +384,7 @@ namespace v2rayN.Handler
|
||||
|
||||
private void UpdateFunc(string indexId, string delay, string speed = "")
|
||||
{
|
||||
_updateFunc(indexId, delay, speed);
|
||||
_updateFunc(new() { IndexId = indexId, Delay = delay, Speed = speed });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using v2rayN.Model;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler.Statistics
|
||||
{
|
||||
internal class StatisticsHandler
|
||||
public class StatisticsHandler
|
||||
{
|
||||
private static readonly Lazy<StatisticsHandler> instance = new(() => new());
|
||||
public static StatisticsHandler Instance => instance.Value;
|
||||
|
||||
private Config _config;
|
||||
private ServerStatItem? _serverStatItem;
|
||||
private List<ServerStatItem> _lstServerStat;
|
||||
@@ -12,20 +13,17 @@ namespace v2rayN.Handler
|
||||
private StatisticsSingbox? _statisticsSingbox;
|
||||
|
||||
public List<ServerStatItem> ServerStat => _lstServerStat;
|
||||
public bool Enable { get; set; }
|
||||
|
||||
public StatisticsHandler(Config config, Action<ServerSpeedItem> update)
|
||||
public void Init(Config config, Action<ServerSpeedItem> update)
|
||||
{
|
||||
_config = config;
|
||||
Enable = config.guiItem.enableStatistics;
|
||||
if (!Enable)
|
||||
_updateFunc = update;
|
||||
if (!config.guiItem.enableStatistics)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updateFunc = update;
|
||||
|
||||
Init();
|
||||
InitData();
|
||||
|
||||
_statisticsV2Ray = new StatisticsV2ray(config, UpdateServerStat);
|
||||
_statisticsSingbox = new StatisticsSingbox(config, UpdateServerStat);
|
||||
@@ -55,7 +53,10 @@ namespace v2rayN.Handler
|
||||
{
|
||||
try
|
||||
{
|
||||
SQLiteHelper.Instance.UpdateAll(_lstServerStat);
|
||||
if (_lstServerStat != null)
|
||||
{
|
||||
SQLiteHelper.Instance.UpdateAll(_lstServerStat);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -63,7 +64,7 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private void Init()
|
||||
private void InitData()
|
||||
{
|
||||
SQLiteHelper.Instance.Execute($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using v2rayN.Model;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler.Statistics
|
||||
{
|
||||
internal class StatisticsSingbox
|
||||
public class StatisticsSingbox
|
||||
{
|
||||
private Config _config;
|
||||
private bool _exitFlag;
|
||||
@@ -27,7 +26,7 @@ namespace v2rayN.Handler
|
||||
|
||||
try
|
||||
{
|
||||
url = $"ws://{Global.Loopback}:{LazyConfig.Instance.StatePort}/traffic";
|
||||
url = $"ws://{Global.Loopback}:{LazyConfig.Instance.StatePort2}/traffic";
|
||||
|
||||
if (webSocket == null)
|
||||
{
|
||||
@@ -61,8 +60,13 @@ namespace v2rayN.Handler
|
||||
|
||||
while (!_exitFlag)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
try
|
||||
{
|
||||
if (!(_config.IsRunningCore(ECoreType.clash)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (webSocket != null)
|
||||
{
|
||||
if (webSocket.State == WebSocketState.Aborted
|
||||
@@ -84,7 +88,7 @@ namespace v2rayN.Handler
|
||||
while (!res.CloseStatus.HasValue)
|
||||
{
|
||||
var result = Encoding.UTF8.GetString(buffer, 0, res.Count);
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
ParseOutput(result, out ulong up, out ulong down);
|
||||
|
||||
@@ -101,10 +105,6 @@ namespace v2rayN.Handler
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace v2rayN.Handler
|
||||
up = 0; down = 0;
|
||||
try
|
||||
{
|
||||
var trafficItem = JsonUtile.Deserialize<TrafficItem>(source);
|
||||
var trafficItem = JsonUtils.Deserialize<TrafficItem>(source);
|
||||
if (trafficItem != null)
|
||||
{
|
||||
up = trafficItem.up;
|
||||
@@ -1,19 +1,18 @@
|
||||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
using ProtosLib.Statistics;
|
||||
using v2rayN.Model;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler.Statistics
|
||||
{
|
||||
internal class StatisticsV2ray
|
||||
public class StatisticsV2ray
|
||||
{
|
||||
private Model.Config _config;
|
||||
private Models.Config _config;
|
||||
private GrpcChannel? _channel;
|
||||
private StatsService.StatsServiceClient? _client;
|
||||
private bool _exitFlag;
|
||||
private Action<ServerSpeedItem> _updateFunc;
|
||||
|
||||
public StatisticsV2ray(Model.Config config, Action<ServerSpeedItem> update)
|
||||
public StatisticsV2ray(Models.Config config, Action<ServerSpeedItem> update)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
@@ -49,14 +48,20 @@ namespace v2rayN.Handler
|
||||
{
|
||||
while (!_exitFlag)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
try
|
||||
{
|
||||
if (!(_config.IsRunningCore(ECoreType.Xray)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_channel?.State == ConnectivityState.Ready)
|
||||
{
|
||||
QueryStatsResponse? res = null;
|
||||
try
|
||||
{
|
||||
res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
|
||||
if (_client != null)
|
||||
res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -68,8 +73,8 @@ namespace v2rayN.Handler
|
||||
_updateFunc(server);
|
||||
}
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
if (_channel != null) await _channel.ConnectAsync();
|
||||
if (_channel != null)
|
||||
await _channel.ConnectAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -80,6 +85,8 @@ namespace v2rayN.Handler
|
||||
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server)
|
||||
{
|
||||
server = new();
|
||||
long aggregateProxyUp = 0;
|
||||
long aggregateProxyDown = 0;
|
||||
try
|
||||
{
|
||||
foreach (Stat stat in source)
|
||||
@@ -94,15 +101,15 @@ namespace v2rayN.Handler
|
||||
name = nStr[1];
|
||||
type = nStr[3];
|
||||
|
||||
if (name == Global.ProxyTag)
|
||||
if (name.StartsWith(Global.ProxyTag))
|
||||
{
|
||||
if (type == "uplink")
|
||||
{
|
||||
server.proxyUp = value;
|
||||
aggregateProxyUp += value;
|
||||
}
|
||||
else if (type == "downlink")
|
||||
{
|
||||
server.proxyDown = value;
|
||||
aggregateProxyDown += value;
|
||||
}
|
||||
}
|
||||
else if (name == Global.DirectTag)
|
||||
@@ -117,6 +124,8 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
}
|
||||
server.proxyUp = aggregateProxyUp;
|
||||
server.proxyDown = aggregateProxyDown;
|
||||
}
|
||||
catch
|
||||
{
|
||||
76
v2rayN/ServiceLib/Handler/TaskHandler.cs
Normal file
76
v2rayN/ServiceLib/Handler/TaskHandler.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public class TaskHandler
|
||||
{
|
||||
private static readonly Lazy<TaskHandler> _instance = new(() => new());
|
||||
public static TaskHandler Instance => _instance.Value;
|
||||
|
||||
public TaskHandler()
|
||||
{
|
||||
}
|
||||
|
||||
public void RegUpdateTask(Config config, Action<bool, string> update)
|
||||
{
|
||||
Task.Run(() => UpdateTaskRunSubscription(config, update));
|
||||
Task.Run(() => UpdateTaskRunGeo(config, update));
|
||||
}
|
||||
|
||||
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> update)
|
||||
{
|
||||
await Task.Delay(60000);
|
||||
Logging.SaveLog("UpdateTaskRunSubscription");
|
||||
|
||||
var updateHandle = new UpdateHandler();
|
||||
while (true)
|
||||
{
|
||||
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
|
||||
var lstSubs = LazyConfig.Instance.SubItems()
|
||||
.Where(t => t.autoUpdateInterval > 0)
|
||||
.Where(t => updateTime - t.updateTime >= t.autoUpdateInterval * 60)
|
||||
.ToList();
|
||||
|
||||
foreach (var item in lstSubs)
|
||||
{
|
||||
updateHandle.UpdateSubscriptionProcess(config, item.id, true, (bool success, string msg) =>
|
||||
{
|
||||
update(success, msg);
|
||||
if (success)
|
||||
Logging.SaveLog("subscription" + msg);
|
||||
});
|
||||
item.updateTime = updateTime;
|
||||
ConfigHandler.AddSubItem(config, item);
|
||||
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
await Task.Delay(60000);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> update)
|
||||
{
|
||||
var autoUpdateGeoTime = DateTime.Now;
|
||||
|
||||
await Task.Delay(1000 * 120);
|
||||
Logging.SaveLog("UpdateTaskRunGeo");
|
||||
|
||||
var updateHandle = new UpdateHandler();
|
||||
while (true)
|
||||
{
|
||||
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) =>
|
||||
{
|
||||
update(false, msg);
|
||||
});
|
||||
autoUpdateGeoTime = dtNow;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(1000 * 3600);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,17 @@
|
||||
using DynamicData;
|
||||
using Splat;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using v2rayN.Model;
|
||||
using v2rayN.Resx;
|
||||
|
||||
namespace v2rayN.Handler
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
internal class UpdateHandle
|
||||
public class UpdateHandler
|
||||
{
|
||||
private Action<bool, string> _updateFunc;
|
||||
private Config _config;
|
||||
private int _timeout = 30;
|
||||
|
||||
public event EventHandler<ResultEventArgs> AbsoluteCompleted;
|
||||
|
||||
public class ResultEventArgs : EventArgs
|
||||
private class ResultEventArgs
|
||||
{
|
||||
public bool Success;
|
||||
public string Msg;
|
||||
@@ -32,37 +25,66 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckUpdateGuiN(Config config, Action<bool, string> update, bool preRelease)
|
||||
public async Task CheckUpdateGuiN(Config config, Action<bool, string> update, bool preRelease)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
var url = string.Empty;
|
||||
var fileName = string.Empty;
|
||||
|
||||
DownloadHandle downloadHandle = new();
|
||||
DownloadHandler downloadHandle = new();
|
||||
downloadHandle.UpdateCompleted += (sender2, args) =>
|
||||
{
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
|
||||
_updateFunc(true, Utils.UrlEncode(fileName));
|
||||
}
|
||||
else
|
||||
{
|
||||
_updateFunc(false, args.Msg);
|
||||
}
|
||||
};
|
||||
downloadHandle.Error += (sender2, args) =>
|
||||
{
|
||||
_updateFunc(false, args.GetException().Message);
|
||||
};
|
||||
|
||||
_updateFunc(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN));
|
||||
var args = await CheckUpdateAsync(downloadHandle, ECoreType.v2rayN, preRelease);
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN));
|
||||
_updateFunc(false, args.Msg);
|
||||
|
||||
url = args.Url;
|
||||
fileName = Utils.GetTempPath(Utils.GetGUID());
|
||||
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
_updateFunc(false, args.Msg);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CheckUpdateCore(ECoreType type, Config config, Action<bool, string> update, bool preRelease)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
var url = string.Empty;
|
||||
var fileName = string.Empty;
|
||||
|
||||
DownloadHandler downloadHandle = new();
|
||||
downloadHandle.UpdateCompleted += (sender2, args) =>
|
||||
{
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
|
||||
_updateFunc(false, ResUI.MsgUnpacking);
|
||||
|
||||
try
|
||||
{
|
||||
string fileName = Utile.GetTempPath(Utile.GetDownloadFileName(url));
|
||||
fileName = Utile.UrlEncode(fileName);
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "v2rayUpgrade.exe",
|
||||
Arguments = fileName.AppendQuotes(),
|
||||
WorkingDirectory = Utile.StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
if (process.Id > 0)
|
||||
{
|
||||
_updateFunc(true, "");
|
||||
}
|
||||
_updateFunc(true, fileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -78,83 +100,25 @@ namespace v2rayN.Handler
|
||||
{
|
||||
_updateFunc(false, args.GetException().Message);
|
||||
};
|
||||
AbsoluteCompleted += (sender2, args) =>
|
||||
|
||||
_updateFunc(false, string.Format(ResUI.MsgStartUpdating, type));
|
||||
var args = await CheckUpdateAsync(downloadHandle, type, preRelease);
|
||||
if (args.Success)
|
||||
{
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, "v2rayN"));
|
||||
_updateFunc(false, args.Msg);
|
||||
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, type));
|
||||
_updateFunc(false, args.Msg);
|
||||
|
||||
url = args.Url;
|
||||
AskToDownload(downloadHandle, url, true).ContinueWith(task =>
|
||||
{
|
||||
_updateFunc(false, url);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Locator.Current.GetService<NoticeHandler>()?.Enqueue(args.Msg);
|
||||
_updateFunc(false, args.Msg);
|
||||
}
|
||||
};
|
||||
_updateFunc(false, string.Format(ResUI.MsgStartUpdating, "v2rayN"));
|
||||
CheckUpdateAsync(ECoreType.v2rayN, preRelease);
|
||||
}
|
||||
|
||||
public void CheckUpdateCore(ECoreType type, Config config, Action<bool, string> update, bool preRelease)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
var url = string.Empty;
|
||||
|
||||
DownloadHandle downloadHandle = new();
|
||||
downloadHandle.UpdateCompleted += (sender2, args) =>
|
||||
url = args.Url;
|
||||
fileName = Utils.GetTempPath(Utils.GetGUID());
|
||||
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
|
||||
_updateFunc(false, ResUI.MsgUnpacking);
|
||||
|
||||
try
|
||||
{
|
||||
_updateFunc(true, url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_updateFunc(false, ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!args.Msg.IsNullOrEmpty())
|
||||
{
|
||||
_updateFunc(false, args.Msg);
|
||||
}
|
||||
};
|
||||
downloadHandle.Error += (sender2, args) =>
|
||||
{
|
||||
_updateFunc(true, args.GetException().Message);
|
||||
};
|
||||
|
||||
AbsoluteCompleted += (sender2, args) =>
|
||||
{
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, "Core"));
|
||||
_updateFunc(false, args.Msg);
|
||||
|
||||
url = args.Url;
|
||||
AskToDownload(downloadHandle, url, true).ContinueWith(task =>
|
||||
{
|
||||
_updateFunc(false, url);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Locator.Current.GetService<NoticeHandler>()?.Enqueue(args.Msg);
|
||||
_updateFunc(false, args.Msg);
|
||||
}
|
||||
};
|
||||
_updateFunc(false, string.Format(ResUI.MsgStartUpdating, "Core"));
|
||||
CheckUpdateAsync(type, preRelease);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action<bool, string> update)
|
||||
@@ -179,7 +143,7 @@ namespace v2rayN.Handler
|
||||
string url = item.url.TrimEx();
|
||||
string userAgent = item.userAgent.TrimEx();
|
||||
string hashCode = $"{item.remarks}->";
|
||||
if (Utile.IsNullOrEmpty(id) || Utile.IsNullOrEmpty(url) || (!Utile.IsNullOrEmpty(subId) && item.id != subId))
|
||||
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (!Utils.IsNullOrEmpty(subId) && item.id != subId))
|
||||
{
|
||||
//_updateFunc(false, $"{hashCode}{ResUI.MsgNoValidSubscription}");
|
||||
continue;
|
||||
@@ -194,7 +158,7 @@ namespace v2rayN.Handler
|
||||
continue;
|
||||
}
|
||||
|
||||
var downloadHandle = new DownloadHandle();
|
||||
var downloadHandle = new DownloadHandler();
|
||||
downloadHandle.Error += (sender2, args) =>
|
||||
{
|
||||
_updateFunc(false, $"{hashCode}{args.GetException().Message}");
|
||||
@@ -203,12 +167,12 @@ namespace v2rayN.Handler
|
||||
_updateFunc(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}");
|
||||
|
||||
//one url
|
||||
url = Utile.GetPunycode(url);
|
||||
url = Utils.GetPunycode(url);
|
||||
//convert
|
||||
if (!Utile.IsNullOrEmpty(item.convertTarget))
|
||||
if (!Utils.IsNullOrEmpty(item.convertTarget))
|
||||
{
|
||||
var subConvertUrl = string.IsNullOrEmpty(config.constItem.subConvertUrl) ? Global.SubConvertUrls.FirstOrDefault() : config.constItem.subConvertUrl;
|
||||
url = string.Format(subConvertUrl!, Utile.UrlEncode(url));
|
||||
var subConvertUrl = Utils.IsNullOrEmpty(config.constItem.subConvertUrl) ? Global.SubConvertUrls.FirstOrDefault() : config.constItem.subConvertUrl;
|
||||
url = string.Format(subConvertUrl!, Utils.UrlEncode(url));
|
||||
if (!url.Contains("target="))
|
||||
{
|
||||
url += string.Format("&target={0}", item.convertTarget);
|
||||
@@ -219,41 +183,38 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
var result = await downloadHandle.TryDownloadString(url, blProxy, userAgent);
|
||||
if (blProxy && Utile.IsNullOrEmpty(result))
|
||||
if (blProxy && Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
result = await downloadHandle.TryDownloadString(url, false, userAgent);
|
||||
}
|
||||
|
||||
//more url
|
||||
if (Utile.IsNullOrEmpty(item.convertTarget) && !Utile.IsNullOrEmpty(item.moreUrl.TrimEx()))
|
||||
if (Utils.IsNullOrEmpty(item.convertTarget) && !Utils.IsNullOrEmpty(item.moreUrl.TrimEx()))
|
||||
{
|
||||
if (!Utile.IsNullOrEmpty(result) && Utile.IsBase64String(result!))
|
||||
if (!Utils.IsNullOrEmpty(result) && Utils.IsBase64String(result!))
|
||||
{
|
||||
result = Utile.Base64Decode(result);
|
||||
result = Utils.Base64Decode(result);
|
||||
}
|
||||
|
||||
var lstUrl = new List<string>
|
||||
{
|
||||
item.moreUrl.TrimEx().Split(",")
|
||||
};
|
||||
var lstUrl = item.moreUrl.TrimEx().Split(",") ?? [];
|
||||
foreach (var it in lstUrl)
|
||||
{
|
||||
var url2 = Utile.GetPunycode(it);
|
||||
if (Utile.IsNullOrEmpty(url2))
|
||||
var url2 = Utils.GetPunycode(it);
|
||||
if (Utils.IsNullOrEmpty(url2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var result2 = await downloadHandle.TryDownloadString(url2, blProxy, userAgent);
|
||||
if (blProxy && Utile.IsNullOrEmpty(result2))
|
||||
if (blProxy && Utils.IsNullOrEmpty(result2))
|
||||
{
|
||||
result2 = await downloadHandle.TryDownloadString(url2, false, userAgent);
|
||||
}
|
||||
if (!Utile.IsNullOrEmpty(result2))
|
||||
if (!Utils.IsNullOrEmpty(result2))
|
||||
{
|
||||
if (Utile.IsBase64String(result2!))
|
||||
if (Utils.IsBase64String(result2!))
|
||||
{
|
||||
result += Utile.Base64Decode(result2);
|
||||
result += Utils.Base64Decode(result2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -263,14 +224,14 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
if (Utile.IsNullOrEmpty(result))
|
||||
if (Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
_updateFunc(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_updateFunc(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}");
|
||||
if (result!.Length < 99)
|
||||
if (result?.Length < 99)
|
||||
{
|
||||
_updateFunc(false, $"{hashCode}{result}");
|
||||
}
|
||||
@@ -293,23 +254,18 @@ namespace v2rayN.Handler
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateGeoFileAll(Config config, Action<bool, string> update)
|
||||
public async Task UpdateGeoFileAll(Config config, Action<bool, string> update)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await UpdateGeoFile("geosite", _config, update);
|
||||
await UpdateGeoFile("geoip", _config, update);
|
||||
|
||||
//await UpdateGeoFile4Singbox("geosite", _config, false, update);
|
||||
//await UpdateGeoFile4Singbox("geoip", _config, true, update);
|
||||
});
|
||||
await UpdateGeoFile("geosite", _config, update);
|
||||
await UpdateGeoFile("geoip", _config, update);
|
||||
_updateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
|
||||
}
|
||||
|
||||
public void RunAvailabilityCheck(Action<bool, string> update)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var time = await (new DownloadHandle()).RunAvailabilityCheck(null);
|
||||
var time = await (new DownloadHandler()).RunAvailabilityCheck(null);
|
||||
|
||||
update(false, string.Format(ResUI.TestMeOutput, time));
|
||||
});
|
||||
@@ -317,44 +273,44 @@ namespace v2rayN.Handler
|
||||
|
||||
#region private
|
||||
|
||||
private async void CheckUpdateAsync(ECoreType type, bool preRelease)
|
||||
private async Task<ResultEventArgs> CheckUpdateAsync(DownloadHandler downloadHandle, ECoreType type, bool preRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo(type);
|
||||
string url = coreInfo.coreReleaseApiUrl;
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
|
||||
var url = coreInfo?.coreReleaseApiUrl;
|
||||
|
||||
var result = await (new DownloadHandle()).DownloadStringAsync(url, true, "");
|
||||
if (!Utile.IsNullOrEmpty(result))
|
||||
var result = await downloadHandle.DownloadStringAsync(url, true, Global.AppName);
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
responseHandler(type, result, preRelease);
|
||||
return await ParseDownloadUrl(type, result, preRelease);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.SaveLog("StatusCode error: " + url);
|
||||
return;
|
||||
return new ResultEventArgs(false, "");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
_updateFunc(false, ex.Message);
|
||||
return new ResultEventArgs(false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取V2RayCore版本
|
||||
/// 获取Core版本
|
||||
/// </summary>
|
||||
private SemanticVersion getCoreVersion(ECoreType type)
|
||||
private SemanticVersion GetCoreVersion(ECoreType type)
|
||||
{
|
||||
try
|
||||
{
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo(type);
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
|
||||
string filePath = string.Empty;
|
||||
foreach (string name in coreInfo.coreExes)
|
||||
{
|
||||
string vName = $"{name}.exe";
|
||||
vName = Utile.GetBinPath(vName, coreInfo.coreType.ToString());
|
||||
string vName = Utils.GetExeName(name);
|
||||
vName = Utils.GetBinPath(vName, coreInfo.coreType.ToString());
|
||||
if (File.Exists(vName))
|
||||
{
|
||||
filePath = vName;
|
||||
@@ -372,7 +328,7 @@ namespace v2rayN.Handler
|
||||
using Process p = new();
|
||||
p.StartInfo.FileName = filePath.AppendQuotes();
|
||||
p.StartInfo.Arguments = coreInfo.versionArg;
|
||||
p.StartInfo.WorkingDirectory = Utile.StartupPath();
|
||||
p.StartInfo.WorkingDirectory = Utils.StartupPath();
|
||||
p.StartInfo.UseShellExecute = false;
|
||||
p.StartInfo.RedirectStandardOutput = true;
|
||||
p.StartInfo.CreateNoWindow = true;
|
||||
@@ -410,20 +366,19 @@ namespace v2rayN.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private void responseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease)
|
||||
private async Task<ResultEventArgs> ParseDownloadUrl(ECoreType type, string gitHubReleaseApi, bool preRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
var gitHubReleases = JsonUtile.Deserialize<List<GitHubRelease>>(gitHubReleaseApi);
|
||||
var gitHubRelease = preRelease ? gitHubReleases!.First() : gitHubReleases!.First(r => r.Prerelease == false);
|
||||
var version = new SemanticVersion(gitHubRelease!.TagName);
|
||||
var body = gitHubRelease!.Body;
|
||||
|
||||
var coreInfo = LazyConfig.Instance.GetCoreInfo(type);
|
||||
var gitHubReleases = JsonUtils.Deserialize<List<GitHubRelease>>(gitHubReleaseApi);
|
||||
var gitHubRelease = preRelease ? gitHubReleases?.First() : gitHubReleases?.First(r => r.Prerelease == false);
|
||||
var version = new SemanticVersion(gitHubRelease?.TagName!);
|
||||
var body = gitHubRelease?.Body;
|
||||
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
|
||||
SemanticVersion curVersion;
|
||||
string message;
|
||||
string url;
|
||||
string? url;
|
||||
switch (type)
|
||||
{
|
||||
case ECoreType.v2fly:
|
||||
@@ -431,89 +386,32 @@ namespace v2rayN.Handler
|
||||
case ECoreType.Xray:
|
||||
case ECoreType.v2fly_v5:
|
||||
{
|
||||
curVersion = getCoreVersion(type);
|
||||
curVersion = GetCoreVersion(type);
|
||||
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
|
||||
string osBit = "64";
|
||||
switch (RuntimeInformation.ProcessArchitecture)
|
||||
{
|
||||
case Architecture.Arm64:
|
||||
osBit = "arm64-v8a";
|
||||
break;
|
||||
|
||||
case Architecture.X86:
|
||||
osBit = "32";
|
||||
break;
|
||||
|
||||
default:
|
||||
osBit = "64";
|
||||
break;
|
||||
}
|
||||
|
||||
url = string.Format(coreInfo.coreDownloadUrl64, version.ToVersionString("v"), osBit);
|
||||
url = string.Format(GetUrlFromCore(coreInfo), version.ToVersionString("v"));
|
||||
break;
|
||||
}
|
||||
case ECoreType.clash:
|
||||
case ECoreType.clash_meta:
|
||||
case ECoreType.mihomo:
|
||||
{
|
||||
curVersion = getCoreVersion(type);
|
||||
curVersion = GetCoreVersion(type);
|
||||
message = string.Format(ResUI.IsLatestCore, type, curVersion);
|
||||
switch (RuntimeInformation.ProcessArchitecture)
|
||||
{
|
||||
case Architecture.Arm64:
|
||||
url = coreInfo.coreDownloadUrlArm64;
|
||||
break;
|
||||
|
||||
case Architecture.X86:
|
||||
url = coreInfo.coreDownloadUrl32;
|
||||
break;
|
||||
|
||||
default:
|
||||
url = coreInfo.coreDownloadUrl64;
|
||||
break;
|
||||
}
|
||||
url = string.Format(url, version.ToVersionString("v"));
|
||||
url = string.Format(GetUrlFromCore(coreInfo), version.ToVersionString("v"));
|
||||
break;
|
||||
}
|
||||
case ECoreType.sing_box:
|
||||
{
|
||||
curVersion = getCoreVersion(type);
|
||||
curVersion = GetCoreVersion(type);
|
||||
message = string.Format(ResUI.IsLatestCore, type, curVersion.ToVersionString("v"));
|
||||
switch (RuntimeInformation.ProcessArchitecture)
|
||||
{
|
||||
case Architecture.Arm64:
|
||||
url = coreInfo.coreDownloadUrlArm64;
|
||||
break;
|
||||
|
||||
case Architecture.X86:
|
||||
url = coreInfo.coreDownloadUrl32;
|
||||
break;
|
||||
|
||||
default:
|
||||
url = coreInfo.coreDownloadUrl64;
|
||||
break;
|
||||
}
|
||||
url = string.Format(url, version.ToVersionString("v"), version);
|
||||
url = string.Format(GetUrlFromCore(coreInfo), version.ToVersionString("v"), version);
|
||||
break;
|
||||
}
|
||||
case ECoreType.v2rayN:
|
||||
{
|
||||
curVersion = new SemanticVersion(FileVersionInfo.GetVersionInfo(Utile.GetExePath()).FileVersion.ToString());
|
||||
curVersion = new SemanticVersion(Utils.GetVersionInfo());
|
||||
message = string.Format(ResUI.IsLatestN, type, curVersion);
|
||||
switch (RuntimeInformation.ProcessArchitecture)
|
||||
{
|
||||
case Architecture.Arm64:
|
||||
url = string.Format(coreInfo.coreDownloadUrlArm64, version);
|
||||
break;
|
||||
|
||||
case Architecture.X86:
|
||||
url = string.Format(coreInfo.coreDownloadUrl32, version);
|
||||
break;
|
||||
|
||||
default:
|
||||
url = string.Format(coreInfo.coreDownloadUrl64, version);
|
||||
break;
|
||||
}
|
||||
url = string.Format(GetUrlFromCore(coreInfo), version);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -522,37 +420,51 @@ namespace v2rayN.Handler
|
||||
|
||||
if (curVersion >= version && version != new SemanticVersion(0, 0, 0))
|
||||
{
|
||||
AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message));
|
||||
return;
|
||||
return new ResultEventArgs(false, message);
|
||||
}
|
||||
|
||||
AbsoluteCompleted?.Invoke(this, new ResultEventArgs(true, body, url));
|
||||
return new ResultEventArgs(true, body, url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
_updateFunc(false, ex.Message);
|
||||
return new ResultEventArgs(false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AskToDownload(DownloadHandle downloadHandle, string url, bool blAsk)
|
||||
private string? GetUrlFromCore(CoreInfo? coreInfo)
|
||||
{
|
||||
bool blDownload = false;
|
||||
if (blAsk)
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
if (UI.ShowYesNo(string.Format(ResUI.DownloadYesNo, url)) == MessageBoxResult.Yes)
|
||||
//Check for standalone windows .Net version
|
||||
if (coreInfo?.coreType == ECoreType.v2rayN
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
)
|
||||
{
|
||||
blDownload = true;
|
||||
return coreInfo?.coreDownloadUrl64.Replace("v2rayN.zip", "zz_v2rayN-SelfContained.zip");
|
||||
}
|
||||
|
||||
return RuntimeInformation.ProcessArchitecture switch
|
||||
{
|
||||
Architecture.Arm64 => coreInfo?.coreDownloadUrlArm64,
|
||||
Architecture.X86 => coreInfo?.coreDownloadUrl32,
|
||||
Architecture.X64 => coreInfo?.coreDownloadUrl64,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
else
|
||||
else if (Utils.IsLinux())
|
||||
{
|
||||
blDownload = true;
|
||||
}
|
||||
if (blDownload)
|
||||
{
|
||||
await downloadHandle.DownloadFileAsync(url, true, 600);
|
||||
return RuntimeInformation.ProcessArchitecture switch
|
||||
{
|
||||
Architecture.Arm64 => coreInfo?.coreDownloadUrlLinuxArm64,
|
||||
Architecture.X86 => coreInfo?.coreDownloadUrlLinux32,
|
||||
Architecture.X64 => coreInfo?.coreDownloadUrlLinux64,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task UpdateGeoFile(string geoName, Config config, Action<bool, string> update)
|
||||
@@ -560,8 +472,9 @@ namespace v2rayN.Handler
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
var url = string.Format(Global.GeoUrl, geoName);
|
||||
var fileName = Utils.GetTempPath(Utils.GetGUID());
|
||||
|
||||
DownloadHandle downloadHandle = new();
|
||||
DownloadHandler downloadHandle = new();
|
||||
downloadHandle.UpdateCompleted += (sender2, args) =>
|
||||
{
|
||||
if (args.Success)
|
||||
@@ -570,15 +483,9 @@ namespace v2rayN.Handler
|
||||
|
||||
try
|
||||
{
|
||||
string fileName = Utile.GetTempPath(Utile.GetDownloadFileName(url));
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
//Global.coreTypes.ForEach(it =>
|
||||
//{
|
||||
// string targetPath = Utile.GetBinPath($"{geoName}.dat", (ECoreType)Enum.Parse(typeof(ECoreType), it));
|
||||
// File.Copy(fileName, targetPath, true);
|
||||
//});
|
||||
string targetPath = Utile.GetBinPath($"{geoName}.dat");
|
||||
string targetPath = Utils.GetBinPath($"{geoName}.dat");
|
||||
File.Copy(fileName, targetPath, true);
|
||||
|
||||
File.Delete(fileName);
|
||||
@@ -599,7 +506,8 @@ namespace v2rayN.Handler
|
||||
{
|
||||
_updateFunc(false, args.GetException().Message);
|
||||
};
|
||||
await AskToDownload(downloadHandle, url, false);
|
||||
|
||||
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
||||
}
|
||||
|
||||
#endregion private
|
||||
171
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
171
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
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 string _webFileName = "backup.zip";
|
||||
private string _logTitle = "WebDav--";
|
||||
|
||||
public WebDavHandler()
|
||||
{
|
||||
_config = LazyConfig.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();
|
||||
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> PutFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
using var outputFileStream = new FileStream(fileName, FileMode.Create);
|
||||
response.Stream.CopyTo(outputFileStream);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetLastError() => _lastDescription ?? string.Empty;
|
||||
}
|
||||
}
|
||||
11
v2rayN/ServiceLib/Models/CheckUpdateItem.cs
Normal file
11
v2rayN/ServiceLib/Models/CheckUpdateItem.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class CheckUpdateItem
|
||||
{
|
||||
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; } = string.Empty;
|
||||
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 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 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; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class ComboItem
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 本软件配置文件实体类
|
||||
@@ -10,10 +10,24 @@
|
||||
|
||||
public string indexId { get; set; }
|
||||
public string subIndexId { get; set; }
|
||||
public ESysProxyType sysProxyType { get; set; }
|
||||
public string systemProxyExceptions { get; set; }
|
||||
public string systemProxyAdvancedProtocol { get; set; }
|
||||
|
||||
public ECoreType runningCoreType { get; set; }
|
||||
|
||||
public bool IsRunningCore(ECoreType type)
|
||||
{
|
||||
if (type == ECoreType.Xray && runningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5 or ECoreType.SagerNet)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (type == ECoreType.clash && runningCoreType is ECoreType.sing_box or ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion property
|
||||
|
||||
#region other entities
|
||||
@@ -24,11 +38,16 @@
|
||||
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 List<InItem> inbound { get; set; }
|
||||
public List<KeyEventItem> globalHotkeys { get; set; }
|
||||
public List<CoreTypeItem> coreTypeItem { get; set; }
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class CoreBasicItem
|
||||
@@ -31,6 +29,10 @@ namespace v2rayN.Model
|
||||
/// 默认用户代理
|
||||
/// </summary>
|
||||
public string defUserAgent { get; set; }
|
||||
|
||||
public bool enableFragment { get; set; }
|
||||
|
||||
public bool enableCacheFile4Sbox { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -43,8 +45,8 @@ namespace v2rayN.Model
|
||||
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; }
|
||||
@@ -105,6 +107,13 @@ namespace v2rayN.Model
|
||||
public bool enableLog { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MsgUIItem
|
||||
{
|
||||
public string? mainMsgFilter { get; set; }
|
||||
public bool? autoRefresh { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UIItem
|
||||
{
|
||||
@@ -114,6 +123,7 @@ namespace v2rayN.Model
|
||||
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; }
|
||||
@@ -122,9 +132,9 @@ namespace v2rayN.Model
|
||||
public int currentFontSize { get; set; }
|
||||
public bool enableDragDropSort { get; set; }
|
||||
public bool doubleClick2Activate { get; set; }
|
||||
public bool autoHideStartup { get; set; } = true;
|
||||
public string mainMsgFilter { get; set; }
|
||||
public bool autoHideStartup { get; set; }
|
||||
public List<ColumnItem> mainColumnItem { get; set; }
|
||||
public bool showInTaskbar { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -145,7 +155,7 @@ namespace v2rayN.Model
|
||||
|
||||
public bool Shift { get; set; }
|
||||
|
||||
public Key? KeyCode { get; set; }
|
||||
public int? KeyCode { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -164,7 +174,7 @@ namespace v2rayN.Model
|
||||
public string stack { get; set; }
|
||||
public int mtu { get; set; }
|
||||
public bool enableExInbound { get; set; }
|
||||
public bool enableIPv6Address { get; set; } = true;
|
||||
public bool enableIPv6Address { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -178,13 +188,8 @@ namespace v2rayN.Model
|
||||
[Serializable]
|
||||
public class RoutingBasicItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 域名解析策略
|
||||
/// </summary>
|
||||
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; }
|
||||
@@ -198,11 +203,20 @@ namespace v2rayN.Model
|
||||
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 max_connections { get; set; }
|
||||
public bool? padding { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -211,4 +225,36 @@ namespace v2rayN.Model
|
||||
public int up_mbps { get; set; }
|
||||
public int down_mbps { 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; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class CoreInfo
|
||||
@@ -18,6 +18,9 @@
|
||||
public string coreDownloadUrl64 { get; set; }
|
||||
|
||||
public string coreDownloadUrlArm64 { get; set; }
|
||||
public string? coreDownloadUrlLinux32 { get; set; }
|
||||
public string? coreDownloadUrlLinux64 { get; set; }
|
||||
public string? coreDownloadUrlLinuxArm64 { get; set; }
|
||||
|
||||
public string match { get; set; }
|
||||
public string versionArg { get; set; }
|
||||
@@ -1,6 +1,6 @@
|
||||
using SQLite;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class DNSItem
|
||||
@@ -15,5 +15,6 @@ namespace v2rayN.Model
|
||||
public string? normalDNS { get; set; }
|
||||
public string? tunDNS { get; set; }
|
||||
public string? domainStrategy4Freedom { get; set; }
|
||||
public string? domainDNSAddress { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class GitHubReleaseAsset
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using SQLite;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ProfileExItem
|
||||
@@ -1,6 +1,6 @@
|
||||
using SQLite;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ProfileItem
|
||||
@@ -48,7 +48,7 @@ namespace v2rayN.Model
|
||||
switch (configType)
|
||||
{
|
||||
case EConfigType.Custom:
|
||||
summary += string.Format("{0}", remarks);
|
||||
summary += string.Format("[{1}]{0}", remarks, coreType.ToString());
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -60,19 +60,19 @@ namespace v2rayN.Model
|
||||
|
||||
public List<string> GetAlpn()
|
||||
{
|
||||
if (Utile.IsNullOrEmpty(alpn))
|
||||
if (Utils.IsNullOrEmpty(alpn))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Utile.String2List(alpn);
|
||||
return Utils.String2List(alpn);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetNetwork()
|
||||
{
|
||||
if (Utile.IsNullOrEmpty(network) || !Global.Networks.Contains(network))
|
||||
if (Utils.IsNullOrEmpty(network) || !Global.Networks.Contains(network))
|
||||
{
|
||||
return Global.DefaultNetwork;
|
||||
}
|
||||
@@ -178,7 +178,7 @@ namespace v2rayN.Model
|
||||
|
||||
public ECoreType? coreType { get; set; }
|
||||
|
||||
public int preSocksPort { get; set; }
|
||||
public int? preSocksPort { get; set; }
|
||||
|
||||
public string fingerprint { get; set; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ProfileItemModel : ProfileItem
|
||||
@@ -1,6 +1,6 @@
|
||||
using SQLite;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class RoutingItem
|
||||
@@ -15,6 +15,7 @@ namespace v2rayN.Model
|
||||
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,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class RoutingItemModel : RoutingItem
|
||||
26
v2rayN/ServiceLib/Models/RulesItem.cs
Normal file
26
v2rayN/ServiceLib/Models/RulesItem.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class RulesItemModel : RulesItem
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
internal class ServerSpeedItem : ServerStatItem
|
||||
public class ServerSpeedItem : ServerStatItem
|
||||
{
|
||||
public long proxyUp
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using SQLite;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ServerStatItem
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
internal class ServerTestItem
|
||||
public class ServerTestItem
|
||||
{
|
||||
public string indexId { get; set; }
|
||||
public string address { get; set; }
|
||||
@@ -1,13 +1,13 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class SingboxConfig
|
||||
{
|
||||
public Log4Sbox log { get; set; }
|
||||
public object dns { get; set; }
|
||||
public Dns4Sbox? dns { get; set; }
|
||||
public List<Inbound4Sbox> inbounds { get; set; }
|
||||
public List<Outbound4Sbox> outbounds { get; set; }
|
||||
public Route4Sbox route { get; set; }
|
||||
public Experimental4Sbox experimental { get; set; }
|
||||
public Experimental4Sbox? experimental { get; set; }
|
||||
}
|
||||
|
||||
public class Log4Sbox
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -35,19 +36,24 @@
|
||||
{
|
||||
public bool? auto_detect_interface { get; set; }
|
||||
public List<Rule4Sbox> rules { get; set; }
|
||||
public List<Ruleset4Sbox>? rule_set { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Rule4Sbox
|
||||
{
|
||||
public string outbound { get; set; }
|
||||
public string server { get; set; }
|
||||
public string? outbound { get; set; }
|
||||
public string? server { get; set; }
|
||||
public bool? disable_cache { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? mode { 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 string type { get; set; }
|
||||
public string mode { get; set; }
|
||||
public string network { 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; }
|
||||
@@ -58,8 +64,9 @@
|
||||
public List<string>? geoip { get; set; }
|
||||
public List<string>? ip_cidr { get; set; }
|
||||
public List<string>? source_ip_cidr { get; set; }
|
||||
|
||||
public List<string>? process_name { get; set; }
|
||||
public List<string>? rule_set { get; set; }
|
||||
public List<Rule4Sbox>? rules { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -93,7 +100,7 @@
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string tag { get; set; }
|
||||
public string server { get; set; }
|
||||
public string? server { get; set; }
|
||||
public int? server_port { get; set; }
|
||||
public string uuid { get; set; }
|
||||
public string security { get; set; }
|
||||
@@ -124,6 +131,8 @@
|
||||
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
|
||||
@@ -141,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
|
||||
@@ -182,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 detour { 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
|
||||
@@ -230,4 +242,15 @@
|
||||
public string? cache_id { get; set; }
|
||||
public bool? store_fakeip { get; set; }
|
||||
}
|
||||
|
||||
public class Ruleset4Sbox
|
||||
{
|
||||
public string? tag { get; set; }
|
||||
public string? type { get; set; }
|
||||
public string? format { get; set; }
|
||||
public string? path { get; set; }
|
||||
public string? url { get; set; }
|
||||
public string? download_detour { 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; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class SsSIP008
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using SQLite;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class SubItem
|
||||
@@ -31,5 +31,7 @@ namespace v2rayN.Model
|
||||
public string? prevProfile { get; set; }
|
||||
|
||||
public string? nextProfile { get; set; }
|
||||
|
||||
public int? preSocksPort { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
internal class SysProxyConfig
|
||||
public class SysProxyConfig
|
||||
{
|
||||
public bool UserSettingsRecorded;
|
||||
public string Flags;
|
||||
@@ -1,12 +1,17 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// v2ray配置文件实体类 例子SampleConfig.txt
|
||||
/// </summary>
|
||||
public class V2rayConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Properties that do not belong to Ray
|
||||
/// </summary>
|
||||
public string? remarks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 日志配置
|
||||
/// </summary>
|
||||
@@ -190,7 +195,7 @@ namespace v2rayN.Model
|
||||
public class Sniffing4Ray
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
public List<string> destOverride { get; set; }
|
||||
public List<string>? destOverride { get; set; }
|
||||
public bool routeOnly { get; set; }
|
||||
}
|
||||
|
||||
@@ -227,7 +232,7 @@ namespace v2rayN.Model
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<VnextItem4Ray> vnext { get; set; }
|
||||
public List<VnextItem4Ray>? vnext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -248,6 +253,8 @@ namespace v2rayN.Model
|
||||
///
|
||||
/// </summary>
|
||||
public int? userLevel { get; set; }
|
||||
|
||||
public FragmentItem4Ray? fragment { get; set; }
|
||||
}
|
||||
|
||||
public class VnextItem4Ray
|
||||
@@ -283,17 +290,17 @@ namespace v2rayN.Model
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string method { get; set; }
|
||||
public string? method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool ota { get; set; }
|
||||
public bool? ota { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string password { get; set; }
|
||||
public string? password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -303,7 +310,7 @@ namespace v2rayN.Model
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int level { get; set; }
|
||||
public int? level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// trojan
|
||||
@@ -331,20 +338,15 @@ namespace v2rayN.Model
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int level { get; set; }
|
||||
public int? level { get; set; }
|
||||
}
|
||||
|
||||
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
|
||||
@@ -363,6 +365,12 @@ namespace v2rayN.Model
|
||||
public List<string> servers { get; set; }
|
||||
}
|
||||
|
||||
public class DnsServer4Ray
|
||||
{
|
||||
public string? address { get; set; }
|
||||
public List<string>? domains { get; set; }
|
||||
}
|
||||
|
||||
public class Routing4Ray
|
||||
{
|
||||
/// <summary>
|
||||
@@ -373,30 +381,47 @@ namespace v2rayN.Model
|
||||
/// <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]
|
||||
public class RulesItem4Ray
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string? type { get; set; }
|
||||
|
||||
public string port { get; set; }
|
||||
public string? port { get; set; }
|
||||
public string? network { get; set; }
|
||||
|
||||
public List<string> inboundTag { get; set; }
|
||||
public List<string>? inboundTag { get; set; }
|
||||
|
||||
public string outboundTag { get; set; }
|
||||
public string? outboundTag { get; set; }
|
||||
|
||||
public List<string> ip { get; set; }
|
||||
public string? balancerTag { get; set; }
|
||||
|
||||
public List<string> domain { get; set; }
|
||||
public List<string>? ip { get; set; }
|
||||
|
||||
public List<string> protocol { get; set; }
|
||||
public List<string>? domain { get; set; }
|
||||
|
||||
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
|
||||
@@ -414,47 +439,52 @@ namespace v2rayN.Model
|
||||
/// <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
|
||||
@@ -481,7 +511,7 @@ namespace v2rayN.Model
|
||||
|
||||
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; }
|
||||
@@ -601,6 +631,17 @@ namespace v2rayN.Model
|
||||
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>
|
||||
@@ -634,7 +675,8 @@ namespace v2rayN.Model
|
||||
|
||||
public class GrpcSettings4Ray
|
||||
{
|
||||
public string serviceName { get; set; }
|
||||
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; }
|
||||
@@ -659,4 +701,11 @@ namespace v2rayN.Model
|
||||
{
|
||||
public string? dialerProxy { get; set; }
|
||||
}
|
||||
|
||||
public class FragmentItem4Ray
|
||||
{
|
||||
public string? packets { get; set; }
|
||||
public string? length { get; set; }
|
||||
public string? interval { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Tcp伪装http的Request,只要Host
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace v2rayN.Model
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// https://github.com/2dust/v2rayN/wiki/
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class VmessQRCode
|
||||
public class VmessQRCode
|
||||
{
|
||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public int v { get; set; } = 2;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -117,9 +117,6 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="BatchExportSubscriptionSuccessfully" xml:space="preserve">
|
||||
<value>صادرات دسته ای محتوای اشتراک به کلیپ بورد با موفقیت انجام شد</value>
|
||||
</data>
|
||||
<data name="BatchExportURLSuccessfully" xml:space="preserve">
|
||||
<value>Batch export share URL to clipboard successfully</value>
|
||||
</data>
|
||||
@@ -159,9 +156,6 @@
|
||||
<data name="FillCorrectServerPort" xml:space="preserve">
|
||||
<value>لطفا فرمت صحیح پورت سرور را پر کنید</value>
|
||||
</data>
|
||||
<data name="FillKcpParameters" xml:space="preserve">
|
||||
<value>لطفاً پارامترهای KCP را به درستی پر کنید</value>
|
||||
</data>
|
||||
<data name="FillLocalListeningPort" xml:space="preserve">
|
||||
<value>لطفاً پورت گوش دادن محلی را پر کنید</value>
|
||||
</data>
|
||||
@@ -174,15 +168,9 @@
|
||||
<data name="FillUUID" xml:space="preserve">
|
||||
<value>لطفا شناسه کاربری را وارد کنید</value>
|
||||
</data>
|
||||
<data name="IncorrectClientConfiguration" xml:space="preserve">
|
||||
<value>فایل پیکربندی مشتری صحیح نیست، لطفا بررسی کنید</value>
|
||||
</data>
|
||||
<data name="Incorrectconfiguration" xml:space="preserve">
|
||||
<value>پیکربندی درستی نیست، لطفا بررسی کنید</value>
|
||||
</data>
|
||||
<data name="IncorrectServerConfiguration" xml:space="preserve">
|
||||
<value>فایل پیکربندی سرور صحیح نیست، لطفا بررسی کنید</value>
|
||||
</data>
|
||||
<data name="InitialConfiguration" xml:space="preserve">
|
||||
<value>پیکربندی اولیه</value>
|
||||
</data>
|
||||
@@ -267,9 +255,6 @@
|
||||
<data name="NonvmessOrssProtocol" xml:space="preserve">
|
||||
<value>Non-VMess or ss protocol</value>
|
||||
</data>
|
||||
<data name="NonVmessService" xml:space="preserve">
|
||||
<value> non-standard service, this feature is invalid</value>
|
||||
</data>
|
||||
<data name="NotFoundCore" xml:space="preserve">
|
||||
<value>The Core file (file name: {1}) was not found under the folder ({0}), please download and put it in the folder, download address: {2}</value>
|
||||
</data>
|
||||
@@ -415,15 +400,9 @@
|
||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||
<value>Please browse to import server configuration</value>
|
||||
</data>
|
||||
<data name="SystemProxy" xml:space="preserve">
|
||||
<value>پروکسی سیستم</value>
|
||||
</data>
|
||||
<data name="Speedtesting" xml:space="preserve">
|
||||
<value>درحال تست کردن...</value>
|
||||
</data>
|
||||
<data name="TooManyServersTip" xml:space="preserve">
|
||||
<value>تعداد سرورها خیلی زیاد است، لطفا رابط اصلی را باز کنید</value>
|
||||
</data>
|
||||
<data name="LabLAN" xml:space="preserve">
|
||||
<value>LAN</value>
|
||||
</data>
|
||||
@@ -541,18 +520,12 @@
|
||||
<data name="menuTcpingServer" xml:space="preserve">
|
||||
<value>تست سرورها با tcping (Ctrl+O)</value>
|
||||
</data>
|
||||
<data name="menuTestMe" xml:space="preserve">
|
||||
<value>وضعیت سرویس فعلی را تست کنید</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfig" xml:space="preserve">
|
||||
<value>سرور انتخابی را برای پیکربندی کلاینت صادر کنید</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||
<value>URL های اشتراک گذاری را به کلیپ بورد صادر کنید (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuExport2SubContent" xml:space="preserve">
|
||||
<value>اشتراک (base64) را به کلیپ بورد صادر کنید</value>
|
||||
</data>
|
||||
<data name="menuAddCustomServer" xml:space="preserve">
|
||||
<value>یک سرور پیکربندی سفارشی اضافه شود</value>
|
||||
</data>
|
||||
@@ -583,9 +556,6 @@
|
||||
<data name="menuMsgViewCopyAll" xml:space="preserve">
|
||||
<value>کپی همه</value>
|
||||
</data>
|
||||
<data name="menuMsgViewFilter" xml:space="preserve">
|
||||
<value>فیلترهای پیام را تنظیم کنید</value>
|
||||
</data>
|
||||
<data name="menuMsgViewSelectAll" xml:space="preserve">
|
||||
<value>انتخاب همه (Ctrl+A)</value>
|
||||
</data>
|
||||
@@ -698,7 +668,7 @@
|
||||
<value>txtPreSocksPort</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* After setting this value, an socks service will be started using sing-box to provide functions such as speed display</value>
|
||||
<value>* After setting this value, an socks service will be started using Xray/sing-box(Tun) to provide functions such as speed display</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
@@ -955,6 +925,9 @@
|
||||
<data name="TbSettingsRouteOnly" xml:space="preserve">
|
||||
<value>فقط مسیر</value>
|
||||
</data>
|
||||
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
|
||||
<value>يەرلىك (Intranet) ئادرېسلارغا ۋاكالەتچى مۇلازىمېتىر ئىشلەتمەڭ</value>
|
||||
</data>
|
||||
<data name="menuMixedTestServer" xml:space="preserve">
|
||||
<value>One-click test Latency and speed (Ctrl+E)</value>
|
||||
</data>
|
||||
@@ -985,15 +958,6 @@
|
||||
<data name="TbSettingsTunMode" xml:space="preserve">
|
||||
<value>تنظیمات TunMode</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectIP" xml:space="preserve">
|
||||
<value>Direct IP CIDR, separated by commas (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectProcess" xml:space="preserve">
|
||||
<value>Direct Process name, separated by commas (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeShowWindow" xml:space="preserve">
|
||||
<value>نمایش کنسول</value>
|
||||
</data>
|
||||
<data name="TbSettingsDefUserAgent" xml:space="preserve">
|
||||
<value>User-Agent</value>
|
||||
</data>
|
||||
@@ -1003,4 +967,70 @@
|
||||
<data name="TbSettingsEnableHWA" xml:space="preserve">
|
||||
<value>فعالسازی شتابدهنده سختافزاری (نیاز به راهاندازی مجدد)</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>فعال کردن کش فایل مجموعه قوانین برای sing-box</value>
|
||||
</data>
|
||||
<data name="TbSorting" xml:space="preserve">
|
||||
<value>مرتب سازی</value>
|
||||
</data>
|
||||
<data name="TbSortingDefault" xml:space="preserve">
|
||||
<value>Default</value>
|
||||
</data>
|
||||
<data name="TbSortingDelay" xml:space="preserve">
|
||||
<value>تاخیر</value>
|
||||
</data>
|
||||
<data name="TbSortingDownSpeed" xml:space="preserve">
|
||||
<value>سرعت دانلود</value>
|
||||
</data>
|
||||
<data name="TbSortingDownTraffic" xml:space="preserve">
|
||||
<value>ترافیک دانلود</value>
|
||||
</data>
|
||||
<data name="TbSortingName" xml:space="preserve">
|
||||
<value>نام</value>
|
||||
</data>
|
||||
<data name="TbSortingTime" xml:space="preserve">
|
||||
<value>زمان</value>
|
||||
</data>
|
||||
<data name="TbSortingUpSpeed" xml:space="preserve">
|
||||
<value>سرعت اپلود</value>
|
||||
</data>
|
||||
<data name="TbSortingUpTraffic" xml:space="preserve">
|
||||
<value>ترافیک آپلود</value>
|
||||
</data>
|
||||
<data name="TbConnections" xml:space="preserve">
|
||||
<value>اتصالات</value>
|
||||
</data>
|
||||
<data name="menuConnectionClose" xml:space="preserve">
|
||||
<value>بستن اتصال</value>
|
||||
</data>
|
||||
<data name="menuConnectionCloseAll" xml:space="preserve">
|
||||
<value>تمام اتصالات را ببندید</value>
|
||||
</data>
|
||||
<data name="TbProxies" xml:space="preserve">
|
||||
<value>پروکسی</value>
|
||||
</data>
|
||||
<data name="menuRulemode" xml:space="preserve">
|
||||
<value>نوع قانون</value>
|
||||
</data>
|
||||
<data name="menuModeDirect" xml:space="preserve">
|
||||
<value>Direct</value>
|
||||
</data>
|
||||
<data name="menuModeGlobal" xml:space="preserve">
|
||||
<value>Global</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>تغییر نده</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>قانون</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytest" xml:space="preserve">
|
||||
<value>Latency Test</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||
<value>Part Node Latency Test</value>
|
||||
</data>
|
||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||
<value>Select active node (Enter)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -117,11 +117,8 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="BatchExportSubscriptionSuccessfully" xml:space="preserve">
|
||||
<value>Batch export subscription to clipboard successfully</value>
|
||||
</data>
|
||||
<data name="BatchExportURLSuccessfully" xml:space="preserve">
|
||||
<value>Batch export share URL to clipboard successfully</value>
|
||||
<value>Export Share Link to Clipboard Successfully</value>
|
||||
</data>
|
||||
<data name="CheckServerSettings" xml:space="preserve">
|
||||
<value>Please check the server settings first</value>
|
||||
@@ -159,9 +156,6 @@
|
||||
<data name="FillCorrectServerPort" xml:space="preserve">
|
||||
<value>Please fill in the correct format server port</value>
|
||||
</data>
|
||||
<data name="FillKcpParameters" xml:space="preserve">
|
||||
<value>Please fill in the KCP parameters correctly</value>
|
||||
</data>
|
||||
<data name="FillLocalListeningPort" xml:space="preserve">
|
||||
<value>Please fill in the local listening port</value>
|
||||
</data>
|
||||
@@ -174,15 +168,9 @@
|
||||
<data name="FillUUID" xml:space="preserve">
|
||||
<value>Please fill in the user ID</value>
|
||||
</data>
|
||||
<data name="IncorrectClientConfiguration" xml:space="preserve">
|
||||
<value>Is not the correct client configuration file, please check</value>
|
||||
</data>
|
||||
<data name="Incorrectconfiguration" xml:space="preserve">
|
||||
<value>Is not the correct configuration, please check</value>
|
||||
</data>
|
||||
<data name="IncorrectServerConfiguration" xml:space="preserve">
|
||||
<value>Is not the correct server configuration file, please check</value>
|
||||
</data>
|
||||
<data name="InitialConfiguration" xml:space="preserve">
|
||||
<value>Initial Configuration</value>
|
||||
</data>
|
||||
@@ -253,10 +241,10 @@
|
||||
<value>Is unpacking......</value>
|
||||
</data>
|
||||
<data name="MsgUpdateSubscriptionEnd" xml:space="preserve">
|
||||
<value>Update subscription end</value>
|
||||
<value>Update subscriptions end</value>
|
||||
</data>
|
||||
<data name="MsgUpdateSubscriptionStart" xml:space="preserve">
|
||||
<value>Update subscription starts</value>
|
||||
<value>Update subscriptions start</value>
|
||||
</data>
|
||||
<data name="MsgUpdateV2rayCoreSuccessfully" xml:space="preserve">
|
||||
<value>Update Core successfully</value>
|
||||
@@ -267,9 +255,6 @@
|
||||
<data name="NonvmessOrssProtocol" xml:space="preserve">
|
||||
<value>Non-VMess or ss protocol</value>
|
||||
</data>
|
||||
<data name="NonVmessService" xml:space="preserve">
|
||||
<value>Non-standard service, this feature is invalid</value>
|
||||
</data>
|
||||
<data name="NotFoundCore" xml:space="preserve">
|
||||
<value>The Core file (file name: {1}) was not found under the folder ({0}), please download and put it in the folder, download address: {2}</value>
|
||||
</data>
|
||||
@@ -314,7 +299,7 @@
|
||||
<value>{0} servers have been imported from clipboard</value>
|
||||
</data>
|
||||
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
||||
<value>Scan import URL successfully</value>
|
||||
<value>Scan import the shared link successfully</value>
|
||||
</data>
|
||||
<data name="TestMeOutput" xml:space="preserve">
|
||||
<value>The ping of current service: {0} ms</value>
|
||||
@@ -359,7 +344,7 @@
|
||||
<value>Please fill in the correct custom DNS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip1" xml:space="preserve">
|
||||
<value>*ws/httpupgrade path</value>
|
||||
<value>*ws/httpupgrade/splithttp path</value>
|
||||
</data>
|
||||
<data name="TransportPathTip2" xml:space="preserve">
|
||||
<value>*h2 path</value>
|
||||
@@ -374,7 +359,7 @@
|
||||
<value>*http host Separated by commas (,)</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip2" xml:space="preserve">
|
||||
<value>*ws/httpupgrade host</value>
|
||||
<value>*ws/httpupgrade/splithttp host</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip3" xml:space="preserve">
|
||||
<value>*h2 host Separated by commas (,)</value>
|
||||
@@ -415,15 +400,9 @@
|
||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||
<value>Please browse to import server configuration</value>
|
||||
</data>
|
||||
<data name="SystemProxy" xml:space="preserve">
|
||||
<value>System proxy</value>
|
||||
</data>
|
||||
<data name="Speedtesting" xml:space="preserve">
|
||||
<value>Testing...</value>
|
||||
</data>
|
||||
<data name="TooManyServersTip" xml:space="preserve">
|
||||
<value>Too many servers, please open the main interface</value>
|
||||
</data>
|
||||
<data name="LabLAN" xml:space="preserve">
|
||||
<value>LAN</value>
|
||||
</data>
|
||||
@@ -473,16 +452,16 @@
|
||||
<value>Update current subscription with proxy</value>
|
||||
</data>
|
||||
<data name="menuSubscription" xml:space="preserve">
|
||||
<value>Subscription group</value>
|
||||
<value>Subscription Group</value>
|
||||
</data>
|
||||
<data name="menuSubSetting" xml:space="preserve">
|
||||
<value>Subscription group settings</value>
|
||||
</data>
|
||||
<data name="menuSubUpdate" xml:space="preserve">
|
||||
<value>Update subscription without proxy</value>
|
||||
<value>Update subscriptions without proxy</value>
|
||||
</data>
|
||||
<data name="menuSubUpdateViaProxy" xml:space="preserve">
|
||||
<value>Update subscription with proxy</value>
|
||||
<value>Update subscriptions with proxy</value>
|
||||
</data>
|
||||
<data name="menuSystemproxy" xml:space="preserve">
|
||||
<value>System proxy</value>
|
||||
@@ -512,7 +491,7 @@
|
||||
<value>Language (Restart)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaClipboard" xml:space="preserve">
|
||||
<value>Import bulk URL from clipboard (Ctrl+V)</value>
|
||||
<value>Importing Share Links from clipboard (Ctrl+V)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaScan" xml:space="preserve">
|
||||
<value>Scan QR code on the screen (Ctrl+S)</value>
|
||||
@@ -544,17 +523,11 @@
|
||||
<data name="menuTcpingServer" xml:space="preserve">
|
||||
<value>Test servers with tcping (Ctrl+O)</value>
|
||||
</data>
|
||||
<data name="menuTestMe" xml:space="preserve">
|
||||
<value>Test current service status</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfig" xml:space="preserve">
|
||||
<value>Export selected server for client configuration</value>
|
||||
<value>Export selected server for complete configuration</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||
<value>Export share URLs to clipboard (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuExport2SubContent" xml:space="preserve">
|
||||
<value>Export subscription (base64) share to clipboard</value>
|
||||
<value>Export Share Link to Clipboard (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuAddCustomServer" xml:space="preserve">
|
||||
<value>Add a custom configuration server</value>
|
||||
@@ -586,9 +559,6 @@
|
||||
<data name="menuMsgViewCopyAll" xml:space="preserve">
|
||||
<value>Copy all</value>
|
||||
</data>
|
||||
<data name="menuMsgViewFilter" xml:space="preserve">
|
||||
<value>Set message filters</value>
|
||||
</data>
|
||||
<data name="menuMsgViewSelectAll" xml:space="preserve">
|
||||
<value>Select all (Ctrl+A)</value>
|
||||
</data>
|
||||
@@ -605,7 +575,7 @@
|
||||
<value>Share</value>
|
||||
</data>
|
||||
<data name="LvEnabled" xml:space="preserve">
|
||||
<value>Enabled Update</value>
|
||||
<value>Enable update</value>
|
||||
</data>
|
||||
<data name="LvSort" xml:space="preserve">
|
||||
<value>Sort</value>
|
||||
@@ -698,10 +668,10 @@
|
||||
<value>Encryption</value>
|
||||
</data>
|
||||
<data name="TbPreSocksPort" xml:space="preserve">
|
||||
<value>txtPreSocksPort</value>
|
||||
<value>Socks port</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* After setting this value, an socks service will be started using sing-box to provide functions such as speed display</value>
|
||||
<value>* After setting this value, an socks service will be started using Xray/sing-box(Tun) to provide functions such as speed display</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
@@ -947,7 +917,7 @@
|
||||
<value>Support DnsObject, Click to view the document</value>
|
||||
</data>
|
||||
<data name="SubUrlTips" xml:space="preserve">
|
||||
<value>Group please leave blank here</value>
|
||||
<value>For group please leave blank here</value>
|
||||
</data>
|
||||
<data name="TipChangeRouting" xml:space="preserve">
|
||||
<value>Routing setting is changed</value>
|
||||
@@ -958,6 +928,9 @@
|
||||
<data name="TbSettingsRouteOnly" xml:space="preserve">
|
||||
<value>RouteOnly</value>
|
||||
</data>
|
||||
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
|
||||
<value>Do not use proxy servers for local (intranet) addresses</value>
|
||||
</data>
|
||||
<data name="menuMixedTestServer" xml:space="preserve">
|
||||
<value>One-click multi test Latency and speed (Ctrl+E)</value>
|
||||
</data>
|
||||
@@ -988,21 +961,9 @@
|
||||
<data name="TbSettingsTunMode" xml:space="preserve">
|
||||
<value>TunMode settings</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectIP" xml:space="preserve">
|
||||
<value>Direct IP CIDR, separated by commas (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectProcess" xml:space="preserve">
|
||||
<value>Direct Process name, separated by commas (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeShowWindow" xml:space="preserve">
|
||||
<value>Show console</value>
|
||||
</data>
|
||||
<data name="menuMoveToGroup" xml:space="preserve">
|
||||
<value>Move to group</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeCustomTemplate" xml:space="preserve">
|
||||
<value>Custom Template</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
||||
<value>Enable Server Drag Drop Sort(Require restart)</value>
|
||||
</data>
|
||||
@@ -1037,7 +998,7 @@
|
||||
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>HTTP port=SOCKS port+1;Pac port=SOCKS port+4;API port=SOCKS port+5;</value>
|
||||
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>Set this with admin privileges, get admin privileges after startup</value>
|
||||
@@ -1045,24 +1006,12 @@
|
||||
<data name="TbSettingsFontSize" xml:space="preserve">
|
||||
<value>Font Size</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeProxyIP" xml:space="preserve">
|
||||
<value>Proxy IP CIDR, separated by commas (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeProxyProcess" xml:space="preserve">
|
||||
<value>Proxy Process name, separated by commas (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeBypassMode" xml:space="preserve">
|
||||
<value>Bypass Mode</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestTimeout" xml:space="preserve">
|
||||
<value>SpeedTest Single Timeout Value</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestUrl" xml:space="preserve">
|
||||
<value>SpeedTest URL</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDNS" xml:space="preserve">
|
||||
<value>DNS object, e.g. {"servers":[]}</value>
|
||||
</data>
|
||||
<data name="menuMoveTo" xml:space="preserve">
|
||||
<value>Move up and down</value>
|
||||
</data>
|
||||
@@ -1091,13 +1040,13 @@
|
||||
<value>Restart as Administrator</value>
|
||||
</data>
|
||||
<data name="LvMoreUrl" xml:space="preserve">
|
||||
<value>More urls, separated by commas;Subscription conversion will be invalid</value>
|
||||
<value>More URLs, separated by commas; Subscription conversion will be invalid</value>
|
||||
</data>
|
||||
<data name="SpeedDisplayText" xml:space="preserve">
|
||||
<value>{0} : {1}/s↑ | {2}/s↓</value>
|
||||
</data>
|
||||
<data name="LvAutoUpdateInterval" xml:space="preserve">
|
||||
<value>Automatic update interval(minutes)</value>
|
||||
<value>Automatic update interval (minutes)</value>
|
||||
</data>
|
||||
<data name="TbSettingsLogEnabledToFile" xml:space="preserve">
|
||||
<value>Enable logging to file</value>
|
||||
@@ -1195,4 +1144,175 @@
|
||||
<data name="SpeedtestingStop" xml:space="preserve">
|
||||
<value>Test terminating...</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip5" xml:space="preserve">
|
||||
<value>*grpc Authority</value>
|
||||
</data>
|
||||
<data name="menuAddHttpServer" xml:space="preserve">
|
||||
<value>Add [Http] server</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>Use Xray and enable non-Tun mode, which conflicts with the group previous proxy</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>Enable fragment</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>Enable cache file for sing-box (ruleset files)</value>
|
||||
</data>
|
||||
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
|
||||
<value>Custom the rule-set of sing-box</value>
|
||||
</data>
|
||||
<data name="NeedRebootTips" xml:space="preserve">
|
||||
<value>Successful operation. Click the settings menu to reboot the app.</value>
|
||||
</data>
|
||||
<data name="menuOpenTheFileLocation" xml:space="preserve">
|
||||
<value>Open the storage location</value>
|
||||
</data>
|
||||
<data name="TbSorting" xml:space="preserve">
|
||||
<value>Sorting</value>
|
||||
</data>
|
||||
<data name="TbSortingChain" xml:space="preserve">
|
||||
<value>Chain</value>
|
||||
</data>
|
||||
<data name="TbSortingDefault" xml:space="preserve">
|
||||
<value>Default</value>
|
||||
</data>
|
||||
<data name="TbSortingDelay" xml:space="preserve">
|
||||
<value>Delay</value>
|
||||
</data>
|
||||
<data name="TbSortingDownSpeed" xml:space="preserve">
|
||||
<value>Download Speed</value>
|
||||
</data>
|
||||
<data name="TbSortingDownTraffic" xml:space="preserve">
|
||||
<value>Download Traffic</value>
|
||||
</data>
|
||||
<data name="TbSortingHost" xml:space="preserve">
|
||||
<value>Host</value>
|
||||
</data>
|
||||
<data name="TbSortingName" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="TbSortingNetwork" xml:space="preserve">
|
||||
<value>Network</value>
|
||||
</data>
|
||||
<data name="TbSortingTime" xml:space="preserve">
|
||||
<value>Time</value>
|
||||
</data>
|
||||
<data name="TbSortingType" xml:space="preserve">
|
||||
<value>Type</value>
|
||||
</data>
|
||||
<data name="TbSortingUpSpeed" xml:space="preserve">
|
||||
<value>Upload Speed</value>
|
||||
</data>
|
||||
<data name="TbSortingUpTraffic" xml:space="preserve">
|
||||
<value>Upload Traffic</value>
|
||||
</data>
|
||||
<data name="TbConnections" xml:space="preserve">
|
||||
<value>Connections</value>
|
||||
</data>
|
||||
<data name="menuConnectionClose" xml:space="preserve">
|
||||
<value>Close Connection</value>
|
||||
</data>
|
||||
<data name="menuConnectionCloseAll" xml:space="preserve">
|
||||
<value>Close All Connection</value>
|
||||
</data>
|
||||
<data name="TbProxies" xml:space="preserve">
|
||||
<value>Proxies</value>
|
||||
</data>
|
||||
<data name="menuRulemode" xml:space="preserve">
|
||||
<value>Rule mode</value>
|
||||
</data>
|
||||
<data name="menuModeDirect" xml:space="preserve">
|
||||
<value>Direct</value>
|
||||
</data>
|
||||
<data name="menuModeGlobal" xml:space="preserve">
|
||||
<value>Global</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>Do not change</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>Rule</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytest" xml:space="preserve">
|
||||
<value>Latency Test</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||
<value>Part Node Latency Test</value>
|
||||
</data>
|
||||
<data name="menuProxiesReload" xml:space="preserve">
|
||||
<value>Refresh Proxies</value>
|
||||
</data>
|
||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||
<value>Select active node (Enter)</value>
|
||||
</data>
|
||||
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
||||
<value>Default domain strategy for outbound</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
||||
<value>Multi-Server lowest latency</value>
|
||||
</data>
|
||||
<data name="TbSettingsMainGirdOrientation" xml:space="preserve">
|
||||
<value>Main layout orientation(Require restart)</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultLoadBalanceServer" xml:space="preserve">
|
||||
<value>Multi-server load balancing</value>
|
||||
</data>
|
||||
<data name="TbSettingsDomainDNSAddress" xml:space="preserve">
|
||||
<value>Outbound DNS address</value>
|
||||
</data>
|
||||
<data name="menuProfileAutofitColumnWidth" xml:space="preserve">
|
||||
<value>Auto column width adjustment</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrlBase64" xml:space="preserve">
|
||||
<value>Export Base64-encoded Share Links to Clipboard</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||
<value>Export selected server for complete configuration to clipboard</value>
|
||||
</data>
|
||||
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
||||
<value>Show or hide the main window</value>
|
||||
</data>
|
||||
<data name="UpdateStandalonePackageTip" xml:space="preserve">
|
||||
<value>You are currently running a standalone package, please manually download the SelfContained.7z file to unzip and overwrite it!</value>
|
||||
</data>
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>Custom config socks port</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>Backup and Restore</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>Backup to local</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>Restore from local</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>Backup to remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>Restore from remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>Local</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>Remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav Url</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav User Name</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav Password</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav Check</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>Remote folder name (optional)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -117,9 +117,6 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="BatchExportSubscriptionSuccessfully" xml:space="preserve">
|
||||
<value>Экспортирование подписок в буфер обмена успешно завершено</value>
|
||||
</data>
|
||||
<data name="BatchExportURLSuccessfully" xml:space="preserve">
|
||||
<value>Экспортирование URL в буфер обмена успешно завершено</value>
|
||||
</data>
|
||||
@@ -159,9 +156,6 @@
|
||||
<data name="FillCorrectServerPort" xml:space="preserve">
|
||||
<value>Пожалуйста, укажите порт сервера в правильном формате</value>
|
||||
</data>
|
||||
<data name="FillKcpParameters" xml:space="preserve">
|
||||
<value>Пожалуйста, заполните параметры KCP корректно</value>
|
||||
</data>
|
||||
<data name="FillLocalListeningPort" xml:space="preserve">
|
||||
<value>Пожалуйста, укажите локальный порт прослушивания</value>
|
||||
</data>
|
||||
@@ -174,15 +168,9 @@
|
||||
<data name="FillUUID" xml:space="preserve">
|
||||
<value>Пожалуйста, заполните идентификатор пользователя</value>
|
||||
</data>
|
||||
<data name="IncorrectClientConfiguration" xml:space="preserve">
|
||||
<value>Некорректный файл конфигурации клиента, пожалуйста, проверьте</value>
|
||||
</data>
|
||||
<data name="Incorrectconfiguration" xml:space="preserve">
|
||||
<value>Некорректная конфигурация, пожалуйста, проверьте</value>
|
||||
</data>
|
||||
<data name="IncorrectServerConfiguration" xml:space="preserve">
|
||||
<value>Некорректный файл конфигурации сервера, пожалуйста, проверьте</value>
|
||||
</data>
|
||||
<data name="InitialConfiguration" xml:space="preserve">
|
||||
<value>Исходная конфигурация</value>
|
||||
</data>
|
||||
@@ -267,9 +255,6 @@
|
||||
<data name="NonvmessOrssProtocol" xml:space="preserve">
|
||||
<value>Не является протоколом Vmess или SS</value>
|
||||
</data>
|
||||
<data name="NonVmessService" xml:space="preserve">
|
||||
<value> нестандартный сервис, эта функция недействительна</value>
|
||||
</data>
|
||||
<data name="NotFoundCore" xml:space="preserve">
|
||||
<value>Файл Core (имя файла: {1}) не найден в папке ({0}), загрузите и поместите его в папку, адрес загрузки: {2}</value>
|
||||
</data>
|
||||
@@ -415,15 +400,9 @@
|
||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||
<value>Пожалуйста, просмотрите, чтобы импортировать конфигурацию сервера</value>
|
||||
</data>
|
||||
<data name="SystemProxy" xml:space="preserve">
|
||||
<value>Системный прокси</value>
|
||||
</data>
|
||||
<data name="Speedtesting" xml:space="preserve">
|
||||
<value>Тестирование...</value>
|
||||
</data>
|
||||
<data name="TooManyServersTip" xml:space="preserve">
|
||||
<value>Слишком много серверов, пожалуйста, откройте главный интерфейс</value>
|
||||
</data>
|
||||
<data name="LabLAN" xml:space="preserve">
|
||||
<value>LAN</value>
|
||||
</data>
|
||||
@@ -544,18 +523,12 @@
|
||||
<data name="menuTcpingServer" xml:space="preserve">
|
||||
<value>Тест задержки с tcping (Ctrl+O)</value>
|
||||
</data>
|
||||
<data name="menuTestMe" xml:space="preserve">
|
||||
<value>Проверить текущий статус службы</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfig" xml:space="preserve">
|
||||
<value>Экспортировать выбранный сервер для клиента</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||
<value>Экспорт URL-адресов общего доступа в буфер обмена (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuExport2SubContent" xml:space="preserve">
|
||||
<value>Экспортировать ссылку-подписку (base64) в буфер обмена</value>
|
||||
</data>
|
||||
<data name="menuAddCustomServer" xml:space="preserve">
|
||||
<value>Добавить сервер пользовательской конфигурации</value>
|
||||
</data>
|
||||
@@ -586,9 +559,6 @@
|
||||
<data name="menuMsgViewCopyAll" xml:space="preserve">
|
||||
<value>Скопировать все</value>
|
||||
</data>
|
||||
<data name="menuMsgViewFilter" xml:space="preserve">
|
||||
<value>Установить фильтры сообщений</value>
|
||||
</data>
|
||||
<data name="menuMsgViewSelectAll" xml:space="preserve">
|
||||
<value>Выбрать все (Ctrl+A)</value>
|
||||
</data>
|
||||
@@ -701,7 +671,7 @@
|
||||
<value>txtPreSocksPort</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* После установки этого значения служба socks будет запущена с использованием sing-box для обеспечения таких функций, как отображение скорости</value>
|
||||
<value>* После установки этого значения служба socks будет запущена с использованием Xray/sing-box(Tun) для обеспечения таких функций, как отображение скорости</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>Просмотр</value>
|
||||
@@ -964,6 +934,9 @@
|
||||
<data name="TbSettingsRouteOnly" xml:space="preserve">
|
||||
<value>Только маршрут</value>
|
||||
</data>
|
||||
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
|
||||
<value>Не используйте прокси-серверы для локальных (интранет) адресов</value>
|
||||
</data>
|
||||
<data name="menuMixedTestServer" xml:space="preserve">
|
||||
<value>Тест задержки и скорости всех серверов (Ctrl+E)</value>
|
||||
</data>
|
||||
@@ -994,21 +967,9 @@
|
||||
<data name="TbSettingsTunMode" xml:space="preserve">
|
||||
<value>Настройки TunMode</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectIP" xml:space="preserve">
|
||||
<value>Прямой IP CIDR, разделенный запятыми (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectProcess" xml:space="preserve">
|
||||
<value>Имя процесса, разделенное запятыми (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeShowWindow" xml:space="preserve">
|
||||
<value>Показать консоль</value>
|
||||
</data>
|
||||
<data name="menuMoveToGroup" xml:space="preserve">
|
||||
<value>Перейти в группу</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeCustomTemplate" xml:space="preserve">
|
||||
<value>Пользовательский шаблон</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
||||
<value>Включить сортировку перетаскиванием сервера (требуется перезагрузка)</value>
|
||||
</data>
|
||||
@@ -1042,9 +1003,6 @@
|
||||
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
|
||||
<value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts, перезапустите настройки</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>HTTP port=socks port+1</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>Установите это с правами администратора</value>
|
||||
</data>
|
||||
@@ -117,11 +117,8 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="BatchExportSubscriptionSuccessfully" xml:space="preserve">
|
||||
<value>批量导出订阅内容至剪贴板成功</value>
|
||||
</data>
|
||||
<data name="BatchExportURLSuccessfully" xml:space="preserve">
|
||||
<value>批量导出分享URL至剪贴板成功</value>
|
||||
<value>导出分享链接至剪贴板成功</value>
|
||||
</data>
|
||||
<data name="CheckServerSettings" xml:space="preserve">
|
||||
<value>请先检查服务器设置</value>
|
||||
@@ -159,9 +156,6 @@
|
||||
<data name="FillCorrectServerPort" xml:space="preserve">
|
||||
<value>请填写正确格式服务器端口</value>
|
||||
</data>
|
||||
<data name="FillKcpParameters" xml:space="preserve">
|
||||
<value>请正确填写KCP参数</value>
|
||||
</data>
|
||||
<data name="FillLocalListeningPort" xml:space="preserve">
|
||||
<value>请填写本地监听端口</value>
|
||||
</data>
|
||||
@@ -174,15 +168,9 @@
|
||||
<data name="FillUUID" xml:space="preserve">
|
||||
<value>请填写用户ID</value>
|
||||
</data>
|
||||
<data name="IncorrectClientConfiguration" xml:space="preserve">
|
||||
<value>不是正确的客户端配置文件,请检查</value>
|
||||
</data>
|
||||
<data name="Incorrectconfiguration" xml:space="preserve">
|
||||
<value>不是正确的配置,请检查</value>
|
||||
</data>
|
||||
<data name="IncorrectServerConfiguration" xml:space="preserve">
|
||||
<value>不是正确的服务端配置文件,请检查</value>
|
||||
</data>
|
||||
<data name="InitialConfiguration" xml:space="preserve">
|
||||
<value>初始化配置</value>
|
||||
</data>
|
||||
@@ -267,9 +255,6 @@
|
||||
<data name="NonvmessOrssProtocol" xml:space="preserve">
|
||||
<value>非VMess或ss协议</value>
|
||||
</data>
|
||||
<data name="NonVmessService" xml:space="preserve">
|
||||
<value>非标准服务,此功能无效</value>
|
||||
</data>
|
||||
<data name="NotFoundCore" xml:space="preserve">
|
||||
<value>在文件夹 ({0}) 下未找到Core文件 (文件名:{1}),请下载后放入文件夹,下载地址: {2}</value>
|
||||
</data>
|
||||
@@ -314,7 +299,7 @@
|
||||
<value>成功从剪贴板导入 {0} 个服务器</value>
|
||||
</data>
|
||||
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
||||
<value>扫描导入URL成功</value>
|
||||
<value>扫描导入分享链接成功</value>
|
||||
</data>
|
||||
<data name="TestMeOutput" xml:space="preserve">
|
||||
<value>当前服务的真连接延迟: {0} ms</value>
|
||||
@@ -359,7 +344,7 @@
|
||||
<value>请填写正确的自定义DNS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip1" xml:space="preserve">
|
||||
<value>*ws/httpupgrade path</value>
|
||||
<value>*ws/httpupgrade/splithttp path</value>
|
||||
</data>
|
||||
<data name="TransportPathTip2" xml:space="preserve">
|
||||
<value>*h2 path</value>
|
||||
@@ -374,7 +359,7 @@
|
||||
<value>*http host中间逗号(,)分隔</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip2" xml:space="preserve">
|
||||
<value>*ws/httpupgrade host</value>
|
||||
<value>*ws/httpupgrade/splithttp host</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip3" xml:space="preserve">
|
||||
<value>*h2 host中间逗号(,)分隔</value>
|
||||
@@ -415,15 +400,9 @@
|
||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||
<value>请浏览导入服务器配置</value>
|
||||
</data>
|
||||
<data name="SystemProxy" xml:space="preserve">
|
||||
<value>系统代理</value>
|
||||
</data>
|
||||
<data name="Speedtesting" xml:space="preserve">
|
||||
<value>测试中...</value>
|
||||
</data>
|
||||
<data name="TooManyServersTip" xml:space="preserve">
|
||||
<value>服务器太多,请打开主界面操作</value>
|
||||
</data>
|
||||
<data name="LabLAN" xml:space="preserve">
|
||||
<value>局域网</value>
|
||||
</data>
|
||||
@@ -512,7 +491,7 @@
|
||||
<value>语言(重启)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaClipboard" xml:space="preserve">
|
||||
<value>从剪贴板导入批量URL (Ctrl+V)</value>
|
||||
<value>从剪贴板导入分享链接 (Ctrl+V)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaScan" xml:space="preserve">
|
||||
<value>扫描屏幕上的二维码 (Ctrl+S)</value>
|
||||
@@ -544,17 +523,11 @@
|
||||
<data name="menuTcpingServer" xml:space="preserve">
|
||||
<value>测试服务器延迟Tcping(多选) (Ctrl+O)</value>
|
||||
</data>
|
||||
<data name="menuTestMe" xml:space="preserve">
|
||||
<value>测试当前服务状态</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfig" xml:space="preserve">
|
||||
<value>导出所选服务器为客户端配置</value>
|
||||
<value>导出所选服务器完整配置</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||
<value>批量导出分享URL至剪贴板(多选) (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuExport2SubContent" xml:space="preserve">
|
||||
<value>批量导出订阅内容至剪贴板(多选)</value>
|
||||
<value>导出分享链接至剪贴板(多选) (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuAddCustomServer" xml:space="preserve">
|
||||
<value>添加自定义配置服务器</value>
|
||||
@@ -586,9 +559,6 @@
|
||||
<data name="menuMsgViewCopyAll" xml:space="preserve">
|
||||
<value>复制所有</value>
|
||||
</data>
|
||||
<data name="menuMsgViewFilter" xml:space="preserve">
|
||||
<value>设置信息过滤器</value>
|
||||
</data>
|
||||
<data name="menuMsgViewSelectAll" xml:space="preserve">
|
||||
<value>全选 (Ctrl+A)</value>
|
||||
</data>
|
||||
@@ -701,7 +671,7 @@
|
||||
<value>Socks端口</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* 自定义配置的Socks端口值,可不设置;当设置此值后,将使用sing-box额外启动一个前置Socks服务,提供分流和速度显示等功能</value>
|
||||
<value>* 自定义配置的Socks端口值,可不设置;当设置此值后,将使用Xray/sing-box(Tun)额外启动一个前置Socks服务,提供分流和速度显示等功能</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>浏览</value>
|
||||
@@ -958,6 +928,9 @@
|
||||
<data name="TbSettingsRouteOnly" xml:space="preserve">
|
||||
<value>RouteOnly</value>
|
||||
</data>
|
||||
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
|
||||
<value>请勿将代理服务器用于本地(Intranet)地址</value>
|
||||
</data>
|
||||
<data name="menuMixedTestServer" xml:space="preserve">
|
||||
<value>一键多线程测试延迟和速度 (Ctrl+E)</value>
|
||||
</data>
|
||||
@@ -988,21 +961,9 @@
|
||||
<data name="TbSettingsTunMode" xml:space="preserve">
|
||||
<value>Tun模式设置</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectIP" xml:space="preserve">
|
||||
<value>直连的IP CIDR,用逗号(,)分隔</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectProcess" xml:space="preserve">
|
||||
<value>直连的进程名,用逗号(,)分隔</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeShowWindow" xml:space="preserve">
|
||||
<value>显示控制台</value>
|
||||
</data>
|
||||
<data name="menuMoveToGroup" xml:space="preserve">
|
||||
<value>移至订阅分组</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeCustomTemplate" xml:space="preserve">
|
||||
<value>自定义配置模板</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
||||
<value>启用服务器拖放排序(需重启)</value>
|
||||
</data>
|
||||
@@ -1037,7 +998,7 @@
|
||||
<value>拷贝字体TTF/TTC文件到目录guiFonts,重启设置</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>http端口=socks端口+1;Pac端口=socks端口+4;API端口=socks端口+5;</value>
|
||||
<value>http端口= +1;Pac端口= +4;*ray API端口= +5;mihomo API端口= +6;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>以管理员权限设置此项,在启动后获得管理员权限</value>
|
||||
@@ -1045,24 +1006,12 @@
|
||||
<data name="TbSettingsFontSize" xml:space="preserve">
|
||||
<value>字体大小</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeProxyIP" xml:space="preserve">
|
||||
<value>代理的IP CIDR,用逗号(,)分隔</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeProxyProcess" xml:space="preserve">
|
||||
<value>代理的进程名,用逗号(,)分隔</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeBypassMode" xml:space="preserve">
|
||||
<value>绕行模式</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestTimeout" xml:space="preserve">
|
||||
<value>测速单个超时值</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestUrl" xml:space="preserve">
|
||||
<value>测速文件地址</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDNS" xml:space="preserve">
|
||||
<value>DNS对象,例如 {"servers":[]}</value>
|
||||
</data>
|
||||
<data name="menuMoveTo" xml:space="preserve">
|
||||
<value>移至上下</value>
|
||||
</data>
|
||||
@@ -1192,4 +1141,175 @@
|
||||
<data name="SpeedtestingStop" xml:space="preserve">
|
||||
<value>测试终止中...</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip5" xml:space="preserve">
|
||||
<value>*grpc Authority</value>
|
||||
</data>
|
||||
<data name="menuAddHttpServer" xml:space="preserve">
|
||||
<value>添加[Http]服务器</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>启用分片(Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>使用Xray且非Tun模式启用,和分组前置代理冲突</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>启用sing-box(规则集文件)的缓存文件</value>
|
||||
</data>
|
||||
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
|
||||
<value>自定义sing-box rule-set</value>
|
||||
</data>
|
||||
<data name="NeedRebootTips" xml:space="preserve">
|
||||
<value>操作成功。请点击设置菜单重启应用。</value>
|
||||
</data>
|
||||
<data name="menuOpenTheFileLocation" xml:space="preserve">
|
||||
<value>打开存储所在的位置</value>
|
||||
</data>
|
||||
<data name="TbSorting" xml:space="preserve">
|
||||
<value>排序</value>
|
||||
</data>
|
||||
<data name="TbSortingChain" xml:space="preserve">
|
||||
<value>路由链</value>
|
||||
</data>
|
||||
<data name="TbSortingDefault" xml:space="preserve">
|
||||
<value>默认</value>
|
||||
</data>
|
||||
<data name="TbSortingDelay" xml:space="preserve">
|
||||
<value>延迟</value>
|
||||
</data>
|
||||
<data name="TbSortingDownSpeed" xml:space="preserve">
|
||||
<value>下载速度</value>
|
||||
</data>
|
||||
<data name="TbSortingDownTraffic" xml:space="preserve">
|
||||
<value>下载流量</value>
|
||||
</data>
|
||||
<data name="TbSortingHost" xml:space="preserve">
|
||||
<value>主机</value>
|
||||
</data>
|
||||
<data name="TbSortingName" xml:space="preserve">
|
||||
<value>名称</value>
|
||||
</data>
|
||||
<data name="TbSortingNetwork" xml:space="preserve">
|
||||
<value>网络</value>
|
||||
</data>
|
||||
<data name="TbSortingTime" xml:space="preserve">
|
||||
<value>时间</value>
|
||||
</data>
|
||||
<data name="TbSortingType" xml:space="preserve">
|
||||
<value>类型</value>
|
||||
</data>
|
||||
<data name="TbSortingUpSpeed" xml:space="preserve">
|
||||
<value>上传速度</value>
|
||||
</data>
|
||||
<data name="TbSortingUpTraffic" xml:space="preserve">
|
||||
<value>上传流量</value>
|
||||
</data>
|
||||
<data name="TbConnections" xml:space="preserve">
|
||||
<value>当前连接</value>
|
||||
</data>
|
||||
<data name="menuConnectionClose" xml:space="preserve">
|
||||
<value>关闭连接</value>
|
||||
</data>
|
||||
<data name="menuConnectionCloseAll" xml:space="preserve">
|
||||
<value>关闭所有连接</value>
|
||||
</data>
|
||||
<data name="TbProxies" xml:space="preserve">
|
||||
<value>当前代理</value>
|
||||
</data>
|
||||
<data name="menuRulemode" xml:space="preserve">
|
||||
<value>规则模式</value>
|
||||
</data>
|
||||
<data name="menuModeDirect" xml:space="preserve">
|
||||
<value>直连</value>
|
||||
</data>
|
||||
<data name="menuModeGlobal" xml:space="preserve">
|
||||
<value>全局</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>随原配置</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>规则</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytest" xml:space="preserve">
|
||||
<value>延迟测试</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||
<value>当前部分节点延迟测试</value>
|
||||
</data>
|
||||
<data name="menuProxiesReload" xml:space="preserve">
|
||||
<value>刷新</value>
|
||||
</data>
|
||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||
<value>设为活动节点 (Enter)</value>
|
||||
</data>
|
||||
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
||||
<value>Outbound默认解析策略</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
||||
<value>多服务器最低延迟 (多选)</value>
|
||||
</data>
|
||||
<data name="TbSettingsMainGirdOrientation" xml:space="preserve">
|
||||
<value>主界面布局方向(需重启)</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultLoadBalanceServer" xml:space="preserve">
|
||||
<value>多服务器负载均衡 (多选)</value>
|
||||
</data>
|
||||
<data name="TbSettingsDomainDNSAddress" xml:space="preserve">
|
||||
<value>Outbound域名解析地址</value>
|
||||
</data>
|
||||
<data name="menuProfileAutofitColumnWidth" xml:space="preserve">
|
||||
<value>自动调整列宽</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrlBase64" xml:space="preserve">
|
||||
<value>导出分享链接至剪贴板(多选) Base64编码</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||
<value>导出所选服务器完整配置至剪贴板</value>
|
||||
</data>
|
||||
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
||||
<value>显示或隐藏主界面</value>
|
||||
</data>
|
||||
<data name="UpdateStandalonePackageTip" xml:space="preserve">
|
||||
<value>您当前运行的是独立包,请手动下载 SelfContained.7z文件解压覆盖</value>
|
||||
</data>
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>自定义配置的Socks端口</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>备份和还原</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>备份到本地</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>从本地恢复</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>备份到远程 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>从远程恢复 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>本地</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>远程 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav 账户</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav 可用检查</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav 密码</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav 服务器地址</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>远程文件夹名称(可选)</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -117,11 +117,8 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="BatchExportSubscriptionSuccessfully" xml:space="preserve">
|
||||
<value>批次匯出訂閱內容至剪貼簿成功</value>
|
||||
</data>
|
||||
<data name="BatchExportURLSuccessfully" xml:space="preserve">
|
||||
<value>批次匯出分享URL至剪貼簿成功</value>
|
||||
<value>匯出分享链接至剪貼簿成功</value>
|
||||
</data>
|
||||
<data name="CheckServerSettings" xml:space="preserve">
|
||||
<value>請先檢查伺服器設定</value>
|
||||
@@ -159,9 +156,6 @@
|
||||
<data name="FillCorrectServerPort" xml:space="preserve">
|
||||
<value>請填寫正確格式伺服器埠</value>
|
||||
</data>
|
||||
<data name="FillKcpParameters" xml:space="preserve">
|
||||
<value>請正確填寫KCP參數</value>
|
||||
</data>
|
||||
<data name="FillLocalListeningPort" xml:space="preserve">
|
||||
<value>請填寫本機監聽埠</value>
|
||||
</data>
|
||||
@@ -174,15 +168,9 @@
|
||||
<data name="FillUUID" xml:space="preserve">
|
||||
<value>請填寫使用者ID</value>
|
||||
</data>
|
||||
<data name="IncorrectClientConfiguration" xml:space="preserve">
|
||||
<value>不是正確的用戶端配置檔案,請檢查</value>
|
||||
</data>
|
||||
<data name="Incorrectconfiguration" xml:space="preserve">
|
||||
<value>不是正確的配置,請檢查</value>
|
||||
</data>
|
||||
<data name="IncorrectServerConfiguration" xml:space="preserve">
|
||||
<value>不是正確的服務端配置檔案,請檢查</value>
|
||||
</data>
|
||||
<data name="InitialConfiguration" xml:space="preserve">
|
||||
<value>初始化配置</value>
|
||||
</data>
|
||||
@@ -267,9 +255,6 @@
|
||||
<data name="NonvmessOrssProtocol" xml:space="preserve">
|
||||
<value>非VMess或SS協定</value>
|
||||
</data>
|
||||
<data name="NonVmessService" xml:space="preserve">
|
||||
<value>非標準服務,此功能無效</value>
|
||||
</data>
|
||||
<data name="NotFoundCore" xml:space="preserve">
|
||||
<value>在資料夾 ({0}) 下未找到Core文件 (檔案名:{1}),請下載後放入資料夾、,下載網址: {2}</value>
|
||||
</data>
|
||||
@@ -313,7 +298,7 @@
|
||||
<value>成功從剪貼簿匯入 {0} 個伺服器</value>
|
||||
</data>
|
||||
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
||||
<value>掃描匯入URL成功</value>
|
||||
<value>掃描匯入分享链接成功</value>
|
||||
</data>
|
||||
<data name="TestMeOutput" xml:space="preserve">
|
||||
<value>目前服務的真連接延遲: {0} ms</value>
|
||||
@@ -358,7 +343,7 @@
|
||||
<value>請填寫正確的自訂DNS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip1" xml:space="preserve">
|
||||
<value>*ws/httpupgrade path</value>
|
||||
<value>*ws/httpupgrade/splithttp path</value>
|
||||
</data>
|
||||
<data name="TransportPathTip2" xml:space="preserve">
|
||||
<value>*h2 path</value>
|
||||
@@ -373,7 +358,7 @@
|
||||
<value>*http host中間逗號(,)分隔</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip2" xml:space="preserve">
|
||||
<value>*ws/httpupgrade host</value>
|
||||
<value>*ws/httpupgrade/splithttp host</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip3" xml:space="preserve">
|
||||
<value>*h2 host中間逗號(,)分隔</value>
|
||||
@@ -414,15 +399,9 @@
|
||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||
<value>請瀏覽匯入伺服器配置</value>
|
||||
</data>
|
||||
<data name="SystemProxy" xml:space="preserve">
|
||||
<value>系統代理</value>
|
||||
</data>
|
||||
<data name="Speedtesting" xml:space="preserve">
|
||||
<value>測試中...</value>
|
||||
</data>
|
||||
<data name="TooManyServersTip" xml:space="preserve">
|
||||
<value>伺服器太多,請打開主介面操作</value>
|
||||
</data>
|
||||
<data name="LabLAN" xml:space="preserve">
|
||||
<value>區域網路</value>
|
||||
</data>
|
||||
@@ -511,7 +490,7 @@
|
||||
<value>語言(重啟)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaClipboard" xml:space="preserve">
|
||||
<value>從剪貼簿匯入批次URL (Ctrl+V)</value>
|
||||
<value>從剪貼簿導入分享鏈接 (Ctrl+V)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaScan" xml:space="preserve">
|
||||
<value>掃描螢幕上的二維碼 (Ctrl+S)</value>
|
||||
@@ -543,17 +522,11 @@
|
||||
<data name="menuTcpingServer" xml:space="preserve">
|
||||
<value>測試伺服器延遲Tcping(多選) (Ctrl+O)</value>
|
||||
</data>
|
||||
<data name="menuTestMe" xml:space="preserve">
|
||||
<value>測試目前服務狀態</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfig" xml:space="preserve">
|
||||
<value>匯出所選伺服器為用戶端配置</value>
|
||||
<value>匯出所選伺服器完整配置</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||
<value>批次匯出分享URL至剪貼簿(多選) (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuExport2SubContent" xml:space="preserve">
|
||||
<value>批次匯出訂閱內容至剪貼簿(多選)</value>
|
||||
<value>匯出分享链接至剪貼簿(多選) (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuAddCustomServer" xml:space="preserve">
|
||||
<value>新增自訂配置伺服器</value>
|
||||
@@ -585,9 +558,6 @@
|
||||
<data name="menuMsgViewCopyAll" xml:space="preserve">
|
||||
<value>複製所有</value>
|
||||
</data>
|
||||
<data name="menuMsgViewFilter" xml:space="preserve">
|
||||
<value>設定資訊過濾器</value>
|
||||
</data>
|
||||
<data name="menuMsgViewSelectAll" xml:space="preserve">
|
||||
<value>全選 (Ctrl+A)</value>
|
||||
</data>
|
||||
@@ -700,7 +670,7 @@
|
||||
<value>SOCKS埠</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* 自訂配置的Socks埠值,可不設定;當設定此值後,將使用sing-box額外啟動一個前置Socks服務,提供分流和速度顯示等功能</value>
|
||||
<value>* 自訂配置的Socks埠值,可不設定;當設定此值後,將使用Xray/sing-box(Tun)額外啟動一個前置Socks服務,提供分流和速度顯示等功能</value>
|
||||
</data>
|
||||
<!--********************************************-->
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
@@ -958,6 +928,9 @@
|
||||
<data name="TbSettingsRouteOnly" xml:space="preserve">
|
||||
<value>RouteOnly</value>
|
||||
</data>
|
||||
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
|
||||
<value>請勿將代理伺服器用於本機(Intranet)位址</value>
|
||||
</data>
|
||||
<data name="menuMixedTestServer" xml:space="preserve">
|
||||
<value>一鍵多執行緒測試延遲和速度 (Ctrl+E)</value>
|
||||
</data>
|
||||
@@ -988,21 +961,9 @@
|
||||
<data name="TbSettingsTunMode" xml:space="preserve">
|
||||
<value>TUN模式設定</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectIP" xml:space="preserve">
|
||||
<value>直連的IP CIDR,用逗號(,)分隔</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDirectProcess" xml:space="preserve">
|
||||
<value>直連的行程名,用逗號(,)分隔</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeShowWindow" xml:space="preserve">
|
||||
<value>顯示控制台</value>
|
||||
</data>
|
||||
<data name="menuMoveToGroup" xml:space="preserve">
|
||||
<value>移至訂閱分組</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeCustomTemplate" xml:space="preserve">
|
||||
<value>自訂配置模板</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
||||
<value>啟動伺服器拖放排序(需重啟)</value>
|
||||
</data>
|
||||
@@ -1037,7 +998,7 @@
|
||||
<value>複製字型TTF/TTC文件到目錄guiFonts,重啟設定</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>HTTP埠=SOCKS埠+1</value>
|
||||
<value>http端口= +1;Pac端口= +4;*ray API端口= +5;mihomo API端口= +6;</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>以管理員權限設定此項,在啟動後獲得管理員權限</value>
|
||||
@@ -1045,24 +1006,12 @@
|
||||
<data name="TbSettingsFontSize" xml:space="preserve">
|
||||
<value>字型大小</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeProxyIP" xml:space="preserve">
|
||||
<value>代理的IP CIDR,用逗號(,)分隔</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeProxyProcess" xml:space="preserve">
|
||||
<value>代理的行程名,用逗號(,)分隔</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeBypassMode" xml:space="preserve">
|
||||
<value>繞行模式</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestTimeout" xml:space="preserve">
|
||||
<value>測速單個超時值</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestUrl" xml:space="preserve">
|
||||
<value>測速檔案位址</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunModeDNS" xml:space="preserve">
|
||||
<value>DNS物件,例如 {"servers":[]}</value>
|
||||
</data>
|
||||
<data name="menuMoveTo" xml:space="preserve">
|
||||
<value>移至上下</value>
|
||||
</data>
|
||||
@@ -1165,4 +1114,82 @@
|
||||
<data name="SpeedtestingStop" xml:space="preserve">
|
||||
<value>測試終止中...</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip5" xml:space="preserve">
|
||||
<value>*grpc Authority</value>
|
||||
</data>
|
||||
<data name="menuAddHttpServer" xml:space="preserve">
|
||||
<value>新增[Http]伺服器</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>啟用分片(Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>使用Xray且非Tun模式啟用,和分組前置代理衝突</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>啟用sing-box(規則集文件)的緩存文件</value>
|
||||
</data>
|
||||
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
|
||||
<value>自訂sing-box rule-set</value>
|
||||
</data>
|
||||
<data name="NeedRebootTips" xml:space="preserve">
|
||||
<value>操作成功。 請點選設定選單重啟應用程式。</value>
|
||||
</data>
|
||||
<data name="menuOpenTheFileLocation" xml:space="preserve">
|
||||
<value>打開儲存所在的位置</value>
|
||||
</data>
|
||||
<data name="menuProfileAutofitColumnWidth" xml:space="preserve">
|
||||
<value>自動調整列寬</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrlBase64" xml:space="preserve">
|
||||
<value>匯出分享链接至剪貼簿(多選) Base64编码</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||
<value>匯出所選伺服器完整配置至剪貼簿</value>
|
||||
</data>
|
||||
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
||||
<value>顯示或隱藏主介面</value>
|
||||
</data>
|
||||
<data name="UpdateStandalonePackageTip" xml:space="preserve">
|
||||
<value>您目前運行的是獨立包,請手動下載 SelfContained.7z檔案解壓縮覆蓋</value>
|
||||
</data>
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>自訂配置的Socks端口</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>備份和還原</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>備份到本地</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>從本地恢復</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>備份到遠端 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>從遠端恢復 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>本地</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>遠端 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav 賬戶</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav 可用檢查</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav 密碼</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav 服務器地址</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>遠端資料夾名稱(可選)</value>
|
||||
</data>
|
||||
</root>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user