From 18ea6fdc0066120d8bd5d4c47027bf14a287dd1d Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 31 Oct 2025 20:25:45 +0800
Subject: [PATCH 01/21] Code clean
---
v2rayN/ServiceLib/Common/Utils.cs | 2 +-
v2rayN/ServiceLib/Common/WindowsJob.cs | 3 ---
v2rayN/ServiceLib/Handler/ConfigHandler.cs | 16 ++++++++--------
v2rayN/ServiceLib/Handler/CoreConfigHandler.cs | 2 +-
v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs | 2 +-
v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs | 4 ++--
.../ServiceLib/Manager/ActionPrecheckManager.cs | 2 +-
v2rayN/ServiceLib/Models/ProfileItem.cs | 2 +-
.../CoreConfig/V2ray/CoreConfigV2rayService.cs | 8 ++++----
.../V2ray/V2rayConfigTemplateService.cs | 6 +++---
.../Services/CoreConfig/V2ray/V2rayDnsService.cs | 4 ++--
.../CoreConfig/V2ray/V2rayInboundService.cs | 2 +-
.../CoreConfig/V2ray/V2rayOutboundService.cs | 14 +++++++-------
.../CoreConfig/V2ray/V2rayStatisticService.cs | 2 +-
v2rayN/ServiceLib/Services/UpdateService.cs | 6 +++---
.../ViewModels/ClashProxiesViewModel.cs | 4 ++--
.../ViewModels/OptionSettingViewModel.cs | 6 +++---
.../ServiceLib/ViewModels/ProfilesViewModel.cs | 2 +-
.../ViewModels/RoutingRuleSettingViewModel.cs | 6 +++---
.../ServiceLib/ViewModels/StatusBarViewModel.cs | 12 ++++++------
20 files changed, 51 insertions(+), 54 deletions(-)
diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs
index 7f842ddf..2075342a 100644
--- a/v2rayN/ServiceLib/Common/Utils.cs
+++ b/v2rayN/ServiceLib/Common/Utils.cs
@@ -424,7 +424,7 @@ public class Utils
// Handle IPv6 addresses, e.g., "[2001:db8::1]:443"
if (authority.StartsWith("[") && authority.Contains("]"))
{
- int closingBracketIndex = authority.LastIndexOf(']');
+ var closingBracketIndex = authority.LastIndexOf(']');
if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':')
{
// Port exists
diff --git a/v2rayN/ServiceLib/Common/WindowsJob.cs b/v2rayN/ServiceLib/Common/WindowsJob.cs
index f7fd2d74..dacf8586 100644
--- a/v2rayN/ServiceLib/Common/WindowsJob.cs
+++ b/v2rayN/ServiceLib/Common/WindowsJob.cs
@@ -1,6 +1,3 @@
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
namespace ServiceLib.Common;
/*
* See:
diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
index 8b5acff2..7d2ca056 100644
--- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
@@ -447,13 +447,13 @@ public static class ConfigHandler
/// 0 if successful, -1 if failed
public static async Task MoveServer(Config config, List lstProfile, int index, EMove eMove, int pos = -1)
{
- int count = lstProfile.Count;
+ var count = lstProfile.Count;
if (index < 0 || index > lstProfile.Count - 1)
{
return -1;
}
- for (int i = 0; i < lstProfile.Count; i++)
+ for (var i = 0; i < lstProfile.Count; i++)
{
ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
}
@@ -527,7 +527,7 @@ public static class ConfigHandler
return -1;
}
var ext = Path.GetExtension(fileName);
- string newFileName = $"{Utils.GetGuid()}{ext}";
+ var newFileName = $"{Utils.GetGuid()}{ext}";
//newFileName = Path.Combine(Utile.GetTempPath(), newFileName);
try
@@ -1356,7 +1356,7 @@ public static class ConfigHandler
}
continue;
}
- var profileItem = FmtHandler.ResolveConfig(str, out string msg);
+ var profileItem = FmtHandler.ResolveConfig(str, out var msg);
if (profileItem is null)
{
continue;
@@ -1440,7 +1440,7 @@ public static class ConfigHandler
{
await RemoveServersViaSubid(config, subid, isSub);
}
- int count = 0;
+ var count = 0;
foreach (var it in lstProfiles)
{
it.Subid = subid;
@@ -1530,7 +1530,7 @@ public static class ConfigHandler
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
if (lstSsServer?.Count > 0)
{
- int counter = 0;
+ var counter = 0;
foreach (var ssItem in lstSsServer)
{
ssItem.Subid = subid;
@@ -1705,7 +1705,7 @@ public static class ConfigHandler
var maxSort = 0;
if (await SQLiteHelper.Instance.TableAsync().CountAsync() > 0)
{
- var lstSubs = (await AppManager.Instance.SubItems());
+ var lstSubs = await AppManager.Instance.SubItems();
maxSort = lstSubs.LastOrDefault()?.Sort ?? 0;
}
item.Sort = maxSort + 1;
@@ -1867,7 +1867,7 @@ public static class ConfigHandler
/// 0 if successful, -1 if failed
public static async Task MoveRoutingRule(List rules, int index, EMove eMove, int pos = -1)
{
- int count = rules.Count;
+ var count = rules.Count;
if (index < 0 || index > rules.Count - 1)
{
return -1;
diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
index 96c72bb1..f51e2051 100644
--- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs
@@ -58,7 +58,7 @@ public static class CoreConfigHandler
File.Delete(fileName);
}
- string addressFileName = node.Address;
+ var addressFileName = node.Address;
if (!File.Exists(addressFileName))
{
addressFileName = Utils.GetConfigPath(addressFileName);
diff --git a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
index decda17f..4fc251b7 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs
@@ -37,7 +37,7 @@ public class FmtHandler
try
{
- string str = config.TrimEx();
+ var str = config.TrimEx();
if (str.IsNullOrEmpty())
{
msg = ResUI.FailedReadConfiguration;
diff --git a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
index 32044e17..5376780f 100644
--- a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
+++ b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs
@@ -33,9 +33,9 @@ public class Hysteria2Fmt : BaseFmt
{
if (item == null)
return null;
- string url = string.Empty;
+ var url = string.Empty;
- string remark = string.Empty;
+ var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
index 8d7077f1..a487401c 100644
--- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
+++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
@@ -85,7 +85,7 @@ public class ActionPrecheckManager(Config config)
break;
case EConfigType.VLESS:
- if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id) && item.Id.Length > 30)
+ if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30))
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
if (!Global.Flows.Contains(item.Flow))
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs
index 729fa7b2..fa84ac59 100644
--- a/v2rayN/ServiceLib/Models/ProfileItem.cs
+++ b/v2rayN/ServiceLib/Models/ProfileItem.cs
@@ -28,7 +28,7 @@ public class ProfileItem : ReactiveObject
public string GetSummary()
{
- var summary = $"[{(ConfigType).ToString()}] ";
+ var summary = $"[{ConfigType.ToString()}] ";
if (IsComplex())
{
summary += $"[{CoreType.ToString()}]{Remarks}";
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
index f7fb384a..6deff64d 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs
@@ -94,8 +94,8 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.InitialConfiguration;
- string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
- string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
+ var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
+ var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
@@ -200,8 +200,8 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.InitialConfiguration;
- string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
- string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
+ var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
+ var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs
index 986e1966..1f2583ff 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs
@@ -87,7 +87,7 @@ public partial class CoreConfigV2rayService
}
var customOutboundsNode = new JsonArray();
-
+
foreach (var outbound in v2rayConfig.outbounds)
{
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
@@ -112,7 +112,7 @@ public partial class CoreConfigV2rayService
}
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
}
-
+
if (fullConfigTemplateNode["outbounds"] is JsonArray templateOutbounds)
{
foreach (var outbound in templateOutbounds)
@@ -120,7 +120,7 @@ public partial class CoreConfigV2rayService
customOutboundsNode.Add(outbound?.DeepClone());
}
}
-
+
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs
index 7170c5f0..2218d013 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs
@@ -347,8 +347,8 @@ public partial class CoreConfigV2rayService
if (obj is null)
{
List servers = [];
- string[] arrDNS = normalDNS.Split(',');
- foreach (string str in arrDNS)
+ var arrDNS = normalDNS.Split(',');
+ foreach (var str in arrDNS)
{
servers.Add(str);
}
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs
index 7753c21e..2cbdfe88 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs
@@ -48,7 +48,7 @@ public partial class CoreConfigV2rayService
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
{
- string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
+ var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
if (result.IsNullOrEmpty())
{
return new();
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
index ef642ce5..39338c77 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
@@ -453,16 +453,16 @@ public partial class CoreConfigV2rayService
};
//request Host
- string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
- string[] arrHost = host.Split(',');
- string host2 = string.Join(",".AppendQuotes(), arrHost);
+ var request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
+ var arrHost = host.Split(',');
+ var host2 = string.Join(",".AppendQuotes(), arrHost);
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
//Path
- string pathHttp = @"/";
+ var pathHttp = @"/";
if (path.IsNotEmpty())
{
- string[] arrPath = path.Split(',');
+ var arrPath = path.Split(',');
pathHttp = string.Join(",".AppendQuotes(), arrPath);
}
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
@@ -623,10 +623,10 @@ public partial class CoreConfigV2rayService
// Cache for chain proxies to avoid duplicate generation
var nextProxyCache = new Dictionary();
var prevProxyTags = new Dictionary(); // Map from profile name to tag
- int prevIndex = 0; // Index for prev outbounds
+ var prevIndex = 0; // Index for prev outbounds
// Process nodes
- int index = 0;
+ var index = 0;
foreach (var node in nodes)
{
index++;
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs
index 1269a11f..b2ec37b4 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs
@@ -6,7 +6,7 @@ public partial class CoreConfigV2rayService
{
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
{
- string tag = EInboundProtocol.api.ToString();
+ var tag = EInboundProtocol.api.ToString();
Metrics4Ray apiObj = new();
Policy4Ray policyObj = new();
SystemPolicy4Ray policySystemSetting = new();
diff --git a/v2rayN/ServiceLib/Services/UpdateService.cs b/v2rayN/ServiceLib/Services/UpdateService.cs
index 16a69464..ac72b01d 100644
--- a/v2rayN/ServiceLib/Services/UpdateService.cs
+++ b/v2rayN/ServiceLib/Services/UpdateService.cs
@@ -167,7 +167,7 @@ public class UpdateService
try
{
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
- string filePath = string.Empty;
+ var filePath = string.Empty;
foreach (var name in coreInfo.CoreExes)
{
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
@@ -180,14 +180,14 @@ public class UpdateService
if (!File.Exists(filePath))
{
- string msg = string.Format(ResUI.NotFoundCore, @"", "", "");
+ var msg = string.Format(ResUI.NotFoundCore, @"", "", "");
//ShowMsg(true, msg);
return new SemanticVersion("");
}
var result = await Utils.GetCliWrapOutput(filePath, coreInfo.VersionArg);
var echo = result ?? "";
- string version = string.Empty;
+ var version = string.Empty;
switch (type)
{
case ECoreType.v2fly:
diff --git a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs
index eb67a9d4..3f636456 100644
--- a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs
@@ -211,7 +211,7 @@ public class ClashProxiesViewModel : MyReactiveObject
}
//from api
- foreach (KeyValuePair kv in _proxies)
+ foreach (var kv in _proxies)
{
if (!Global.allowSelectType.Contains(kv.Value.type.ToLower()))
{
@@ -319,7 +319,7 @@ public class ClashProxiesViewModel : MyReactiveObject
//from providers
if (_providers != null)
{
- foreach (KeyValuePair kv in _providers)
+ foreach (var kv in _providers)
{
if (Global.proxyVehicleType.Contains(kv.Value.vehicleType.ToLower()))
{
diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
index 8bf21763..5d4764b8 100644
--- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs
@@ -273,12 +273,12 @@ public class OptionSettingViewModel : MyReactiveObject
NoticeManager.Instance.Enqueue(ResUI.FillLocalListeningPort);
return;
}
- var needReboot = (EnableStatistics != _config.GuiItem.EnableStatistics
+ var needReboot = EnableStatistics != _config.GuiItem.EnableStatistics
|| DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed
|| EnableDragDropSort != _config.UiItem.EnableDragDropSort
|| EnableHWA != _config.GuiItem.EnableHWA
|| CurrentFontFamily != _config.UiItem.CurrentFontFamily
- || MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation);
+ || MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation;
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
@@ -375,7 +375,7 @@ public class OptionSettingViewModel : MyReactiveObject
private async Task SaveCoreType()
{
- for (int k = 1; k <= _config.CoreTypeItem.Count; k++)
+ for (var k = 1; k <= _config.CoreTypeItem.Count; k++)
{
var item = _config.CoreTypeItem[k - 1];
var type = string.Empty;
diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
index 1780e513..12f4804b 100644
--- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs
@@ -658,7 +658,7 @@ public class ProfilesViewModel : MyReactiveObject
}
_dicHeaderSort.TryAdd(colName, true);
- _dicHeaderSort.TryGetValue(colName, out bool asc);
+ _dicHeaderSort.TryGetValue(colName, out var asc);
if (await ConfigHandler.SortServers(_config, _config.SubIndexId, colName, asc) != 0)
{
return;
diff --git a/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs
index 445e61bc..71d42218 100644
--- a/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs
@@ -215,7 +215,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
private async Task SaveRoutingAsync()
{
- string remarks = SelectedRouting.Remarks;
+ var remarks = SelectedRouting.Remarks;
if (remarks.IsNullOrEmpty())
{
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
@@ -286,7 +286,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
return;
}
- DownloadService downloadHandle = new DownloadService();
+ var downloadHandle = new DownloadService();
var result = await downloadHandle.TryDownloadString(url, true, "");
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
if (ret == 0)
@@ -298,7 +298,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
private async Task AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData)
{
- bool blReplace = false;
+ var blReplace = false;
if (await _updateView?.Invoke(EViewAction.AddBatchRoutingRulesYesNo, null) == false)
{
blReplace = true;
diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
index 707722ad..42cdafa8 100644
--- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
@@ -313,10 +313,10 @@ public class StatusBarViewModel : MyReactiveObject
}
BlServers = true;
- for (int k = 0; k < lstModel.Count; k++)
+ for (var k = 0; k < lstModel.Count; k++)
{
ProfileItem it = lstModel[k];
- string name = it.GetSummary();
+ var name = it.GetSummary();
var item = new ComboItem() { ID = it.IndexId, Text = name };
Servers.Add(item);
@@ -394,10 +394,10 @@ public class StatusBarViewModel : MyReactiveObject
{
await SysProxyHandler.UpdateSysProxy(_config, false);
- BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
- BlSystemProxySet = (type == ESysProxyType.ForcedChange);
- BlSystemProxyNothing = (type == ESysProxyType.Unchanged);
- BlSystemProxyPac = (type == ESysProxyType.Pac);
+ BlSystemProxyClear = type == ESysProxyType.ForcedClear;
+ BlSystemProxySet = type == ESysProxyType.ForcedChange;
+ BlSystemProxyNothing = type == ESysProxyType.Unchanged;
+ BlSystemProxyPac = type == ESysProxyType.Pac;
if (blChange)
{
From 1b5069a933cba0c2d87240e3600442b5b7405bba Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 31 Oct 2025 20:25:50 +0800
Subject: [PATCH 02/21] Code clean
---
v2rayN/v2rayN/App.xaml.cs | 4 +--
v2rayN/v2rayN/Common/UI.cs | 2 +-
v2rayN/v2rayN/Common/WindowsUtils.cs | 12 ++++----
v2rayN/v2rayN/Manager/HotkeyManager.cs | 4 +--
v2rayN/v2rayN/Manager/WindowsManager.cs | 12 ++++----
.../v2rayN/Views/AddGroupServerWindow.xaml.cs | 12 ++++----
v2rayN/v2rayN/Views/AddServer2Window.xaml.cs | 8 +++---
v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 8 +++---
.../v2rayN/Views/BackupAndRestoreView.xaml.cs | 4 +--
v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs | 4 +--
.../Views/FullConfigTemplateWindow.xaml.cs | 4 +--
.../Views/GlobalHotkeySettingWindow.xaml.cs | 6 ++--
v2rayN/v2rayN/Views/MainWindow.xaml.cs | 28 +++++++++----------
v2rayN/v2rayN/Views/MsgView.xaml.cs | 4 +--
.../v2rayN/Views/OptionSettingWindow.xaml.cs | 10 +++----
.../v2rayN/Views/ProfilesSelectWindow.xaml.cs | 2 +-
v2rayN/v2rayN/Views/ProfilesView.xaml.cs | 18 ++++++------
.../Views/RoutingRuleDetailsWindow.xaml.cs | 6 ++--
.../Views/RoutingRuleSettingWindow.xaml.cs | 16 +++++------
.../v2rayN/Views/RoutingSettingWindow.xaml.cs | 16 +++++------
v2rayN/v2rayN/Views/StatusBarView.xaml.cs | 4 +--
v2rayN/v2rayN/Views/SubEditWindow.xaml.cs | 6 ++--
v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs | 14 +++++-----
23 files changed, 102 insertions(+), 102 deletions(-)
diff --git a/v2rayN/v2rayN/App.xaml.cs b/v2rayN/v2rayN/App.xaml.cs
index de9a5370..ca56311b 100644
--- a/v2rayN/v2rayN/App.xaml.cs
+++ b/v2rayN/v2rayN/App.xaml.cs
@@ -9,7 +9,7 @@ public partial class App : Application
public App()
{
- this.DispatcherUnhandledException += App_DispatcherUnhandledException;
+ DispatcherUnhandledException += App_DispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
@@ -23,7 +23,7 @@ public partial class App : Application
var exePathKey = Utils.GetMd5(Utils.GetExePath());
var rebootas = (e.Args ?? Array.Empty()).Any(t => t == Global.RebootAs);
- ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out bool bCreatedNew);
+ ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out var bCreatedNew);
if (!rebootas && !bCreatedNew)
{
ProgramStarted.Set();
diff --git a/v2rayN/v2rayN/Common/UI.cs b/v2rayN/v2rayN/Common/UI.cs
index 1d5e860c..4ad19a09 100644
--- a/v2rayN/v2rayN/Common/UI.cs
+++ b/v2rayN/v2rayN/Common/UI.cs
@@ -1,6 +1,6 @@
using Microsoft.Win32;
-namespace v2rayN;
+namespace v2rayN.Common;
internal class UI
{
diff --git a/v2rayN/v2rayN/Common/WindowsUtils.cs b/v2rayN/v2rayN/Common/WindowsUtils.cs
index 80778268..cc05f56d 100644
--- a/v2rayN/v2rayN/Common/WindowsUtils.cs
+++ b/v2rayN/v2rayN/Common/WindowsUtils.cs
@@ -3,7 +3,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
-namespace v2rayN;
+namespace v2rayN.Common;
internal static class WindowsUtils
{
@@ -40,13 +40,13 @@ internal static class WindowsUtils
}
[DllImport("dwmapi.dll")]
- public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
+ public static extern int DwmSetWindowAttribute(nint hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
public static ImageSource IconToImageSource(Icon icon)
{
return Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
- new System.Windows.Int32Rect(0, 0, icon.Width, icon.Height),
+ new Int32Rect(0, 0, icon.Width, icon.Height),
BitmapSizeOptions.FromEmptyOptions());
}
@@ -65,9 +65,9 @@ internal static class WindowsUtils
private static void SetDarkBorder(Window window, bool dark)
{
// Make sure the handle is created before the window is shown
- IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
- int attribute = dark ? 1 : 0;
- uint attributeSize = (uint)Marshal.SizeOf(attribute);
+ var hWnd = new WindowInteropHelper(window).EnsureHandle();
+ var attribute = dark ? 1 : 0;
+ var attributeSize = (uint)Marshal.SizeOf(attribute);
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, ref attribute, attributeSize);
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize);
}
diff --git a/v2rayN/v2rayN/Manager/HotkeyManager.cs b/v2rayN/v2rayN/Manager/HotkeyManager.cs
index dc9eec8e..6b6bc546 100644
--- a/v2rayN/v2rayN/Manager/HotkeyManager.cs
+++ b/v2rayN/v2rayN/Manager/HotkeyManager.cs
@@ -43,7 +43,7 @@ public sealed class HotkeyManager
modifiers |= KeyModifiers.Alt;
}
- key = key << 16 | (int)modifiers;
+ key = (key << 16) | (int)modifiers;
if (!_hotkeyTriggerDic.ContainsKey(key))
{
_hotkeyTriggerDic.Add(key, new() { item.EGlobalHotkey });
@@ -103,7 +103,7 @@ public sealed class HotkeyManager
private (int fsModifiers, int vKey, string hotkeyStr, List Names) GetHotkeyInfo(int hotkeyCode)
{
var fsModifiers = hotkeyCode & 0xffff;
- var vKey = hotkeyCode >> 16 & 0xffff;
+ var vKey = (hotkeyCode >> 16) & 0xffff;
var hotkeyStr = new StringBuilder();
var names = new List();
diff --git a/v2rayN/v2rayN/Manager/WindowsManager.cs b/v2rayN/v2rayN/Manager/WindowsManager.cs
index c3bb98d7..0b91f2fb 100644
--- a/v2rayN/v2rayN/Manager/WindowsManager.cs
+++ b/v2rayN/v2rayN/Manager/WindowsManager.cs
@@ -60,18 +60,18 @@ public sealed class WindowsManager
return null;
}
- Color color = ColorTranslator.FromHtml("#3399CC");
- int index = (int)config.SystemProxyItem.SysProxyType;
+ var color = ColorTranslator.FromHtml("#3399CC");
+ var index = (int)config.SystemProxyItem.SysProxyType;
if (index > 0)
{
color = (new[] { Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.DarkSlateBlue, Color.RoyalBlue })[index - 1];
}
- int width = 128;
- int height = 128;
+ var width = 128;
+ var height = 128;
Bitmap bitmap = new(width, height);
- Graphics graphics = Graphics.FromImage(bitmap);
+ var graphics = Graphics.FromImage(bitmap);
SolidBrush drawBrush = new(color);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
@@ -79,7 +79,7 @@ public sealed class WindowsManager
graphics.DrawImage(new Bitmap(item.CustomIcon), 0, 0, width, height);
graphics.FillEllipse(drawBrush, width / 2, width / 2, width / 2, width / 2);
- Icon createdIcon = Icon.FromHandle(bitmap.GetHicon());
+ var createdIcon = Icon.FromHandle(bitmap.GetHicon());
drawBrush.Dispose();
graphics.Dispose();
diff --git a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
index a08d7962..ad3c7ac6 100644
--- a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
@@ -6,9 +6,9 @@ public partial class AddGroupServerWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
- this.Loaded += Window_Loaded;
- this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
+ Owner = Application.Current.MainWindow;
+ Loaded += Window_Loaded;
+ PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
lstChild.SelectionChanged += LstChild_SelectionChanged;
menuSelectAllChild.Click += MenuSelectAllChild_Click;
@@ -27,11 +27,11 @@ public partial class AddGroupServerWindow
switch (profileItem.ConfigType)
{
case EConfigType.PolicyGroup:
- this.Title = ResUI.TbConfigTypePolicyGroup;
+ Title = ResUI.TbConfigTypePolicyGroup;
break;
case EConfigType.ProxyChain:
- this.Title = ResUI.TbConfigTypeProxyChain;
+ Title = ResUI.TbConfigTypeProxyChain;
gridPolicyGroup.Visibility = Visibility.Collapsed;
break;
}
@@ -61,7 +61,7 @@ public partial class AddGroupServerWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs b/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs
index 0f429a49..0ea4d085 100644
--- a/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs
+++ b/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs
@@ -6,8 +6,8 @@ public partial class AddServer2Window
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
- this.Loaded += Window_Loaded;
+ Owner = Application.Current.MainWindow;
+ Loaded += Window_Loaded;
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
cmbCoreType.ItemsSource = Utils.GetEnumNames().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
@@ -32,11 +32,11 @@ public partial class AddServer2Window
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
case EViewAction.BrowseServer:
- if (UI.OpenFileDialog(out string fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true)
+ if (UI.OpenFileDialog(out var fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true)
{
return false;
}
diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs
index 65383c32..ad6b6c9e 100644
--- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs
@@ -8,8 +8,8 @@ public partial class AddServerWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
- this.Loaded += Window_Loaded;
+ Owner = Application.Current.MainWindow;
+ Loaded += Window_Loaded;
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
@@ -191,7 +191,7 @@ public partial class AddServerWindow
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
- this.Title = $"{profileItem.ConfigType}";
+ Title = $"{profileItem.ConfigType}";
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
}
@@ -200,7 +200,7 @@ public partial class AddServerWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs b/v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs
index cc5a3610..56bb0b61 100644
--- a/v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs
+++ b/v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs
@@ -28,7 +28,7 @@ public partial class BackupAndRestoreView
private void MenuLocalBackup_Click(object sender, RoutedEventArgs e)
{
- if (UI.SaveFileDialog(out string fileName, "Zip|*.zip") != true)
+ if (UI.SaveFileDialog(out var fileName, "Zip|*.zip") != true)
{
return;
}
@@ -37,7 +37,7 @@ public partial class BackupAndRestoreView
private void MenuLocalRestore_Click(object sender, RoutedEventArgs e)
{
- if (UI.OpenFileDialog(out string fileName, "Zip|*.zip|All|*.*") != true)
+ if (UI.OpenFileDialog(out var fileName, "Zip|*.zip|All|*.*") != true)
{
return;
}
diff --git a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs
index 3eabd26f..4242063c 100644
--- a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs
@@ -8,7 +8,7 @@ public partial class DNSSettingWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
+ Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
@@ -78,7 +78,7 @@ public partial class DNSSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs b/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs
index e0cd8b14..a9f95a53 100644
--- a/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs
@@ -8,7 +8,7 @@ public partial class FullConfigTemplateWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
+ Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
@@ -35,7 +35,7 @@ public partial class FullConfigTemplateWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs b/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs
index f71fe7cc..f49fe80f 100644
--- a/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs
@@ -11,14 +11,14 @@ public partial class GlobalHotkeySettingWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
+ Owner = Application.Current.MainWindow;
ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
btnReset.Click += btnReset_Click;
HotkeyManager.Instance.IsPause = true;
- this.Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
+ Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
this.WhenActivated(disposables =>
{
@@ -35,7 +35,7 @@ public partial class GlobalHotkeySettingWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs
index a9114813..f6bab699 100644
--- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs
@@ -19,8 +19,8 @@ public partial class MainWindow
ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
App.Current.SessionEnding += Current_SessionEnding;
- this.Closing += MainWindow_Closing;
- this.PreviewKeyDown += MainWindow_PreviewKeyDown;
+ Closing += MainWindow_Closing;
+ PreviewKeyDown += MainWindow_PreviewKeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuClose.Click += menuClose_Click;
@@ -150,10 +150,10 @@ public partial class MainWindow
.DisposeWith(disposables);
});
- this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
+ Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
if (_config.UiItem.AutoHideStartup)
{
- this.WindowState = WindowState.Minimized;
+ WindowState = WindowState.Minimized;
}
if (!_config.GuiItem.EnableHWA)
@@ -187,35 +187,35 @@ public partial class MainWindow
case EViewAction.AddServerWindow:
if (obj is null)
return false;
- return (new AddServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
+ return new AddServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddServer2Window:
if (obj is null)
return false;
- return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
+ return new AddServer2Window((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddGroupServerWindow:
if (obj is null)
return false;
- return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
+ return new AddGroupServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.DNSSettingWindow:
- return (new DNSSettingWindow().ShowDialog() ?? false);
+ return new DNSSettingWindow().ShowDialog() ?? false;
case EViewAction.RoutingSettingWindow:
- return (new RoutingSettingWindow().ShowDialog() ?? false);
+ return new RoutingSettingWindow().ShowDialog() ?? false;
case EViewAction.OptionSettingWindow:
- return (new OptionSettingWindow().ShowDialog() ?? false);
+ return new OptionSettingWindow().ShowDialog() ?? false;
case EViewAction.FullConfigTemplateWindow:
- return (new FullConfigTemplateWindow().ShowDialog() ?? false);
+ return new FullConfigTemplateWindow().ShowDialog() ?? false;
case EViewAction.GlobalHotkeySettingWindow:
- return (new GlobalHotkeySettingWindow().ShowDialog() ?? false);
+ return new GlobalHotkeySettingWindow().ShowDialog() ?? false;
case EViewAction.SubSettingWindow:
- return (new SubSettingWindow().ShowDialog() ?? false);
+ return new SubSettingWindow().ShowDialog() ?? false;
case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync();
@@ -372,7 +372,7 @@ public partial class MainWindow
this?.Show();
if (this?.WindowState == WindowState.Minimized)
{
- this.WindowState = WindowState.Normal;
+ WindowState = WindowState.Normal;
}
this?.Activate();
this?.Focus();
diff --git a/v2rayN/v2rayN/Views/MsgView.xaml.cs b/v2rayN/v2rayN/Views/MsgView.xaml.cs
index 61fbe469..683e0787 100644
--- a/v2rayN/v2rayN/Views/MsgView.xaml.cs
+++ b/v2rayN/v2rayN/Views/MsgView.xaml.cs
@@ -31,10 +31,10 @@ public partial class MsgView
case EViewAction.DispatcherShowMsg:
if (obj is null)
return false;
- Application.Current?.Dispatcher.Invoke((() =>
+ Application.Current?.Dispatcher.Invoke(() =>
{
ShowMsg(obj);
- }), DispatcherPriority.ApplicationIdle);
+ }, DispatcherPriority.ApplicationIdle);
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
index 70d27894..409db086 100644
--- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs
@@ -10,7 +10,7 @@ public partial class OptionSettingWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
+ Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
@@ -136,7 +136,7 @@ public partial class OptionSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
case EViewAction.InitSettingFont:
@@ -168,12 +168,12 @@ public partial class OptionSettingWindow
foreach (var ttf in files)
{
var families = Fonts.GetFontFamilies(Utils.GetFontsPath(ttf));
- foreach (FontFamily family in families)
+ foreach (var family in families)
{
var typefaces = family.GetTypefaces();
- foreach (Typeface typeface in typefaces)
+ foreach (var typeface in typefaces)
{
- typeface.TryGetGlyphTypeface(out GlyphTypeface glyph);
+ typeface.TryGetGlyphTypeface(out var glyph);
//var fontFace = glyph.Win32FaceNames[new CultureInfo("en-us")];
//if (!fontFace.Equals("Regular") && !fontFace.Equals("Normal"))
//{
diff --git a/v2rayN/v2rayN/Views/ProfilesSelectWindow.xaml.cs b/v2rayN/v2rayN/Views/ProfilesSelectWindow.xaml.cs
index 1b8419a0..e357044c 100644
--- a/v2rayN/v2rayN/Views/ProfilesSelectWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/ProfilesSelectWindow.xaml.cs
@@ -71,7 +71,7 @@ public partial class ProfilesSelectWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs
index ac79a8c8..59e42b7b 100644
--- a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs
+++ b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs
@@ -127,7 +127,7 @@ public partial class ProfilesView
case EViewAction.SaveFileDialog:
if (obj is null)
return false;
- if (UI.SaveFileDialog(out string fileName, "Config|*.json") != true)
+ if (UI.SaveFileDialog(out var fileName, "Config|*.json") != true)
{
return false;
}
@@ -137,17 +137,17 @@ public partial class ProfilesView
case EViewAction.AddServerWindow:
if (obj is null)
return false;
- return (new AddServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
+ return new AddServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddServer2Window:
if (obj is null)
return false;
- return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
+ return new AddServer2Window((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddGroupServerWindow:
if (obj is null)
return false;
- return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
+ return new AddGroupServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.ShareServer:
if (obj is null)
@@ -158,7 +158,7 @@ public partial class ProfilesView
case EViewAction.SubEditWindow:
if (obj is null)
return false;
- return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false;
+ return new SubEditWindow((SubItem)obj).ShowDialog() ?? false;
case EViewAction.DispatcherRefreshServersBiz:
Application.Current?.Dispatcher.Invoke(RefreshServersBiz, DispatcherPriority.Normal);
@@ -415,8 +415,8 @@ public partial class ProfilesView
private void LstProfiles_MouseMove(object sender, MouseEventArgs e)
{
// Get the current mouse position
- Point mousePos = e.GetPosition(null);
- Vector diff = startPoint - mousePos;
+ var mousePos = e.GetPosition(null);
+ var diff = startPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
@@ -429,7 +429,7 @@ public partial class ProfilesView
if (listViewItem == null)
return; // Abort
// Find the data behind the ListViewItem
- ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
+ var item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
if (item == null)
return; // Abort
// Initialize the drag & drop operation
@@ -462,7 +462,7 @@ public partial class ProfilesView
return;
}
// Find the data behind the Item
- ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
+ var item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
if (item == null)
return;
// Move item into observable collection
diff --git a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs
index 0586067a..e824d324 100644
--- a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs
@@ -6,8 +6,8 @@ public partial class RoutingRuleDetailsWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
- this.Loaded += Window_Loaded;
+ Owner = Application.Current.MainWindow;
+ Loaded += Window_Loaded;
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
@@ -54,7 +54,7 @@ public partial class RoutingRuleDetailsWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs
index 153aaedc..b72dfc37 100644
--- a/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs
@@ -6,9 +6,9 @@ public partial class RoutingRuleSettingWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
- this.Loaded += Window_Loaded;
- this.PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown;
+ Owner = Application.Current.MainWindow;
+ Loaded += Window_Loaded;
+ PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown;
lstRules.SelectionChanged += lstRules_SelectionChanged;
lstRules.MouseDoubleClick += LstRules_MouseDoubleClick;
menuRuleSelectAll.Click += menuRuleSelectAll_Click;
@@ -57,7 +57,7 @@ public partial class RoutingRuleSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -80,11 +80,11 @@ public partial class RoutingRuleSettingWindow
if (obj is null)
return false;
- return (new RoutingRuleDetailsWindow((RulesItem)obj)).ShowDialog() ?? false;
+ return new RoutingRuleDetailsWindow((RulesItem)obj).ShowDialog() ?? false;
case EViewAction.ImportRulesFromFile:
- if (UI.OpenFileDialog(out string fileName, "Rules|*.json|All|*.*") != true)
+ if (UI.OpenFileDialog(out var fileName, "Rules|*.json|All|*.*") != true)
{
return false;
}
@@ -174,7 +174,7 @@ public partial class RoutingRuleSettingWindow
private void btnBrowseCustomIcon_Click(object sender, System.Windows.RoutedEventArgs e)
{
- if (UI.OpenFileDialog(out string fileName,
+ if (UI.OpenFileDialog(out var fileName,
"PNG,ICO|*.png;*.ico") != true)
{
return;
@@ -185,7 +185,7 @@ public partial class RoutingRuleSettingWindow
private void btnBrowseCustomRulesetPath4Singbox_Click(object sender, RoutedEventArgs e)
{
- if (UI.OpenFileDialog(out string fileName,
+ if (UI.OpenFileDialog(out var fileName,
"Config|*.json|All|*.*") != true)
{
return;
diff --git a/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs
index 565897b0..588ea228 100644
--- a/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs
@@ -6,9 +6,9 @@ public partial class RoutingSettingWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
- this.Closing += RoutingSettingWindow_Closing;
- this.PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown;
+ Owner = Application.Current.MainWindow;
+ Closing += RoutingSettingWindow_Closing;
+ PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.MouseDoubleClick += LstRoutings_MouseDoubleClick;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
@@ -44,7 +44,7 @@ public partial class RoutingSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -58,7 +58,7 @@ public partial class RoutingSettingWindow
if (obj is null)
return false;
- return (new RoutingRuleSettingWindow((RoutingItem)obj)).ShowDialog() ?? false;
+ return new RoutingRuleSettingWindow((RoutingItem)obj).ShowDialog() ?? false;
}
return await Task.FromResult(true);
}
@@ -67,7 +67,7 @@ public partial class RoutingSettingWindow
{
if (ViewModel?.IsModified == true)
{
- this.DialogResult = true;
+ DialogResult = true;
}
}
@@ -122,11 +122,11 @@ public partial class RoutingSettingWindow
{
if (ViewModel?.IsModified == true)
{
- this.DialogResult = true;
+ DialogResult = true;
}
else
{
- this.Close();
+ Close();
}
}
}
diff --git a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
index 906308ab..47cf90bf 100644
--- a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
+++ b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs
@@ -71,11 +71,11 @@ public partial class StatusBarView
switch (action)
{
case EViewAction.DispatcherRefreshIcon:
- Application.Current?.Dispatcher.Invoke((async () =>
+ Application.Current?.Dispatcher.Invoke(async () =>
{
tbNotify.Icon = await WindowsManager.Instance.GetNotifyIcon(_config);
Application.Current.MainWindow.Icon = WindowsManager.Instance.GetAppIcon(_config);
- }), DispatcherPriority.Normal);
+ }, DispatcherPriority.Normal);
break;
case EViewAction.SetClipboardData:
diff --git a/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs b/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs
index 488420fa..d1451a9c 100644
--- a/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs
@@ -6,8 +6,8 @@ public partial class SubEditWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
- this.Loaded += Window_Loaded;
+ Owner = Application.Current.MainWindow;
+ Loaded += Window_Loaded;
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
@@ -39,7 +39,7 @@ public partial class SubEditWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs
index 14b3aa38..2e4ffe8f 100644
--- a/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs
@@ -8,10 +8,10 @@ public partial class SubSettingWindow
{
InitializeComponent();
- this.Owner = Application.Current.MainWindow;
+ Owner = Application.Current.MainWindow;
ViewModel = new SubSettingViewModel(UpdateViewHandler);
- this.Closing += SubSettingWindow_Closing;
+ Closing += SubSettingWindow_Closing;
lstSubscription.MouseDoubleClick += LstSubscription_MouseDoubleClick;
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
menuClose.Click += menuClose_Click;
@@ -34,7 +34,7 @@ public partial class SubSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
- this.DialogResult = true;
+ DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -47,7 +47,7 @@ public partial class SubSettingWindow
case EViewAction.SubEditWindow:
if (obj is null)
return false;
- return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false;
+ return new SubEditWindow((SubItem)obj).ShowDialog() ?? false;
case EViewAction.ShareSub:
if (obj is null)
@@ -78,7 +78,7 @@ public partial class SubSettingWindow
{
if (ViewModel?.IsModified == true)
{
- this.DialogResult = true;
+ DialogResult = true;
}
}
@@ -99,11 +99,11 @@ public partial class SubSettingWindow
{
if (ViewModel?.IsModified == true)
{
- this.DialogResult = true;
+ DialogResult = true;
}
else
{
- this.Close();
+ Close();
}
}
}
From d727ff40bb8bf0792248326b161943af294ad57a Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Fri, 31 Oct 2025 20:25:54 +0800
Subject: [PATCH 03/21] Code clean
---
v2rayN/v2rayN.Desktop/GlobalUsings.cs | 6 ++---
.../Views/AddGroupServerWindow.axaml.cs | 12 +++++-----
.../Views/AddServer2Window.axaml.cs | 6 ++---
.../Views/AddServerWindow.axaml.cs | 8 +++----
.../Views/ClashProxiesView.axaml.cs | 2 +-
.../Views/DNSSettingWindow.axaml.cs | 4 ++--
.../Views/FullConfigTemplateWindow.axaml.cs | 4 ++--
.../Views/GlobalHotkeySettingWindow.axaml.cs | 6 ++---
.../v2rayN.Desktop/Views/MainWindow.axaml.cs | 24 +++++++++----------
.../Views/OptionSettingWindow.axaml.cs | 4 ++--
.../Views/RoutingRuleDetailsWindow.axaml.cs | 6 ++---
.../Views/RoutingRuleSettingWindow.axaml.cs | 8 +++----
.../Views/RoutingSettingWindow.axaml.cs | 10 ++++----
.../Views/SubEditWindow.axaml.cs | 4 ++--
.../Views/SubSettingWindow.axaml.cs | 8 +++----
.../Views/SudoPasswordInputView.axaml.cs | 2 +-
16 files changed, 57 insertions(+), 57 deletions(-)
diff --git a/v2rayN/v2rayN.Desktop/GlobalUsings.cs b/v2rayN/v2rayN.Desktop/GlobalUsings.cs
index 6a2f1d3b..e7499b01 100644
--- a/v2rayN/v2rayN.Desktop/GlobalUsings.cs
+++ b/v2rayN/v2rayN.Desktop/GlobalUsings.cs
@@ -17,13 +17,13 @@ global using Avalonia.Markup.Xaml;
global using Avalonia.Media;
global using Avalonia.Media.Imaging;
global using Avalonia.Platform;
-global using ReactiveUI.Avalonia;
global using Avalonia.Styling;
global using Avalonia.Threading;
-global using ReactiveUI;
-global using ReactiveUI.Fody.Helpers;
global using DynamicData;
global using MsBox.Avalonia.Enums;
+global using ReactiveUI;
+global using ReactiveUI.Avalonia;
+global using ReactiveUI.Fody.Helpers;
global using ServiceLib;
global using ServiceLib.Base;
global using ServiceLib.Common;
diff --git a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
index 0430a57c..a75f0e43 100644
--- a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
@@ -13,8 +13,8 @@ public partial class AddGroupServerWindow : WindowBase
{
InitializeComponent();
- this.Loaded += Window_Loaded;
- btnCancel.Click += (s, e) => this.Close();
+ Loaded += Window_Loaded;
+ btnCancel.Click += (s, e) => Close();
lstChild.SelectionChanged += LstChild_SelectionChanged;
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
@@ -32,11 +32,11 @@ public partial class AddGroupServerWindow : WindowBase
switch (profileItem.ConfigType)
{
case EConfigType.PolicyGroup:
- this.Title = ResUI.TbConfigTypePolicyGroup;
+ Title = ResUI.TbConfigTypePolicyGroup;
break;
case EConfigType.ProxyChain:
- this.Title = ResUI.TbConfigTypeProxyChain;
+ Title = ResUI.TbConfigTypeProxyChain;
gridPolicyGroup.IsVisible = false;
break;
}
@@ -64,7 +64,7 @@ public partial class AddGroupServerWindow : WindowBase
menuSelectAllChild.Click += (s, e) => lstChild.SelectAll();
// Keyboard shortcuts when focus is within grid
- this.AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
+ AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
lstChild.LoadingRow += LstChild_LoadingRow;
}
@@ -78,7 +78,7 @@ public partial class AddGroupServerWindow : WindowBase
switch (action)
{
case EViewAction.CloseWindow:
- this.Close(true);
+ Close(true);
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN.Desktop/Views/AddServer2Window.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServer2Window.axaml.cs
index 2e6731b7..f1624a6b 100644
--- a/v2rayN/v2rayN.Desktop/Views/AddServer2Window.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/AddServer2Window.axaml.cs
@@ -14,8 +14,8 @@ public partial class AddServer2Window : WindowBase
{
InitializeComponent();
- this.Loaded += Window_Loaded;
- btnCancel.Click += (s, e) => this.Close();
+ Loaded += Window_Loaded;
+ btnCancel.Click += (s, e) => Close();
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
cmbCoreType.ItemsSource = Utils.GetEnumNames().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
@@ -39,7 +39,7 @@ public partial class AddServer2Window : WindowBase
switch (action)
{
case EViewAction.CloseWindow:
- this.Close(true);
+ Close(true);
break;
case EViewAction.BrowseServer:
diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs
index 255ace75..5acf6915 100644
--- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs
@@ -13,8 +13,8 @@ public partial class AddServerWindow : WindowBase
{
InitializeComponent();
- this.Loaded += Window_Loaded;
- btnCancel.Click += (s, e) => this.Close();
+ Loaded += Window_Loaded;
+ btnCancel.Click += (s, e) => Close();
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
@@ -196,7 +196,7 @@ public partial class AddServerWindow : WindowBase
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
- this.Title = $"{profileItem.ConfigType}";
+ Title = $"{profileItem.ConfigType}";
}
private async Task UpdateViewHandler(EViewAction action, object? obj)
@@ -204,7 +204,7 @@ public partial class AddServerWindow : WindowBase
switch (action)
{
case EViewAction.CloseWindow:
- this.Close(true);
+ Close(true);
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs
index 9a86846b..42068af4 100644
--- a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs
@@ -7,7 +7,7 @@ public partial class ClashProxiesView : ReactiveUserControl
{
diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
index a37f2c14..e673a025 100644
--- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs
@@ -12,7 +12,7 @@ public partial class DNSSettingWindow : WindowBase
_config = AppManager.Instance.Config;
Loaded += Window_Loaded;
- btnCancel.Click += (s, e) => this.Close();
+ btnCancel.Click += (s, e) => Close();
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms;
@@ -77,7 +77,7 @@ public partial class DNSSettingWindow : WindowBase
switch (action)
{
case EViewAction.CloseWindow:
- this.Close(true);
+ Close(true);
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs
index 1a8d5061..bfe0c2c5 100644
--- a/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs
@@ -12,7 +12,7 @@ public partial class FullConfigTemplateWindow : WindowBase this.Close();
+ btnCancel.Click += (s, e) => Close();
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
@@ -36,7 +36,7 @@ public partial class FullConfigTemplateWindow : WindowBase HotkeyManager.Instance.IsPause = false;
- btnCancel.Click += (s, e) => this.Close();
+ Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
+ btnCancel.Click += (s, e) => Close();
this.WhenActivated(disposables =>
{
@@ -34,7 +34,7 @@ public partial class GlobalHotkeySettingWindow : WindowBase
_config = AppManager.Instance.Config;
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.TopRight };
- this.KeyDown += MainWindow_KeyDown;
+ KeyDown += MainWindow_KeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuCheckUpdate.Click += MenuCheckUpdate_Click;
@@ -153,14 +153,14 @@ public partial class MainWindow : WindowBase
if (Utils.IsWindows())
{
- this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
+ Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
HotkeyManager.Instance.Init(_config, OnHotkeyHandler);
}
else
{
- this.Title = $"{Utils.GetVersion()}";
+ Title = $"{Utils.GetVersion()}";
menuRebootAsAdmin.IsVisible = false;
menuSettingsSetUWP.IsVisible = false;
@@ -170,7 +170,7 @@ public partial class MainWindow : WindowBase
if (_config.UiItem.AutoHideStartup && Utils.IsWindows())
{
- this.WindowState = WindowState.Minimized;
+ WindowState = WindowState.Minimized;
}
AddHelpMenuItem();
@@ -407,27 +407,27 @@ public partial class MainWindow : WindowBase
: !_config.UiItem.ShowInTaskbar);
if (bl)
{
- this.Show();
- if (this.WindowState == WindowState.Minimized)
+ Show();
+ if (WindowState == WindowState.Minimized)
{
- this.WindowState = WindowState.Normal;
+ WindowState = WindowState.Normal;
}
- this.Activate();
- this.Focus();
+ Activate();
+ Focus();
}
else
{
if (Utils.IsLinux() && _config.UiItem.Hide2TrayWhenClose == false)
{
- this.WindowState = WindowState.Minimized;
+ WindowState = WindowState.Minimized;
return;
}
- foreach (var ownedWindow in this.OwnedWindows)
+ foreach (var ownedWindow in OwnedWindows)
{
ownedWindow.Close();
}
- this.Hide();
+ Hide();
}
_config.UiItem.ShowInTaskbar = bl;
diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
index bfebcddd..dd918007 100644
--- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs
@@ -11,7 +11,7 @@ public partial class OptionSettingWindow : WindowBase
InitializeComponent();
Loaded += Window_Loaded;
- btnCancel.Click += (s, e) => this.Close();
+ btnCancel.Click += (s, e) => Close();
_config = AppManager.Instance.Config;
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
@@ -153,7 +153,7 @@ public partial class OptionSettingWindow : WindowBase
switch (action)
{
case EViewAction.CloseWindow:
- this.Close(true);
+ Close(true);
break;
case EViewAction.InitSettingFont:
diff --git a/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs
index 6c3f663d..e2979244 100644
--- a/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs
@@ -13,8 +13,8 @@ public partial class RoutingRuleDetailsWindow : WindowBase this.Close();
+ Loaded += Window_Loaded;
+ btnCancel.Click += (s, e) => Close();
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
@@ -61,7 +61,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase this.Close();
- this.KeyDown += RoutingRuleSettingWindow_KeyDown;
+ Loaded += Window_Loaded;
+ btnCancel.Click += (s, e) => Close();
+ KeyDown += RoutingRuleSettingWindow_KeyDown;
lstRules.SelectionChanged += lstRules_SelectionChanged;
lstRules.DoubleTapped += LstRules_DoubleTapped;
menuRuleSelectAll.Click += menuRuleSelectAll_Click;
@@ -64,7 +64,7 @@ public partial class RoutingRuleSettingWindow : WindowBase
InitializeComponent();
Loaded += Window_Loaded;
- this.Closing += RoutingSettingWindow_Closing;
- btnCancel.Click += (s, e) => this.Close();
- this.KeyDown += RoutingSettingWindow_KeyDown;
+ Closing += RoutingSettingWindow_Closing;
+ btnCancel.Click += (s, e) => Close();
+ KeyDown += RoutingSettingWindow_KeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.DoubleTapped += LstRoutings_DoubleTapped;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
@@ -48,7 +48,7 @@ public partial class RoutingSettingWindow : WindowBase
switch (action)
{
case EViewAction.CloseWindow:
- this.Close(true);
+ Close(true);
break;
case EViewAction.ShowYesNo:
@@ -116,7 +116,7 @@ public partial class RoutingSettingWindow : WindowBase
private void btnCancel_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
- this.Close(ViewModel?.IsModified);
+ Close(ViewModel?.IsModified);
}
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
diff --git a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs
index a25dcd28..9d749ba8 100644
--- a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs
@@ -14,7 +14,7 @@ public partial class SubEditWindow : WindowBase
InitializeComponent();
Loaded += Window_Loaded;
- btnCancel.Click += (s, e) => this.Close();
+ btnCancel.Click += (s, e) => Close();
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
@@ -45,7 +45,7 @@ public partial class SubEditWindow : WindowBase
switch (action)
{
case EViewAction.CloseWindow:
- this.Close(true);
+ Close(true);
break;
}
return await Task.FromResult(true);
diff --git a/v2rayN/v2rayN.Desktop/Views/SubSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/SubSettingWindow.axaml.cs
index cf0f5d75..970f5c5b 100644
--- a/v2rayN/v2rayN.Desktop/Views/SubSettingWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/SubSettingWindow.axaml.cs
@@ -14,8 +14,8 @@ public partial class SubSettingWindow : WindowBase
menuClose.Click += menuClose_Click;
Loaded += Window_Loaded;
- this.Closing += SubSettingWindow_Closing;
- this.KeyDown += SubSettingWindow_KeyDown;
+ Closing += SubSettingWindow_Closing;
+ KeyDown += SubSettingWindow_KeyDown;
ViewModel = new SubSettingViewModel(UpdateViewHandler);
lstSubscription.DoubleTapped += LstSubscription_DoubleTapped;
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
@@ -37,7 +37,7 @@ public partial class SubSettingWindow : WindowBase
switch (action)
{
case EViewAction.CloseWindow:
- this.Close();
+ Close();
break;
case EViewAction.ShowYesNo:
@@ -89,7 +89,7 @@ public partial class SubSettingWindow : WindowBase
private void menuClose_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
- this.Close(ViewModel?.IsModified);
+ Close(ViewModel?.IsModified);
}
private void SubSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
diff --git a/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs
index cb5182eb..6373c683 100644
--- a/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs
@@ -9,7 +9,7 @@ public partial class SudoPasswordInputView : UserControl
{
InitializeComponent();
- this.Loaded += (s, e) => txtPassword.Focus();
+ Loaded += (s, e) => txtPassword.Focus();
btnSave.Click += async (_, _) => await SavePasswordAsync();
From 7b5686cd8f56f07e96250fdec03bfa5f35acbc51 Mon Sep 17 00:00:00 2001
From: 2dust <31833384+2dust@users.noreply.github.com>
Date: Sat, 1 Nov 2025 21:13:39 +0800
Subject: [PATCH 04/21] In the policy group, automatically add filtered
configurations from the subscription group.
https://github.com/2dust/v2rayN/issues/8214
---
.../Manager/ActionPrecheckManager.cs | 8 +++-
.../Manager/ProfileGroupItemManager.cs | 34 +++++++++++++--
v2rayN/ServiceLib/Models/ProfileGroupItem.cs | 9 ++++
v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 11 ++++-
v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 3 ++
v2rayN/ServiceLib/Resx/ResUI.fr.resx | 5 ++-
v2rayN/ServiceLib/Resx/ResUI.hu.resx | 3 ++
v2rayN/ServiceLib/Resx/ResUI.resx | 3 ++
v2rayN/ServiceLib/Resx/ResUI.ru.resx | 3 ++
v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 3 ++
v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 ++
.../ViewModels/AddGroupServerViewModel.cs | 32 +++++++++++----
.../Views/AddGroupServerWindow.axaml | 36 +++++++++++++++-
.../Views/AddGroupServerWindow.axaml.cs | 3 ++
v2rayN/v2rayN/Views/AddGroupServerWindow.xaml | 41 +++++++++++++++++++
.../v2rayN/Views/AddGroupServerWindow.xaml.cs | 3 ++
16 files changed, 184 insertions(+), 16 deletions(-)
diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
index a487401c..f4f998b3 100644
--- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
+++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs
@@ -115,7 +115,7 @@ public class ActionPrecheckManager(Config config)
if (item.ConfigType.IsGroupType())
{
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
- if (group is null || group.ChildItems.IsNullOrEmpty())
+ if (group is null || group.NotHasChild())
{
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
return errors;
@@ -128,7 +128,11 @@ public class ActionPrecheckManager(Config config)
return errors;
}
- foreach (var child in Utils.String2List(group.ChildItems))
+ var childIds = Utils.String2List(group.ChildItems) ?? [];
+ var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
+ childIds.AddRange(subItems.Select(p => p.IndexId));
+
+ foreach (var child in childIds)
{
var childErrors = new List();
if (child.IsNullOrEmpty())
diff --git a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
index bf52dcb2..6cd260d3 100644
--- a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
+++ b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs
@@ -220,11 +220,14 @@ public class ProfileGroupItemManager
public static async Task<(List Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId)
{
Instance.TryGet(indexId, out var profileGroupItem);
- if (profileGroupItem == null || profileGroupItem.ChildItems.IsNullOrEmpty())
+ if (profileGroupItem == null || profileGroupItem.NotHasChild())
{
return (new List(), profileGroupItem);
}
var items = await GetChildProfileItems(profileGroupItem);
+ var subItems = await GetSubChildProfileItems(profileGroupItem);
+ items.AddRange(subItems);
+
return (items, profileGroupItem);
}
@@ -248,14 +251,39 @@ public class ProfileGroupItemManager
return childProfiles;
}
+ public static async Task> GetSubChildProfileItems(ProfileGroupItem? group)
+ {
+ if (group == null || group.SubChildItems.IsNullOrEmpty())
+ {
+ return new();
+ }
+ var childProfiles = await AppManager.Instance.ProfileItems(group.SubChildItems);
+
+ return childProfiles.Where(p =>
+ p != null &&
+ p.IsValid() &&
+ !p.ConfigType.IsComplexType() &&
+ (group.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, group.Filter))
+ )
+ .ToList();
+ }
+
public static async Task> GetAllChildDomainAddresses(string indexId)
{
// include grand children
var childAddresses = new HashSet();
- if (!Instance.TryGet(indexId, out var groupItem) || groupItem.ChildItems.IsNullOrEmpty())
+ if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null)
+ {
return childAddresses;
+ }
- var childIds = Utils.String2List(groupItem.ChildItems);
+ if (groupItem.SubChildItems.IsNotEmpty())
+ {
+ var subItems = await GetSubChildProfileItems(groupItem);
+ subItems.ForEach(p => childAddresses.Add(p.Address));
+ }
+
+ var childIds = Utils.String2List(groupItem.ChildItems) ?? [];
foreach (var childId in childIds)
{
diff --git a/v2rayN/ServiceLib/Models/ProfileGroupItem.cs b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs
index c6131275..12c0f899 100644
--- a/v2rayN/ServiceLib/Models/ProfileGroupItem.cs
+++ b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs
@@ -8,5 +8,14 @@ public class ProfileGroupItem
public string ChildItems { get; set; }
+ public string? SubChildItems { get; set; }
+
+ public string? Filter { get; set; }
+
public EMultipleLoad MultipleLoad { get; set; } = EMultipleLoad.LeastPing;
+
+ public bool NotHasChild()
+ {
+ return string.IsNullOrWhiteSpace(ChildItems) && string.IsNullOrWhiteSpace(SubChildItems);
+ }
}
diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
index 90b25530..90a44ae5 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
+++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
@@ -19,7 +19,7 @@ namespace ServiceLib.Resx {
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ResUI {
@@ -2958,6 +2958,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Auto add filtered configuration from subscription groups 的本地化字符串。
+ ///
+ public static string TbPolicyGroupSubChildTip {
+ get {
+ return ResourceManager.GetString("TbPolicyGroupSubChildTip", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Policy Group Type 的本地化字符串。
///
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
index 08d4d111..edbd21da 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
@@ -1599,4 +1599,7 @@
Test real delay
+
+ Auto add filtered configuration from subscription groups
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx
index a56802e3..542d9ef9 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx
@@ -1596,4 +1596,7 @@
Test 1-clic de latence réelle
-
+
+ Auto add filtered configuration from subscription groups
+
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
index abb43c0a..e61f279e 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
@@ -1599,4 +1599,7 @@
Test real delay
+
+ Auto add filtered configuration from subscription groups
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index 9a1639ce..f1123407 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1599,4 +1599,7 @@
Test real delay
+
+ Auto add filtered configuration from subscription groups
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
index 67067b9a..14edb9b8 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
@@ -1599,4 +1599,7 @@
Test real delay
+
+ Auto add filtered configuration from subscription groups
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
index 96c470c6..cfb89284 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
@@ -1596,4 +1596,7 @@
一键测试真连接延迟
+
+ 自动从订阅分组添加过滤后的配置
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
index d03b0d20..4a3822b4 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
@@ -1596,4 +1596,7 @@
一鍵測試真連線延遲
+
+ 自動從訂閱分組新增過濾後的配置
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs
index b5bfe80d..5b0778a5 100644
--- a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs
@@ -17,6 +17,14 @@ public class AddGroupServerViewModel : MyReactiveObject
[Reactive]
public string? PolicyGroupType { get; set; }
+ [Reactive]
+ public SubItem? SelectedSubItem { get; set; }
+
+ [Reactive]
+ public string? Filter { get; set; }
+
+ public IObservableCollection SubItems { get; } = new ObservableCollectionExtended();
+
public IObservableCollection ChildItemsObs { get; } = new ObservableCollectionExtended();
//public ReactiveCommand AddCmd { get; }
@@ -64,10 +72,14 @@ public class AddGroupServerViewModel : MyReactiveObject
});
SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem);
-
CoreType = (SelectedSource?.CoreType ?? ECoreType.Xray).ToString();
- ProfileGroupItemManager.Instance.TryGet(profileItem.IndexId, out var profileGroup);
+ _ = Init();
+ }
+
+ public async Task Init()
+ {
+ ProfileGroupItemManager.Instance.TryGet(SelectedSource.IndexId, out var profileGroup);
PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
{
EMultipleLoad.LeastPing => ResUI.TbLeastPing,
@@ -78,15 +90,16 @@ public class AddGroupServerViewModel : MyReactiveObject
_ => ResUI.TbLeastPing,
};
- _ = Init();
- }
+ var subs = await AppManager.Instance.SubItems();
+ subs.Add(new SubItem());
+ SubItems.AddRange(subs);
+ SelectedSubItem = SubItems.Where(s => s.Id == profileGroup?.SubChildItems).FirstOrDefault();
+ Filter = profileGroup?.Filter;
- public async Task Init()
- {
var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId);
if (childItemMulti != null)
{
- var childIndexIds = childItemMulti.ChildItems.IsNullOrEmpty() ? new List() : Utils.String2List(childItemMulti.ChildItems);
+ var childIndexIds = Utils.String2List(childItemMulti.ChildItems) ?? [];
foreach (var item in childIndexIds)
{
var child = await AppManager.Instance.GetProfileItem(item);
@@ -181,7 +194,7 @@ public class AddGroupServerViewModel : MyReactiveObject
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
return;
}
- if (ChildItemsObs.Count == 0)
+ if (ChildItemsObs.Count == 0 && SelectedSubItem?.Id.IsNullOrEmpty() == true)
{
NoticeManager.Instance.Enqueue(ResUI.PleaseAddAtLeastOneServer);
return;
@@ -213,6 +226,9 @@ public class AddGroupServerViewModel : MyReactiveObject
_ => EMultipleLoad.LeastPing,
};
+ profileGroup.SubChildItems = SelectedSubItem?.Id;
+ profileGroup.Filter = Filter;
+
var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId);
if (hasCycle)
{
diff --git a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml
index e24fa67c..4279dc4a 100644
--- a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml
@@ -38,7 +38,7 @@
+ RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
+
+
+
+
+
+
+
diff --git a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
index a75f0e43..a5692ffc 100644
--- a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
+++ b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs
@@ -46,6 +46,9 @@ public partial class AddGroupServerWindow : WindowBase
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.SelectedValue).DisposeWith(disposables);
+ //this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
diff --git a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml
index b1867be8..b46d3dcd 100644
--- a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml
+++ b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml
@@ -59,6 +59,8 @@
+
+
@@ -130,6 +132,45 @@
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbPolicyGroupType}"
Style="{StaticResource DefComboBox}" />
+
+
+
+
+
+
+
diff --git a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
index ad3c7ac6..e542c922 100644
--- a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
+++ b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs
@@ -41,6 +41,9 @@ public partial class AddGroupServerWindow
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.Text).DisposeWith(disposables);
+ this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
+ this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
From b218f0b50168d12491675d234b69bc16955cb85e Mon Sep 17 00:00:00 2001
From: DHR60
Date: Sun, 2 Nov 2025 15:17:47 +0800
Subject: [PATCH 05/21] Cert Pinning (#8234)
* Cert Pinning
* Cert Chain Pinning
* Add Trusted Ca Pinning
* Tip
* Perf UI
---
v2rayN/ServiceLib/Handler/ConfigHandler.cs | 1 +
v2rayN/ServiceLib/Manager/CertPemManager.cs | 323 ++++++++++++++++++
v2rayN/ServiceLib/Models/ProfileItem.cs | 1 +
v2rayN/ServiceLib/Models/SingboxConfig.cs | 1 +
v2rayN/ServiceLib/Models/V2rayConfig.cs | 8 +
v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 66 +++-
v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 22 ++
v2rayN/ServiceLib/Resx/ResUI.fr.resx | 22 ++
v2rayN/ServiceLib/Resx/ResUI.hu.resx | 22 ++
v2rayN/ServiceLib/Resx/ResUI.resx | 22 ++
v2rayN/ServiceLib/Resx/ResUI.ru.resx | 22 ++
v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 22 ++
v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 22 ++
.../Singbox/SingboxOutboundService.cs | 111 +++---
.../CoreConfig/V2ray/V2rayOutboundService.cs | 22 ++
.../ViewModels/AddServerViewModel.cs | 91 +++++
.../Views/AddServerWindow.axaml | 69 +++-
.../Views/AddServerWindow.axaml.cs | 4 +
v2rayN/v2rayN/Views/AddServerWindow.xaml | 58 ++++
v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 4 +
20 files changed, 858 insertions(+), 55 deletions(-)
create mode 100644 v2rayN/ServiceLib/Manager/CertPemManager.cs
diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
index 7d2ca056..72addec4 100644
--- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs
+++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs
@@ -252,6 +252,7 @@ public static class ConfigHandler
item.Mldsa65Verify = profileItem.Mldsa65Verify;
item.Extra = profileItem.Extra;
item.MuxEnabled = profileItem.MuxEnabled;
+ item.Cert = profileItem.Cert;
}
var ret = item.ConfigType switch
diff --git a/v2rayN/ServiceLib/Manager/CertPemManager.cs b/v2rayN/ServiceLib/Manager/CertPemManager.cs
new file mode 100644
index 00000000..7469f351
--- /dev/null
+++ b/v2rayN/ServiceLib/Manager/CertPemManager.cs
@@ -0,0 +1,323 @@
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+
+namespace ServiceLib.Manager;
+
+///
+/// Manager for certificate operations with CA pinning to prevent MITM attacks
+///
+public class CertPemManager
+{
+ private static readonly string _tag = "CertPemManager";
+ private static readonly Lazy _instance = new(() => new());
+ public static CertPemManager Instance => _instance.Value;
+
+ ///
+ /// Trusted CA certificate thumbprints (SHA256) to prevent MITM attacks
+ ///
+ private static readonly HashSet TrustedCaThumbprints = new(StringComparer.OrdinalIgnoreCase)
+ {
+ "EBD41040E4BB3EC742C9E381D31EF2A41A48B6685C96E7CEF3C1DF6CD4331C99", // GlobalSign Root CA
+ "6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177", // Entrust.net Premium 2048 Secure Server CA
+ "73C176434F1BC6D5ADF45B0E76E727287C8DE57616C1E6E6141A2B2CBC7D8E4C", // Entrust Root Certification Authority
+ "D8E0FEBC1DB2E38D00940F37D27D41344D993E734B99D5656D9778D4D8143624", // Certum Root CA
+ "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
+ "2530CC8E98321502BAD96F9B1FBA1B099E2D299E0F4548BB914F363BC0D4531F", // Izenpe.com
+ "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
+ "55926084EC963A64B96E2ABE01CE0BA86A64FBFEBCC7AAB5AFC155B37FD76066", // Actalis Authentication Root CA
+ "9A114025197C5BB95D94E63D55CD43790847B646B23CDF11ADA4A00EFF15FB48", // Buypass Class 2 Root CA
+ "EDF7EBBCA27A2A384D387B7D4010C666E2EDB4843E4C29B4AE1D5B9332E6B24D", // Buypass Class 3 Root CA
+ "FD73DAD31C644FF1B43BEF0CCDDA96710B9CD9875ECA7E31707AF3E96D522BBD", // T-TeleSec GlobalRoot Class 3
+ "49E7A442ACF0EA6287050054B52564B650E4F49E42E348D6AA38E039E957B1C1", // D-TRUST Root Class 3 CA 2 2009
+ "EEC5496B988CE98625B934092EEC2908BED0B0F316C2D4730C84EAF1F3D34881", // D-TRUST Root Class 3 CA 2 EV 2009
+ "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
+ "8FE4FB0AF93A4D0D67DB0BEBB23E37C71BF325DCBCDD240EA04DAF58B47E1840", // QuoVadis Root CA 2 G3
+ "88EF81DE202EB018452E43F864725CEA5FBD1FC2D9D205730709C5D8B8690F46", // QuoVadis Root CA 3 G3
+ "7D05EBB682339F8C9451EE094EEBFEFA7953A114EDB2F44949452FAB7D2FC185", // DigiCert Assured ID Root G2
+ "7E37CB8B4C47090CAB36551BA6F45DB840680FBA166A952DB100717F43053FC2", // DigiCert Assured ID Root G3
+ "CB3CCBB76031E5E0138F8DD39A23F9DE47FFC35E43C1144CEA27D46A5AB1CB5F", // DigiCert Global Root G2
+ "31AD6648F8104138C738F39EA4320133393E3A18CC02296EF97C2AC9EF6731D0", // DigiCert Global Root G3
+ "552F7BDCF1A7AF9E6CE672017F4F12ABF77240C78E761AC203D1D9D20AC89988", // DigiCert Trusted Root G4
+ "52F0E1C4E58EC629291B60317F074671B85D7EA80D5B07273463534B32B40234", // COMODO RSA Certification Authority
+ "E793C9B02FD8AA13E21C31228ACCB08119643B749C898964B1746D46C3D4CBD2", // USERTrust RSA Certification Authority
+ "4FF460D54B9C86DABFBCFC5712E0400D2BED3FBC4D4FBDAA86E06ADCD2A9AD7A", // USERTrust ECC Certification Authority
+ "179FBC148A3DD00FD24EA13458CC43BFA7F59C8182D783A513F6EBEC100C8924", // GlobalSign ECC Root CA - R5
+ "3C4FB0B95AB8B30032F432B86F535FE172C185D0FD39865837CF36187FA6F428", // Staat der Nederlanden Root CA - G3
+ "5D56499BE4D2E08BCFCAD08A3E38723D50503BDE706948E42F55603019E528AE", // IdenTrust Commercial Root CA 1
+ "30D0895A9A448A262091635522D1F52010B5867ACAE12C78EF958FD4F4389F2F", // IdenTrust Public Sector Root CA 1
+ "43DF5774B03E7FEF5FE40D931A7BEDF1BB2E6B42738C4E6D3841103D3AA7F339", // Entrust Root Certification Authority - G2
+ "02ED0EB28C14DA45165C566791700D6451D7FB56F0B2AB1D3B8EB070E56EDFF5", // Entrust Root Certification Authority - EC1
+ "5CC3D78E4E1D5E45547A04E6873E64F90CF9536D1CCC2EF800F355C4C5FD70FD", // CFCA EV ROOT
+ "6B9C08E86EB0F767CFAD65CD98B62149E5494A67F5845E7BD1ED019F27B86BD6", // OISTE WISeKey Global Root GB CA
+ "A1339D33281A0B56E557D3D32B1CE7F9367EB094BD5FA72A7E5004C8DED7CAFE", // SZAFIR ROOT CA2
+ "B676F2EDDAE8775CD36CB0F63CD1D4603961F49E6265BA013A2F0307B6D0B804", // Certum Trusted Network CA 2
+ "A040929A02CE53B4ACF4F2FFC6981CE4496F755E6D45FE0B2A692BCD52523F36", // Hellenic Academic and Research Institutions RootCA 2015
+ "44B545AA8A25E65A73CA15DC27FC36D24C1CB9953A066539B11582DC487B4833", // Hellenic Academic and Research Institutions ECC RootCA 2015
+ "96BCEC06264976F37460779ACF28C5A7CFE8A3C0AAE11A8FFCEE05C0BDDF08C6", // ISRG Root X1
+ "EBC5570C29018C4D67B1AA127BAF12F703B4611EBC17B7DAB5573894179B93FA", // AC RAIZ FNMT-RCM
+ "8ECDE6884F3D87B1125BA31AC3FCB13D7016DE7F57CC904FE1CB97C6AE98196E", // Amazon Root CA 1
+ "1BA5B2AA8C65401A82960118F80BEC4F62304D83CEC4713A19C39C011EA46DB4", // Amazon Root CA 2
+ "18CE6CFE7BF14E60B2E347B8DFE868CB31D02EBB3ADA271569F50343B46DB3A4", // Amazon Root CA 3
+ "E35D28419ED02025CFA69038CD623962458DA5C695FBDEA3C22B0BFB25897092", // Amazon Root CA 4
+ "A1A86D04121EB87F027C66F53303C28E5739F943FC84B38AD6AF009035DD9457", // D-TRUST Root CA 3 2013
+ "46EDC3689046D53A453FB3104AB80DCAEC658B2660EA1629DD7E867990648716", // TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1
+ "BFFF8FD04433487D6A8AA60C1A29767A9FC2BBB05E420F713A13B992891D3893", // GDCA TrustAUTH R5 ROOT
+ "85666A562EE0BE5CE925C1D8890A6F76A87EC16D4D7D5F29EA7419CF20123B69", // SSL.com Root Certification Authority RSA
+ "3417BB06CC6007DA1B961C920B8AB4CE3FAD820E4AA30B9ACBC4A74EBDCEBC65", // SSL.com Root Certification Authority ECC
+ "2E7BF16CC22485A7BBE2AA8696750761B0AE39BE3B2FE9D0CC6D4EF73491425C", // SSL.com EV Root Certification Authority RSA R2
+ "22A2C1F7BDED704CC1E701B5F408C310880FE956B5DE2A4A44F99C873A25A7C8", // SSL.com EV Root Certification Authority ECC
+ "2CABEAFE37D06CA22ABA7391C0033D25982952C453647349763A3AB5AD6CCF69", // GlobalSign Root CA - R6
+ "8560F91C3624DABA9570B5FEA0DBE36FF11A8323BE9486854FB3F34A5571198D", // OISTE WISeKey Global Root GC CA
+ "9BEA11C976FE014764C1BE56A6F914B5A560317ABD9988393382E5161AA0493C", // UCA Global G2 Root
+ "D43AF9B35473755C9684FC06D7D8CB70EE5C28E773FB294EB41EE71722924D24", // UCA Extended Validation Root
+ "D48D3D23EEDB50A459E55197601C27774B9D7B18C94D5A059511A10250B93168", // Certigna Root CA
+ "40F6AF0346A99AA1CD1D555A4E9CCE62C7F9634603EE406615833DC8C8D00367", // emSign Root CA - G1
+ "86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B", // emSign ECC Root CA - G3
+ "125609AA301DA0A249B97A8239CB6A34216F44DCAC9F3954B14292F2E8C8608F", // emSign Root CA - C1
+ "BC4D809B15189D78DB3E1D8CF4F9726A795DA1643CA5F1358E1DDB0EDC0D7EB3", // emSign ECC Root CA - C3
+ "5A2FC03F0C83B090BBFA40604B0988446C7636183DF9846E17101A447FB8EFD6", // Hongkong Post Root CA 3
+ "DB3517D1F6732A2D5AB97C533EC70779EE3270A62FB4AC4238372460E6F01E88", // Entrust Root Certification Authority - G4
+ "358DF39D764AF9E1B766E9C972DF352EE15CFAC227AF6AD1D70E8E4A6EDCBA02", // Microsoft ECC Root Certificate Authority 2017
+ "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
+ "2E44102AB58CB85419451C8E19D9ACF3662CAFBC614B6A53960A30F7D0E2EB41", // TunTrust Root CA
+ "D95D0E8EDA79525BF9BEB11B14D2100D3294985F0C62D9FABD9CD999ECCB7B1D", // HARICA TLS RSA Root CA 2021
+ "3F99CC474ACFCE4DFED58794665E478D1547739F2E780F1BB4CA9B133097D401", // HARICA TLS ECC Root CA 2021
+ "1BE7ABE30686B16348AFD1C61B6866A0EA7F4821E67D5E8AF937CF8011BC750D", // HARICA Client RSA Root CA 2021
+ "8DD4B5373CB0DE36769C12339280D82746B3AA6CD426E797A31BABE4279CF00B", // HARICA Client ECC Root CA 2021
+ "57DE0583EFD2B26E0361DA99DA9DF4648DEF7EE8441C3B728AFA9BCDE0F9B26A", // Autoridad de Certificacion Firmaprofesional CIF A62634068
+ "30FBBA2C32238E2A98547AF97931E550428B9B3F1C8EEB6633DCFA86C5B27DD3", // vTrus ECC Root CA
+ "8A71DE6559336F426C26E53880D00D88A18DA4C6A91F0DCB6194E206C5C96387", // vTrus Root CA
+ "69729B8E15A86EFC177A57AFB7171DFC64ADD28C2FCA8CF1507E34453CCB1470", // ISRG Root X2
+ "F015CE3CC239BFEF064BE9F1D2C417E1A0264A0A94BE1F0C8D121864EB6949CC", // HiPKI Root CA - G1
+ "B085D70B964F191A73E4AF0D54AE7A0E07AAFDAF9B71DD0862138AB7325A24A2", // GlobalSign ECC Root CA - R4
+ "D947432ABDE7B7FA90FC2E6B59101B1280E0E1C7E4E40FA3C6887FFF57A7F4CF", // GTS Root R1
+ "8D25CD97229DBF70356BDA4EB3CC734031E24CF00FAFCFD32DC76EB5841C7EA8", // GTS Root R2
+ "34D8A73EE208D9BCDB0D956520934B4E40E69482596E8B6F73C8426B010A6F48", // GTS Root R3
+ "349DFA4058C5E263123B398AE795573C4E1313C83FE68F93556CD5E8031B3C7D", // GTS Root R4
+ "242B69742FCB1E5B2ABF98898B94572187544E5B4D9911786573621F6A74B82C", // Telia Root CA v2
+ "E59AAA816009C22BFF5B25BAD37DF306F049797C1F81D85AB089E657BD8F0044", // D-TRUST BR Root CA 1 2020
+ "08170D1AA36453901A2F959245E347DB0C8D37ABAABC56B81AA100DC958970DB", // D-TRUST EV Root CA 1 2020
+ "018E13F0772532CF809BD1B17281867283FC48C6E13BE9C69812854A490C1B05", // DigiCert TLS ECC P384 Root G5
+ "371A00DC0533B3721A7EEB40E8419E70799D2B0A0F2C1D80693165F7CEC4AD75", // DigiCert TLS RSA4096 Root G5
+ "E8E8176536A60CC2C4E10187C3BEFCA20EF263497018F566D5BEA0F94D0C111B", // DigiCert SMIME ECC P384 Root G5
+ "90370D3EFA88BF58C30105BA25104A358460A7FA52DFC2011DF233A0F417912A", // DigiCert SMIME RSA4096 Root G5
+ "77B82CD8644C4305F7ACC5CB156B45675004033D51C60C6202A8E0C33467D3A0", // Certainly Root R1
+ "B4585F22E4AC756A4E8612A1361C5D9D031A93FD84FEBB778FA3068B0FC42DC2", // Certainly Root E1
+ "82BD5D851ACF7F6E1BA7BFCBC53030D0E7BC3C21DF772D858CAB41D199BDF595", // DIGITALSIGN GLOBAL ROOT RSA CA
+ "261D7114AE5F8FF2D8C7209A9DE4289E6AFC9D717023D85450909199F1857CFE", // DIGITALSIGN GLOBAL ROOT ECDSA CA
+ "E74FBDA55BD564C473A36B441AA799C8A68E077440E8288B9FA1E50E4BBACA11", // Security Communication ECC RootCA1
+ "F3896F88FE7C0A882766A7FA6AD2749FB57A7F3E98FB769C1FA7B09C2C44D5AE", // BJCA Global Root CA1
+ "574DF6931E278039667B720AFDC1600FC27EB66DD3092979FB73856487212882", // BJCA Global Root CA2
+ "48E1CF9E43B688A51044160F46D773B8277FE45BEAAD0E4DF90D1974382FEA99", // LAWtrust Root CA2 (4096)
+ "22D9599234D60F1D4BC7C7E96F43FA555B07301FD475175089DAFB8C25E477B3", // Sectigo Public Email Protection Root E46
+ "D5917A7791EB7CF20A2E57EB98284A67B28A57E89182DA53D546678C9FDE2B4F", // Sectigo Public Email Protection Root R46
+ "C90F26F0FB1B4018B22227519B5CA2B53E2CA5B3BE5CF18EFE1BEF47380C5383", // Sectigo Public Server Authentication Root E46
+ "7BB647A62AEEAC88BF257AA522D01FFEA395E0AB45C73F93F65654EC38F25A06", // Sectigo Public Server Authentication Root R46
+ "8FAF7D2E2CB4709BB8E0B33666BF75A5DD45B5DE480F8EA8D4BFE6BEBC17F2ED", // SSL.com TLS RSA Root CA 2022
+ "C32FFD9F46F936D16C3673990959434B9AD60AAFBB9E7CF33654F144CC1BA143", // SSL.com TLS ECC Root CA 2022
+ "AD7DD58D03AEDB22A30B5084394920CE12230C2D8017AD9B81AB04079BDD026B", // SSL.com Client ECC Root CA 2022
+ "1D4CA4A2AB21D0093659804FC0EB2175A617279B56A2475245C9517AFEB59153", // SSL.com Client RSA Root CA 2022
+ "E38655F4B0190C84D3B3893D840A687E190A256D98052F159E6D4A39F589A6EB", // Atos TrustedRoot Root CA ECC G2 2020
+ "78833A783BB2986C254B9370D3C20E5EBA8FA7840CBF63FE17297A0B0119685E", // Atos TrustedRoot Root CA RSA G2 2020
+ "B2FAE53E14CCD7AB9212064701AE279C1D8988FACB775FA8A008914E663988A8", // Atos TrustedRoot Root CA ECC TLS 2021
+ "81A9088EA59FB364C548A6F85559099B6F0405EFBF18E5324EC9F457BA00112F", // Atos TrustedRoot Root CA RSA TLS 2021
+ "E0D3226AEB1163C2E48FF9BE3B50B4C6431BE7BB1EACC5C36B5D5EC509039A08", // TrustAsia Global Root CA G3
+ "BE4B56CB5056C0136A526DF444508DAA36A0B54F42E4AC38F72AF470E479654C", // TrustAsia Global Root CA G4
+ "D92C171F5CF890BA428019292927FE22F3207FD2B54449CB6F675AF4922146E2", // D-Trust SBR Root CA 1 2022
+ "DBA84DD7EF622D485463A90137EA4D574DF8550928F6AFA03B4D8B1141E636CC", // D-Trust SBR Root CA 2 2022
+ "3AE6DF7E0D637A65A8C81612EC6F9A142F85A16834C10280D88E707028518755", // Telekom Security SMIME ECC Root 2021
+ "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
+ "4B009C1034494F9AB56BBA3BA1D62731FC4D20D8955ADCEC10A925607261E338", // SecureSign Root CA14
+ "E778F0F095FE843729CD1A0082179E5314A9C291442805E1FB1D8FB6B8886C3A", // SecureSign Root CA15
+ "0552E6F83FDF65E8FA9670E666DF28A4E21340B510CBE52566F97C4FB94B2BD1", // D-TRUST BR Root CA 2 2023
+ "436472C1009A325C54F1A5BBB5468A7BAEECCBE05DE5F099CB70D3FE41E13C16", // TrustAsia SMIME ECC Root CA
+ "C7796BEB62C101BB143D262A7C96A0C6168183223EF50D699632D86E03B8CC9B", // TrustAsia SMIME RSA Root CA
+ "C0076B9EF0531FB1A656D67C4EBE97CD5DBAA41EF44598ACC2489878C92D8711", // TrustAsia TLS ECC Root CA
+ "06C08D7DAFD876971EB1124FE67F847EC0C7A158D3EA53CBE940E2EA9791F4C3", // TrustAsia TLS RSA Root CA
+ "8E8221B2E7D4007836A1672F0DCC299C33BC07D316F132FA1A206D587150F1CE", // D-TRUST EV Root CA 2 2023
+ "9A12C392BFE57891A0C545309D4D9FD567E480CB613D6342278B195C79A7931F", // SwissSign RSA SMIME Root CA 2022 - 1
+ "193144F431E0FDDB740717D4DE926A571133884B4360D30E272913CBE660CE41", // SwissSign RSA TLS Root CA 2022 - 1
+ "D9A32485A8CCA85539CEF12FFFFF711378A17851D73DA2732AB4302D763BD62B", // OISTE Client Root ECC G1
+ "D02A0F994A868C66395F2E7A880DF509BD0C29C96DE16015A0FD501EDA4F96A9", // OISTE Client Root RSA G1
+ "EEC997C0C30F216F7E3B8B307D2BAE42412D753FC8219DAFD1520B2572850F49", // OISTE Server Root ECC G1
+ "9AE36232A5189FFDDB353DFD26520C015395D22777DAC59DB57B98C089A651E6", // OISTE Server Root RSA G1
+ };
+
+ ///
+ /// Get certificate in PEM format from a server with CA pinning validation
+ ///
+ public async Task GetCertPemAsync(string target, string serverName)
+ {
+ try
+ {
+ var (domain, _, port, _) = Utils.ParseUrl(target);
+
+ using var client = new TcpClient();
+ await client.ConnectAsync(domain, port > 0 ? port : 443);
+
+ using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
+
+ await ssl.AuthenticateAsClientAsync(serverName);
+
+ var remote = ssl.RemoteCertificate;
+ if (remote == null)
+ {
+ return null;
+ }
+
+ var leaf = new X509Certificate2(remote);
+ return ExportCertToPem(leaf);
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ return null;
+ }
+ }
+
+ ///
+ /// Get certificate chain in PEM format from a server with CA pinning validation
+ ///
+ public async Task> GetCertChainPemAsync(string target, string serverName)
+ {
+ try
+ {
+ var pemList = new List();
+ var (domain, _, port, _) = Utils.ParseUrl(target);
+
+ using var client = new TcpClient();
+ await client.ConnectAsync(domain, port > 0 ? port : 443);
+
+ using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate);
+
+ await ssl.AuthenticateAsClientAsync(serverName);
+
+ if (ssl.RemoteCertificate is not X509Certificate2 certChain)
+ {
+ return pemList;
+ }
+
+ var chain = new X509Chain();
+ chain.Build(certChain);
+
+ foreach (var element in chain.ChainElements)
+ {
+ var pem = ExportCertToPem(element.Certificate);
+ pemList.Add(pem);
+ }
+
+ return pemList;
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ return new List();
+ }
+ }
+
+ ///
+ /// Validate server certificate with CA pinning
+ ///
+ private bool ValidateServerCertificate(
+ object sender,
+ X509Certificate? certificate,
+ X509Chain? chain,
+ SslPolicyErrors sslPolicyErrors)
+ {
+ if (certificate == null)
+ {
+ return false;
+ }
+
+ // 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;
+
+ certChain.Build(cert2);
+
+ // Find root CA
+ if (certChain.ChainElements.Count == 0)
+ {
+ return false;
+ }
+
+ var rootCert = certChain.ChainElements[certChain.ChainElements.Count - 1].Certificate;
+ var rootThumbprint = rootCert.GetCertHashString(HashAlgorithmName.SHA256);
+
+ return TrustedCaThumbprints.Contains(rootThumbprint);
+ }
+
+ public string ExportCertToPem(X509Certificate2 cert)
+ {
+ var der = cert.Export(X509ContentType.Cert);
+ var b64 = Convert.ToBase64String(der, Base64FormattingOptions.InsertLineBreaks);
+ return $"-----BEGIN CERTIFICATE-----\n{b64}\n-----END CERTIFICATE-----\n";
+ }
+}
diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs
index fa84ac59..b832a5e7 100644
--- a/v2rayN/ServiceLib/Models/ProfileItem.cs
+++ b/v2rayN/ServiceLib/Models/ProfileItem.cs
@@ -141,4 +141,5 @@ public class ProfileItem : ReactiveObject
public string Mldsa65Verify { get; set; }
public string Extra { get; set; }
public bool? MuxEnabled { get; set; }
+ public string Cert { get; set; }
}
diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs
index 9474631b..6dde5e7c 100644
--- a/v2rayN/ServiceLib/Models/SingboxConfig.cs
+++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs
@@ -181,6 +181,7 @@ public class Tls4Sbox
public bool? fragment { get; set; }
public string? fragment_fallback_delay { get; set; }
public bool? record_fragment { get; set; }
+ public List? certificate { get; set; }
}
public class Multiplex4Sbox
diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs
index 22dcf9f4..e10cd0d9 100644
--- a/v2rayN/ServiceLib/Models/V2rayConfig.cs
+++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs
@@ -354,6 +354,14 @@ public class TlsSettings4Ray
public string? shortId { get; set; }
public string? spiderX { get; set; }
public string? mldsa65Verify { get; set; }
+ public List? certificates { get; set; }
+ public bool? disableSystemRoot { get; set; }
+}
+
+public class CertificateSettings4Ray
+{
+ public List? certificate { get; set; }
+ public string? usage { get; set; }
}
public class TcpSettings4Ray
diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
index 90a44ae5..ea649da5 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
+++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs
@@ -19,7 +19,7 @@ namespace ServiceLib.Resx {
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ResUI {
@@ -87,6 +87,24 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Certificate not set 的本地化字符串。
+ ///
+ public static string CertNotSet {
+ get {
+ return ResourceManager.GetString("CertNotSet", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Certificate set 的本地化字符串。
+ ///
+ public static string CertSet {
+ get {
+ return ResourceManager.GetString("CertSet", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Please check the Configuration settings first. 的本地化字符串。
///
@@ -2301,6 +2319,15 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Please set a valid domain 的本地化字符串。
+ ///
+ public static string ServerNameMustBeValidDomain {
+ get {
+ return ResourceManager.GetString("ServerNameMustBeValidDomain", resourceCulture);
+ }
+ }
+
///
/// 查找类似 {0} : {1}/s↑ | {2}/s↓ 的本地化字符串。
///
@@ -2562,6 +2589,25 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Certificate Pinning 的本地化字符串。
+ ///
+ public static string TbCertPinning {
+ get {
+ return ResourceManager.GetString("TbCertPinning", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Server certificate (PEM format, optional). Entering a certificate will pin it.
+ ///Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. 的本地化字符串。
+ ///
+ public static string TbCertPinningTips {
+ get {
+ return ResourceManager.GetString("TbCertPinningTips", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Clear system proxy 的本地化字符串。
///
@@ -2769,6 +2815,24 @@ namespace ServiceLib.Resx {
}
}
+ ///
+ /// 查找类似 Fetch Certificate 的本地化字符串。
+ ///
+ public static string TbFetchCert {
+ get {
+ return ResourceManager.GetString("TbFetchCert", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 Fetch Certificate Chain 的本地化字符串。
+ ///
+ public static string TbFetchCertChain {
+ get {
+ return ResourceManager.GetString("TbFetchCertChain", resourceCulture);
+ }
+ }
+
///
/// 查找类似 Fingerprint 的本地化字符串。
///
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
index edbd21da..64d74cd9 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx
@@ -1602,4 +1602,26 @@
Auto add filtered configuration from subscription groups
+
+ Certificate Pinning
+
+
+ Server certificate (PEM format, optional). Entering a certificate will pin it.
+Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.
+
+
+ Fetch Certificate
+
+
+ Fetch Certificate Chain
+
+
+ Please set a valid domain
+
+
+ Certificate not set
+
+
+ Certificate set
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx
index 542d9ef9..88a724fb 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx
@@ -1599,4 +1599,26 @@
Auto add filtered configuration from subscription groups
+
+ Certificate Pinning
+
+
+ Server certificate (PEM format, optional). Entering a certificate will pin it.
+Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.
+
+
+ Fetch Certificate
+
+
+ Fetch Certificate Chain
+
+
+ Please set a valid domain
+
+
+ Certificate not set
+
+
+ Certificate set
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
index e61f279e..ce57a3ba 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx
@@ -1602,4 +1602,26 @@
Auto add filtered configuration from subscription groups
+
+ Certificate Pinning
+
+
+ Server certificate (PEM format, optional). Entering a certificate will pin it.
+Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.
+
+
+ Fetch Certificate
+
+
+ Fetch Certificate Chain
+
+
+ Please set a valid domain
+
+
+ Certificate not set
+
+
+ Certificate set
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx
index f1123407..03bd9660 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.resx
@@ -1602,4 +1602,26 @@
Auto add filtered configuration from subscription groups
+
+ Certificate Pinning
+
+
+ Server certificate (PEM format, optional). Entering a certificate will pin it.
+Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.
+
+
+ Fetch Certificate
+
+
+ Fetch Certificate Chain
+
+
+ Please set a valid domain
+
+
+ Certificate not set
+
+
+ Certificate set
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
index 14edb9b8..dd7604fc 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx
@@ -1602,4 +1602,26 @@
Auto add filtered configuration from subscription groups
+
+ Certificate Pinning
+
+
+ Server certificate (PEM format, optional). Entering a certificate will pin it.
+Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.
+
+
+ Fetch Certificate
+
+
+ Fetch Certificate Chain
+
+
+ Please set a valid domain
+
+
+ Certificate not set
+
+
+ Certificate set
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
index cfb89284..87443803 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
@@ -1599,4 +1599,26 @@
自动从订阅分组添加过滤后的配置
+
+ 固定证书
+
+
+ 服务器证书(PEM 格式,可选)。填入后将固定该证书。
+启用“跳过证书验证”时,请勿使用 '获取证书'。
+
+
+ 获取证书
+
+
+ 获取证书链
+
+
+ 请设置有效的域名
+
+
+ 证书未设置
+
+
+ 证书已设置
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
index 4a3822b4..c12f023b 100644
--- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
+++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
@@ -1599,4 +1599,26 @@
自動從訂閱分組新增過濾後的配置
+
+ Certificate Pinning
+
+
+ Server certificate (PEM format, optional). Entering a certificate will pin it.
+Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled.
+
+
+ Fetch Certificate
+
+
+ Fetch Certificate Chain
+
+
+ Please set a valid domain
+
+
+ Certificate not set
+
+
+ Certificate set
+
\ No newline at end of file
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
index 84111652..1f1e68a0 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs
@@ -204,54 +204,6 @@ public partial class CoreConfigSingboxService
return await Task.FromResult(null);
}
- private async Task GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
- {
- try
- {
- if (!node.ConfigType.IsGroupType())
- {
- return -1;
- }
- var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
- if (hasCycle)
- {
- return -1;
- }
-
- var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
- if (childProfiles.Count <= 0)
- {
- return -1;
- }
- switch (node.ConfigType)
- {
- case EConfigType.PolicyGroup:
- if (ignoreOriginChain)
- {
- await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
- }
- else
- {
- await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
- }
-
- break;
-
- case EConfigType.ProxyChain:
- await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName);
- break;
-
- default:
- break;
- }
- }
- catch (Exception ex)
- {
- Logging.SaveLog(_tag, ex);
- }
- return await Task.FromResult(0);
- }
-
private async Task GenOutboundMux(ProfileItem node, Outbound4Sbox outbound)
{
try
@@ -280,7 +232,7 @@ public partial class CoreConfigSingboxService
{
try
{
- if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity)
+ if (node.StreamSecurity is Global.StreamSecurityReality or Global.StreamSecurity)
{
var server_name = string.Empty;
if (node.Sni.IsNotEmpty())
@@ -307,7 +259,18 @@ public partial class CoreConfigSingboxService
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
};
}
- if (node.StreamSecurity == Global.StreamSecurityReality)
+ if (node.StreamSecurity == Global.StreamSecurity)
+ {
+ var certs = node.Cert
+ ?.Split("-----END CERTIFICATE-----", StringSplitOptions.RemoveEmptyEntries)
+ .Select(s => s.TrimEx())
+ .Where(s => !s.IsNullOrEmpty())
+ .Select(s => s + "\n-----END CERTIFICATE-----")
+ .Select(s => s.Replace("\r\n", "\n"))
+ .ToList() ?? new();
+ tls.certificate = certs.Count > 0 ? certs : null;
+ }
+ else
{
tls.reality = new Reality4Sbox()
{
@@ -404,6 +367,54 @@ public partial class CoreConfigSingboxService
return await Task.FromResult(0);
}
+ private async Task GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false)
+ {
+ try
+ {
+ if (!node.ConfigType.IsGroupType())
+ {
+ return -1;
+ }
+ var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId);
+ if (hasCycle)
+ {
+ return -1;
+ }
+
+ var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId);
+ if (childProfiles.Count <= 0)
+ {
+ return -1;
+ }
+ switch (node.ConfigType)
+ {
+ case EConfigType.PolicyGroup:
+ if (ignoreOriginChain)
+ {
+ await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
+ }
+ else
+ {
+ await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName);
+ }
+
+ break;
+
+ case EConfigType.ProxyChain:
+ await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName);
+ break;
+
+ default:
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logging.SaveLog(_tag, ex);
+ }
+ return await Task.FromResult(0);
+ }
+
private async Task GenMoreOutbounds(ProfileItem node, SingboxConfig singboxConfig)
{
if (node.Subid.IsNullOrEmpty())
diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
index 39338c77..46169c37 100644
--- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
+++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs
@@ -245,6 +245,13 @@ public partial class CoreConfigV2rayService
var host = node.RequestHost.TrimEx();
var path = node.Path.TrimEx();
var sni = node.Sni.TrimEx();
+ var certs = node.Cert
+ ?.Split("-----END CERTIFICATE-----", StringSplitOptions.RemoveEmptyEntries)
+ .Select(s => s.TrimEx())
+ .Where(s => !s.IsNullOrEmpty())
+ .Select(s => s + "\n-----END CERTIFICATE-----")
+ .Select(s => s.Replace("\r\n", "\n"))
+ .ToList() ?? new();
var useragent = "";
if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty())
{
@@ -277,6 +284,21 @@ public partial class CoreConfigV2rayService
{
tlsSettings.serverName = Utils.String2List(host)?.First();
}
+ if (certs.Count > 0)
+ {
+ var certsettings = new List();
+ foreach (var cert in certs)
+ {
+ var certPerLine = cert.Split("\n").ToList();
+ certsettings.Add(new CertificateSettings4Ray
+ {
+ certificate = certPerLine,
+ usage = "verify",
+ });
+ }
+ tlsSettings.certificates = certsettings;
+ tlsSettings.disableSystemRoot = true;
+ }
streamSettings.tlsSettings = tlsSettings;
}
diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs
index addd5bcc..121bf5da 100644
--- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs
+++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs
@@ -2,12 +2,22 @@ namespace ServiceLib.ViewModels;
public class AddServerViewModel : MyReactiveObject
{
+ private string _certError = string.Empty;
+
[Reactive]
public ProfileItem SelectedSource { get; set; }
[Reactive]
public string? CoreType { get; set; }
+ [Reactive]
+ public string Cert { get; set; }
+
+ [Reactive]
+ public string CertTip { get; set; }
+
+ public ReactiveCommand FetchCertCmd { get; }
+ public ReactiveCommand FetchCertChainCmd { get; }
public ReactiveCommand SaveCmd { get; }
public AddServerViewModel(ProfileItem profileItem, Func>? updateView)
@@ -15,11 +25,22 @@ public class AddServerViewModel : MyReactiveObject
_config = AppManager.Instance.Config;
_updateView = updateView;
+ FetchCertCmd = ReactiveCommand.CreateFromTask(async () =>
+ {
+ await FetchCert();
+ });
+ FetchCertChainCmd = ReactiveCommand.CreateFromTask(async () =>
+ {
+ await FetchCertChain();
+ });
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SaveServerAsync();
});
+ this.WhenAnyValue(x => x.Cert)
+ .Subscribe(_ => UpdateCertTip());
+
if (profileItem.IndexId.IsNullOrEmpty())
{
profileItem.Network = Global.DefaultNetwork;
@@ -33,6 +54,7 @@ public class AddServerViewModel : MyReactiveObject
SelectedSource = JsonUtils.DeepCopy(profileItem);
}
CoreType = SelectedSource?.CoreType?.ToString();
+ Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
}
private async Task SaveServerAsync()
@@ -77,6 +99,7 @@ public class AddServerViewModel : MyReactiveObject
}
}
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
+ SelectedSource.Cert = Cert.IsNullOrEmpty() ? null : Cert;
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
{
@@ -88,4 +111,72 @@ public class AddServerViewModel : MyReactiveObject
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
}
}
+
+ private void UpdateCertTip()
+ {
+ CertTip = _certError.IsNullOrEmpty()
+ ? (Cert.IsNullOrEmpty() ? ResUI.CertNotSet : ResUI.CertSet)
+ : _certError;
+ }
+
+ private async Task FetchCert()
+ {
+ if (SelectedSource.StreamSecurity != Global.StreamSecurity)
+ {
+ return;
+ }
+ var domain = SelectedSource.Address;
+ var serverName = SelectedSource.Sni;
+ if (serverName.IsNullOrEmpty())
+ {
+ serverName = SelectedSource.RequestHost;
+ }
+ if (serverName.IsNullOrEmpty())
+ {
+ serverName = SelectedSource.Address;
+ }
+ if (!Utils.IsDomain(serverName))
+ {
+ _certError = ResUI.ServerNameMustBeValidDomain;
+ UpdateCertTip();
+ _certError = string.Empty;
+ return;
+ }
+ if (SelectedSource.Port > 0)
+ {
+ domain += $":{SelectedSource.Port}";
+ }
+ Cert = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
+ }
+
+ private async Task FetchCertChain()
+ {
+ if (SelectedSource.StreamSecurity != Global.StreamSecurity)
+ {
+ return;
+ }
+ var domain = SelectedSource.Address;
+ var serverName = SelectedSource.Sni;
+ if (serverName.IsNullOrEmpty())
+ {
+ serverName = SelectedSource.RequestHost;
+ }
+ if (serverName.IsNullOrEmpty())
+ {
+ serverName = SelectedSource.Address;
+ }
+ if (!Utils.IsDomain(serverName))
+ {
+ _certError = ResUI.ServerNameMustBeValidDomain;
+ UpdateCertTip();
+ _certError = string.Empty;
+ return;
+ }
+ if (SelectedSource.Port > 0)
+ {
+ domain += $":{SelectedSource.Port}";
+ }
+ var certs = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
+ Cert = string.Join("\n", certs);
+ }
}
diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml
index 7e3dda10..f01da56d 100644
--- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml
+++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml
@@ -607,10 +607,10 @@