mirror of
https://github.com/2dust/v2rayN.git
synced 2026-07-26 18:02:05 +03:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da81101cfd | ||
|
|
3d4f202cd7 | ||
|
|
35fbb137e8 | ||
|
|
1869a95700 | ||
|
|
34359885ab | ||
|
|
93832656dd | ||
|
|
b1a400b393 | ||
|
|
879c736924 | ||
|
|
8147b39723 | ||
|
|
5fed566a32 | ||
|
|
f0ee792778 | ||
|
|
cd4aa5531c | ||
|
|
e6d08c2db1 | ||
|
|
4dc9c60b85 | ||
|
|
eb9c974436 | ||
|
|
27b8f889fe | ||
|
|
0d5169e4cd | ||
|
|
abcdd9497c | ||
|
|
da65faa49f | ||
|
|
b6cfce069a | ||
|
|
39ec9239d2 | ||
|
|
a2a5940d91 | ||
|
|
ce8caa89a4 | ||
|
|
af3d579890 | ||
|
|
b2bbc865af | ||
|
|
2eb325643a | ||
|
|
8609e2b8d1 |
2
.github/workflows/build-linux.yml
vendored
2
.github/workflows/build-linux.yml
vendored
@@ -341,7 +341,7 @@ jobs:
|
||||
QCOW2_IMAGE: debian13-loong64.qcow2
|
||||
EFI_CODE: edk2-loongarch64-code.fd
|
||||
EFI_VARS: edk2-loongarch64-vars.fd
|
||||
QEMU_VERSION: 10.2.2
|
||||
QEMU_VERSION: 10.2.3
|
||||
|
||||
steps:
|
||||
- name: Prepare host tools
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.22.5</Version>
|
||||
<Version>7.22.7</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -7,32 +7,32 @@
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.4.1" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.16" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.16" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.17" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.17" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.4.0" />
|
||||
<PackageVersion Include="DialogHost.Avalonia" Version="0.11.0" />
|
||||
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.4.12" />
|
||||
<PackageVersion Include="IPNetwork2" Version="4.3.0" />
|
||||
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.4.13" />
|
||||
<PackageVersion Include="CliWrap" Version="3.10.1" />
|
||||
<PackageVersion Include="Downloader" Version="5.5.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
|
||||
<PackageVersion Include="Downloader" Version="5.7.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
|
||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
||||
<PackageVersion Include="MaterialDesignThemes" Version="5.3.2" />
|
||||
<PackageVersion Include="QRCoder" Version="1.8.0" />
|
||||
<PackageVersion Include="ReactiveUI" Version="23.2.27" />
|
||||
<PackageVersion Include="ReactiveUI" Version="23.2.28" />
|
||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageVersion Include="ReactiveUI.WPF" Version="23.2.27" />
|
||||
<PackageVersion Include="ReactiveUI.WPF" Version="23.2.28" />
|
||||
<PackageVersion Include="Semi.Avalonia" Version="11.3.14" />
|
||||
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.2" />
|
||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.3" />
|
||||
<PackageVersion Include="NLog" Version="6.1.3" />
|
||||
<PackageVersion Include="sqlite-net-e" Version="1.11.0" />
|
||||
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.1.7" />
|
||||
<PackageVersion Include="Repobot.SQLite.Unofficial" Version="3.53.2" />
|
||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||
<PackageVersion Include="Tmds.DBus.Protocol" Version="0.21.3" />
|
||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||
<PackageVersion Include="xunit.v3" Version="3.2.2" />
|
||||
<PackageVersion Include="YamlDotNet" Version="17.1.0" />
|
||||
<PackageVersion Include="YamlDotNet" Version="18.0.0" />
|
||||
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.22" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -17,7 +17,7 @@ internal static class CoreConfigTestFactory
|
||||
{
|
||||
return new Config
|
||||
{
|
||||
CoreBasicItem = new CoreBasicItem { Loglevel = "warning", MuxEnabled = false },
|
||||
CoreBasicItem = new CoreBasicItem { Loglevel = "warning" },
|
||||
TunModeItem = new TunModeItem { EnableTun = false, IcmpRouting = "default" },
|
||||
KcpItem = new KcpItem(),
|
||||
GrpcItem = new GrpcItem(),
|
||||
@@ -187,7 +187,7 @@ internal static class CoreConfigTestFactory
|
||||
SimpleDnsItem = config.SimpleDNSItem,
|
||||
AllProxiesMap = new Dictionary<string, ProfileItem> { [node.IndexId] = node },
|
||||
FullConfigTemplate = null,
|
||||
IsTunEnabled = false,
|
||||
IsTunEnabled = config.TunModeItem.EnableTun,
|
||||
ProtectDomainList = [],
|
||||
};
|
||||
}
|
||||
@@ -206,4 +206,12 @@ internal static class CoreConfigTestFactory
|
||||
config.SimpleDNSItem.BootstrapDNS = bootstrapDns;
|
||||
return config;
|
||||
}
|
||||
|
||||
public static Config CreateConfigWithTunRouteExcludeAddress(ECoreType coreType)
|
||||
{
|
||||
var config = CreateConfig(coreType);
|
||||
config.TunModeItem.EnableTun = true;
|
||||
config.TunModeItem.RouteExcludeAddress = ["10.0.0.1/32", "192.168.1.0/24", "fc00::/7"];
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,7 +554,7 @@ public class CoreConfigSingboxServiceTests
|
||||
|
||||
cfg.dns.servers.Should().Contain(s => s.tag == "remote" && s.type == "udp" && s.server == "8.8.8.8");
|
||||
cfg.dns.servers.Should().Contain(s => s.tag == Global.SingboxLocalDNSTag);
|
||||
cfg.dns.rules.Should().Contain(r => r.clash_mode == ERuleMode.Global.ToString());
|
||||
cfg.dns.rules.Should().Contain(r => r.clash_mode == ERuleMode.Direct.ToString());
|
||||
cfg.dns.rules.Should().Contain(r => r.clash_mode == nameof(ERuleMode.Global));
|
||||
cfg.dns.rules.Should().Contain(r => r.clash_mode == nameof(ERuleMode.Direct));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,4 +536,27 @@ public class CoreConfigV2rayServiceTests
|
||||
directOutbound.Should().NotBeNull();
|
||||
directOutbound!.settings.domainStrategy.Should().Be("UseIPv4");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenerateClientConfigContent_TunRouteExcludeAddress()
|
||||
{
|
||||
var config = CoreConfigTestFactory.CreateConfigWithTunRouteExcludeAddress(ECoreType.Xray);
|
||||
CoreConfigTestFactory.BindAppManagerConfig(config);
|
||||
|
||||
var node = CoreConfigTestFactory.CreateVmessNode(ECoreType.Xray, "n-main", "main");
|
||||
var context = CoreConfigTestFactory.CreateContext(config, node, ECoreType.Xray);
|
||||
|
||||
var result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
|
||||
|
||||
result.Success.Should().BeTrue();
|
||||
|
||||
var cfg = JsonUtils.Deserialize<V2rayConfig>(result.Data!.ToString())!;
|
||||
var tunInbound = cfg.inbounds.FirstOrDefault(i => i.protocol == "tun");
|
||||
|
||||
tunInbound.Should().NotBeNull();
|
||||
|
||||
tunInbound!.settings.autoSystemRoutingTable.Should().NotContain("0.0.0.0/0");
|
||||
tunInbound!.settings.autoSystemRoutingTable.Should().Contain("10.0.0.0/32");
|
||||
tunInbound!.settings.autoSystemRoutingTable.Should().Contain("10.0.0.2/31");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,6 @@ public static class CountryExtension
|
||||
return null;
|
||||
}
|
||||
|
||||
return CountryEmojiMap.TryGetValue(countryCode, out var emoji) ? emoji : null;
|
||||
return CountryEmojiMap.GetValueOrDefault(countryCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public static class Extension
|
||||
|
||||
public static string TrimEx(this string? value)
|
||||
{
|
||||
return value == null ? string.Empty : value.Trim();
|
||||
return value?.Trim() ?? string.Empty;
|
||||
}
|
||||
|
||||
public static string RemovePrefix(this string value, char prefix)
|
||||
@@ -118,4 +118,20 @@ public static class Extension
|
||||
destination.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace all cross-platform newline characters with the specified string
|
||||
/// </summary>
|
||||
public static string ReplaceLineBreaks(this string input, string replacement)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
// You must replace \r\n first, and then replace the single characters \r and \n.
|
||||
return input.Replace("\r\n", replacement)
|
||||
.Replace("\r", replacement)
|
||||
.Replace("\n", replacement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ public static class FileUtils
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteExpiredFiles(string sourceDir, DateTime dtLine)
|
||||
public static void DeleteExpiredFiles(string sourceDir, DateTime dtLine, string? contains = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -214,6 +214,10 @@ public static class FileUtils
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (contains.IsNotEmpty() && !file.Name.Contains(contains))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class Utils
|
||||
|
||||
try
|
||||
{
|
||||
str = str.Replace(Environment.NewLine, string.Empty);
|
||||
str = str.ReplaceLineBreaks(string.Empty);
|
||||
return new List<string>(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -114,9 +114,7 @@ public class Utils
|
||||
}
|
||||
|
||||
plainText = plainText.Trim()
|
||||
.Replace(Environment.NewLine, "")
|
||||
.Replace("\n", "")
|
||||
.Replace("\r", "")
|
||||
.ReplaceLineBreaks("")
|
||||
.Replace('_', '/')
|
||||
.Replace('-', '+')
|
||||
.Replace(" ", "");
|
||||
@@ -321,7 +319,9 @@ public class Utils
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.Replace(",", ",").Replace(Environment.NewLine, ",");
|
||||
return text.Replace(",", ",")
|
||||
.Replace(" ", "")
|
||||
.ReplaceLineBreaks(",");
|
||||
}
|
||||
|
||||
public static List<string> GetEnumNames<TEnum>() where TEnum : Enum
|
||||
|
||||
@@ -90,6 +90,8 @@ public class Global
|
||||
public const string XrayLocalCert = "XRAY_LOCATION_CERT";
|
||||
public const int SpeedTestPageSize = 1000;
|
||||
public const string LinuxBash = "/bin/bash";
|
||||
public const string StringTrue = "true";
|
||||
public const string StringFalse = "false";
|
||||
|
||||
public const string SingboxDirectDNSTag = "direct_dns";
|
||||
public const string SingboxRemoteDNSTag = "remote_dns";
|
||||
@@ -330,12 +332,12 @@ public class Global
|
||||
|
||||
public static readonly Dictionary<string, string> KcpHeaderMaskMap = new()
|
||||
{
|
||||
{ "srtp", "header-srtp" },
|
||||
{ "utp", "header-utp" },
|
||||
{ "wechat-video", "header-wechat" },
|
||||
{ "dtls", "header-dtls" },
|
||||
{ "wireguard", "header-wireguard" },
|
||||
{ "dns", "header-dns" }
|
||||
{ "srtp", "srtp" },
|
||||
{ "utp", "utp" },
|
||||
{ "wechat-video", "wechat" },
|
||||
{ "dtls", "dtls" },
|
||||
{ "wireguard", "wireguard" },
|
||||
{ "dns", "dns" }
|
||||
};
|
||||
|
||||
public static readonly List<string> CoreTypes =
|
||||
@@ -421,13 +423,6 @@ public class Global
|
||||
"stream-one"
|
||||
];
|
||||
|
||||
public static readonly List<string> AllowInsecure =
|
||||
[
|
||||
"true",
|
||||
"false",
|
||||
""
|
||||
];
|
||||
|
||||
public static readonly List<string> DomainStrategy =
|
||||
[
|
||||
"AsIs",
|
||||
@@ -478,7 +473,7 @@ public class Global
|
||||
"zh-Hans",
|
||||
"zh-Hant",
|
||||
"en",
|
||||
"fa-Ir",
|
||||
"fa",
|
||||
"fr",
|
||||
"ru",
|
||||
"hu"
|
||||
|
||||
@@ -91,6 +91,20 @@ public class CoreConfigContextBuilder
|
||||
context.AllProxiesMap[$"remark:{ruleItem.OutboundTag}"] = actRuleNode;
|
||||
}
|
||||
}
|
||||
if (context.IsTunEnabled && context.AppConfig.TunModeItem.RouteExcludeAddress is { Count: > 0 })
|
||||
{
|
||||
foreach (var addr in context.AppConfig.TunModeItem.RouteExcludeAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPNetwork2.Parse(addr);
|
||||
}
|
||||
catch
|
||||
{
|
||||
validatorResult.Errors.Add(string.Format(ResUI.MsgTunRouteExcludeInvalidAddress, addr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CoreConfigContextBuilderResult(context, validatorResult);
|
||||
}
|
||||
|
||||
@@ -29,35 +29,6 @@ public class NodeValidator
|
||||
return v.ToResult();
|
||||
}
|
||||
|
||||
private class ValidationContext
|
||||
{
|
||||
public List<string> Errors { get; } = [];
|
||||
public List<string> Warnings { get; } = [];
|
||||
|
||||
public void Error(string message)
|
||||
{
|
||||
Errors.Add(message);
|
||||
}
|
||||
|
||||
public void Warning(string message)
|
||||
{
|
||||
Warnings.Add(message);
|
||||
}
|
||||
|
||||
public void Assert(bool condition, string errorMsg)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Error(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public NodeValidatorResult ToResult()
|
||||
{
|
||||
return new NodeValidatorResult(Errors, Warnings);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateNodeAndCoreSupport(ProfileItem item, ECoreType coreType, ValidationContext v)
|
||||
{
|
||||
if (item.ConfigType is EConfigType.Custom)
|
||||
@@ -126,6 +97,21 @@ public class NodeValidator
|
||||
break;
|
||||
}
|
||||
|
||||
if (coreType is ECoreType.Xray
|
||||
&& (protocolExtra.Flow ?? string.Empty).StartsWith("xtls", StringComparison.OrdinalIgnoreCase)
|
||||
&& item.MuxEnabled == true)
|
||||
{
|
||||
v.Warning(string.Format(ResUI.MsgOptionsConflict, "XTLS", "Mux.Cool"));
|
||||
}
|
||||
|
||||
if (item.GetNetwork() is nameof(ETransport.ws)
|
||||
&& item.EchConfigList.IsNullOrEmpty()
|
||||
&& item.GetAlpn()?.FirstOrDefault() == "h3")
|
||||
{
|
||||
v.Warning(
|
||||
"WebSocket but ALPN is set to h3, the core may ignore the ALPN setting or cause unexpected issues.");
|
||||
}
|
||||
|
||||
// TLS & Security
|
||||
if (item.StreamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
@@ -136,10 +122,23 @@ public class NodeValidator
|
||||
}
|
||||
|
||||
// Check for deprecated allowInsecure property when TLS is enabled
|
||||
if (item.AllowInsecure == "true")
|
||||
if (item.GetAllowInsecure()
|
||||
&& item.Cert.IsNullOrEmpty()
|
||||
&& item.CertSha.IsNullOrEmpty())
|
||||
{
|
||||
v.Warning(ResUI.MsgAllowInsecureDeprecated);
|
||||
}
|
||||
|
||||
if ((coreType == ECoreType.Xray
|
||||
&& item.GetAllowInsecure()
|
||||
&& item.Cert.IsNullOrEmpty()
|
||||
&& item.CertSha.IsNullOrEmpty())
|
||||
|| (coreType == ECoreType.sing_box
|
||||
&& item.GetAllowInsecure()
|
||||
&& item.Cert.IsNullOrEmpty()))
|
||||
{
|
||||
v.Warning("Insecure configuration detected: AllowInsecure is enabled but no certificate is provided. This may cause MITM attacks.");
|
||||
}
|
||||
}
|
||||
|
||||
if (item.StreamSecurity == Global.StreamSecurityReality)
|
||||
@@ -189,4 +188,33 @@ public class NodeValidator
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ValidationContext
|
||||
{
|
||||
public List<string> Errors { get; } = [];
|
||||
public List<string> Warnings { get; } = [];
|
||||
|
||||
public void Error(string message)
|
||||
{
|
||||
Errors.Add(message);
|
||||
}
|
||||
|
||||
public void Warning(string message)
|
||||
{
|
||||
Warnings.Add(message);
|
||||
}
|
||||
|
||||
public void Assert(bool condition, string errorMsg)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Error(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public NodeValidatorResult ToResult()
|
||||
{
|
||||
return new NodeValidatorResult(Errors, Warnings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,15 +39,14 @@ public static class ConfigHandler
|
||||
{
|
||||
LogEnabled = false,
|
||||
Loglevel = "warning",
|
||||
MuxEnabled = false,
|
||||
};
|
||||
|
||||
if (config.Inbound == null)
|
||||
{
|
||||
config.Inbound = new List<InItem>();
|
||||
config.Inbound = [];
|
||||
InItem inItem = new()
|
||||
{
|
||||
Protocol = EInboundProtocol.socks.ToString(),
|
||||
Protocol = nameof(EInboundProtocol.socks),
|
||||
LocalPort = 10808,
|
||||
UdpEnabled = true,
|
||||
SniffingEnabled = true,
|
||||
@@ -60,7 +59,7 @@ public static class ConfigHandler
|
||||
{
|
||||
if (config.Inbound.Count > 0)
|
||||
{
|
||||
config.Inbound.First().Protocol = EInboundProtocol.socks.ToString();
|
||||
config.Inbound.First().Protocol = nameof(EInboundProtocol.socks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +98,8 @@ public static class ConfigHandler
|
||||
config.MsgUIItem ??= new();
|
||||
|
||||
config.UiItem ??= new();
|
||||
config.UiItem.MainColumnItem ??= new();
|
||||
config.UiItem.WindowSizeItem ??= new();
|
||||
config.UiItem.MainColumnItem ??= [];
|
||||
config.UiItem.WindowSizeItem ??= [];
|
||||
|
||||
if (config.UiItem.CurrentLanguage.IsNullOrEmpty())
|
||||
{
|
||||
@@ -158,7 +157,7 @@ public static class ConfigHandler
|
||||
DownMbps = 100
|
||||
};
|
||||
config.ClashUIItem ??= new();
|
||||
config.ClashUIItem.ConnectionsColumnItem ??= new();
|
||||
config.ClashUIItem.ConnectionsColumnItem ??= [];
|
||||
config.SystemProxyItem ??= new();
|
||||
config.WebDavItem ??= new();
|
||||
config.CheckUpdateItem ??= new();
|
||||
@@ -168,7 +167,7 @@ public static class ConfigHandler
|
||||
Length = "50-100",
|
||||
Interval = "10-20"
|
||||
};
|
||||
config.GlobalHotkeys ??= new();
|
||||
config.GlobalHotkeys ??= [];
|
||||
|
||||
if (config.SystemProxyItem.SystemProxyExceptions.IsNullOrEmpty())
|
||||
{
|
||||
@@ -255,6 +254,7 @@ public static class ConfigHandler
|
||||
item.Cert = profileItem.Cert;
|
||||
item.CertSha = profileItem.CertSha;
|
||||
item.EchConfigList = profileItem.EchConfigList;
|
||||
item.VerifyPeerCertByName = profileItem.VerifyPeerCertByName;
|
||||
item.Finalmask = profileItem.Finalmask;
|
||||
item.ProtoExtra = profileItem.ProtoExtra;
|
||||
item.TransportExtra = profileItem.TransportExtra;
|
||||
@@ -874,7 +874,7 @@ public static class ConfigHandler
|
||||
profileItem.Fingerprint = string.Empty;
|
||||
profileItem.Alpn = string.Empty;
|
||||
profileItem.Network = string.Empty;
|
||||
profileItem.AllowInsecure = "false";
|
||||
profileItem.AllowInsecure = string.Empty;
|
||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||
@@ -1056,8 +1056,8 @@ public static class ConfigHandler
|
||||
return new Tuple<int, int>(0, 0);
|
||||
}
|
||||
|
||||
List<ProfileItem> lstKeep = new();
|
||||
List<ProfileItem> lstRemove = new();
|
||||
List<ProfileItem> lstKeep = [];
|
||||
List<ProfileItem> lstRemove = [];
|
||||
if (!config.GuiItem.KeepOlderDedupl)
|
||||
{
|
||||
lstProfile.Reverse();
|
||||
@@ -1106,10 +1106,6 @@ public static class ConfigHandler
|
||||
}
|
||||
else
|
||||
{
|
||||
if (profileItem.AllowInsecure.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.AllowInsecure = config.CoreBasicItem.DefAllowInsecure.ToString().ToLower();
|
||||
}
|
||||
if (profileItem.Fingerprint.IsNullOrEmpty() && profileItem.StreamSecurity == Global.StreamSecurityReality)
|
||||
{
|
||||
profileItem.Fingerprint = config.CoreBasicItem.DefFingerprint;
|
||||
@@ -1537,7 +1533,7 @@ public static class ConfigHandler
|
||||
}
|
||||
|
||||
var countServers = 0;
|
||||
List<ProfileItem> lstAdd = new();
|
||||
List<ProfileItem> lstAdd = [];
|
||||
var arrData = strData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty());
|
||||
if (isSub)
|
||||
{
|
||||
@@ -1633,7 +1629,7 @@ public static class ConfigHandler
|
||||
{
|
||||
lstProfiles = V2rayFmt.ResolveFullArray(strData, subRemarks);
|
||||
}
|
||||
if (lstProfiles != null && lstProfiles.Count > 0)
|
||||
if (lstProfiles is { Count: > 0 })
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var it in lstProfiles)
|
||||
|
||||
@@ -80,14 +80,14 @@ public static class ConnectionHandler
|
||||
UseProxy = webProxy != null
|
||||
});
|
||||
|
||||
List<int> oneTime = new();
|
||||
List<int> oneTime = [];
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
await client.GetAsync(url, cts.Token).ConfigureAwait(false);
|
||||
timer.Stop();
|
||||
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
|
||||
await Task.Delay(100);
|
||||
await Task.Delay(100, cts.Token);
|
||||
}
|
||||
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ public class BaseFmt
|
||||
{
|
||||
dicQuery.Add("ech", Utils.UrlEncode(item.EchConfigList));
|
||||
}
|
||||
if (item.VerifyPeerCertByName.IsNotEmpty())
|
||||
{
|
||||
dicQuery.Add("vcn", Utils.UrlEncode(item.VerifyPeerCertByName));
|
||||
}
|
||||
if (item.CertSha.IsNotEmpty())
|
||||
{
|
||||
dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha));
|
||||
@@ -199,7 +203,7 @@ public class BaseFmt
|
||||
|
||||
private static int ToUriQueryAllowInsecure(ProfileItem item, ref Dictionary<string, string> dicQuery)
|
||||
{
|
||||
if (item.AllowInsecure.Equals(Global.AllowInsecure.First()))
|
||||
if (item.GetAllowInsecure())
|
||||
{
|
||||
// Add two for compatibility
|
||||
dicQuery.Add("insecure", "1");
|
||||
@@ -227,6 +231,7 @@ public class BaseFmt
|
||||
item.SpiderX = GetQueryDecoded(query, "spx");
|
||||
item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
|
||||
item.EchConfigList = GetQueryDecoded(query, "ech");
|
||||
item.VerifyPeerCertByName = GetQueryDecoded(query, "vcn");
|
||||
item.CertSha = GetQueryDecoded(query, "pcs");
|
||||
|
||||
var finalmaskDecoded = GetQueryDecoded(query, "fm");
|
||||
@@ -249,11 +254,11 @@ public class BaseFmt
|
||||
|
||||
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
||||
{
|
||||
item.AllowInsecure = Global.AllowInsecure.First();
|
||||
item.AllowInsecure = Global.StringTrue;
|
||||
}
|
||||
else if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "0"))
|
||||
{
|
||||
item.AllowInsecure = Global.AllowInsecure.Skip(1).First();
|
||||
item.AllowInsecure = Global.StringFalse;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -185,7 +185,7 @@ public class InnerFmt
|
||||
return null;
|
||||
}
|
||||
// Check Enum.IsDefined
|
||||
if (!Enum.IsDefined(typeof(EConfigType), profileItem.ConfigType))
|
||||
if (!Enum.IsDefined(profileItem.ConfigType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class SocksFmt : BaseFmt
|
||||
return null;
|
||||
}
|
||||
var arr21 = arr1.First().Split(':');
|
||||
var indexPort = arr1.Last().LastIndexOf(":");
|
||||
var indexPort = arr1.Last().LastIndexOf(':');
|
||||
if (arr21.Length != 2 || indexPort < 0)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -66,7 +66,9 @@ public class VmessFmt : BaseFmt
|
||||
sni = item.Sni,
|
||||
alpn = item.Alpn,
|
||||
fp = item.Fingerprint,
|
||||
insecure = item.AllowInsecure.Equals(Global.AllowInsecure.First()) ? "1" : "0"
|
||||
insecure = item.GetAllowInsecure() ? "1" : "0",
|
||||
vcn = item.VerifyPeerCertByName,
|
||||
pcs = item.CertSha
|
||||
};
|
||||
|
||||
var url = JsonUtils.Serialize(vmessQRCode);
|
||||
@@ -140,7 +142,9 @@ public class VmessFmt : BaseFmt
|
||||
item.Sni = Utils.ToString(vmessQRCode.sni);
|
||||
item.Alpn = Utils.ToString(vmessQRCode.alpn);
|
||||
item.Fingerprint = Utils.ToString(vmessQRCode.fp);
|
||||
item.AllowInsecure = vmessQRCode.insecure == "1" ? Global.AllowInsecure.First() : string.Empty;
|
||||
item.AllowInsecure = vmessQRCode.insecure == "1" ? Global.StringTrue : string.Empty;
|
||||
item.VerifyPeerCertByName = Utils.ToString(vmessQRCode.vcn);
|
||||
item.CertSha = Utils.ToString(vmessQRCode.pcs);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -133,12 +133,12 @@ public static class SubscriptionHandler
|
||||
|
||||
if (!url.Contains("target="))
|
||||
{
|
||||
url += string.Format("&target={0}", item.ConvertTarget);
|
||||
url += $"&target={item.ConvertTarget}";
|
||||
}
|
||||
|
||||
if (!url.Contains("config="))
|
||||
{
|
||||
url += string.Format("&config={0}", Global.SubConvertConfig.FirstOrDefault());
|
||||
url += $"&config={Global.SubConvertConfig.FirstOrDefault()}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ public sealed class AppManager
|
||||
.ToListAsync();
|
||||
var itemMap = items.ToDictionary(it => it.IndexId);
|
||||
|
||||
return idList.Select(id => itemMap.GetValueOrDefault(id))
|
||||
return idList.Select(itemMap.GetValueOrDefault)
|
||||
.Where(item => item != null)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@@ -4,16 +4,15 @@ using System.Security.Cryptography.X509Certificates;
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
/// <summary>
|
||||
/// Manager for certificate operations with CA pinning to prevent MITM attacks
|
||||
/// Manager for certificate operations with CA pinning to prevent MITM attacks
|
||||
/// </summary>
|
||||
public class CertPemManager
|
||||
{
|
||||
private static readonly string _tag = "CertPemManager";
|
||||
private static readonly Lazy<CertPemManager> _instance = new(() => new());
|
||||
public static CertPemManager Instance => _instance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Trusted CA certificate thumbprints (SHA256) to prevent MITM attacks
|
||||
/// Trusted CA certificate thumbprints (SHA256) to prevent MITM attacks
|
||||
/// </summary>
|
||||
private static readonly HashSet<string> TrustedCaThumbprints = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
@@ -24,21 +23,17 @@ public class CertPemManager
|
||||
"D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", // Comodo AAA Services root
|
||||
"85A0DD7DD720ADB7FF05F83D542B209DC7FF4528F7D677B18389FEA5E5C49E86", // QuoVadis Root CA 2
|
||||
"18F1FC7F205DF8ADDDEB7FE007DD57E3AF375A9C4D8D73546BF4F1FED1E18D35", // QuoVadis Root CA 3
|
||||
"CECDDC905099D8DADFC5B1D209B737CBE2C18CFB2C10C0FF0BCF0D3286FC1AA2", // XRamp Global CA Root
|
||||
"C3846BF24B9E93CA64274C0EC67C1ECC5E024FFCACD2D74019350E81FE546AE4", // Go Daddy Class 2 CA
|
||||
"1465FA205397B876FAA6F0A9958E5590E40FCC7FAA4FB7C2C8677521FB5FB658", // Starfield Class 2 CA
|
||||
"3E9099B5015E8F486C00BCEA9D111EE721FABA355A89BCF1DF69561E3DC6325C", // DigiCert Assured ID Root CA
|
||||
"4348A0E9444C78CB265E058D5E8944B4D84F9662BD26DB257F8934A443C70161", // DigiCert Global Root CA
|
||||
"7431E5F4C3C1CE4690774F0B61E05440883BA9A01ED00BA6ABD7806ED3B118CF", // DigiCert High Assurance EV Root CA
|
||||
"62DD0BE9B9F50A163EA0F8E75C053B1ECA57EA55C8688F647C6881F2C8357B95", // SwissSign Gold CA - G2
|
||||
"F1C1B50AE5A20DD8030EC9F6BC24823DD367B5255759B4E71B61FCE9F7375D73", // SecureTrust CA
|
||||
"4200F5043AC8590EBB527D209ED1503029FBCBD41CA1B506EC27F15ADE7DAC69", // Secure Global CA
|
||||
"0C2CD63DF7806FA399EDE809116B575BF87989F06518F9808C860503178BAF66", // COMODO Certification Authority
|
||||
"1793927A0614549789ADCE2F8F34F7F0B66D0F3AE3A3B84D21EC15DBBA4FADC7", // COMODO ECC Certification Authority
|
||||
"41C923866AB4CAD6B7AD578081582E020797A6CBDF4FFF78CE8396B38937D7F5", // OISTE WISeKey Global Root GA CA
|
||||
"E3B6A2DB2ED7CE48842F7AC53241C7B71D54144BFB40C11F3F1D0B42F5EEA12D", // Certigna
|
||||
"C0A6F4DC63A24BFDCF54EF2A6A082A0A72DE35803E2FF5FF527AE5D87206DFD5", // ePKI Root Certification Authority
|
||||
"EAA962C4FA4A6BAFEBE415196D351CCD888D4F53F3FA8AE6D7C466A94E6042BB", // certSIGN ROOT CA
|
||||
"6C61DAC3A2DEF031506BE036D2A6FE401994FBD13DF9C8D466599274C446EC98", // NetLock Arany (Class Gold) Főtanúsítvány
|
||||
"3C5F81FEA5FAB82C64BFA2EAECAFCDE8E077FC8620A7CAE537163DF36EDBF378", // Microsec e-Szigno Root CA 2009
|
||||
"CBB522D7B7F127AD6A0113865BDF1CD4102E7D0759AF635A7CF4720DC963C53B", // GlobalSign Root CA - R3
|
||||
@@ -46,10 +41,6 @@ public class CertPemManager
|
||||
"45140B3247EB9CC8C5B4F0D7B53091F73292089E6E5A63E2749DD3ACA9198EDA", // Go Daddy Root Certificate Authority - G2
|
||||
"2CE1CB0BF9D2F9E102993FBE215152C3B2DD0CABDE1C68E5319B839154DBB7F5", // Starfield Root Certificate Authority - G2
|
||||
"568D6905A2C88708A4B3025190EDCFEDB1974A606A13C6E5290FCB2AE63EDAB5", // Starfield Services Root Certificate Authority - G2
|
||||
"0376AB1D54C5F9803CE4B2E201A0EE7EEF7B57B636E8A93C9B8D4860C96F5FA7", // AffirmTrust Commercial
|
||||
"0A81EC5A929777F145904AF38D5D509F66B5E2C58FCDB531058B0E17F3F0B41B", // AffirmTrust Networking
|
||||
"70A73F7F376B60074248904534B11482D5BF0E698ECC498DF52577EBF2E93B9A", // AffirmTrust Premium
|
||||
"BD71FDF6DA97E4CF62D1647ADD2581B07D79ADF8397EB4ECBA9C5E8488821423", // AffirmTrust Premium ECC
|
||||
"5C58468D55F58E497E743982D2B50010B6D165374ACF83A7D4A32DB768C4408E", // Certum Trusted Network CA
|
||||
"BFD88FE1101C41AE3E801BF8BE56350EE9BAD1A6B9BD515EDC5C6D5B8711AC44", // TWCA Root Certification Authority
|
||||
"513B2CECB810D4CDE5DD85391ADFC6C2DD60D87BB736D2B521484AA47A0EBEF6", // Security Communication RootCA2
|
||||
@@ -62,7 +53,6 @@ public class CertPemManager
|
||||
"E23D4A036D7B70E9F595B1422079D2B91EDFBB1FB651A0633EAA8A9DC5F80703", // CA Disig Root R2
|
||||
"9A6EC012E1A7DA9DBE34194D478AD7C0DB1822FB071DF12981496ED104384113", // ACCVRAIZ1
|
||||
"59769007F7685D0FCD50872F9F95D5755A5B2B457D81F3692B610A98672F0E1B", // TWCA Global Root CA
|
||||
"DD6936FE21F8F077C123A1A521C12224F72255B73E03A7260693E8A24B0FA389", // TeliaSonera Root CA v1
|
||||
"91E2F5788D5810EBA7BA58737DE1548A8ECACD014598BC0B143E041B17052552", // T-TeleSec GlobalRoot Class 2
|
||||
"F356BEA244B7A91EB35D53CA9AD7864ACE018E2D35D5F8F96DDF68A6F41AA474", // Atos TrustedRoot 2011
|
||||
"8A866FD1B276B57E578E921C65828A2BED58E9F2F288054134B7F1F4BFC9CC74", // QuoVadis Root CA 1 G3
|
||||
@@ -116,16 +106,12 @@ public class CertPemManager
|
||||
"C741F70F4B2A8D88BF2E71C14122EF53EF10EBA0CFA5E64CFA20F418853073E0", // Microsoft RSA Root Certificate Authority 2017
|
||||
"BEB00B30839B9BC32C32E4447905950641F26421B15ED089198B518AE2EA1B99", // e-Szigno Root CA 2017
|
||||
"657CFE2FA73FAA38462571F332A2363A46FCE7020951710702CDFBB6EEDA3305", // certSIGN Root CA G2
|
||||
"97552015F5DDFC3C8788C006944555408894450084F100867086BC1A2BB58DC8", // Trustwave Global Certification Authority
|
||||
"945BBC825EA554F489D1FD51A73DDF2EA624AC7019A05205225C22A78CCFA8B4", // Trustwave Global ECC P256 Certification Authority
|
||||
"55903859C8C0C3EBB8759ECE4E2557225FF5758BBD38EBD48276601E1BD58097", // Trustwave Global ECC P384 Certification Authority
|
||||
"88F438DCF8FFD1FA8F429115FFE5F82AE1E06E0C70C375FAAD717B34A49E7265", // NAVER Global Root Certification Authority
|
||||
"554153B13D2CF9DDB753BFBE1A4E0AE08D0AA4187058FE60A2B862B2E4B87BCB", // AC RAIZ FNMT-RCM SERVIDORES SEGUROS
|
||||
"319AF0A7729E6F89269C131EA6A3A16FCD86389FDCAB3C47A4A675C161A3F974", // GlobalSign Secure Mail Root R45
|
||||
"5CBF6FB81FD417EA4128CD6F8172A3C9402094F74AB2ED3A06B4405D04F30B19", // GlobalSign Secure Mail Root E45
|
||||
"4FA3126D8D3A11D1C4855A4F807CBAD6CF919D3A5A88B03BEA2C6372D93C40C9", // GlobalSign Root R46
|
||||
"CBB9C44D84B8043E1050EA31A69F514955D7BFD2E2C6B49301019AD61D9F5058", // GlobalSign Root E46
|
||||
"9A296A5182D1D451A2E37F439B74DAAFA267523329F90F9A0D2007C334E23C9A", // GLOBALTRUST 2020
|
||||
"FB8FEC759169B9106B1E511644C618C51304373F6C0643088D8BEFFD1B997599", // ANF Secure Server Root CA
|
||||
"6B328085625318AA50D173C98D8BDA09D57E27413D114CF787A0F5D06C030CF6", // Certum EC-384 CA
|
||||
"FE7696573855773E37A95E7AD4D9CC96C30157C15D31765BA9B15704E1AE78FD", // Certum Trusted Root CA
|
||||
@@ -179,7 +165,6 @@ public class CertPemManager
|
||||
"578AF4DED0853F4E5998DB4AEAF9CBEA8D945F60B620A38D1A3C13B2BC7BA8E1", // Telekom Security TLS ECC Root 2020
|
||||
"78A656344F947E9CC0F734D9053D32F6742086B6B9CD2CAE4FAE1A2E4EFDE048", // Telekom Security SMIME RSA Root 2023
|
||||
"EFC65CADBB59ADB6EFE84DA22311B35624B71B3B1EA0DA8B6655174EC8978646", // Telekom Security TLS RSA Root 2023
|
||||
"BEF256DAF26E9C69BDEC1602359798F3CAF71821A03E018257C53C65617F3D4A", // FIRMAPROFESIONAL CA ROOT-A WEB
|
||||
"3F63BB2814BE174EC8B6439CF08D6D56F0B7C405883A5648A334424D6B3EC558", // TWCA CYBER Root CA
|
||||
"3A0072D49FFC04E996C59AEB75991D3C340F3615D6FD4DCE90AC0B3D88EAD4F4", // TWCA Global Root CA G2
|
||||
"3F034BB5704D44B2D08545A02057DE93EBF3905FCE721ACBC730C06DDAEE904E", // SecureSign Root CA12
|
||||
@@ -200,10 +185,13 @@ public class CertPemManager
|
||||
"B49141502D00663D740F2E7EC340C52800962666121A36D09CF7DD2B90384FB4", // e-Szigno TLS Root CA 2023
|
||||
};
|
||||
|
||||
public static CertPemManager Instance => _instance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Get certificate in PEM format from a server with CA pinning validation
|
||||
/// Get certificate in PEM format from a server with CA pinning validation
|
||||
/// </summary>
|
||||
public async Task<(string?, string?)> GetCertPemAsync(string target, string serverName, int timeout = 4, bool allowInsecure = false)
|
||||
public async Task<(string?, string?)> GetCertPemAsync(string target, string serverName, int timeout = 4,
|
||||
List<string>? verifyPeerCertByName = null, bool allowInsecure = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -216,13 +204,14 @@ public class CertPemManager
|
||||
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
||||
|
||||
var callback = new RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) =>
|
||||
ValidateServerCertificate(sender, certificate, chain, sslPolicyErrors, allowInsecure));
|
||||
ValidateServerCertificate(sender, certificate, chain, sslPolicyErrors, verifyPeerCertByName ?? [],
|
||||
allowInsecure));
|
||||
await using var ssl = new SslStream(client.GetStream(), false, callback);
|
||||
|
||||
var sslOptions = new SslClientAuthenticationOptions
|
||||
{
|
||||
TargetHost = serverName,
|
||||
RemoteCertificateValidationCallback = callback
|
||||
RemoteCertificateValidationCallback = callback,
|
||||
};
|
||||
|
||||
await ssl.AuthenticateAsClientAsync(sslOptions, cts.Token);
|
||||
@@ -249,9 +238,10 @@ public class CertPemManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get certificate chain in PEM format from a server with CA pinning validation
|
||||
/// Get certificate chain in PEM format from a server with CA pinning validation
|
||||
/// </summary>
|
||||
public async Task<(List<string>, string?)> GetCertChainPemAsync(string target, string serverName, int timeout = 4, bool allowInsecure = false)
|
||||
public async Task<(List<string>, string?)> GetCertChainPemAsync(string target, string serverName, int timeout = 4,
|
||||
List<string>? verifyPeerCertByName = null, bool allowInsecure = false)
|
||||
{
|
||||
var pemList = new List<string>();
|
||||
try
|
||||
@@ -265,13 +255,14 @@ public class CertPemManager
|
||||
await client.ConnectAsync(domain, port > 0 ? port : 443, cts.Token);
|
||||
|
||||
var callback = new RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) =>
|
||||
ValidateServerCertificate(sender, certificate, chain, sslPolicyErrors, allowInsecure));
|
||||
ValidateServerCertificate(sender, certificate, chain, sslPolicyErrors, verifyPeerCertByName ?? [],
|
||||
allowInsecure));
|
||||
await using var ssl = new SslStream(client.GetStream(), false, callback);
|
||||
|
||||
var sslOptions = new SslClientAuthenticationOptions
|
||||
{
|
||||
TargetHost = serverName,
|
||||
RemoteCertificateValidationCallback = callback
|
||||
RemoteCertificateValidationCallback = callback,
|
||||
};
|
||||
|
||||
await ssl.AuthenticateAsClientAsync(sslOptions, cts.Token);
|
||||
@@ -301,13 +292,14 @@ public class CertPemManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate server certificate with CA pinning
|
||||
/// Validate server certificate with CA pinning
|
||||
/// </summary>
|
||||
private bool ValidateServerCertificate(
|
||||
private static bool ValidateServerCertificate(
|
||||
object _,
|
||||
X509Certificate? certificate,
|
||||
X509Chain? chain,
|
||||
SslPolicyErrors sslPolicyErrors,
|
||||
List<string> verifyPeerCertByName,
|
||||
bool allowInsecure)
|
||||
{
|
||||
if (certificate == null)
|
||||
@@ -321,22 +313,21 @@ public class CertPemManager
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check certificate name mismatch
|
||||
if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build certificate chain
|
||||
var cert2 = certificate as X509Certificate2 ?? new X509Certificate2(certificate);
|
||||
var certChain = chain ?? new X509Chain();
|
||||
|
||||
certChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
|
||||
certChain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
|
||||
certChain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
|
||||
certChain.ChainPolicy.VerificationTime = DateTime.Now;
|
||||
if (chain == null)
|
||||
{
|
||||
certChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
|
||||
certChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
|
||||
certChain.ChainPolicy.VerificationTime = DateTime.UtcNow;
|
||||
|
||||
certChain.Build(cert2);
|
||||
if (!certChain.Build(cert2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Find root CA
|
||||
if (certChain.ChainElements.Count == 0)
|
||||
@@ -344,10 +335,37 @@ public class CertPemManager
|
||||
return false;
|
||||
}
|
||||
|
||||
var rootCert = certChain.ChainElements[certChain.ChainElements.Count - 1].Certificate;
|
||||
var rootCert = certChain.ChainElements[^1].Certificate;
|
||||
var rootThumbprint = rootCert.GetCertHashString(HashAlgorithmName.SHA256);
|
||||
|
||||
return TrustedCaThumbprints.Contains(rootThumbprint);
|
||||
if (!TrustedCaThumbprints.Contains(rootThumbprint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sslPolicyErrors.HasFlag(
|
||||
SslPolicyErrors.RemoteCertificateNameMismatch))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (verifyPeerCertByName.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var ext in cert2.Extensions)
|
||||
{
|
||||
if (ext is not X509SubjectAlternativeNameExtension san)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return san.EnumerateDnsNames().Any(dnsName =>
|
||||
verifyPeerCertByName.Contains(dnsName, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string ExportCertToPem(X509Certificate2 cert)
|
||||
@@ -358,8 +376,8 @@ public class CertPemManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse concatenated PEM certificates string into a list of individual certificates
|
||||
/// Normalizes format: removes line breaks from base64 content for better compatibility
|
||||
/// Parse concatenated PEM certificates string into a list of individual certificates
|
||||
/// Normalizes format: removes line breaks from base64 content for better compatibility
|
||||
/// </summary>
|
||||
/// <param name="pemChain">Concatenated PEM certificates string (supports both \r\n and \n line endings)</param>
|
||||
/// <returns>List of individual PEM certificate strings with normalized format</returns>
|
||||
@@ -411,18 +429,13 @@ public class CertPemManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenate a list of PEM certificates into a single string
|
||||
/// Concatenate a list of PEM certificates into a single string
|
||||
/// </summary>
|
||||
/// <param name="pemList">List of individual PEM certificate strings</param>
|
||||
/// <returns>Concatenated PEM certificates string</returns>
|
||||
public static string ConcatenatePemChain(IEnumerable<string> pemList)
|
||||
public static string ConcatenatePemChain(IEnumerable<string>? pemList)
|
||||
{
|
||||
if (pemList == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return string.Concat(pemList);
|
||||
return pemList == null ? string.Empty : string.Concat(pemList);
|
||||
}
|
||||
|
||||
public static string GetCertSha256Thumbprint(string pemCert, bool includeColon = false)
|
||||
@@ -431,11 +444,7 @@ public class CertPemManager
|
||||
{
|
||||
var cert = X509Certificate2.CreateFromPem(pemCert);
|
||||
var thumbprint = cert.GetCertHashString(HashAlgorithmName.SHA256);
|
||||
if (includeColon)
|
||||
{
|
||||
return string.Join(":", thumbprint.Chunk(2).Select(c => new string(c)));
|
||||
}
|
||||
return thumbprint;
|
||||
return includeColon ? string.Join(":", thumbprint.Chunk(2).Select(c => new string(c))) : thumbprint;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -45,19 +45,14 @@ public sealed class ClashApiManager
|
||||
{
|
||||
await GetClashProxiesAsync();
|
||||
}
|
||||
lstProxy = new List<ClashProxyModel>();
|
||||
foreach (var kv in _proxies ?? [])
|
||||
{
|
||||
if (Global.notAllowTestType.Contains(kv.Value.type?.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
lstProxy.Add(new ClashProxyModel()
|
||||
{
|
||||
Name = kv.Value.name,
|
||||
Type = kv.Value.type?.ToLower(),
|
||||
});
|
||||
}
|
||||
lstProxy = [];
|
||||
lstProxy.AddRange(from kv in _proxies ?? []
|
||||
where !Global.notAllowTestType.Contains(kv.Value.type?.ToLower())
|
||||
select new ClashProxyModel()
|
||||
{
|
||||
Name = kv.Value.name,
|
||||
Type = kv.Value.type?.ToLower(),
|
||||
});
|
||||
}
|
||||
|
||||
if (lstProxy is not { Count: > 0 })
|
||||
|
||||
@@ -9,7 +9,7 @@ public class GroupProfileManager
|
||||
|
||||
public static async Task<bool> HasCycle(string? indexId, ProtocolExtraItem? extraInfo)
|
||||
{
|
||||
return await HasCycle(indexId, extraInfo, new HashSet<string>(), new HashSet<string>());
|
||||
return await HasCycle(indexId, extraInfo, [], []);
|
||||
}
|
||||
|
||||
private static async Task<bool> HasCycle(string? indexId, ProtocolExtraItem? extraInfo, HashSet<string> visited, HashSet<string> stack)
|
||||
@@ -55,7 +55,7 @@ public class GroupProfileManager
|
||||
var childItems = await AppManager.Instance.GetProfileItemsByIndexIds(childIds);
|
||||
foreach (var childItem in childItems)
|
||||
{
|
||||
if (await HasCycle(childItem.IndexId, childItem?.GetProtocolExtra(), visited, stack))
|
||||
if (await HasCycle(childItem.IndexId, childItem.GetProtocolExtra(), visited, stack))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class GroupProfileManager
|
||||
|
||||
public static async Task<(List<ProfileItem> Items, ProtocolExtraItem? Extra)> GetChildProfileItems(ProfileItem profileItem)
|
||||
{
|
||||
var protocolExtra = profileItem?.GetProtocolExtra();
|
||||
var protocolExtra = profileItem.GetProtocolExtra();
|
||||
return (await GetChildProfileItemsByProtocolExtra(protocolExtra), protocolExtra);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ public class ProfileExManager
|
||||
public async Task ClearAll()
|
||||
{
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem ");
|
||||
_lstProfileEx = new();
|
||||
_lstProfileEx = [];
|
||||
}
|
||||
|
||||
public async Task SaveTo()
|
||||
@@ -182,6 +182,6 @@ public class ProfileExManager
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
|
||||
return _lstProfileEx.Max(t => t?.Sort ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class StatisticsManager
|
||||
{
|
||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem ");
|
||||
_serverStatItem = null;
|
||||
_lstServerStat = new();
|
||||
_lstServerStat = [];
|
||||
}
|
||||
|
||||
public async Task SaveTo()
|
||||
|
||||
@@ -56,7 +56,7 @@ public class TaskManager
|
||||
{
|
||||
//Logging.SaveLog("Execute delete expired files");
|
||||
|
||||
FileUtils.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
|
||||
FileUtils.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1), "Test");
|
||||
FileUtils.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
|
||||
FileUtils.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@ public class CoreBasicItem
|
||||
|
||||
public string Loglevel { get; set; }
|
||||
|
||||
public bool MuxEnabled { get; set; }
|
||||
|
||||
public bool DefAllowInsecure { get; set; }
|
||||
|
||||
public string DefFingerprint { get; set; }
|
||||
|
||||
public string DefUserAgent { get; set; }
|
||||
@@ -21,6 +17,8 @@ public class CoreBasicItem
|
||||
|
||||
public bool EnableFragment { get; set; }
|
||||
|
||||
public bool EnableFinalFragment { get; set; }
|
||||
|
||||
public bool EnableCacheFile4Sbox { get; set; } = true;
|
||||
}
|
||||
|
||||
@@ -148,6 +146,7 @@ public class TunModeItem
|
||||
public bool EnableIPv6Address { get; set; }
|
||||
public string IcmpRouting { get; set; }
|
||||
public bool EnableLegacyProtect { get; set; }
|
||||
public List<string>? RouteExcludeAddress { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -93,6 +93,7 @@ public class Rule4Sbox
|
||||
public List<string>? wifi_bssid { get; set; }
|
||||
public bool? rule_set_ip_cidr_match_source { get; set; }
|
||||
public bool? rule_set_ip_cidr_accept_empty { get; set; }
|
||||
public bool? tls_record_fragment { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -110,6 +111,7 @@ public class Inbound4Sbox
|
||||
public bool? endpoint_independent_nat { get; set; }
|
||||
public string? stack { get; set; }
|
||||
public List<User4Sbox> users { get; set; }
|
||||
public List<string>? route_exclude_address { get; set; }
|
||||
}
|
||||
|
||||
public class User4Sbox
|
||||
|
||||
@@ -20,7 +20,7 @@ public class Stats4Ray
|
||||
|
||||
public class Metrics4Ray
|
||||
{
|
||||
public string tag { get; set; }
|
||||
public string listen { get; set; }
|
||||
}
|
||||
|
||||
public class Policy4Ray
|
||||
@@ -86,7 +86,7 @@ public class Inboundsettings4Ray
|
||||
|
||||
public string? autoOutboundsInterface { get; set; }
|
||||
|
||||
// public List<string>? dns { get; set; }
|
||||
public List<string>? dns { get; set; }
|
||||
}
|
||||
|
||||
public class UsersItem4Ray
|
||||
@@ -136,7 +136,7 @@ public class Outboundsettings4Ray
|
||||
|
||||
public Response4Ray? response { get; set; }
|
||||
|
||||
public string domainStrategy { get; set; }
|
||||
public string? domainStrategy { get; set; }
|
||||
|
||||
public int? userLevel { get; set; }
|
||||
|
||||
@@ -373,6 +373,7 @@ public class TlsSettings4Ray
|
||||
public string? spiderX { get; set; }
|
||||
public string? mldsa65Verify { get; set; }
|
||||
public List<CertificateSettings4Ray>? certificates { get; set; }
|
||||
public string? verifyPeerCertByName { get; set; }
|
||||
public string? pinnedPeerCertSha256 { get; set; }
|
||||
public bool? disableSystemRoot { get; set; }
|
||||
public string? echConfigList { get; set; }
|
||||
@@ -488,6 +489,7 @@ public class UdpHop4Ray
|
||||
|
||||
public class Finalmask4Ray
|
||||
{
|
||||
public List<Mask4Ray>? tcp { get; set; }
|
||||
public List<Mask4Ray>? udp { get; set; }
|
||||
public QuicParams4Ray? quicParams { get; set; }
|
||||
}
|
||||
@@ -500,8 +502,10 @@ public class Mask4Ray
|
||||
|
||||
public class MaskSettings4Ray
|
||||
{
|
||||
public string? header { get; set; }
|
||||
public string? value { get; set; }
|
||||
|
||||
public string? password { get; set; }
|
||||
public string? domain { get; set; }
|
||||
|
||||
// fragment
|
||||
public string? packets { get; set; }
|
||||
|
||||
@@ -22,7 +22,7 @@ public readonly record struct IpInfoResult(string Country, string? Ip)
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
var emoji = Country.CountryToEmoji();
|
||||
var emoji = Utils.IsWindows() ? null : Country.CountryToEmoji();
|
||||
return $"{emoji}({Country}) {Ip}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
public class RetResult
|
||||
public class RetResult(bool success, string? msg, object? data)
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Msg { get; set; }
|
||||
public object? Data { get; set; }
|
||||
|
||||
public RetResult(bool success = false)
|
||||
public RetResult(bool success = false) : this(success, null, null)
|
||||
{
|
||||
Success = success;
|
||||
}
|
||||
|
||||
public RetResult(bool success, string? msg)
|
||||
public RetResult(bool success, string? msg) : this(success, msg, null)
|
||||
{
|
||||
Success = success;
|
||||
Msg = msg;
|
||||
}
|
||||
|
||||
public RetResult(bool success, string? msg, object? data)
|
||||
{
|
||||
Success = success;
|
||||
Msg = msg;
|
||||
Data = data;
|
||||
}
|
||||
public bool Success { get; set; } = success;
|
||||
public string? Msg { get; set; } = msg;
|
||||
public object? Data { get; set; } = data;
|
||||
}
|
||||
|
||||
@@ -40,4 +40,8 @@ public class VmessQRCode
|
||||
public string fp { get; set; } = string.Empty;
|
||||
|
||||
public string insecure { get; set; } = string.Empty;
|
||||
|
||||
public string vcn { get; set; } = string.Empty;
|
||||
|
||||
public string pcs { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -146,6 +146,11 @@ public class ProfileItem
|
||||
TransportExtra = JsonUtils.Serialize(transportExtra, false);
|
||||
}
|
||||
|
||||
public bool GetAllowInsecure()
|
||||
{
|
||||
return AllowInsecure == Global.StringTrue;
|
||||
}
|
||||
|
||||
#endregion function
|
||||
|
||||
[PrimaryKey]
|
||||
@@ -191,6 +196,7 @@ public class ProfileItem
|
||||
public string Cert { get; set; }
|
||||
public string CertSha { get; set; }
|
||||
public string EchConfigList { get; set; }
|
||||
public string VerifyPeerCertByName { get; set; }
|
||||
public string Finalmask { get; set; }
|
||||
|
||||
public string ProtoExtra { get; set; }
|
||||
|
||||
63
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
63
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -2112,6 +2112,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Conflict between {0} and {1} 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string MsgOptionsConflict {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgOptionsConflict", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Resolved {0} successfully 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2229,6 +2238,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Invalid address in TUN route exclude list: {0} 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string MsgTunRouteExcludeInvalidAddress {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgTunRouteExcludeInvalidAddress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Unpacking... 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -3015,6 +3033,24 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable Final Fragment 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbEnableFinalFragment {
|
||||
get {
|
||||
return ResourceManager.GetString("TbEnableFinalFragment", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Split the tail of packets into smaller fragments. This may affect throughput and latency. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbEnableFinalFragmentTip {
|
||||
get {
|
||||
return ResourceManager.GetString("TbEnableFinalFragmentTip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable Tun 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -3537,6 +3573,24 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Route Exclude Address 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRouteExcludeAddress {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRouteExcludeAddress", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Use commas (,) to separate. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRouteExcludeAddressTip {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRouteExcludeAddressTip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 tun: TUN inbound, socks: local port, socks2: second local port, socks3: LAN port 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -4707,6 +4761,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Verify Peer Cert By Name 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbVerifyPeerCertByName {
|
||||
get {
|
||||
return ResourceManager.GetString("TbVerifyPeerCertByName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The delay: {0} ms, {1} 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -1746,4 +1746,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="menuNewUpdate" xml:space="preserve">
|
||||
<value>New Update</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
@@ -1581,6 +1581,9 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="TbEchConfigList" xml:space="preserve">
|
||||
<value>EchConfigList</value>
|
||||
</data>
|
||||
<data name="TbVerifyPeerCertByName" xml:space="preserve">
|
||||
<value>Verify Peer Cert By Name</value>
|
||||
</data>
|
||||
<data name="TbFullCertTips" xml:space="preserve">
|
||||
<value>Full certificate (chain), PEM format</value>
|
||||
</data>
|
||||
@@ -1749,4 +1752,22 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="MsgAllowInsecureDeprecated" xml:space="preserve">
|
||||
<value>Warning: Xray will disable allowInsecure (skip certificate verification) in August 2026. Please switch to pinnedPeerCertSha256 (fixed certificate fingerprint) as soon as possible. allowInsecure will not be usable after its expiration.</value>
|
||||
</data>
|
||||
<data name="TbRouteExcludeAddress" xml:space="preserve">
|
||||
<value>Route Exclude Address</value>
|
||||
</data>
|
||||
<data name="TbRouteExcludeAddressTip" xml:space="preserve">
|
||||
<value>Use commas (,) to separate.</value>
|
||||
</data>
|
||||
<data name="MsgTunRouteExcludeInvalidAddress" xml:space="preserve">
|
||||
<value>Invalid address in TUN route exclude list: {0}</value>
|
||||
</data>
|
||||
<data name="MsgOptionsConflict" xml:space="preserve">
|
||||
<value>Conflict between {0} and {1}</value>
|
||||
</data>
|
||||
<data name="TbEnableFinalFragment" xml:space="preserve">
|
||||
<value>Enable Final Fragment</value>
|
||||
</data>
|
||||
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
|
||||
<value>Split the tail of packets into smaller fragments. This may affect throughput and latency.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1746,4 +1746,25 @@
|
||||
<data name="MsgAllowInsecureDeprecated" xml:space="preserve">
|
||||
<value>警告:Xray 将在 2026.8.1 禁用跳过证书验证 allowInsecure ,请尽快改用证书固定指纹 pinnedPeerCertSha256。到期后无法使用 </value>
|
||||
</data>
|
||||
<data name="TbVerifyPeerCertByName" xml:space="preserve">
|
||||
<value>Verify Peer Cert By Name</value>
|
||||
</data>
|
||||
<data name="TbRouteExcludeAddress" xml:space="preserve">
|
||||
<value>路由排除地址</value>
|
||||
</data>
|
||||
<data name="TbRouteExcludeAddressTip" xml:space="preserve">
|
||||
<value>使用逗号 (,) 分隔</value>
|
||||
</data>
|
||||
<data name="MsgTunRouteExcludeInvalidAddress" xml:space="preserve">
|
||||
<value>TUN 路由排除列表包含无效地址:{0}</value>
|
||||
</data>
|
||||
<data name="MsgOptionsConflict" xml:space="preserve">
|
||||
<value>{0} 与 {1} 冲突</value>
|
||||
</data>
|
||||
<data name="TbEnableFinalFragment" xml:space="preserve">
|
||||
<value>启用末端分片</value>
|
||||
</data>
|
||||
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
|
||||
<value>将数据包末端拆分为更小片段发送,可能影响吞吐与延迟。</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1333,7 +1333,7 @@
|
||||
<value>開啟第二個本機監聽埠</value>
|
||||
</data>
|
||||
<data name="TbRoutingInboundTagTips" xml:space="preserve">
|
||||
<value>socks:本地埠,socks2:第二個本地埠,socks3:區域網路埠</value>
|
||||
<value>Tun:TUN 入站,Socks:本機連接埠,Socks2:第二個本機連接埠,Socks3:區域網路連接埠</value>
|
||||
</data>
|
||||
<data name="TbSettingsTheme" xml:space="preserve">
|
||||
<value>主題</value>
|
||||
@@ -1746,4 +1746,25 @@
|
||||
<data name="MsgAllowInsecureDeprecated" xml:space="preserve">
|
||||
<value>警告:Xray 將在 2026.8.1 停用跳過憑證驗證 allowInsecure ,請盡快改用憑證固定指紋 pinnedPeerCertSha256。到期後無法使用 allowInsecure。</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="TbVerifyPeerCertByName" xml:space="preserve">
|
||||
<value>Verify Peer Cert By Name</value>
|
||||
</data>
|
||||
<data name="TbRouteExcludeAddress" xml:space="preserve">
|
||||
<value>路由排除位址</value>
|
||||
</data>
|
||||
<data name="TbRouteExcludeAddressTip" xml:space="preserve">
|
||||
<value>使用逗號 (,) 分隔</value>
|
||||
</data>
|
||||
<data name="MsgTunRouteExcludeInvalidAddress" xml:space="preserve">
|
||||
<value>TUN 路由排除清單中的位址無效: {0}</value>
|
||||
</data>
|
||||
<data name="MsgOptionsConflict" xml:space="preserve">
|
||||
<value>{0} 與 {1} 發生衝突</value>
|
||||
</data>
|
||||
<data name="TbEnableFinalFragment" xml:space="preserve">
|
||||
<value>啟用末端分片</value>
|
||||
</data>
|
||||
<data name="TbEnableFinalFragmentTip" xml:space="preserve">
|
||||
<value>將封包末端拆分為更小的片段進行傳送,可能會影響吞吐量與延遲</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -17,14 +17,6 @@
|
||||
],
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
{
|
||||
"inboundTag": [
|
||||
"api"
|
||||
],
|
||||
"outboundTag": "api",
|
||||
"type": "field"
|
||||
}
|
||||
]
|
||||
"rules": []
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@
|
||||
"172.18.0.1/30",
|
||||
"fdfe:dcba:9876::1/126"
|
||||
],
|
||||
"dns": [
|
||||
"172.18.0.1"
|
||||
],
|
||||
"autoSystemRoutingTable": [
|
||||
"0.0.0.0/0",
|
||||
"::/0"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Downloader" />
|
||||
<PackageReference Include="IPNetwork2" />
|
||||
<PackageReference Include="ReactiveUI">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
@@ -56,7 +57,7 @@
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<EmbeddedResource Update="Resx\ResUI.fa-Ir.resx">
|
||||
<EmbeddedResource Update="Resx\ResUI.fa.resx">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
|
||||
@@ -3,16 +3,10 @@ namespace ServiceLib.Services.CoreConfig;
|
||||
/// <summary>
|
||||
/// Core configuration file processing class
|
||||
/// </summary>
|
||||
public class CoreConfigClashService
|
||||
public class CoreConfigClashService(Config config)
|
||||
{
|
||||
private Config _config;
|
||||
private static readonly string _tag = "CoreConfigClashService";
|
||||
|
||||
public CoreConfigClashService(Config config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
@@ -56,7 +50,7 @@ public class CoreConfigClashService
|
||||
var tagYamlStr1 = "!<str>";
|
||||
var tagYamlStr2 = "__strn__";
|
||||
var tagYamlStr3 = "!!str";
|
||||
var txtFile = File.ReadAllText(addressFileName);
|
||||
var txtFile = await File.ReadAllTextAsync(addressFileName);
|
||||
txtFile = txtFile.Replace(tagYamlStr1, tagYamlStr2);
|
||||
|
||||
//YAML anchors
|
||||
@@ -75,13 +69,13 @@ public class CoreConfigClashService
|
||||
//mixed-port
|
||||
fileContent["mixed-port"] = AppManager.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||
//log-level
|
||||
fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel);
|
||||
fileContent["log-level"] = GetLogLevel(config.CoreBasicItem.Loglevel);
|
||||
|
||||
//external-controller
|
||||
fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}";
|
||||
fileContent.Remove("secret");
|
||||
//allow-lan
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
if (config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
fileContent["allow-lan"] = "true";
|
||||
fileContent["bind-address"] = "*";
|
||||
@@ -92,23 +86,23 @@ public class CoreConfigClashService
|
||||
}
|
||||
|
||||
//ipv6
|
||||
fileContent["ipv6"] = _config.ClashUIItem.EnableIPv6;
|
||||
fileContent["ipv6"] = config.ClashUIItem.EnableIPv6;
|
||||
|
||||
//mode
|
||||
if (!fileContent.ContainsKey("mode"))
|
||||
{
|
||||
fileContent["mode"] = ERuleMode.Rule.ToString().ToLower();
|
||||
fileContent["mode"] = nameof(ERuleMode.Rule).ToLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_config.ClashUIItem.RuleMode != ERuleMode.Unchanged)
|
||||
if (config.ClashUIItem.RuleMode != ERuleMode.Unchanged)
|
||||
{
|
||||
fileContent["mode"] = _config.ClashUIItem.RuleMode.ToString().ToLower();
|
||||
fileContent["mode"] = config.ClashUIItem.RuleMode.ToString().ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
//enable tun mode
|
||||
if (_config.TunModeItem.EnableTun)
|
||||
if (config.TunModeItem.EnableTun)
|
||||
{
|
||||
var tun = EmbedUtils.GetEmbedText(Global.ClashTunYaml);
|
||||
if (tun.IsNotEmpty())
|
||||
@@ -156,7 +150,7 @@ public class CoreConfigClashService
|
||||
|
||||
private async Task MixinContent(Dictionary<string, object> fileContent, ProfileItem node)
|
||||
{
|
||||
if (!_config.ClashUIItem.EnableMixinContent)
|
||||
if (!config.ClashUIItem.EnableMixinContent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -177,7 +171,7 @@ public class CoreConfigClashService
|
||||
}
|
||||
foreach (var item in mixinContent)
|
||||
{
|
||||
if (!_config.TunModeItem.EnableTun && item.Key == "tun")
|
||||
if (!config.TunModeItem.EnableTun && item.Key == "tun")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -220,9 +214,8 @@ public class CoreConfigClashService
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blRemoved && !fileContent.ContainsKey(key))
|
||||
if (!blRemoved && fileContent.TryAdd(key, value))
|
||||
{
|
||||
fileContent.Add(key, value);
|
||||
return;
|
||||
}
|
||||
var lstOri = (List<object>)fileContent[key];
|
||||
@@ -244,7 +237,7 @@ public class CoreConfigClashService
|
||||
}
|
||||
else
|
||||
{
|
||||
lstValue.ForEach(item => lstOri.Add(item));
|
||||
lstValue.ForEach(lstOri.Add);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ public partial class CoreConfigSingboxService(CoreConfigContext context)
|
||||
{
|
||||
listen = Global.Loopback,
|
||||
listen_port = port,
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
type = nameof(EInboundProtocol.mixed),
|
||||
};
|
||||
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
||||
_coreConfig.inbounds.Add(inbound);
|
||||
@@ -229,7 +229,7 @@ public partial class CoreConfigSingboxService(CoreConfigContext context)
|
||||
tag = $"{EInboundProtocol.mixed}{port}",
|
||||
listen = Global.Loopback,
|
||||
listen_port = port,
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
type = nameof(EInboundProtocol.mixed),
|
||||
});
|
||||
ApplyOutboundBindInterface();
|
||||
ApplyOutboundSendThrough();
|
||||
|
||||
@@ -23,7 +23,7 @@ public partial class CoreConfigSingboxService
|
||||
}
|
||||
|
||||
// Process outbounds
|
||||
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : [];
|
||||
var customOutboundsNode = fullConfigTemplateNode["outbounds"] as JsonArray ?? [];
|
||||
foreach (var outbound in _coreConfig.outbounds)
|
||||
{
|
||||
if (outbound.type.ToLower() is "direct" or "block")
|
||||
@@ -42,9 +42,9 @@ public partial class CoreConfigSingboxService
|
||||
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
|
||||
|
||||
// Process endpoints
|
||||
if (_coreConfig.endpoints != null && _coreConfig.endpoints.Count > 0)
|
||||
if (_coreConfig.endpoints is { Count: > 0 })
|
||||
{
|
||||
var customEndpointsNode = fullConfigTemplateNode["endpoints"] is JsonArray endpoints ? endpoints : [];
|
||||
var customEndpointsNode = fullConfigTemplateNode["endpoints"] as JsonArray ?? [];
|
||||
foreach (var endpoint in _coreConfig.endpoints)
|
||||
{
|
||||
if (endpoint.detour.IsNullOrEmpty() && !fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
|
||||
|
||||
@@ -26,7 +26,7 @@ public partial class CoreConfigSingboxService
|
||||
{
|
||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||
|
||||
if (rules?.LastOrDefault() is { } lastRule && lastRule.OutboundTag == Global.DirectTag)
|
||||
if (rules?.LastOrDefault() is { OutboundTag: Global.DirectTag } lastRule)
|
||||
{
|
||||
var noDomain = lastRule.Domain == null || lastRule.Domain.Count == 0;
|
||||
var noProcess = lastRule.Process == null || lastRule.Process.Count == 0;
|
||||
@@ -82,7 +82,7 @@ public partial class CoreConfigSingboxService
|
||||
if (simpleDnsItem.UseSystemHosts == true)
|
||||
{
|
||||
var systemHosts = Utils.GetSystemHosts();
|
||||
if (systemHosts != null && systemHosts.Count > 0)
|
||||
if (systemHosts is { Count: > 0 })
|
||||
{
|
||||
foreach (var host in systemHosts)
|
||||
{
|
||||
@@ -182,13 +182,13 @@ public partial class CoreConfigSingboxService
|
||||
{
|
||||
server = Global.SingboxRemoteDNSTag,
|
||||
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Proxy),
|
||||
clash_mode = ERuleMode.Global.ToString()
|
||||
clash_mode = nameof(ERuleMode.Global)
|
||||
},
|
||||
new Rule4Sbox
|
||||
{
|
||||
server = Global.SingboxDirectDNSTag,
|
||||
strategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom),
|
||||
clash_mode = ERuleMode.Direct.ToString()
|
||||
clash_mode = nameof(ERuleMode.Direct)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -454,12 +454,12 @@ public partial class CoreConfigSingboxService
|
||||
dns4Sbox.rules.Insert(0, new()
|
||||
{
|
||||
server = tag,
|
||||
clash_mode = ERuleMode.Direct.ToString()
|
||||
clash_mode = nameof(ERuleMode.Direct)
|
||||
});
|
||||
dns4Sbox.rules.Insert(0, new()
|
||||
{
|
||||
server = dns4Sbox.servers.Where(t => t.detour == Global.ProxyTag).Select(t => t.tag).FirstOrDefault() ?? "remote",
|
||||
clash_mode = ERuleMode.Global.ToString()
|
||||
clash_mode = nameof(ERuleMode.Global)
|
||||
});
|
||||
|
||||
var finalDnsAddress = string.IsNullOrEmpty(dnsItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dnsItem?.DomainDNSAddress;
|
||||
|
||||
@@ -15,8 +15,8 @@ public partial class CoreConfigSingboxService
|
||||
{
|
||||
var inbound = new Inbound4Sbox()
|
||||
{
|
||||
type = EInboundProtocol.mixed.ToString(),
|
||||
tag = EInboundProtocol.socks.ToString(),
|
||||
type = nameof(EInboundProtocol.mixed),
|
||||
tag = nameof(EInboundProtocol.socks),
|
||||
listen = Global.Loopback,
|
||||
};
|
||||
_coreConfig.inbounds.Add(inbound);
|
||||
@@ -71,6 +71,7 @@ public partial class CoreConfigSingboxService
|
||||
{
|
||||
tunInbound.address = ["172.18.0.1/30"];
|
||||
}
|
||||
tunInbound.route_exclude_address = _config.TunModeItem.RouteExcludeAddress;
|
||||
|
||||
_coreConfig.inbounds.Add(tunInbound);
|
||||
}
|
||||
@@ -86,7 +87,7 @@ public partial class CoreConfigSingboxService
|
||||
var inbound = JsonUtils.DeepCopy(inItem);
|
||||
inbound.tag = protocol.ToString();
|
||||
inbound.listen_port = inItem.listen_port + (int)protocol;
|
||||
inbound.type = EInboundProtocol.mixed.ToString();
|
||||
inbound.type = nameof(EInboundProtocol.mixed);
|
||||
return inbound;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,15 +190,16 @@ public partial class CoreConfigSingboxService
|
||||
|
||||
outbound.packet_encoding = "xudp";
|
||||
|
||||
if (!protocolExtra.Flow.IsNullOrEmpty())
|
||||
if (protocolExtra.Flow is "xtls-rprx-vision" or "xtls-rprx-vision-udp443")
|
||||
{
|
||||
outbound.flow = "xtls-rprx-vision";
|
||||
}
|
||||
else if (!protocolExtra.Flow.IsNullOrEmpty())
|
||||
{
|
||||
outbound.flow = protocolExtra.Flow;
|
||||
}
|
||||
else
|
||||
{
|
||||
FillOutboundMux(outbound);
|
||||
}
|
||||
|
||||
FillOutboundMux(outbound);
|
||||
FillOutboundTransport(outbound);
|
||||
break;
|
||||
}
|
||||
@@ -342,7 +343,7 @@ public partial class CoreConfigSingboxService
|
||||
{
|
||||
try
|
||||
{
|
||||
var muxEnabled = _node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||
var muxEnabled = _node.MuxEnabled ?? false;
|
||||
if (muxEnabled && _config.Mux4SboxItem.Protocol.IsNotEmpty())
|
||||
{
|
||||
var mux = new Multiplex4Sbox()
|
||||
@@ -396,7 +397,7 @@ public partial class CoreConfigSingboxService
|
||||
enabled = true,
|
||||
record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
|
||||
server_name = serverName,
|
||||
insecure = Utils.ToBool(_node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : _node.AllowInsecure),
|
||||
insecure = _node.GetAllowInsecure(),
|
||||
alpn = _node.GetAlpn(),
|
||||
};
|
||||
if (_node.Fingerprint.IsNotEmpty())
|
||||
@@ -729,8 +730,8 @@ public partial class CoreConfigSingboxService
|
||||
{
|
||||
return;
|
||||
}
|
||||
var outbounds = servers.Where(s => s is Outbound4Sbox).Cast<Outbound4Sbox>().ToList();
|
||||
var endpoints = servers.Where(s => s is Endpoints4Sbox).Cast<Endpoints4Sbox>().ToList();
|
||||
var outbounds = servers.OfType<Outbound4Sbox>().ToList();
|
||||
var endpoints = servers.OfType<Endpoints4Sbox>().ToList();
|
||||
singboxConfig.endpoints ??= [];
|
||||
if (prepend)
|
||||
{
|
||||
|
||||
@@ -96,6 +96,15 @@ public partial class CoreConfigSingboxService
|
||||
new() { protocol = ["dns"] },
|
||||
],
|
||||
});
|
||||
if (_config.CoreBasicItem.EnableFinalFragment)
|
||||
{
|
||||
_coreConfig.route.rules.Add(new()
|
||||
{
|
||||
protocol = ["tls"],
|
||||
action = "route-options",
|
||||
tls_record_fragment = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -104,6 +113,14 @@ public partial class CoreConfigSingboxService
|
||||
port = [53],
|
||||
action = "hijack-dns",
|
||||
});
|
||||
if (_config.CoreBasicItem.EnableFinalFragment)
|
||||
{
|
||||
_coreConfig.route.rules.Add(new()
|
||||
{
|
||||
action = "route-options",
|
||||
tls_record_fragment = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var hostsDomains = new List<string>();
|
||||
@@ -176,12 +193,12 @@ public partial class CoreConfigSingboxService
|
||||
_coreConfig.route.rules.Add(new()
|
||||
{
|
||||
outbound = Global.DirectTag,
|
||||
clash_mode = ERuleMode.Direct.ToString()
|
||||
clash_mode = nameof(ERuleMode.Direct)
|
||||
});
|
||||
_coreConfig.route.rules.Add(new()
|
||||
{
|
||||
outbound = Global.ProxyTag,
|
||||
clash_mode = ERuleMode.Global.ToString()
|
||||
clash_mode = nameof(ERuleMode.Global)
|
||||
});
|
||||
|
||||
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.NullIfEmpty();
|
||||
|
||||
@@ -18,15 +18,15 @@ public partial class CoreConfigSingboxService
|
||||
//convert route geosite & geoip to ruleset
|
||||
foreach (var rule in _coreConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set ??= new List<string>();
|
||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
||||
rule.rule_set ??= [];
|
||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList() ?? []);
|
||||
rule.geosite = null;
|
||||
AddRuleSets(ruleSets, rule.rule_set);
|
||||
}
|
||||
foreach (var rule in _coreConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set ??= new List<string>();
|
||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
||||
rule.rule_set ??= [];
|
||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList() ?? []);
|
||||
rule.geoip = null;
|
||||
AddRuleSets(ruleSets, rule.rule_set);
|
||||
}
|
||||
@@ -34,14 +34,14 @@ public partial class CoreConfigSingboxService
|
||||
//convert dns geosite & geoip to ruleset
|
||||
foreach (var rule in _coreConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set ??= new List<string>();
|
||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList());
|
||||
rule.rule_set ??= [];
|
||||
rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList() ?? []);
|
||||
rule.geosite = null;
|
||||
}
|
||||
foreach (var rule in _coreConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? [])
|
||||
{
|
||||
rule.rule_set ??= new List<string>();
|
||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList());
|
||||
rule.rule_set ??= [];
|
||||
rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList() ?? []);
|
||||
rule.geoip = null;
|
||||
}
|
||||
foreach (var dnsRule in _coreConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? [])
|
||||
|
||||
@@ -60,6 +60,10 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
|
||||
{
|
||||
ApplyOutboundFragment();
|
||||
}
|
||||
if (_config.CoreBasicItem.EnableFinalFragment)
|
||||
{
|
||||
ApplyFinalFragment();
|
||||
}
|
||||
ApplyOutboundBindInterface();
|
||||
ApplyOutboundSendThrough();
|
||||
|
||||
@@ -161,7 +165,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
|
||||
{
|
||||
listen = Global.Loopback,
|
||||
port = port,
|
||||
protocol = EInboundProtocol.mixed.ToString(),
|
||||
protocol = nameof(EInboundProtocol.mixed),
|
||||
settings = new Inboundsettings4Ray()
|
||||
{
|
||||
udp = true,
|
||||
@@ -204,6 +208,10 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
|
||||
{
|
||||
ApplyOutboundFragment();
|
||||
}
|
||||
if (_config.CoreBasicItem.EnableFinalFragment)
|
||||
{
|
||||
ApplyFinalFragment();
|
||||
}
|
||||
ApplyOutboundBindInterface();
|
||||
ApplyOutboundSendThrough();
|
||||
//ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary());
|
||||
@@ -262,7 +270,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
|
||||
tag = $"{EInboundProtocol.socks}{port}",
|
||||
listen = Global.Loopback,
|
||||
port = port,
|
||||
protocol = EInboundProtocol.mixed.ToString(),
|
||||
protocol = nameof(EInboundProtocol.mixed),
|
||||
settings = new Inboundsettings4Ray()
|
||||
{
|
||||
udp = true,
|
||||
@@ -276,6 +284,10 @@ public partial class CoreConfigV2rayService(CoreConfigContext context)
|
||||
{
|
||||
ApplyOutboundFragment();
|
||||
}
|
||||
if (_config.CoreBasicItem.EnableFinalFragment)
|
||||
{
|
||||
ApplyFinalFragment();
|
||||
}
|
||||
ApplyOutboundBindInterface();
|
||||
ApplyOutboundSendThrough();
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
else
|
||||
{
|
||||
_coreConfig.burstObservatory.subjectSelector ??= new();
|
||||
_coreConfig.burstObservatory.subjectSelector ??= [];
|
||||
_coreConfig.burstObservatory.subjectSelector.Add(baseTagName);
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
else
|
||||
{
|
||||
_coreConfig.observatory.subjectSelector ??= new();
|
||||
_coreConfig.observatory.subjectSelector ??= [];
|
||||
_coreConfig.observatory.subjectSelector.Add(baseTagName);
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ public partial class CoreConfigV2rayService
|
||||
tag = balancerTag,
|
||||
fallbackTag = multipleLoad == EMultipleLoad.Fallback ? Global.DirectTag : null,
|
||||
};
|
||||
_coreConfig.routing.balancers ??= new();
|
||||
_coreConfig.routing.balancers ??= [];
|
||||
_coreConfig.routing.balancers.Add(balancer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +46,7 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
|
||||
// Ensure routing node exists
|
||||
if (fullConfigTemplateNode["routing"] == null)
|
||||
{
|
||||
fullConfigTemplateNode["routing"] = new JsonObject();
|
||||
}
|
||||
fullConfigTemplateNode["routing"] ??= new JsonObject();
|
||||
|
||||
// Handle balancers - append instead of override
|
||||
if (fullConfigTemplateNode["routing"]["balancers"] is JsonArray customBalancersNode)
|
||||
|
||||
@@ -34,7 +34,7 @@ public partial class CoreConfigV2rayService
|
||||
return;
|
||||
}
|
||||
var simpleDnsItem = context.SimpleDnsItem;
|
||||
var dnsItem = _coreConfig.dns is Dns4Ray dns4Ray ? dns4Ray : new Dns4Ray();
|
||||
var dnsItem = _coreConfig.dns as Dns4Ray ?? new Dns4Ray();
|
||||
|
||||
var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs;
|
||||
//Outbound Freedom domainStrategy
|
||||
@@ -246,7 +246,7 @@ public partial class CoreConfigV2rayService
|
||||
|
||||
var useDirectDns = false;
|
||||
|
||||
if (rules?.LastOrDefault() is { } lastRule && lastRule.OutboundTag == Global.DirectTag)
|
||||
if (rules?.LastOrDefault() is { OutboundTag: Global.DirectTag } lastRule)
|
||||
{
|
||||
var noDomain = lastRule.Domain == null || lastRule.Domain.Count == 0;
|
||||
var noProcess = lastRule.Process == null || lastRule.Process.Count == 0;
|
||||
@@ -384,9 +384,11 @@ public partial class CoreConfigV2rayService
|
||||
var outbound = _coreConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
||||
if (outbound != null)
|
||||
{
|
||||
outbound.settings = new();
|
||||
outbound.settings.domainStrategy = domainStrategy4Freedom;
|
||||
outbound.settings.userLevel = 0;
|
||||
outbound.settings = new()
|
||||
{
|
||||
domainStrategy = domainStrategy4Freedom,
|
||||
userLevel = 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,14 +30,19 @@ public partial class CoreConfigV2rayService
|
||||
inbound3.listen = listen;
|
||||
_coreConfig.inbounds.Add(inbound3);
|
||||
|
||||
//auth
|
||||
// auth
|
||||
if (_config.Inbound.First().User.IsNotEmpty() && _config.Inbound.First().Pass.IsNotEmpty())
|
||||
{
|
||||
inbound3.settings.auth = "password";
|
||||
inbound3.settings.accounts = new List<AccountsItem4Ray>
|
||||
{
|
||||
new() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass }
|
||||
};
|
||||
inbound3.settings.accounts =
|
||||
[
|
||||
new()
|
||||
{
|
||||
user = _config.Inbound.First().User,
|
||||
pass = _config.Inbound.First().Pass,
|
||||
},
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -53,12 +58,15 @@ public partial class CoreConfigV2rayService
|
||||
{
|
||||
_config.TunModeItem.Mtu = Global.TunMtus.First();
|
||||
}
|
||||
var tunInbound = JsonUtils.Deserialize<Inbounds4Ray>(EmbedUtils.GetEmbedText(Global.V2raySampleTunInbound)) ?? new Inbounds4Ray { };
|
||||
var tunInbound =
|
||||
JsonUtils.Deserialize<Inbounds4Ray>(EmbedUtils.GetEmbedText(Global.V2raySampleTunInbound)) ??
|
||||
new Inbounds4Ray();
|
||||
tunInbound.settings.name = context.IsMacOS ? $"utun{new Random().Next(99)}" : "xray_tun";
|
||||
tunInbound.settings.MTU = _config.TunModeItem.Mtu;
|
||||
if (_config.TunModeItem.EnableIPv6Address == false)
|
||||
if (!_config.TunModeItem.EnableIPv6Address)
|
||||
{
|
||||
tunInbound.settings.gateway = ["172.18.0.1/30"];
|
||||
tunInbound.settings.autoSystemRoutingTable = ["0.0.0.0/0"];
|
||||
}
|
||||
var bindInterface = _config.CoreBasicItem.BindInterface?.TrimEx();
|
||||
if (!bindInterface.IsNullOrEmpty())
|
||||
@@ -66,6 +74,53 @@ public partial class CoreConfigV2rayService
|
||||
tunInbound.settings.autoOutboundsInterface = bindInterface;
|
||||
}
|
||||
tunInbound.sniffing = inbound.sniffing;
|
||||
|
||||
if (_config.TunModeItem.RouteExcludeAddress is { Count: > 0 })
|
||||
{
|
||||
var wholeInternet = IPNetwork2.Parse("0.0.0.0/0");
|
||||
var wholeInternetV6 = IPNetwork2.Parse("::/0");
|
||||
|
||||
var excludeList = _config.TunModeItem.RouteExcludeAddress.Select(IPNetwork2.Parse)
|
||||
.Where(x => x != null).ToList();
|
||||
|
||||
var includeList = new List<IPNetwork2> { wholeInternet };
|
||||
var includeListV6 = new List<IPNetwork2> { wholeInternetV6 };
|
||||
|
||||
foreach (var exclude in excludeList)
|
||||
{
|
||||
var temp = new List<IPNetwork2>();
|
||||
if (exclude.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
foreach (var net in includeList)
|
||||
{
|
||||
temp.AddRange(net.Subtract(exclude));
|
||||
}
|
||||
includeList = temp;
|
||||
}
|
||||
else if (exclude.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
foreach (var net in includeListV6)
|
||||
{
|
||||
temp.AddRange(net.Subtract(exclude));
|
||||
}
|
||||
includeListV6 = temp;
|
||||
}
|
||||
}
|
||||
|
||||
includeList = IPNetwork2.Supernet(includeList.ToArray()).ToList();
|
||||
includeListV6 = IPNetwork2.Supernet(includeListV6.ToArray()).ToList();
|
||||
|
||||
if (_config.TunModeItem.EnableIPv6Address)
|
||||
{
|
||||
tunInbound.settings.autoSystemRoutingTable = includeList.Select(x => x.ToString())
|
||||
.Concat(includeListV6.Select(x => x.ToString())).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
tunInbound.settings.autoSystemRoutingTable = includeList.Select(x => x.ToString()).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
_coreConfig.inbounds.Add(tunInbound);
|
||||
}
|
||||
}
|
||||
@@ -90,7 +145,7 @@ public partial class CoreConfigV2rayService
|
||||
}
|
||||
inbound.tag = protocol.ToString();
|
||||
inbound.port = inItem.LocalPort + (int)protocol;
|
||||
inbound.protocol = EInboundProtocol.mixed.ToString();
|
||||
inbound.protocol = nameof(EInboundProtocol.mixed);
|
||||
inbound.settings.udp = inItem.UdpEnabled;
|
||||
inbound.sniffing.enabled = inItem.SniffingEnabled;
|
||||
inbound.sniffing.destOverride = inItem.DestOverride;
|
||||
|
||||
@@ -62,7 +62,7 @@ public partial class CoreConfigV2rayService
|
||||
try
|
||||
{
|
||||
var protocolExtra = _node.GetProtocolExtra();
|
||||
var muxEnabled = _node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||
var muxEnabled = _node.MuxEnabled ?? false;
|
||||
switch (_node.ConfigType)
|
||||
{
|
||||
case EConfigType.VMess:
|
||||
@@ -243,9 +243,9 @@ public partial class CoreConfigV2rayService
|
||||
version = 2,
|
||||
address = _node.Address,
|
||||
port = _node.Port,
|
||||
vnext = null,
|
||||
servers = null,
|
||||
};
|
||||
outbound.settings.vnext = null;
|
||||
outbound.settings.servers = null;
|
||||
break;
|
||||
}
|
||||
case EConfigType.WireGuard:
|
||||
@@ -380,10 +380,11 @@ public partial class CoreConfigV2rayService
|
||||
|
||||
TlsSettings4Ray tlsSettings = new()
|
||||
{
|
||||
allowInsecure = Utils.ToBool(_node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : _node.AllowInsecure),
|
||||
allowInsecure = _node.GetAllowInsecure(),
|
||||
alpn = _node.GetAlpn(),
|
||||
fingerprint = _node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : _node.Fingerprint,
|
||||
echConfigList = _node.EchConfigList.NullIfEmpty(),
|
||||
verifyPeerCertByName = _node.VerifyPeerCertByName.NullIfEmpty(),
|
||||
};
|
||||
if (sni.IsNotEmpty())
|
||||
{
|
||||
@@ -449,14 +450,13 @@ public partial class CoreConfigV2rayService
|
||||
KcpSettings4Ray kcpSettings = new()
|
||||
{
|
||||
mtu = kcpMtu,
|
||||
tti = _config.KcpItem.Tti
|
||||
tti = _config.KcpItem.Tti,
|
||||
uplinkCapacity = _config.KcpItem.UplinkCapacity,
|
||||
downlinkCapacity = _config.KcpItem.DownlinkCapacity,
|
||||
cwndMultiplier = _config.KcpItem.CwndMultiplier,
|
||||
maxSendingWindow = _config.KcpItem.MaxSendingWindow,
|
||||
};
|
||||
|
||||
kcpSettings.uplinkCapacity = _config.KcpItem.UplinkCapacity;
|
||||
kcpSettings.downlinkCapacity = _config.KcpItem.DownlinkCapacity;
|
||||
|
||||
kcpSettings.cwndMultiplier = _config.KcpItem.CwndMultiplier;
|
||||
kcpSettings.maxSendingWindow = _config.KcpItem.MaxSendingWindow;
|
||||
var kcpFinalmask = new Finalmask4Ray();
|
||||
if (Global.KcpHeaderMaskMap.TryGetValue(headerType, out var header))
|
||||
{
|
||||
@@ -464,8 +464,8 @@ public partial class CoreConfigV2rayService
|
||||
[
|
||||
new Mask4Ray
|
||||
{
|
||||
type = header,
|
||||
settings = null
|
||||
type = "mkcp-legacy",
|
||||
settings = new MaskSettings4Ray { header = header },
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -474,15 +474,15 @@ public partial class CoreConfigV2rayService
|
||||
{
|
||||
kcpFinalmask.udp.Add(new Mask4Ray
|
||||
{
|
||||
type = "mkcp-original"
|
||||
type = "mkcp-legacy",
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
kcpFinalmask.udp.Add(new Mask4Ray
|
||||
{
|
||||
type = "mkcp-aes128gcm",
|
||||
settings = new MaskSettings4Ray { password = kcpSeed }
|
||||
type = "mkcp-legacy",
|
||||
settings = new MaskSettings4Ray { value = kcpSeed },
|
||||
});
|
||||
}
|
||||
streamSettings.kcpSettings = kcpSettings;
|
||||
@@ -530,7 +530,7 @@ public partial class CoreConfigV2rayService
|
||||
break;
|
||||
//xhttp
|
||||
case nameof(ETransport.xhttp):
|
||||
streamSettings.network = ETransport.xhttp.ToString();
|
||||
streamSettings.network = nameof(ETransport.xhttp);
|
||||
XhttpSettings4Ray xhttpSettings = new();
|
||||
|
||||
if (path.IsNotEmpty())
|
||||
@@ -825,29 +825,7 @@ public partial class CoreConfigV2rayService
|
||||
&& (n.streamSettings?.sockopt?.dialerProxy?.IsNullOrEmpty() ?? true))
|
||||
.ToList();
|
||||
|
||||
var configPackets = _config.Fragment4RayItem?.Packets ?? "tlshello";
|
||||
var configLength = _config.Fragment4RayItem?.Length ?? "50-100";
|
||||
var configDelay = _config.Fragment4RayItem?.Interval ?? "10-20";
|
||||
|
||||
var fragmentMask = new Mask4Ray
|
||||
{
|
||||
type = "fragment",
|
||||
settings = new MaskSettings4Ray
|
||||
{
|
||||
packets = configPackets,
|
||||
length = configLength,
|
||||
delay = configDelay,
|
||||
}
|
||||
};
|
||||
var noiseMask = new Mask4Ray
|
||||
{
|
||||
type = "noise",
|
||||
settings = new MaskSettings4Ray
|
||||
{
|
||||
length = "10-20",
|
||||
delay = "10-16",
|
||||
}
|
||||
};
|
||||
var (fragmentMask, noiseMask) = BuildFragmentsMasks();
|
||||
|
||||
foreach (var outbound in actOutboundWithTlsList)
|
||||
{
|
||||
@@ -881,4 +859,67 @@ public partial class CoreConfigV2rayService
|
||||
outbound.streamSettings.finalmask = finalMaskJsonObj;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyFinalFragment()
|
||||
{
|
||||
var (fragmentMask, noiseMask) = BuildFragmentsMasks();
|
||||
var actOutboundList = _coreConfig.outbounds.Where(n => n.tag.StartsWith(Global.ProxyTag)).ToList();
|
||||
|
||||
var fragmentFreedom = new Outbounds4Ray()
|
||||
{
|
||||
tag = $"{Global.ProxyTag}-fragment-freedom",
|
||||
protocol = "freedom",
|
||||
streamSettings = new StreamSettings4Ray
|
||||
{
|
||||
finalmask = new Finalmask4Ray
|
||||
{
|
||||
tcp = [fragmentMask],
|
||||
udp = [noiseMask],
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var outbound in actOutboundList)
|
||||
{
|
||||
var index = _coreConfig.outbounds.IndexOf(outbound);
|
||||
var originalTag = outbound.tag;
|
||||
var afterTag = $"fragment-{originalTag}";
|
||||
var cloneFragmentFreedom = JsonUtils.DeepCopy(fragmentFreedom);
|
||||
cloneFragmentFreedom.tag = originalTag;
|
||||
cloneFragmentFreedom.streamSettings.sockopt ??= new();
|
||||
cloneFragmentFreedom.streamSettings.sockopt.dialerProxy = afterTag;
|
||||
|
||||
outbound.tag = afterTag;
|
||||
_coreConfig.outbounds.Insert(index, cloneFragmentFreedom);
|
||||
}
|
||||
}
|
||||
|
||||
private (Mask4Ray tcpFragment, Mask4Ray udpNoise) BuildFragmentsMasks()
|
||||
{
|
||||
var configPackets = _config.Fragment4RayItem?.Packets ?? "tlshello";
|
||||
var configLength = _config.Fragment4RayItem?.Length ?? "50-100";
|
||||
var configDelay = _config.Fragment4RayItem?.Interval ?? "10-20";
|
||||
|
||||
var fragmentMask = new Mask4Ray
|
||||
{
|
||||
type = "fragment",
|
||||
settings = new MaskSettings4Ray
|
||||
{
|
||||
packets = configPackets,
|
||||
length = configLength,
|
||||
delay = configDelay,
|
||||
}
|
||||
};
|
||||
var noiseMask = new Mask4Ray
|
||||
{
|
||||
type = "noise",
|
||||
settings = new MaskSettings4Ray
|
||||
{
|
||||
length = "10-20",
|
||||
delay = "10-16",
|
||||
}
|
||||
};
|
||||
|
||||
return (fragmentMask, noiseMask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,45 +6,19 @@ public partial class CoreConfigV2rayService
|
||||
{
|
||||
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
|
||||
{
|
||||
var tag = EInboundProtocol.api.ToString();
|
||||
Metrics4Ray apiObj = new();
|
||||
Metrics4Ray metricsObj = new();
|
||||
Policy4Ray policyObj = new();
|
||||
SystemPolicy4Ray policySystemSetting = new();
|
||||
|
||||
_coreConfig.stats = new Stats4Ray();
|
||||
|
||||
apiObj.tag = tag;
|
||||
_coreConfig.metrics = apiObj;
|
||||
metricsObj.listen = $"{Global.Loopback}:{AppManager.Instance.StatePort}";
|
||||
_coreConfig.metrics = metricsObj;
|
||||
|
||||
policySystemSetting.statsOutboundDownlink = true;
|
||||
policySystemSetting.statsOutboundUplink = true;
|
||||
policyObj.system = policySystemSetting;
|
||||
_coreConfig.policy = policyObj;
|
||||
|
||||
if (!_coreConfig.inbounds.Exists(item => item.tag == tag))
|
||||
{
|
||||
Inbounds4Ray apiInbound = new();
|
||||
Inboundsettings4Ray apiInboundSettings = new();
|
||||
apiInbound.tag = tag;
|
||||
apiInbound.listen = Global.Loopback;
|
||||
apiInbound.port = AppManager.Instance.StatePort;
|
||||
apiInbound.protocol = Global.InboundAPIProtocol;
|
||||
apiInboundSettings.address = Global.Loopback;
|
||||
apiInbound.settings = apiInboundSettings;
|
||||
_coreConfig.inbounds.Add(apiInbound);
|
||||
}
|
||||
|
||||
if (!_coreConfig.routing.rules.Exists(item => item.outboundTag == tag))
|
||||
{
|
||||
RulesItem4Ray apiRoutingRule = new()
|
||||
{
|
||||
inboundTag = new List<string> { tag },
|
||||
outboundTag = tag,
|
||||
type = "field"
|
||||
};
|
||||
|
||||
_coreConfig.routing.rules.Add(apiRoutingRule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
private static readonly string _tag = "SpeedtestService";
|
||||
private readonly Config? _config = config;
|
||||
private readonly Func<SpeedTestResult, Task>? _updateFunc = updateFunc;
|
||||
private static readonly ConcurrentBag<string> _lstExitLoop = new();
|
||||
private static readonly ConcurrentBag<string> _lstExitLoop = [];
|
||||
private readonly int _speedTestPageSize = config.SpeedTestItem.SpeedTestPageSize ?? Global.SpeedTestPageSize;
|
||||
private readonly TimeSpan _delayInterval = TimeSpan.FromSeconds(config.SpeedTestItem.SpeedTestDelayInterval ?? 1);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
|
||||
private static bool ShouldStopTest(string exitLoopKey)
|
||||
{
|
||||
return !_lstExitLoop.Any(p => p == exitLoopKey);
|
||||
return _lstExitLoop.All(p => p != exitLoopKey);
|
||||
}
|
||||
|
||||
private async Task RunAsync(ESpeedActionType actionType, List<ProfileItem> selecteds)
|
||||
@@ -173,7 +173,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
}
|
||||
var lstTest = GetTestBatchItem(lstSelected, pageSize);
|
||||
|
||||
List<ServerTestItem> lstFailed = new();
|
||||
List<ServerTestItem> lstFailed = [];
|
||||
foreach (var lst in lstTest)
|
||||
{
|
||||
var ret = await RunRealPingAsync(lst, exitLoopKey);
|
||||
@@ -219,7 +219,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
|
||||
List<Task> tasks = new();
|
||||
List<Task> tasks = [];
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (!it.AllowTest)
|
||||
@@ -262,7 +262,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
}
|
||||
var lstTest = GetTestBatchItem(lstSelected, pageSize);
|
||||
|
||||
List<ServerTestItem> lstFailed = new();
|
||||
List<ServerTestItem> lstFailed = [];
|
||||
foreach (var lst in lstTest)
|
||||
{
|
||||
var ret = await RunUdpTestAsync(lst, exitLoopKey);
|
||||
@@ -300,7 +300,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
|
||||
List<Task> tasks = new();
|
||||
List<Task> tasks = [];
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (!it.AllowTest)
|
||||
@@ -338,7 +338,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
{
|
||||
using var concurrencySemaphore = new SemaphoreSlim(concurrencyCount);
|
||||
var downloadHandle = new DownloadService();
|
||||
List<Task> tasks = new();
|
||||
List<Task> tasks = [];
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (ShouldStopTest(exitLoopKey))
|
||||
@@ -489,7 +489,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
||||
|
||||
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
|
||||
{
|
||||
List<List<ServerTestItem>> lstTest = new();
|
||||
List<List<ServerTestItem>> lstTest = [];
|
||||
var lst1 = lstSelected.Where(t => t.CoreType == ECoreType.Xray).ToList();
|
||||
var lst2 = lstSelected.Where(t => t.CoreType == ECoreType.sing_box).ToList();
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@ public sealed class WindowsJobService : IDisposable
|
||||
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
|
||||
(uint)length))
|
||||
{
|
||||
throw new Exception(string.Format("Unable to set information. Error: {0}",
|
||||
Marshal.GetLastWin32Error()));
|
||||
throw new Exception($"Unable to set information. Error: {Marshal.GetLastWin32Error()}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -216,7 +216,7 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||
NoticeManager.Instance.Enqueue(ResUI.PleaseAddAtLeastOneServer);
|
||||
return;
|
||||
}
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? ECoreType.Xray : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? ECoreType.Xray : Enum.Parse<ECoreType>(CoreType);
|
||||
if (SelectedSource.CoreType is not (ECoreType.Xray or ECoreType.sing_box) ||
|
||||
SelectedSource.ConfigType is not (EConfigType.ProxyChain or EConfigType.PolicyGroup))
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@ public class AddServer2ViewModel : MyReactiveObject
|
||||
NoticeManager.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
return;
|
||||
}
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : Enum.Parse<ECoreType>(CoreType);
|
||||
|
||||
if (await ConfigHandler.EditCustomServer(_config, SelectedSource) == 0)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,12 @@ public class AddServerViewModel : MyReactiveObject
|
||||
[Reactive]
|
||||
public string? CoreType { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AllowInsecure { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool MuxEnabled { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string Cert { get; set; }
|
||||
|
||||
@@ -273,8 +279,10 @@ public class AddServerViewModel : MyReactiveObject
|
||||
SelectedSource = JsonUtils.DeepCopy(profileItem);
|
||||
}
|
||||
CoreType = SelectedSource?.CoreType?.ToString();
|
||||
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
||||
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
|
||||
AllowInsecure = SelectedSource?.GetAllowInsecure() == true;
|
||||
MuxEnabled = SelectedSource?.MuxEnabled == true;
|
||||
Cert = SelectedSource?.Cert ?? string.Empty;
|
||||
CertSha = SelectedSource?.CertSha ?? string.Empty;
|
||||
|
||||
var protocolExtra = SelectedSource?.GetProtocolExtra() ?? new();
|
||||
var transport = SelectedSource?.GetTransportExtra() ?? new();
|
||||
@@ -352,7 +360,9 @@ public class AddServerViewModel : MyReactiveObject
|
||||
return;
|
||||
}
|
||||
}
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : Enum.Parse<ECoreType>(CoreType);
|
||||
SelectedSource.AllowInsecure = AllowInsecure ? Global.StringTrue : Global.StringFalse;
|
||||
SelectedSource.MuxEnabled = MuxEnabled;
|
||||
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
|
||||
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
|
||||
if (!Global.Networks.Contains(SelectedSource.Network))
|
||||
@@ -464,7 +474,9 @@ public class AddServerViewModel : MyReactiveObject
|
||||
domain += $":{SelectedSource.Port}";
|
||||
}
|
||||
|
||||
(Cert, var certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName, allowInsecure: AllowInsecureCertFetch);
|
||||
(Cert, var certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName,
|
||||
verifyPeerCertByName: Utils.String2List(SelectedSource.VerifyPeerCertByName),
|
||||
allowInsecure: AllowInsecureCertFetch);
|
||||
UpdateCertTip(certError);
|
||||
}
|
||||
|
||||
@@ -489,7 +501,9 @@ public class AddServerViewModel : MyReactiveObject
|
||||
domain += $":{SelectedSource.Port}";
|
||||
}
|
||||
|
||||
var (certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName, allowInsecure: AllowInsecureCertFetch);
|
||||
var (certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName,
|
||||
verifyPeerCertByName: Utils.String2List(SelectedSource.VerifyPeerCertByName),
|
||||
allowInsecure: AllowInsecureCertFetch);
|
||||
Cert = CertPemManager.ConcatenatePemChain(certs);
|
||||
UpdateCertTip(certError);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
||||
|
||||
var dtNow = DateTime.Now;
|
||||
var lstModel = new List<ClashConnectionModel>();
|
||||
foreach (var item in connections ?? new())
|
||||
foreach (var item in connections ?? [])
|
||||
{
|
||||
var host = $"{(item.metadata.host.IsNullOrEmpty() ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
|
||||
@@ -85,7 +85,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
||||
Host = host,
|
||||
Time = (dtNow - item.start).TotalSeconds < 0 ? 1 : (dtNow - item.start).TotalSeconds,
|
||||
Elapsed = (dtNow - item.start).ToString(@"hh\:mm\:ss"),
|
||||
Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}"
|
||||
Chain = $"{item.rule} , {string.Join("->", item.chains ?? [])}"
|
||||
};
|
||||
|
||||
lstModel.Add(model);
|
||||
|
||||
@@ -67,7 +67,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedGroup,
|
||||
y => y != null && y.Name.IsNotEmpty())
|
||||
.Subscribe(c => RefreshProxyDetails(c));
|
||||
.Subscribe(RefreshProxyDetails);
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.RuleModeSelected,
|
||||
@@ -77,7 +77,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
this.WhenAnyValue(
|
||||
x => x.SortingSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(c => DoSortingSelected(c));
|
||||
.Subscribe(DoSortingSelected);
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
@@ -188,15 +188,14 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
ProxyGroups.Clear();
|
||||
|
||||
var proxyGroups = ClashApiManager.Instance.GetClashProxyGroups();
|
||||
if (proxyGroups != null && proxyGroups.Count > 0)
|
||||
if (proxyGroups is { Count: > 0 })
|
||||
{
|
||||
foreach (var it in proxyGroups)
|
||||
{
|
||||
if (it.name.IsNullOrEmpty() || !_proxies.ContainsKey(it.name))
|
||||
if (it.name.IsNullOrEmpty() || !_proxies.TryGetValue(it.name, out var item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = _proxies[it.name];
|
||||
if (!Global.allowSelectType.Contains(item.type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
@@ -230,7 +229,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
});
|
||||
}
|
||||
|
||||
if (ProxyGroups != null && ProxyGroups.Count > 0)
|
||||
if (ProxyGroups is { Count: > 0 })
|
||||
{
|
||||
if (selectedName != null && ProxyGroups.Any(t => t.Name == selectedName))
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||
});
|
||||
|
||||
this.WhenAnyValue(x => x.RayCustomDNSEnableCompatible, x => x.SBCustomDNSEnableCompatible)
|
||||
.Select(x => !(x.Item1 && x.Item2))
|
||||
.Select(x => x is not { Item1: true, Item2: true })
|
||||
.ToPropertyEx(this, x => x.IsSimpleDNSEnabled);
|
||||
|
||||
_ = Init();
|
||||
|
||||
@@ -14,10 +14,8 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
[Reactive] public bool NewPort4LAN { get; set; }
|
||||
[Reactive] public string User { get; set; }
|
||||
[Reactive] public string Pass { get; set; }
|
||||
[Reactive] public bool MuxEnabled { get; set; }
|
||||
[Reactive] public bool LogEnabled { get; set; }
|
||||
[Reactive] public string Loglevel { get; set; }
|
||||
[Reactive] public bool DefAllowInsecure { get; set; }
|
||||
[Reactive] public string DefFingerprint { get; set; }
|
||||
[Reactive] public string DefUserAgent { get; set; }
|
||||
[Reactive] public string SendThrough { get; set; }
|
||||
@@ -27,6 +25,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
[Reactive] public int? HyUpMbps { get; set; }
|
||||
[Reactive] public int? HyDownMbps { get; set; }
|
||||
[Reactive] public bool EnableFragment { get; set; }
|
||||
[Reactive] public bool EnableFinalFragment { get; set; }
|
||||
|
||||
#endregion Core
|
||||
|
||||
@@ -100,6 +99,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
[Reactive] public bool TunEnableIPv6Address { get; set; }
|
||||
[Reactive] public string TunIcmpRouting { get; set; }
|
||||
[Reactive] public bool TunEnableLegacyProtect { get; set; }
|
||||
[Reactive] public string TunRouteExcludeAddress { get; set; }
|
||||
|
||||
#endregion Tun mode
|
||||
|
||||
@@ -151,10 +151,8 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
NewPort4LAN = inbound.NewPort4LAN;
|
||||
User = inbound.User;
|
||||
Pass = inbound.Pass;
|
||||
MuxEnabled = _config.CoreBasicItem.MuxEnabled;
|
||||
LogEnabled = _config.CoreBasicItem.LogEnabled;
|
||||
Loglevel = _config.CoreBasicItem.Loglevel;
|
||||
DefAllowInsecure = _config.CoreBasicItem.DefAllowInsecure;
|
||||
DefFingerprint = _config.CoreBasicItem.DefFingerprint;
|
||||
DefUserAgent = _config.CoreBasicItem.DefUserAgent;
|
||||
SendThrough = _config.CoreBasicItem.SendThrough ?? string.Empty;
|
||||
@@ -164,6 +162,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
HyUpMbps = _config.HysteriaItem.UpMbps;
|
||||
HyDownMbps = _config.HysteriaItem.DownMbps;
|
||||
EnableFragment = _config.CoreBasicItem.EnableFragment;
|
||||
EnableFinalFragment = _config.CoreBasicItem.EnableFinalFragment;
|
||||
|
||||
#endregion Core
|
||||
|
||||
@@ -228,6 +227,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
|
||||
TunIcmpRouting = _config.TunModeItem.IcmpRouting;
|
||||
TunEnableLegacyProtect = _config.TunModeItem.EnableLegacyProtect;
|
||||
TunRouteExcludeAddress = Utils.List2String(_config.TunModeItem.RouteExcludeAddress, true);
|
||||
|
||||
#endregion Tun mode
|
||||
|
||||
@@ -236,12 +236,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
|
||||
private async Task InitCoreType()
|
||||
{
|
||||
if (_config.CoreTypeItem == null)
|
||||
{
|
||||
_config.CoreTypeItem = new List<CoreTypeItem>();
|
||||
}
|
||||
_config.CoreTypeItem ??= [];
|
||||
|
||||
foreach (EConfigType it in Enum.GetValues(typeof(EConfigType)))
|
||||
foreach (var it in Enum.GetValues<EConfigType>())
|
||||
{
|
||||
if (_config.CoreTypeItem.FindIndex(t => t.ConfigType == it) >= 0)
|
||||
{
|
||||
@@ -344,8 +341,6 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
}
|
||||
_config.CoreBasicItem.LogEnabled = LogEnabled;
|
||||
_config.CoreBasicItem.Loglevel = Loglevel;
|
||||
_config.CoreBasicItem.MuxEnabled = MuxEnabled;
|
||||
_config.CoreBasicItem.DefAllowInsecure = DefAllowInsecure;
|
||||
_config.CoreBasicItem.DefFingerprint = DefFingerprint;
|
||||
_config.CoreBasicItem.DefUserAgent = DefUserAgent;
|
||||
_config.CoreBasicItem.SendThrough = SendThrough.TrimEx();
|
||||
@@ -355,6 +350,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
_config.HysteriaItem.UpMbps = HyUpMbps ?? 0;
|
||||
_config.HysteriaItem.DownMbps = HyDownMbps ?? 0;
|
||||
_config.CoreBasicItem.EnableFragment = EnableFragment;
|
||||
_config.CoreBasicItem.EnableFinalFragment = EnableFinalFragment;
|
||||
|
||||
_config.GuiItem.AutoRun = AutoRun;
|
||||
_config.GuiItem.EnableStatistics = EnableStatistics;
|
||||
@@ -397,6 +393,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
|
||||
_config.TunModeItem.IcmpRouting = TunIcmpRouting;
|
||||
_config.TunModeItem.EnableLegacyProtect = TunEnableLegacyProtect;
|
||||
_config.TunModeItem.RouteExcludeAddress = Utils.String2List(TunRouteExcludeAddress);
|
||||
|
||||
//coreType
|
||||
await SaveCoreType();
|
||||
@@ -458,7 +455,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
item.CoreType = (ECoreType)Enum.Parse(typeof(ECoreType), type);
|
||||
item.CoreType = Enum.Parse<ECoreType>(type);
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -5,13 +5,10 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||
#region private prop
|
||||
|
||||
private string _serverFilter = string.Empty;
|
||||
private Dictionary<string, bool> _dicHeaderSort = new();
|
||||
private readonly Dictionary<string, bool> _dicHeaderSort = new();
|
||||
private string _subIndexId = string.Empty;
|
||||
|
||||
// ConfigType filter state: default include-mode with all types selected
|
||||
private List<EConfigType> _filterConfigTypes = new();
|
||||
|
||||
private bool _filterExclude = false;
|
||||
|
||||
#endregion private prop
|
||||
|
||||
@@ -33,18 +30,11 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||
public string ServerFilter { get; set; }
|
||||
|
||||
// Include/Exclude filter for ConfigType
|
||||
public List<EConfigType> FilterConfigTypes
|
||||
{
|
||||
get => _filterConfigTypes;
|
||||
set => this.RaiseAndSetIfChanged(ref _filterConfigTypes, value);
|
||||
}
|
||||
[Reactive]
|
||||
public List<EConfigType> FilterConfigTypes { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool FilterExclude
|
||||
{
|
||||
get => _filterExclude;
|
||||
set => this.RaiseAndSetIfChanged(ref _filterExclude, value);
|
||||
}
|
||||
public bool FilterExclude { get; set; }
|
||||
|
||||
#endregion ObservableCollection
|
||||
|
||||
@@ -91,11 +81,11 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||
try
|
||||
{
|
||||
FilterExclude = false;
|
||||
FilterConfigTypes = Enum.GetValues(typeof(EConfigType)).Cast<EConfigType>().ToList();
|
||||
FilterConfigTypes = Enum.GetValues<EConfigType>().ToList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
FilterConfigTypes = new();
|
||||
FilterConfigTypes = [];
|
||||
}
|
||||
|
||||
await RefreshSubscriptions();
|
||||
@@ -165,14 +155,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||
if (lstModel.Count > 0)
|
||||
{
|
||||
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
|
||||
if (selected != null)
|
||||
{
|
||||
SelectedProfile = selected;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedProfile = lstModel.First();
|
||||
}
|
||||
SelectedProfile = selected ?? lstModel.First();
|
||||
}
|
||||
|
||||
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
|
||||
@@ -213,7 +196,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||
}).OrderBy(t => t.Sort).ToList();
|
||||
|
||||
// Apply ConfigType filter (include or exclude)
|
||||
if (FilterConfigTypes != null && FilterConfigTypes.Count > 0)
|
||||
if (FilterConfigTypes is { Count: > 0 })
|
||||
{
|
||||
if (FilterExclude)
|
||||
{
|
||||
@@ -321,7 +304,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||
// External setter for ConfigType filter
|
||||
public void SetConfigTypeFilter(IEnumerable<EConfigType> types, bool exclude = false)
|
||||
{
|
||||
FilterConfigTypes = types?.Distinct().ToList() ?? new List<EConfigType>();
|
||||
FilterConfigTypes = types?.Distinct().ToList() ?? [];
|
||||
FilterExclude = exclude;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
|
||||
private List<ProfileItem> _lstProfile;
|
||||
private string _serverFilter = string.Empty;
|
||||
private Dictionary<string, bool> _dicHeaderSort = new();
|
||||
private readonly Dictionary<string, bool> _dicHeaderSort = new();
|
||||
private SpeedtestService? _speedtestService;
|
||||
private string? _pendingSelectIndexId;
|
||||
|
||||
@@ -190,7 +190,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
}, canEditRemove);
|
||||
SortServerResultCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SortServer(EServerColName.DelayVal.ToString());
|
||||
await SortServer(nameof(EServerColName.DelayVal));
|
||||
});
|
||||
RemoveInvalidServerResultCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
|
||||
@@ -73,7 +73,7 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
|
||||
}
|
||||
SelectedSource.Protocol = ProtocolItems?.ToList();
|
||||
SelectedSource.InboundTag = InboundTagItems?.ToList();
|
||||
SelectedSource.RuleType = RuleType.IsNullOrEmpty() ? null : (ERuleType)Enum.Parse(typeof(ERuleType), RuleType);
|
||||
SelectedSource.RuleType = RuleType.IsNullOrEmpty() ? null : Enum.Parse<ERuleType>(RuleType);
|
||||
|
||||
var hasRule = SelectedSource.Domain?.Count > 0
|
||||
|| SelectedSource.Ip?.Count > 0
|
||||
|
||||
@@ -86,7 +86,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||
|
||||
SelectedSource = new();
|
||||
SelectedRouting = routingItem;
|
||||
_rules = routingItem.Id.IsNullOrEmpty() ? new() : JsonUtils.Deserialize<List<RulesItem>>(SelectedRouting.RuleSet);
|
||||
_rules = routingItem.Id.IsNullOrEmpty() ? [] : JsonUtils.Deserialize<List<RulesItem>>(SelectedRouting.RuleSet);
|
||||
|
||||
RefreshRulesItems();
|
||||
}
|
||||
|
||||
@@ -313,9 +313,8 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
}
|
||||
|
||||
BlServers = true;
|
||||
for (var k = 0; k < lstModel.Count; k++)
|
||||
foreach (var it in lstModel)
|
||||
{
|
||||
var it = lstModel[k];
|
||||
var name = it.GetSummary();
|
||||
|
||||
var item = new ComboItem() { ID = it.IndexId, Text = name };
|
||||
|
||||
@@ -23,8 +23,8 @@ public partial class App : Application
|
||||
DataContext = StatusBarViewModel.Instance;
|
||||
}
|
||||
|
||||
desktop.Exit += OnExit;
|
||||
desktop.MainWindow = new MainWindow();
|
||||
var mainWindow = new MainWindow();
|
||||
desktop.MainWindow = mainWindow;
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
@@ -35,6 +35,8 @@ public partial class App : Application
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
#region MacOS Activation
|
||||
|
||||
private void OnMacOSActivated(object? sender, ActivatedEventArgs args)
|
||||
{
|
||||
if (args.Kind != ActivationKind.Reopen)
|
||||
@@ -42,17 +44,69 @@ public partial class App : Application
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow is not MainWindow mainWindow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var isMiniaturized = MacAppUtils.IsWindowMiniaturized(mainWindow);
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
((ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow as MainWindow)?.ShowHideWindow(true);
|
||||
if (isMiniaturized)
|
||||
{
|
||||
RestoreMacOSAccessoryPolicyAfterMiniaturize(mainWindow);
|
||||
mainWindow.ShowHideWindow(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AppManager.Instance.Config.UiItem.MacOSShowInDock)
|
||||
{
|
||||
MacAppUtils.SetActivationPolicyAccessory();
|
||||
}
|
||||
|
||||
mainWindow.ShowHideWindow(true);
|
||||
});
|
||||
}
|
||||
|
||||
private static void RestoreMacOSAccessoryPolicyAfterMiniaturize(MainWindow mainWindow)
|
||||
{
|
||||
if (AppManager.Instance.Config.UiItem.MacOSShowInDock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mainWindow
|
||||
.GetObservable(Window.WindowStateProperty)
|
||||
.Skip(1)
|
||||
.Where(state => state != WindowState.Minimized)
|
||||
.Take(1)
|
||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||
.Subscribe(_ => QueueMacOSAccessoryPolicyRestore(mainWindow));
|
||||
}
|
||||
|
||||
private static void QueueMacOSAccessoryPolicyRestore(MainWindow mainWindow)
|
||||
{
|
||||
// AppKit may keep isMiniaturized set until the Dock restore animation finishes.
|
||||
DispatcherTimer.RunOnce(() => RestoreMacOSAccessoryPolicy(mainWindow), TimeSpan.FromMilliseconds(300));
|
||||
}
|
||||
|
||||
private static void RestoreMacOSAccessoryPolicy(MainWindow mainWindow)
|
||||
{
|
||||
if (AppManager.Instance.Config.UiItem.MacOSShowInDock || MacAppUtils.IsWindowMiniaturized(mainWindow))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MacAppUtils.SetActivationPolicyAccessory();
|
||||
mainWindow.Activate();
|
||||
mainWindow.Focus();
|
||||
}
|
||||
|
||||
#endregion MacOS Activation
|
||||
|
||||
#region App Event
|
||||
|
||||
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
if (e.ExceptionObject != null)
|
||||
@@ -66,20 +120,20 @@ public partial class App : Application
|
||||
Logging.SaveLog("TaskScheduler_UnobservedTaskException", e.Exception);
|
||||
}
|
||||
|
||||
private void OnExit(object? sender, ControlledApplicationLifetimeExitEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private async void MenuAddServerViaClipboardClick(object? sender, EventArgs e)
|
||||
{
|
||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
try
|
||||
{
|
||||
if (desktop.MainWindow != null)
|
||||
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: not null })
|
||||
{
|
||||
AppEvents.AddServerViaClipboardRequested.Publish();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("MenuAddServerViaClipboardClick", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async void MenuExit_Click(object? sender, EventArgs e)
|
||||
@@ -87,4 +141,6 @@ public partial class App : Application
|
||||
await AppManager.Instance.AppExitAsync(false);
|
||||
AppManager.Instance.Shutdown(true);
|
||||
}
|
||||
|
||||
#endregion App Event
|
||||
}
|
||||
|
||||
@@ -4,7 +4,15 @@ public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewMode
|
||||
{
|
||||
public WindowBase()
|
||||
{
|
||||
Initialized += OnWindowInitialized;
|
||||
Loaded += OnLoaded;
|
||||
Loaded += (s, e) =>
|
||||
{
|
||||
if (Owner != null && !ShowInTaskbar)
|
||||
{
|
||||
CanMinimize = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void ReactiveWindowBase_Closed(object? sender, EventArgs e)
|
||||
@@ -12,30 +20,33 @@ public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewMode
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected virtual void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
private void OnWindowInitialized(object? sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppManager.Instance.Config, GetType().Name);
|
||||
if (sizeItem == null)
|
||||
if (sizeItem is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Width = sizeItem.Width;
|
||||
Height = sizeItem.Height;
|
||||
if (sizeItem.Width > 0 && !Width.Equals(sizeItem.Width))
|
||||
{
|
||||
Width = sizeItem.Width;
|
||||
}
|
||||
|
||||
var workingArea = (Screens.ScreenFromWindow(this) ?? Screens.Primary).WorkingArea;
|
||||
var scaling = (Utils.IsMacOS() ? null : VisualRoot?.RenderScaling) ?? 1.0;
|
||||
|
||||
var x = workingArea.X + ((workingArea.Width - (Width * scaling)) / 2);
|
||||
var y = workingArea.Y + ((workingArea.Height - (Height * scaling)) / 2);
|
||||
|
||||
Position = new PixelPoint((int)x, (int)y);
|
||||
if (sizeItem.Height > 0 && !Height.Equals(sizeItem.Height))
|
||||
{
|
||||
Height = sizeItem.Height;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
protected virtual void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
|
||||
@@ -2,24 +2,48 @@ namespace v2rayN.Desktop.Common;
|
||||
|
||||
public static class AppBuilderExtension
|
||||
{
|
||||
private static readonly string DefaultFontFamilyName =
|
||||
Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC");
|
||||
|
||||
public static AppBuilder WithFontByDefault(this AppBuilder appBuilder)
|
||||
{
|
||||
var fallbacks = new List<FontFallback>();
|
||||
|
||||
var notoSansSc = new FontFamily(Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC"));
|
||||
fallbacks.Add(new FontFallback { FontFamily = notoSansSc });
|
||||
var notoSansSc = new FontFamily(DefaultFontFamilyName);
|
||||
|
||||
if (OperatingSystem.IsLinux())
|
||||
fallbacks.Add(new FontFallback
|
||||
{
|
||||
fallbacks.Add(new FontFallback
|
||||
{
|
||||
FontFamily = new FontFamily("Noto Color Emoji")
|
||||
});
|
||||
FontFamily = notoSansSc
|
||||
});
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
AddFontFallback(fallbacks, "Segoe UI Emoji");
|
||||
AddFontFallback(fallbacks, "Segoe UI Symbol");
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
AddFontFallback(fallbacks, "Apple Color Emoji");
|
||||
AddFontFallback(fallbacks, "Apple Symbols");
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
AddFontFallback(fallbacks, "Noto Color Emoji");
|
||||
AddFontFallback(fallbacks, "Noto Sans Symbols");
|
||||
}
|
||||
|
||||
return appBuilder.With(new FontManagerOptions
|
||||
{
|
||||
DefaultFamilyName = DefaultFontFamilyName,
|
||||
FontFallbacks = fallbacks.ToArray()
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddFontFallback(List<FontFallback> fallbacks, string fontFamilyName)
|
||||
{
|
||||
fallbacks.Add(new FontFallback
|
||||
{
|
||||
FontFamily = new FontFamily(fontFamilyName)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ internal static class MacAppUtils
|
||||
sel_registerName("setActivationPolicy:"),
|
||||
ActivationPolicyAccessory);
|
||||
|
||||
public static bool IsWindowMiniaturized(Window window)
|
||||
=> window.TryGetPlatformHandle() is IMacOSTopLevelPlatformHandle { NSWindow: not 0 } handle
|
||||
&& objc_msgSend_bool(handle.NSWindow, sel_registerName("isMiniaturized"));
|
||||
|
||||
[DllImport(LibObjC)]
|
||||
private static extern nint objc_getClass(string name);
|
||||
|
||||
@@ -22,6 +26,10 @@ internal static class MacAppUtils
|
||||
[DllImport(LibObjC, EntryPoint = "objc_msgSend")]
|
||||
private static extern nint objc_msgSend(nint receiver, nint selector);
|
||||
|
||||
[DllImport(LibObjC, EntryPoint = "objc_msgSend")]
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
private static extern bool objc_msgSend_bool(nint receiver, nint selector);
|
||||
|
||||
[DllImport(LibObjC, EntryPoint = "objc_msgSend")]
|
||||
private static extern void objc_msgSend(nint receiver, nint selector, nint argument);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
{
|
||||
_config.UiItem.CurrentTheme = CurrentTheme;
|
||||
ModifyTheme();
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
_ = ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
{
|
||||
_config.UiItem.CurrentFontSize = CurrentFontSize;
|
||||
ModifyFontSize();
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
_ = ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -67,7 +67,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
{
|
||||
_config.UiItem.CurrentLanguage = CurrentLanguage;
|
||||
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
_ = ConfigHandler.SaveConfig(_config);
|
||||
NoticeManager.Instance.Enqueue(ResUI.NeedRebootTips);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
|
||||
btnCancel.Click += (s, e) => Close();
|
||||
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
|
||||
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != nameof(ECoreType.v2rayN)).ToList().AppendEmpty();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
||||
@@ -1060,7 +1060,7 @@
|
||||
Grid.Row="8"
|
||||
ColumnDefinitions="300,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
@@ -1108,12 +1108,12 @@
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbAllowInsecure}" />
|
||||
<ComboBox
|
||||
x:Name="cmbAllowInsecure"
|
||||
<ToggleSwitch
|
||||
x:Name="togAllowInsecure"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
@@ -1129,6 +1129,20 @@
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbVerifyPeerCertByName}" />
|
||||
<TextBox
|
||||
x:Name="txtVerifyPeerCertByName"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
|
||||
@@ -37,7 +37,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||
|
||||
cmbFingerprint.ItemsSource = Global.Fingerprints;
|
||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
||||
cmbAlpn.ItemsSource = Global.Alpns;
|
||||
|
||||
var lstStreamSecurity = new List<string> { string.Empty, Global.StreamSecurity };
|
||||
@@ -116,7 +115,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||
gridFinalmask.IsVisible = false;
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbAlpn.IsEnabled = false;
|
||||
cmbAllowInsecure.IsEnabled = false;
|
||||
togAllowInsecure.IsEnabled = false;
|
||||
|
||||
cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls;
|
||||
break;
|
||||
@@ -138,14 +137,14 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowsocks:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.SOCKS:
|
||||
@@ -158,13 +157,13 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Trojan:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Hysteria2:
|
||||
@@ -231,7 +230,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AllowInsecure, v => v.togAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
|
||||
@@ -240,6 +239,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||
this.Bind(ViewModel, vm => vm.AllowInsecureCertFetch, v => v.togAllowInsecureCertFetch.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AllowInsecureCertFetch, v => v.txtAllowInsecureCertFetchTips.IsVisible).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.VerifyPeerCertByName, v => v.txtVerifyPeerCertByName.Text).DisposeWith(disposables);
|
||||
|
||||
//reality
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
||||
|
||||
@@ -112,7 +112,7 @@ public partial class ClashConnectionsView : ReactiveUserControl<ClashConnections
|
||||
{
|
||||
try
|
||||
{
|
||||
List<ColumnItem> lvColumnItem = new();
|
||||
List<ColumnItem> lvColumnItem = [];
|
||||
foreach (var item2 in lstConnections.Columns)
|
||||
{
|
||||
if (item2.Tag == null)
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingViewModel>
|
||||
{
|
||||
private readonly List<object> _textBoxKeyEventItem = new();
|
||||
private readonly List<object> _textBoxKeyEventItem = [];
|
||||
|
||||
public GlobalHotkeySettingWindow()
|
||||
{
|
||||
|
||||
@@ -144,13 +144,13 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||
AppEvents.ShutdownRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||
.Subscribe(content => Shutdown(content))
|
||||
.Subscribe(Shutdown)
|
||||
.DisposeWith(disposables);
|
||||
|
||||
AppEvents.ShowHideWindowRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||
.Subscribe(blShow => ShowHideWindow(blShow))
|
||||
.Subscribe(ShowHideWindow)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
|
||||
@@ -413,7 +413,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||
public void ShowHideWindow(bool? blShow)
|
||||
{
|
||||
var bl = blShow ??
|
||||
(Utils.IsLinux()
|
||||
(Utils.IsLinux() || Utils.IsMacOS()
|
||||
? (!AppManager.Instance.ShowInTaskbar ^ (WindowState == WindowState.Minimized))
|
||||
: !AppManager.Instance.ShowInTaskbar);
|
||||
if (bl)
|
||||
|
||||
@@ -18,6 +18,8 @@ public partial class MessageBoxDialog : Window
|
||||
|
||||
btnYes.Click += BtnYes_Click;
|
||||
btnNo.Click += BtnNo_Click;
|
||||
|
||||
CanMinimize = false;
|
||||
}
|
||||
|
||||
private void BtnYes_Click(object? sender, RoutedEventArgs e)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<Grid
|
||||
Margin="{StaticResource Margin4}"
|
||||
ColumnDefinitions="Auto,Auto,*"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
@@ -180,19 +180,6 @@
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="11"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled"
|
||||
Grid.Row="11"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="12"
|
||||
Grid.Column="0"
|
||||
@@ -219,19 +206,6 @@
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="14"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsDefAllowInsecure}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togdefAllowInsecure"
|
||||
Grid.Row="14"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="15"
|
||||
Grid.Column="0"
|
||||
@@ -331,19 +305,19 @@
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsBindInterface}" />
|
||||
<TextBox
|
||||
x:Name="txtbindInterface"
|
||||
Text="{x:Static resx:ResUI.TbEnableFinalFragment}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togenableFinalFragment"
|
||||
Grid.Row="21"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="21"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsBindInterfaceTip}"
|
||||
Text="{x:Static resx:ResUI.TbEnableFinalFragmentTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
@@ -351,16 +325,36 @@
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsBindInterface}" />
|
||||
<TextBox
|
||||
x:Name="txtbindInterface"
|
||||
Grid.Row="22"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsBindInterfaceTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSendThrough}" />
|
||||
<TextBox
|
||||
x:Name="txtsendThrough"
|
||||
Grid.Row="22"
|
||||
Grid.Row="23"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Watermark="0.0.0.0" />
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Row="23"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -818,107 +812,132 @@
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsTunMode}">
|
||||
<Grid
|
||||
Margin="{StaticResource Margin4}"
|
||||
ColumnDefinitions="Auto,Auto,Auto"
|
||||
DockPanel.Dock="Top"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<Grid
|
||||
Margin="{StaticResource Margin4}"
|
||||
ColumnDefinitions="Auto,Auto,Auto"
|
||||
DockPanel.Dock="Top"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunAutoRoute}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togAutoRoute"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunAutoRoute}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togAutoRoute"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStrictRoute}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togStrictRoute"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStrictRoute}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togStrictRoute"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStack}" />
|
||||
<ComboBox
|
||||
x:Name="cmbStack"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStack}" />
|
||||
<ComboBox
|
||||
x:Name="cmbStack"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbMtu}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMtu"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
IsEditable="True" />
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbMtu}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMtu"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
IsEditable="True" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbIcmpRoutingPolicy}" />
|
||||
<ComboBox
|
||||
x:Name="cmbIcmpRoutingPolicy"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbIcmpRoutingPolicy}" />
|
||||
<ComboBox
|
||||
x:Name="cmbIcmpRoutingPolicy"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableIPv6Address}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableIPv6Address"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableIPv6Address}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableIPv6Address"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbLegacyProtect}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableLegacyProtect"
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbLegacyProtect}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableLegacyProtect"
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRouteExcludeAddress}" />
|
||||
<TextBox
|
||||
x:Name="txtRouteExcludeAddress"
|
||||
Grid.Row="9"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="TextArea"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRouteExcludeAddressTip}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreType}">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Avalonia.Media;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
@@ -71,10 +72,8 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
this.Bind(ViewModel, vm => vm.NewPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.User, v => v.txtuser.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Pass, v => v.txtpass.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.LogEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Loglevel, v => v.cmbloglevel.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefFingerprint, v => v.cmbdefFingerprint.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefUserAgent, v => v.cmbdefUserAgent.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.BindInterface, v => v.txtbindInterface.Text).DisposeWith(disposables);
|
||||
@@ -84,12 +83,12 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
this.Bind(ViewModel, vm => vm.HyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.HyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableFinalFragment, v => v.togenableFinalFragment.IsChecked).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.AutoRun, v => v.togAutoRun.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableStatistics, v => v.togEnableStatistics.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DisplayRealTimeSpeed, v => v.togDisplayRealTimeSpeed.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.KeepOlderDedupl, v => v.togKeepOlderDedupl.IsChecked).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.EnableAutoAdjustMainLvColWidth, v => v.togEnableAutoAdjustMainLvColWidth.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Hide2TrayWhenClose, v => v.togHide2TrayWhenClose.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MacOSShowInDock, v => v.togMacOSShowInDock.IsChecked).DisposeWith(disposables);
|
||||
@@ -122,6 +121,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);
|
||||
@@ -166,9 +166,10 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
var lstFonts = new List<string>();
|
||||
try
|
||||
{
|
||||
var lst = Avalonia.Media.FontManager.Current.SystemFonts
|
||||
var lst = FontManager.Current.SystemFonts
|
||||
.Select(t => t.Name)
|
||||
.Where(t => t.IsNotEmpty())
|
||||
.Append(GetDefaultFontFamilyName())
|
||||
.Where(IsInstalledFontFamilyName)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(t => t)
|
||||
.ToList();
|
||||
@@ -181,6 +182,20 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
return lstFonts;
|
||||
}
|
||||
|
||||
private static string GetDefaultFontFamilyName() => "Noto Sans SC";
|
||||
|
||||
private static bool IsInstalledFontFamilyName(string fontFamilyName)
|
||||
{
|
||||
return fontFamilyName.IsNotEmpty()
|
||||
&& !IsCssFontFamilyAlias(fontFamilyName);
|
||||
}
|
||||
|
||||
private static bool IsCssFontFamilyAlias(string fontFamilyName)
|
||||
{
|
||||
return fontFamilyName.StartsWith("-", StringComparison.Ordinal)
|
||||
|| fontFamilyName.Equals("BlinkMacSystemFont", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private void ClbdestOverride_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (ViewModel != null)
|
||||
|
||||
@@ -447,7 +447,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
{
|
||||
try
|
||||
{
|
||||
List<ColumnItem> lvColumnItem = new();
|
||||
List<ColumnItem> lvColumnItem = [];
|
||||
foreach (var item2 in lstProfiles.Columns)
|
||||
{
|
||||
if (item2.Tag == null)
|
||||
|
||||
@@ -47,10 +47,7 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
||||
switch (action)
|
||||
{
|
||||
case EViewAction.DispatcherRefreshIcon:
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
RefreshIcon();
|
||||
},
|
||||
Dispatcher.UIThread.Post(RefreshIcon,
|
||||
DispatcherPriority.Default);
|
||||
break;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
<AssemblyName>v2rayN</AssemblyName>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CETCompat>false</CETCompat>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -22,7 +22,7 @@ public partial class App : Application
|
||||
{
|
||||
var exePathKey = Utils.GetMd5(Utils.GetExePath());
|
||||
|
||||
var rebootas = (e.Args ?? Array.Empty<string>()).Any(t => t == Global.RebootAs);
|
||||
var rebootas = e.Args.Any(t => t == Global.RebootAs);
|
||||
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out var bCreatedNew);
|
||||
if (!rebootas && !bCreatedNew)
|
||||
{
|
||||
|
||||
@@ -38,9 +38,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
if (!_config.UiItem.ColorPrimaryName.IsNullOrEmpty())
|
||||
{
|
||||
var swatch = new SwatchesProvider().Swatches.FirstOrDefault(t => t.Name == _config.UiItem.ColorPrimaryName);
|
||||
if (swatch != null
|
||||
&& swatch.ExemplarHue != null
|
||||
&& swatch.ExemplarHue?.Color != null)
|
||||
if (swatch?.ExemplarHue?.Color is not null)
|
||||
{
|
||||
ChangePrimaryColor(swatch.ExemplarHue.Color);
|
||||
}
|
||||
@@ -67,7 +65,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
{
|
||||
_config.UiItem.CurrentTheme = CurrentTheme;
|
||||
ModifyTheme();
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
_ = ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -87,7 +85,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
{
|
||||
_config.UiItem.ColorPrimaryName = SelectedSwatch?.Name;
|
||||
ChangePrimaryColor(SelectedSwatch.ExemplarHue.Color);
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
_ = ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -100,7 +98,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
{
|
||||
_config.UiItem.CurrentFontSize = CurrentFontSize;
|
||||
ModifyFontSize();
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
_ = ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -113,7 +111,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
{
|
||||
_config.UiItem.CurrentLanguage = CurrentLanguage;
|
||||
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
|
||||
ConfigHandler.SaveConfig(_config);
|
||||
_ = ConfigHandler.SaveConfig(_config);
|
||||
NoticeManager.Instance.Enqueue(ResUI.NeedRebootTips);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ public partial class AddServer2Window
|
||||
Loaded += Window_Loaded;
|
||||
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
|
||||
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != nameof(ECoreType.v2rayN)).ToList().AppendEmpty();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
||||
@@ -1377,6 +1377,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" />
|
||||
@@ -1437,13 +1438,12 @@
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbAllowInsecure}" />
|
||||
<ComboBox
|
||||
x:Name="cmbAllowInsecure"
|
||||
<ToggleButton
|
||||
x:Name="togAllowInsecure"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
@@ -1461,6 +1461,22 @@
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbVerifyPeerCertByName}" />
|
||||
<TextBox
|
||||
x:Name="txtVerifyPeerCertByName"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
|
||||
@@ -36,7 +36,6 @@ public partial class AddServerWindow
|
||||
|
||||
cmbFingerprint.ItemsSource = Global.Fingerprints;
|
||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
||||
cmbAlpn.ItemsSource = Global.Alpns;
|
||||
|
||||
var lstStreamSecurity = new List<string> { string.Empty, Global.StreamSecurity };
|
||||
@@ -115,7 +114,7 @@ public partial class AddServerWindow
|
||||
gridFinalmask.Visibility = Visibility.Collapsed;
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbAlpn.IsEnabled = false;
|
||||
cmbAllowInsecure.IsEnabled = false;
|
||||
togAllowInsecure.IsEnabled = false;
|
||||
|
||||
cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls;
|
||||
break;
|
||||
@@ -137,14 +136,14 @@ public partial class AddServerWindow
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowsocks:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.SOCKS:
|
||||
@@ -157,13 +156,13 @@ public partial class AddServerWindow
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Trojan:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Hysteria2:
|
||||
@@ -229,7 +228,7 @@ public partial class AddServerWindow
|
||||
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AllowInsecure, v => v.togAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
|
||||
@@ -241,6 +240,7 @@ public partial class AddServerWindow
|
||||
.BindTo(this, v => v.txtAllowInsecureCertFetchTips.Visibility);
|
||||
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.VerifyPeerCertByName, v => v.txtVerifyPeerCertByName.Text).DisposeWith(disposables);
|
||||
|
||||
//reality
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
||||
|
||||
@@ -111,7 +111,7 @@ public partial class ClashConnectionsView
|
||||
{
|
||||
try
|
||||
{
|
||||
List<ColumnItem> lvColumnItem = new();
|
||||
List<ColumnItem> lvColumnItem = [];
|
||||
foreach (var col in lstConnections.Columns.Cast<MyDGTextColumn>())
|
||||
{
|
||||
var name = col.ExName;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace v2rayN.Views;
|
||||
|
||||
public partial class GlobalHotkeySettingWindow
|
||||
{
|
||||
private readonly List<object> _textBoxKeyEventItem = new();
|
||||
private readonly List<object> _textBoxKeyEventItem = [];
|
||||
|
||||
public GlobalHotkeySettingWindow()
|
||||
{
|
||||
|
||||
@@ -144,13 +144,13 @@ public partial class MainWindow
|
||||
AppEvents.ShutdownRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||
.Subscribe(content => Shutdown(content))
|
||||
.Subscribe(Shutdown)
|
||||
.DisposeWith(disposables);
|
||||
|
||||
AppEvents.ShowHideWindowRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||
.Subscribe(blShow => ShowHideWindow(blShow))
|
||||
.Subscribe(ShowHideWindow)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -227,20 +228,6 @@
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="11"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleButton
|
||||
x:Name="togmuxEnabled"
|
||||
Grid.Row="11"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="12"
|
||||
Grid.Column="0"
|
||||
@@ -271,20 +258,6 @@
|
||||
materialDesign:HintAssist.Hint="Level"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="14"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsDefAllowInsecure}" />
|
||||
<ToggleButton
|
||||
x:Name="togdefAllowInsecure"
|
||||
Grid.Row="14"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="15"
|
||||
Grid.Column="0"
|
||||
@@ -399,21 +372,20 @@
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsBindInterface}" />
|
||||
<TextBox
|
||||
x:Name="txtbindInterface"
|
||||
Text="{x:Static resx:ResUI.TbEnableFinalFragment}" />
|
||||
<ToggleButton
|
||||
x:Name="togenableFinalFragment"
|
||||
Grid.Row="21"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="21"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsBindInterfaceTip}"
|
||||
Text="{x:Static resx:ResUI.TbEnableFinalFragmentTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
@@ -422,17 +394,40 @@
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsBindInterface}" />
|
||||
<TextBox
|
||||
x:Name="txtbindInterface"
|
||||
Grid.Row="22"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsBindInterfaceTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSendThrough}" />
|
||||
<TextBox
|
||||
x:Name="txtsendThrough"
|
||||
Grid.Row="22"
|
||||
Grid.Row="23"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
materialDesign:HintAssist.Hint="0.0.0.0"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Row="23"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -1065,129 +1060,161 @@
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsTunMode}">
|
||||
<Grid Margin="{StaticResource Margin8}" DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<Grid Margin="{StaticResource Margin8}" DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunAutoRoute}" />
|
||||
<ToggleButton
|
||||
x:Name="togAutoRoute"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunAutoRoute}" />
|
||||
<ToggleButton
|
||||
x:Name="togAutoRoute"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStrictRoute}" />
|
||||
<ToggleButton
|
||||
x:Name="togStrictRoute"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStrictRoute}" />
|
||||
<ToggleButton
|
||||
x:Name="togStrictRoute"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStack}" />
|
||||
<ComboBox
|
||||
x:Name="cmbStack"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsTunStack}" />
|
||||
<ComboBox
|
||||
x:Name="cmbStack"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbMtu}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMtu"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbMtu}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMtu"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbIcmpRoutingPolicy}" />
|
||||
<ComboBox
|
||||
x:Name="cmbIcmpRoutingPolicy"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbIcmpRoutingPolicy}" />
|
||||
<ComboBox
|
||||
x:Name="cmbIcmpRoutingPolicy"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableIPv6Address}" />
|
||||
<ToggleButton
|
||||
x:Name="togEnableIPv6Address"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableIPv6Address}" />
|
||||
<ToggleButton
|
||||
x:Name="togEnableIPv6Address"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbLegacyProtect}" />
|
||||
<ToggleButton
|
||||
x:Name="togEnableLegacyProtect"
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbLegacyProtect}" />
|
||||
<ToggleButton
|
||||
x:Name="togEnableLegacyProtect"
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbRouteExcludeAddress}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtRouteExcludeAddress"
|
||||
Grid.Row="9"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Stretch"
|
||||
AcceptsReturn="True"
|
||||
BorderThickness="1"
|
||||
MinLines="2"
|
||||
Style="{StaticResource DefTextBox}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto" />
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbRouteExcludeAddressTip}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreType}">
|
||||
|
||||
@@ -68,10 +68,8 @@ public partial class OptionSettingWindow
|
||||
this.Bind(ViewModel, vm => vm.NewPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.User, v => v.txtuser.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Pass, v => v.txtpass.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.LogEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Loglevel, v => v.cmbloglevel.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefFingerprint, v => v.cmbdefFingerprint.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DefUserAgent, v => v.cmbdefUserAgent.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SendThrough, v => v.txtsendThrough.Text).DisposeWith(disposables);
|
||||
@@ -81,6 +79,7 @@ public partial class OptionSettingWindow
|
||||
this.Bind(ViewModel, vm => vm.HyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.HyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.EnableFinalFragment, v => v.togenableFinalFragment.IsChecked).DisposeWith(disposables);
|
||||
|
||||
//this.Bind(ViewModel, vm => vm.Kcpmtu, v => v.txtKcpmtu.Text).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.Kcptti, v => v.txtKcptti.Text).DisposeWith(disposables);
|
||||
@@ -126,6 +125,7 @@ public partial class OptionSettingWindow
|
||||
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunEnableLegacyProtect, v => v.togEnableLegacyProtect.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunRouteExcludeAddress, v => v.txtRouteExcludeAddress.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.Text).DisposeWith(disposables);
|
||||
|
||||
@@ -400,7 +400,7 @@ public partial class ProfilesView
|
||||
{
|
||||
try
|
||||
{
|
||||
List<ColumnItem> lvColumnItem = new();
|
||||
List<ColumnItem> lvColumnItem = [];
|
||||
foreach (var item2 in lstProfiles.Columns.Cast<MyDGTextColumn>())
|
||||
{
|
||||
lvColumnItem.Add(new()
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
|
||||
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CETCompat>false</CETCompat>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user