From 1e9a6cb06b953a42cd879cf7fe24068ed06cef38 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 4 Jun 2024 09:48:04 +0800 Subject: [PATCH] Refactor code --- v2rayN/v2rayN/Common/QRCodeHelper.cs | 4 +- v2rayN/v2rayN/Handler/ConfigHandler.cs | 65 +- .../{ => CoreConfig}/CoreConfigHandler.cs | 6 +- .../{ => CoreConfig}/CoreConfigSingbox.cs | 4 +- .../{ => CoreConfig}/CoreConfigV2ray.cs | 8 +- v2rayN/v2rayN/Handler/CoreHandler.cs | 3 +- v2rayN/v2rayN/Handler/Fmt/BaseFmt.cs | 184 +++ v2rayN/v2rayN/Handler/Fmt/FmtHandler.cs | 95 ++ v2rayN/v2rayN/Handler/Fmt/Hysteria2Fmt.cs | 68 ++ v2rayN/v2rayN/Handler/Fmt/ShadowsocksFmt.cs | 150 +++ v2rayN/v2rayN/Handler/Fmt/SocksFmt.cs | 131 +++ v2rayN/v2rayN/Handler/Fmt/TrojanFmt.cs | 53 + v2rayN/v2rayN/Handler/Fmt/TuicFmt.cs | 68 ++ v2rayN/v2rayN/Handler/Fmt/VLESSFmt.cs | 64 ++ v2rayN/v2rayN/Handler/Fmt/VmessFmt.cs | 228 ++++ v2rayN/v2rayN/Handler/Fmt/WireguardFmt.cs | 73 ++ v2rayN/v2rayN/Handler/MainFormHandler.cs | 1 + v2rayN/v2rayN/Handler/ShareHandler.cs | 1004 ----------------- v2rayN/v2rayN/Models/ConfigItems.cs | 2 +- v2rayN/v2rayN/Models/SingboxConfig.cs | 2 +- .../v2rayN/ViewModels/MainWindowViewModel.cs | 7 +- .../ViewModels/OptionSettingViewModel.cs | 2 +- .../ViewModels/RoutingRuleSettingViewModel.cs | 2 +- .../v2rayN/Views/OptionSettingWindow.xaml.cs | 2 +- 24 files changed, 1170 insertions(+), 1056 deletions(-) rename v2rayN/v2rayN/Handler/{ => CoreConfig}/CoreConfigHandler.cs (95%) rename v2rayN/v2rayN/Handler/{ => CoreConfig}/CoreConfigSingbox.cs (99%) rename v2rayN/v2rayN/Handler/{ => CoreConfig}/CoreConfigV2ray.cs (99%) create mode 100644 v2rayN/v2rayN/Handler/Fmt/BaseFmt.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/FmtHandler.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/Hysteria2Fmt.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/ShadowsocksFmt.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/SocksFmt.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/TrojanFmt.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/TuicFmt.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/VLESSFmt.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/VmessFmt.cs create mode 100644 v2rayN/v2rayN/Handler/Fmt/WireguardFmt.cs delete mode 100644 v2rayN/v2rayN/Handler/ShareHandler.cs diff --git a/v2rayN/v2rayN/Common/QRCodeHelper.cs b/v2rayN/v2rayN/Common/QRCodeHelper.cs index 9b02a48b..255ab475 100644 --- a/v2rayN/v2rayN/Common/QRCodeHelper.cs +++ b/v2rayN/v2rayN/Common/QRCodeHelper.cs @@ -1,13 +1,13 @@ using QRCoder; using QRCoder.Xaml; using System.Drawing; -using System.Windows.Interop; using System.Windows; +using System.Windows.Interop; using System.Windows.Media; +using ZXing; using ZXing.Common; using ZXing.QrCode; using ZXing.Windows.Compatibility; -using ZXing; namespace v2rayN { diff --git a/v2rayN/v2rayN/Handler/ConfigHandler.cs b/v2rayN/v2rayN/Handler/ConfigHandler.cs index b2b11278..a8a248bb 100644 --- a/v2rayN/v2rayN/Handler/ConfigHandler.cs +++ b/v2rayN/v2rayN/Handler/ConfigHandler.cs @@ -2,6 +2,7 @@ using System.IO; using System.Text.RegularExpressions; using v2rayN.Enums; +using v2rayN.Handler.Fmt; using v2rayN.Models; namespace v2rayN.Handler @@ -1070,12 +1071,12 @@ namespace v2rayN.Handler /// 批量添加服务器 /// /// - /// + /// /// /// 成功导入的数量 - private static int AddBatchServers(Config config, string clipboardData, string subid, bool isSub, List lstOriSub) + private static int AddBatchServers(Config config, string strData, string subid, bool isSub, List lstOriSub) { - if (Utils.IsNullOrEmpty(clipboardData)) + if (Utils.IsNullOrEmpty(strData)) { return -1; } @@ -1092,7 +1093,7 @@ namespace v2rayN.Handler //Check for duplicate indexId List? lstDbIndexId = null; List lstAdd = new(); - var arrData = clipboardData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty()); + var arrData = strData.Split(Environment.NewLine.ToCharArray()).Where(t => !t.IsNullOrEmpty()); if (isSub) { arrData = arrData.Distinct(); @@ -1108,7 +1109,7 @@ namespace v2rayN.Handler } continue; } - var profileItem = ShareHandler.ImportFromClipboardConfig(str, out string msg); + var profileItem = FmtHandler.ResolveConfig(str, out string msg); if (profileItem is null) { continue; @@ -1177,9 +1178,9 @@ namespace v2rayN.Handler return countServers; } - private static int AddBatchServers4Custom(Config config, string clipboardData, string subid, bool isSub, List lstOriSub) + private static int AddBatchServers4Custom(Config config, string strData, string subid, bool isSub, List lstOriSub) { - if (Utils.IsNullOrEmpty(clipboardData)) + if (Utils.IsNullOrEmpty(strData)) { return -1; } @@ -1195,7 +1196,7 @@ namespace v2rayN.Handler } //Is v2ray array configuration - var configObjects = JsonUtils.Deserialize(clipboardData); + var configObjects = JsonUtils.Deserialize(strData); if (configObjects != null && configObjects.Length > 0) { if (isSub && !Utils.IsNullOrEmpty(subid)) @@ -1236,42 +1237,42 @@ namespace v2rayN.Handler ProfileItem profileItem = new(); //Is v2ray configuration - var v2rayConfig = JsonUtils.Deserialize(clipboardData); + var v2rayConfig = JsonUtils.Deserialize(strData); if (v2rayConfig?.inbounds?.Count > 0 && v2rayConfig.outbounds?.Count > 0) { var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); - File.WriteAllText(fileName, clipboardData); + File.WriteAllText(fileName, strData); profileItem.coreType = ECoreType.Xray; profileItem.address = fileName; profileItem.remarks = v2rayConfig.remarks ?? "v2ray_custom"; } //Is Clash configuration - else if (Contains(clipboardData, "port", "socks-port", "proxies")) + else if (Contains(strData, "port", "socks-port", "proxies")) { var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.yaml"); - File.WriteAllText(fileName, clipboardData); + File.WriteAllText(fileName, strData); profileItem.coreType = ECoreType.mihomo; profileItem.address = fileName; profileItem.remarks = "clash_custom"; } //Is hysteria configuration - else if (Contains(clipboardData, "server", "up", "down", "listen", "", "")) + else if (Contains(strData, "server", "up", "down", "listen", "", "")) { var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); - File.WriteAllText(fileName, clipboardData); + File.WriteAllText(fileName, strData); profileItem.coreType = ECoreType.hysteria; profileItem.address = fileName; profileItem.remarks = "hysteria_custom"; } //Is naiveproxy configuration - else if (Contains(clipboardData, "listen", "proxy", "", "")) + else if (Contains(strData, "listen", "proxy", "", "")) { var fileName = Utils.GetTempPath($"{Utils.GetGUID(false)}.json"); - File.WriteAllText(fileName, clipboardData); + File.WriteAllText(fileName, strData); profileItem.coreType = ECoreType.naiveproxy; profileItem.address = fileName; @@ -1282,7 +1283,7 @@ namespace v2rayN.Handler { return -1; //var fileName = Utile.GetTempPath($"{Utile.GetGUID(false)}.txt"); - //File.WriteAllText(fileName, clipboardData); + //File.WriteAllText(fileName, strData); //profileItem.address = fileName; //profileItem.remarks = "other_custom"; @@ -1314,9 +1315,9 @@ namespace v2rayN.Handler } } - private static int AddBatchServers4SsSIP008(Config config, string clipboardData, string subid, bool isSub, List lstOriSub) + private static int AddBatchServers4SsSIP008(Config config, string strData, string subid, bool isSub, List lstOriSub) { - if (Utils.IsNullOrEmpty(clipboardData)) + if (Utils.IsNullOrEmpty(strData)) { return -1; } @@ -1327,10 +1328,10 @@ namespace v2rayN.Handler } //SsSIP008 - var lstSsServer = JsonUtils.Deserialize>(clipboardData); + var lstSsServer = JsonUtils.Deserialize>(strData); if (lstSsServer?.Count <= 0) { - var ssSIP008 = JsonUtils.Deserialize(clipboardData); + var ssSIP008 = JsonUtils.Deserialize(strData); if (ssSIP008?.servers?.Count > 0) { lstSsServer = ssSIP008.servers; @@ -1365,7 +1366,7 @@ namespace v2rayN.Handler return -1; } - public static int AddBatchServers(Config config, string clipboardData, string subid, bool isSub) + public static int AddBatchServers(Config config, string strData, string subid, bool isSub) { List? lstOriSub = null; if (isSub && !Utils.IsNullOrEmpty(subid)) @@ -1374,28 +1375,28 @@ namespace v2rayN.Handler } var counter = 0; - if (Utils.IsBase64String(clipboardData)) + if (Utils.IsBase64String(strData)) { - counter = AddBatchServers(config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub); + counter = AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub); } if (counter < 1) { - counter = AddBatchServers(config, clipboardData, subid, isSub, lstOriSub); + counter = AddBatchServers(config, strData, subid, isSub, lstOriSub); } if (counter < 1) { - counter = AddBatchServers(config, Utils.Base64Decode(clipboardData), subid, isSub, lstOriSub); + counter = AddBatchServers(config, Utils.Base64Decode(strData), subid, isSub, lstOriSub); } if (counter < 1) { - counter = AddBatchServers4SsSIP008(config, clipboardData, subid, isSub, lstOriSub); + counter = AddBatchServers4SsSIP008(config, strData, subid, isSub, lstOriSub); } //maybe other sub if (counter < 1) { - counter = AddBatchServers4Custom(config, clipboardData, subid, isSub, lstOriSub); + counter = AddBatchServers4Custom(config, strData, subid, isSub, lstOriSub); } return counter; @@ -1533,16 +1534,16 @@ namespace v2rayN.Handler /// AddBatchRoutingRules /// /// - /// + /// /// - public static int AddBatchRoutingRules(ref RoutingItem routingItem, string clipboardData) + public static int AddBatchRoutingRules(ref RoutingItem routingItem, string strData) { - if (Utils.IsNullOrEmpty(clipboardData)) + if (Utils.IsNullOrEmpty(strData)) { return -1; } - var lstRules = JsonUtils.Deserialize>(clipboardData); + var lstRules = JsonUtils.Deserialize>(strData); if (lstRules == null) { return -1; diff --git a/v2rayN/v2rayN/Handler/CoreConfigHandler.cs b/v2rayN/v2rayN/Handler/CoreConfig/CoreConfigHandler.cs similarity index 95% rename from v2rayN/v2rayN/Handler/CoreConfigHandler.cs rename to v2rayN/v2rayN/Handler/CoreConfig/CoreConfigHandler.cs index be9b91ce..3f73f542 100644 --- a/v2rayN/v2rayN/Handler/CoreConfigHandler.cs +++ b/v2rayN/v2rayN/Handler/CoreConfig/CoreConfigHandler.cs @@ -3,7 +3,7 @@ using v2rayN.Enums; using v2rayN.Models; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler.CoreConfig { /// /// Core configuration file processing class @@ -155,7 +155,7 @@ namespace v2rayN.Handler { if (coreType == ECoreType.sing_box) { - if ((new CoreConfigSingbox(config)).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0) + if (new CoreConfigSingbox(config).GenerateClientSpeedtestConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0) { return -1; } @@ -163,7 +163,7 @@ namespace v2rayN.Handler } else { - if ((new CoreConfigV2ray(config)).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0) + if (new CoreConfigV2ray(config).GenerateClientSpeedtestConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0) { return -1; } diff --git a/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs b/v2rayN/v2rayN/Handler/CoreConfig/CoreConfigSingbox.cs similarity index 99% rename from v2rayN/v2rayN/Handler/CoreConfigSingbox.cs rename to v2rayN/v2rayN/Handler/CoreConfig/CoreConfigSingbox.cs index 370f213a..887bfd22 100644 --- a/v2rayN/v2rayN/Handler/CoreConfigSingbox.cs +++ b/v2rayN/v2rayN/Handler/CoreConfig/CoreConfigSingbox.cs @@ -4,7 +4,7 @@ using v2rayN.Enums; using v2rayN.Models; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler.CoreConfig { internal class CoreConfigSingbox { @@ -120,7 +120,7 @@ namespace v2rayN.Handler var listen = "::"; singboxConfig.inbounds = []; - if (!_config.tunModeItem.enableTun || (_config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound)) + if (!_config.tunModeItem.enableTun || _config.tunModeItem.enableTun && _config.tunModeItem.enableExInbound) { var inbound = new Inbound4Sbox() { diff --git a/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs b/v2rayN/v2rayN/Handler/CoreConfig/CoreConfigV2ray.cs similarity index 99% rename from v2rayN/v2rayN/Handler/CoreConfigV2ray.cs rename to v2rayN/v2rayN/Handler/CoreConfig/CoreConfigV2ray.cs index ed4ddcf5..e1820080 100644 --- a/v2rayN/v2rayN/Handler/CoreConfigV2ray.cs +++ b/v2rayN/v2rayN/Handler/CoreConfig/CoreConfigV2ray.cs @@ -4,7 +4,7 @@ using v2rayN.Enums; using v2rayN.Models; using v2rayN.Resx; -namespace v2rayN.Handler +namespace v2rayN.Handler.CoreConfig { internal class CoreConfigV2ray { @@ -277,8 +277,8 @@ namespace v2rayN.Handler if (!hasDomainIp) { if (!Utils.IsNullOrEmpty(rules.port) - || (rules.protocol?.Count > 0) - || (rules.inboundTag?.Count > 0) + || rules.protocol?.Count > 0 + || rules.inboundTag?.Count > 0 ) { var it = JsonUtils.DeepCopy(rules); @@ -674,7 +674,7 @@ namespace v2rayN.Handler { authority = Utils.IsNullOrEmpty(host) ? null : host, serviceName = node.path, - multiMode = (node.headerType == Global.GrpcMultiMode), + multiMode = node.headerType == Global.GrpcMultiMode, idle_timeout = _config.grpcItem.idle_timeout, health_check_timeout = _config.grpcItem.health_check_timeout, permit_without_stream = _config.grpcItem.permit_without_stream, diff --git a/v2rayN/v2rayN/Handler/CoreHandler.cs b/v2rayN/v2rayN/Handler/CoreHandler.cs index 3024203d..dbe70658 100644 --- a/v2rayN/v2rayN/Handler/CoreHandler.cs +++ b/v2rayN/v2rayN/Handler/CoreHandler.cs @@ -3,6 +3,7 @@ using System.IO; using System.Reactive.Linq; using System.Text; using v2rayN.Enums; +using v2rayN.Handler.CoreConfig; using v2rayN.Models; using v2rayN.Resx; @@ -39,7 +40,7 @@ namespace v2rayN.Handler string fileName = Utils.GetConfigPath(Global.CoreConfigFileName); if (CoreConfigHandler.GenerateClientConfig(node, fileName, out string msg, out string content) != 0) { - ShowMsg(false, msg); + ShowMsg(false, msg); return; } else diff --git a/v2rayN/v2rayN/Handler/Fmt/BaseFmt.cs b/v2rayN/v2rayN/Handler/Fmt/BaseFmt.cs new file mode 100644 index 00000000..8bc9bb48 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/BaseFmt.cs @@ -0,0 +1,184 @@ +using System.Collections.Specialized; +using v2rayN.Enums; +using v2rayN.Models; + +namespace v2rayN.Handler.Fmt +{ + internal class BaseFmt + { + protected static string GetIpv6(string address) + { + if (Utils.IsIpv6(address)) + { + // 检查地址是否已经被方括号包围,如果没有,则添加方括号 + return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]"; + } + return address; // 如果不是IPv6地址,直接返回原地址 + } + + protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary dicQuery) + { + if (!Utils.IsNullOrEmpty(item.flow)) + { + dicQuery.Add("flow", item.flow); + } + + if (!Utils.IsNullOrEmpty(item.streamSecurity)) + { + dicQuery.Add("security", item.streamSecurity); + } + else + { + if (securityDef != null) + { + dicQuery.Add("security", securityDef); + } + } + if (!Utils.IsNullOrEmpty(item.sni)) + { + dicQuery.Add("sni", item.sni); + } + if (!Utils.IsNullOrEmpty(item.alpn)) + { + dicQuery.Add("alpn", Utils.UrlEncode(item.alpn)); + } + if (!Utils.IsNullOrEmpty(item.fingerprint)) + { + dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint)); + } + if (!Utils.IsNullOrEmpty(item.publicKey)) + { + dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey)); + } + if (!Utils.IsNullOrEmpty(item.shortId)) + { + dicQuery.Add("sid", Utils.UrlEncode(item.shortId)); + } + if (!Utils.IsNullOrEmpty(item.spiderX)) + { + dicQuery.Add("spx", Utils.UrlEncode(item.spiderX)); + } + + dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp)); + + switch (item.network) + { + case nameof(ETransport.tcp): + dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None); + if (!Utils.IsNullOrEmpty(item.requestHost)) + { + dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); + } + break; + + case nameof(ETransport.kcp): + dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None); + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("seed", Utils.UrlEncode(item.path)); + } + break; + + case nameof(ETransport.ws): + case nameof(ETransport.httpupgrade): + if (!Utils.IsNullOrEmpty(item.requestHost)) + { + dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); + } + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("path", Utils.UrlEncode(item.path)); + } + break; + + case nameof(ETransport.http): + case nameof(ETransport.h2): + dicQuery["type"] = nameof(ETransport.http); + if (!Utils.IsNullOrEmpty(item.requestHost)) + { + dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); + } + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("path", Utils.UrlEncode(item.path)); + } + break; + + case nameof(ETransport.quic): + dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None); + dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost)); + dicQuery.Add("key", Utils.UrlEncode(item.path)); + break; + + case nameof(ETransport.grpc): + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("authority", Utils.UrlEncode(item.requestHost)); + dicQuery.Add("serviceName", Utils.UrlEncode(item.path)); + if (item.headerType is Global.GrpcGunMode or Global.GrpcMultiMode) + { + dicQuery.Add("mode", Utils.UrlEncode(item.headerType)); + } + } + break; + } + return 0; + } + + protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item) + { + item.flow = query["flow"] ?? ""; + item.streamSecurity = query["security"] ?? ""; + item.sni = query["sni"] ?? ""; + item.alpn = Utils.UrlDecode(query["alpn"] ?? ""); + item.fingerprint = Utils.UrlDecode(query["fp"] ?? ""); + item.publicKey = Utils.UrlDecode(query["pbk"] ?? ""); + item.shortId = Utils.UrlDecode(query["sid"] ?? ""); + item.spiderX = Utils.UrlDecode(query["spx"] ?? ""); + + item.network = query["type"] ?? nameof(ETransport.tcp); + switch (item.network) + { + case nameof(ETransport.tcp): + item.headerType = query["headerType"] ?? Global.None; + item.requestHost = Utils.UrlDecode(query["host"] ?? ""); + + break; + + case nameof(ETransport.kcp): + item.headerType = query["headerType"] ?? Global.None; + item.path = Utils.UrlDecode(query["seed"] ?? ""); + break; + + case nameof(ETransport.ws): + case nameof(ETransport.httpupgrade): + item.requestHost = Utils.UrlDecode(query["host"] ?? ""); + item.path = Utils.UrlDecode(query["path"] ?? "/"); + break; + + case nameof(ETransport.http): + case nameof(ETransport.h2): + item.network = nameof(ETransport.h2); + item.requestHost = Utils.UrlDecode(query["host"] ?? ""); + item.path = Utils.UrlDecode(query["path"] ?? "/"); + break; + + case nameof(ETransport.quic): + item.headerType = query["headerType"] ?? Global.None; + item.requestHost = query["quicSecurity"] ?? Global.None; + item.path = Utils.UrlDecode(query["key"] ?? ""); + break; + + case nameof(ETransport.grpc): + item.requestHost = Utils.UrlDecode(query["authority"] ?? ""); + item.path = Utils.UrlDecode(query["serviceName"] ?? ""); + item.headerType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode); + break; + + default: + break; + } + return 0; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/FmtHandler.cs b/v2rayN/v2rayN/Handler/Fmt/FmtHandler.cs new file mode 100644 index 00000000..34ec25a3 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/FmtHandler.cs @@ -0,0 +1,95 @@ +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class FmtHandler + { + public static string? GetShareUri(ProfileItem item) + { + try + { + var url = item.configType switch + { + EConfigType.VMess => VmessFmt.ToUri(item), + EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item), + EConfigType.Socks => SocksFmt.ToUri(item), + EConfigType.Trojan => TrojanFmt.ToUri(item), + EConfigType.VLESS => VLESSFmt.ToUri(item), + EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item), + EConfigType.Tuic => TuicFmt.ToUri(item), + EConfigType.Wireguard => WireguardFmt.ToUri(item), + _ => null, + }; + + return url; + } + catch (Exception ex) + { + Logging.SaveLog(ex.Message, ex); + return ""; + } + } + + + public static ProfileItem? ResolveConfig(string config, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + try + { + string str = config.TrimEx(); + if (Utils.IsNullOrEmpty(str)) + { + msg = ResUI.FailedReadConfiguration; + return null; + } + + if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess])) + { + return VmessFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks])) + { + return ShadowsocksFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Socks])) + { + return SocksFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Trojan])) + { + return TrojanFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.VLESS])) + { + return VLESSFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare)) + { + return Hysteria2Fmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Tuic])) + { + return TuicFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Wireguard])) + { + return WireguardFmt.Resolve(str, out msg); + } + else + { + msg = ResUI.NonvmessOrssProtocol; + return null; + } + } + catch (Exception ex) + { + Logging.SaveLog(ex.Message, ex); + msg = ResUI.Incorrectconfiguration; + return null; + } + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/v2rayN/Handler/Fmt/Hysteria2Fmt.cs new file mode 100644 index 00000000..6565e935 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/Hysteria2Fmt.cs @@ -0,0 +1,68 @@ +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class Hysteria2Fmt : BaseFmt + { + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + ProfileItem item = new() + { + configType = EConfigType.Hysteria2 + }; + + Uri url = new(str); + + item.address = url.IdnHost; + item.port = url.Port; + item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + item.id = Utils.UrlDecode(url.UserInfo); + + var query = Utils.ParseQueryString(url.Query); + ResolveStdTransport(query, ref item); + item.path = Utils.UrlDecode(query["obfs-password"] ?? ""); + item.allowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false"; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) return null; + string url = string.Empty; + + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + var dicQuery = new Dictionary(); + if (!Utils.IsNullOrEmpty(item.sni)) + { + dicQuery.Add("sni", item.sni); + } + if (!Utils.IsNullOrEmpty(item.alpn)) + { + dicQuery.Add("alpn", Utils.UrlEncode(item.alpn)); + } + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("obfs", "salamander"); + dicQuery.Add("obfs-password", Utils.UrlEncode(item.path)); + } + dicQuery.Add("insecure", item.allowInsecure.ToLower() == "true" ? "1" : "0"); + + string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); + + url = string.Format("{0}@{1}:{2}", + item.id, + GetIpv6(item.address), + item.port); + url = $"{Global.ProtocolShares[EConfigType.Hysteria2]}{url}/{query}{remark}"; + return url; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/ShadowsocksFmt.cs b/v2rayN/v2rayN/Handler/Fmt/ShadowsocksFmt.cs new file mode 100644 index 00000000..92f51400 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/ShadowsocksFmt.cs @@ -0,0 +1,150 @@ +using System.Text.RegularExpressions; +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class ShadowsocksFmt : BaseFmt + { + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + ProfileItem? item; + + item = ResolveSSLegacy(str) ?? ResolveSip002(str); + if (item == null) + { + return null; + } + if (item.address.Length == 0 || item.port == 0 || item.security.Length == 0 || item.id.Length == 0) + { + return null; + } + + item.configType = EConfigType.Shadowsocks; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) return null; + string url = string.Empty; + + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + //url = string.Format("{0}:{1}@{2}:{3}", + // item.security, + // item.id, + // item.address, + // item.port); + //url = Utile.Base64Encode(url); + //new Sip002 + var pw = Utils.Base64Encode($"{item.security}:{item.id}"); + url = $"{pw}@{GetIpv6(item.address)}:{item.port}"; + url = $"{Global.ProtocolShares[EConfigType.Shadowsocks]}{url}{remark}"; + return url; + } + + private static readonly Regex UrlFinder = new(@"ss://(?[A-Za-z0-9+-/=_]+)(?:#(?\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex DetailsParser = new(@"^((?.+?):(?.*)@(?.+?):(?\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static ProfileItem? ResolveSSLegacy(string result) + { + var match = UrlFinder.Match(result); + if (!match.Success) + return null; + + ProfileItem item = new(); + var base64 = match.Groups["base64"].Value.TrimEnd('/'); + var tag = match.Groups["tag"].Value; + if (!Utils.IsNullOrEmpty(tag)) + { + item.remarks = Utils.UrlDecode(tag); + } + Match details; + try + { + details = DetailsParser.Match(Utils.Base64Decode(base64)); + } + catch (FormatException) + { + return null; + } + if (!details.Success) + return null; + item.security = details.Groups["method"].Value; + item.id = details.Groups["password"].Value; + item.address = details.Groups["hostname"].Value; + item.port = Utils.ToInt(details.Groups["port"].Value); + return item; + } + + private static ProfileItem? ResolveSip002(string result) + { + Uri parsedUrl; + try + { + parsedUrl = new Uri(result); + } + catch (UriFormatException) + { + return null; + } + ProfileItem item = new() + { + remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + address = parsedUrl.IdnHost, + port = parsedUrl.Port, + }; + string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped); + //2022-blake3 + if (rawUserInfo.Contains(':')) + { + string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length != 2) + { + return null; + } + item.security = userInfoParts[0]; + item.id = Utils.UrlDecode(userInfoParts[1]); + } + else + { + // parse base64 UserInfo + string userInfo = Utils.Base64Decode(rawUserInfo); + string[] userInfoParts = userInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length != 2) + { + return null; + } + item.security = userInfoParts[0]; + item.id = userInfoParts[1]; + } + + var queryParameters = Utils.ParseQueryString(parsedUrl.Query); + if (queryParameters["plugin"] != null) + { + //obfs-host exists + var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host")); + if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost)) + { + obfsHost = obfsHost?.Replace("obfs-host=", ""); + item.network = Global.DefaultNetwork; + item.headerType = Global.TcpHeaderHttp; + item.requestHost = obfsHost ?? ""; + } + else + { + return null; + } + } + + return item; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/SocksFmt.cs b/v2rayN/v2rayN/Handler/Fmt/SocksFmt.cs new file mode 100644 index 00000000..ae99f3f8 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/SocksFmt.cs @@ -0,0 +1,131 @@ +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class SocksFmt : BaseFmt + { + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + ProfileItem? item; + + item = ResolveSocksNew(str) ?? ResolveSocks(str); + if (item == null) + { + return null; + } + if (item.address.Length == 0 || item.port == 0) + { + return null; + } + + item.configType = EConfigType.Socks; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) return null; + string url = string.Empty; + + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + //url = string.Format("{0}:{1}@{2}:{3}", + // item.security, + // item.id, + // item.address, + // item.port); + //url = Utile.Base64Encode(url); + //new + var pw = Utils.Base64Encode($"{item.security}:{item.id}"); + url = $"{pw}@{GetIpv6(item.address)}:{item.port}"; + url = $"{Global.ProtocolShares[EConfigType.Socks]}{url}{remark}"; + return url; + } + + private static ProfileItem? ResolveSocks(string result) + { + ProfileItem item = new() + { + configType = EConfigType.Socks + }; + result = result[Global.ProtocolShares[EConfigType.Socks].Length..]; + //remark + int indexRemark = result.IndexOf("#"); + if (indexRemark > 0) + { + try + { + item.remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1)); + } + catch { } + result = result[..indexRemark]; + } + //part decode + int indexS = result.IndexOf("@"); + if (indexS > 0) + { + } + else + { + result = Utils.Base64Decode(result); + } + + string[] arr1 = result.Split('@'); + if (arr1.Length != 2) + { + return null; + } + string[] arr21 = arr1[0].Split(':'); + //string[] arr22 = arr1[1].Split(':'); + int indexPort = arr1[1].LastIndexOf(":"); + if (arr21.Length != 2 || indexPort < 0) + { + return null; + } + item.address = arr1[1][..indexPort]; + item.port = Utils.ToInt(arr1[1][(indexPort + 1)..]); + item.security = arr21[0]; + item.id = arr21[1]; + + return item; + } + + private static ProfileItem? ResolveSocksNew(string result) + { + Uri parsedUrl; + try + { + parsedUrl = new Uri(result); + } + catch (UriFormatException) + { + return null; + } + ProfileItem item = new() + { + remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + address = parsedUrl.IdnHost, + port = parsedUrl.Port, + }; + + // parse base64 UserInfo + string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped); + string userInfo = Utils.Base64Decode(rawUserInfo); + string[] userInfoParts = userInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length == 2) + { + item.security = userInfoParts[0]; + item.id = userInfoParts[1]; + } + + return item; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/TrojanFmt.cs b/v2rayN/v2rayN/Handler/Fmt/TrojanFmt.cs new file mode 100644 index 00000000..71f23805 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/TrojanFmt.cs @@ -0,0 +1,53 @@ +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class TrojanFmt : BaseFmt + { + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + ProfileItem item = new() + { + configType = EConfigType.Trojan + }; + + Uri url = new(str); + + item.address = url.IdnHost; + item.port = url.Port; + item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + item.id = Utils.UrlDecode(url.UserInfo); + + var query = Utils.ParseQueryString(url.Query); + ResolveStdTransport(query, ref item); + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) return null; + string url = string.Empty; + + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + var dicQuery = new Dictionary(); + GetStdTransport(item, null, ref dicQuery); + string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); + + url = string.Format("{0}@{1}:{2}", + item.id, + GetIpv6(item.address), + item.port); + url = $"{Global.ProtocolShares[EConfigType.Trojan]}{url}{query}{remark}"; + return url; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/TuicFmt.cs b/v2rayN/v2rayN/Handler/Fmt/TuicFmt.cs new file mode 100644 index 00000000..f1c64cd9 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/TuicFmt.cs @@ -0,0 +1,68 @@ +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class TuicFmt : BaseFmt + { + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + ProfileItem item = new() + { + configType = EConfigType.Tuic + }; + + Uri url = new(str); + + item.address = url.IdnHost; + item.port = url.Port; + item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + var userInfoParts = url.UserInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length == 2) + { + item.id = userInfoParts[0]; + item.security = userInfoParts[1]; + } + + var query = Utils.ParseQueryString(url.Query); + ResolveStdTransport(query, ref item); + item.headerType = query["congestion_control"] ?? ""; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) return null; + string url = string.Empty; + + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + var dicQuery = new Dictionary(); + if (!Utils.IsNullOrEmpty(item.sni)) + { + dicQuery.Add("sni", item.sni); + } + if (!Utils.IsNullOrEmpty(item.alpn)) + { + dicQuery.Add("alpn", Utils.UrlEncode(item.alpn)); + } + dicQuery.Add("congestion_control", item.headerType); + + string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); + + url = string.Format("{0}@{1}:{2}", + $"{item.id}:{item.security}", + GetIpv6(item.address), + item.port); + url = $"{Global.ProtocolShares[EConfigType.Tuic]}{url}{query}{remark}"; + return url; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/VLESSFmt.cs b/v2rayN/v2rayN/Handler/Fmt/VLESSFmt.cs new file mode 100644 index 00000000..b7451ca2 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/VLESSFmt.cs @@ -0,0 +1,64 @@ +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class VLESSFmt : BaseFmt + { + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + ProfileItem item = new() + { + configType = EConfigType.VLESS, + security = Global.None + }; + + Uri url = new(str); + + item.address = url.IdnHost; + item.port = url.Port; + item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + item.id = Utils.UrlDecode(url.UserInfo); + + var query = Utils.ParseQueryString(url.Query); + item.security = query["encryption"] ?? Global.None; + item.streamSecurity = query["security"] ?? ""; + ResolveStdTransport(query, ref item); + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) return null; + string url = string.Empty; + + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + var dicQuery = new Dictionary(); + if (!Utils.IsNullOrEmpty(item.security)) + { + dicQuery.Add("encryption", item.security); + } + else + { + dicQuery.Add("encryption", Global.None); + } + GetStdTransport(item, Global.None, ref dicQuery); + string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); + + url = string.Format("{0}@{1}:{2}", + item.id, + GetIpv6(item.address), + item.port); + url = $"{Global.ProtocolShares[EConfigType.VLESS]}{url}{query}{remark}"; + return url; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/VmessFmt.cs b/v2rayN/v2rayN/Handler/Fmt/VmessFmt.cs new file mode 100644 index 00000000..5e0b230f --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/VmessFmt.cs @@ -0,0 +1,228 @@ +using System.Text.RegularExpressions; +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class VmessFmt : BaseFmt + { + private static readonly Regex StdVmessUserInfo = new( + @"^(?[a-z]+)(\+(?[a-z]+))?:(?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", RegexOptions.Compiled); + + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + ProfileItem? item; + int indexSplit = str.IndexOf("?"); + if (indexSplit > 0) + { + item = ResolveStdVmess(str) ?? ResolveVmess4Kitsunebi(str); + } + else + { + item = ResolveVmess(str, out msg); + } + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) return null; + string url = string.Empty; + + VmessQRCode vmessQRCode = new() + { + v = item.configVersion, + ps = item.remarks.TrimEx(), + add = item.address, + port = item.port, + id = item.id, + aid = item.alterId, + scy = item.security, + net = item.network, + type = item.headerType, + host = item.requestHost, + path = item.path, + tls = item.streamSecurity, + sni = item.sni, + alpn = item.alpn, + fp = item.fingerprint + }; + + url = JsonUtils.Serialize(vmessQRCode); + url = Utils.Base64Encode(url); + url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}"; + + return url; + } + + private static ProfileItem? ResolveVmess(string result, out string msg) + { + msg = string.Empty; + var item = new ProfileItem + { + configType = EConfigType.VMess + }; + + result = result[Global.ProtocolShares[EConfigType.VMess].Length..]; + result = Utils.Base64Decode(result); + + //转成Json + VmessQRCode? vmessQRCode = JsonUtils.Deserialize(result); + if (vmessQRCode == null) + { + msg = ResUI.FailedConversionConfiguration; + return null; + } + + item.network = Global.DefaultNetwork; + item.headerType = Global.None; + + item.configVersion = Utils.ToInt(vmessQRCode.v); + item.remarks = Utils.ToString(vmessQRCode.ps); + item.address = Utils.ToString(vmessQRCode.add); + item.port = Utils.ToInt(vmessQRCode.port); + item.id = Utils.ToString(vmessQRCode.id); + item.alterId = Utils.ToInt(vmessQRCode.aid); + item.security = Utils.ToString(vmessQRCode.scy); + + item.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity; + if (!Utils.IsNullOrEmpty(vmessQRCode.net)) + { + item.network = vmessQRCode.net; + } + if (!Utils.IsNullOrEmpty(vmessQRCode.type)) + { + item.headerType = vmessQRCode.type; + } + + item.requestHost = Utils.ToString(vmessQRCode.host); + item.path = Utils.ToString(vmessQRCode.path); + item.streamSecurity = Utils.ToString(vmessQRCode.tls); + item.sni = Utils.ToString(vmessQRCode.sni); + item.alpn = Utils.ToString(vmessQRCode.alpn); + item.fingerprint = Utils.ToString(vmessQRCode.fp); + + return item; + } + + private static ProfileItem? ResolveStdVmess(string result) + { + ProfileItem item = new() + { + configType = EConfigType.VMess, + security = "auto" + }; + + Uri u = new(result); + + item.address = u.IdnHost; + item.port = u.Port; + item.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + var query = Utils.ParseQueryString(u.Query); + + var m = StdVmessUserInfo.Match(u.UserInfo); + if (!m.Success) return null; + + item.id = m.Groups["id"].Value; + + if (m.Groups["streamSecurity"].Success) + { + item.streamSecurity = m.Groups["streamSecurity"].Value; + } + switch (item.streamSecurity) + { + case Global.StreamSecurity: + break; + + default: + if (!Utils.IsNullOrEmpty(item.streamSecurity)) + return null; + break; + } + + item.network = m.Groups["network"].Value; + switch (item.network) + { + case nameof(ETransport.tcp): + string t1 = query["type"] ?? Global.None; + item.headerType = t1; + break; + + case nameof(ETransport.kcp): + item.headerType = query["type"] ?? Global.None; + break; + + case nameof(ETransport.ws): + case nameof(ETransport.httpupgrade): + string p1 = query["path"] ?? "/"; + string h1 = query["host"] ?? ""; + item.requestHost = Utils.UrlDecode(h1); + item.path = p1; + break; + + case nameof(ETransport.http): + case nameof(ETransport.h2): + item.network = nameof(ETransport.h2); + string p2 = query["path"] ?? "/"; + string h2 = query["host"] ?? ""; + item.requestHost = Utils.UrlDecode(h2); + item.path = p2; + break; + + case nameof(ETransport.quic): + string s = query["security"] ?? Global.None; + string k = query["key"] ?? ""; + string t3 = query["type"] ?? Global.None; + item.headerType = t3; + item.requestHost = Utils.UrlDecode(s); + item.path = k; + break; + + default: + return null; + } + + return item; + } + + private static ProfileItem? ResolveVmess4Kitsunebi(string result) + { + ProfileItem item = new() + { + configType = EConfigType.VMess + }; + result = result[Global.ProtocolShares[EConfigType.VMess].Length..]; + int indexSplit = result.IndexOf("?"); + if (indexSplit > 0) + { + result = result[..indexSplit]; + } + result = Utils.Base64Decode(result); + + string[] arr1 = result.Split('@'); + if (arr1.Length != 2) + { + return null; + } + string[] arr21 = arr1[0].Split(':'); + string[] arr22 = arr1[1].Split(':'); + if (arr21.Length != 2 || arr22.Length != 2) + { + return null; + } + + item.address = arr22[0]; + item.port = Utils.ToInt(arr22[1]); + item.security = arr21[0]; + item.id = arr21[1]; + + item.network = Global.DefaultNetwork; + item.headerType = Global.None; + item.remarks = "Alien"; + + return item; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/Fmt/WireguardFmt.cs b/v2rayN/v2rayN/Handler/Fmt/WireguardFmt.cs new file mode 100644 index 00000000..08af7262 --- /dev/null +++ b/v2rayN/v2rayN/Handler/Fmt/WireguardFmt.cs @@ -0,0 +1,73 @@ +using v2rayN.Enums; +using v2rayN.Models; +using v2rayN.Resx; + +namespace v2rayN.Handler.Fmt +{ + internal class WireguardFmt : BaseFmt + { + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + ProfileItem item = new() + { + configType = EConfigType.Wireguard + }; + + Uri url = new(str); + + item.address = url.IdnHost; + item.port = url.Port; + item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + item.id = Utils.UrlDecode(url.UserInfo); + + var query = Utils.ParseQueryString(url.Query); + + item.publicKey = Utils.UrlDecode(query["publickey"] ?? ""); + item.path = Utils.UrlDecode(query["reserved"] ?? ""); + item.requestHost = Utils.UrlDecode(query["address"] ?? ""); + item.shortId = Utils.UrlDecode(query["mtu"] ?? ""); + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) return null; + string url = string.Empty; + + string remark = string.Empty; + if (!Utils.IsNullOrEmpty(item.remarks)) + { + remark = "#" + Utils.UrlEncode(item.remarks); + } + + var dicQuery = new Dictionary(); + if (!Utils.IsNullOrEmpty(item.publicKey)) + { + dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey)); + } + if (!Utils.IsNullOrEmpty(item.path)) + { + dicQuery.Add("reserved", Utils.UrlEncode(item.path)); + } + if (!Utils.IsNullOrEmpty(item.requestHost)) + { + dicQuery.Add("address", Utils.UrlEncode(item.requestHost)); + } + if (!Utils.IsNullOrEmpty(item.shortId)) + { + dicQuery.Add("mtu", Utils.UrlEncode(item.shortId)); + } + string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); + + url = string.Format("{0}@{1}:{2}", + Utils.UrlEncode(item.id), + GetIpv6(item.address), + item.port); + url = $"{Global.ProtocolShares[EConfigType.Wireguard]}{url}/{query}{remark}"; + return url; + } + } +} \ No newline at end of file diff --git a/v2rayN/v2rayN/Handler/MainFormHandler.cs b/v2rayN/v2rayN/Handler/MainFormHandler.cs index add0629e..607bafd1 100644 --- a/v2rayN/v2rayN/Handler/MainFormHandler.cs +++ b/v2rayN/v2rayN/Handler/MainFormHandler.cs @@ -4,6 +4,7 @@ using System.Drawing; using System.IO; using System.Windows.Media.Imaging; using v2rayN.Enums; +using v2rayN.Handler.CoreConfig; using v2rayN.Models; using v2rayN.Resx; diff --git a/v2rayN/v2rayN/Handler/ShareHandler.cs b/v2rayN/v2rayN/Handler/ShareHandler.cs deleted file mode 100644 index 8e93a11d..00000000 --- a/v2rayN/v2rayN/Handler/ShareHandler.cs +++ /dev/null @@ -1,1004 +0,0 @@ -using System.Collections.Specialized; -using System.Text.RegularExpressions; -using v2rayN.Enums; -using v2rayN.Models; -using v2rayN.Resx; - -namespace v2rayN.Handler -{ - internal class ShareHandler - { - #region GetShareUrl - - /// - /// GetShareUrl - /// - /// - /// - public static string? GetShareUrl(ProfileItem item) - { - try - { - string? url = string.Empty; - - url = item.configType switch - { - EConfigType.VMess => ShareVmess(item), - EConfigType.Shadowsocks => ShareShadowsocks(item), - EConfigType.Socks => ShareSocks(item), - EConfigType.Trojan => ShareTrojan(item), - EConfigType.VLESS => ShareVLESS(item), - EConfigType.Hysteria2 => ShareHysteria2(item), - EConfigType.Tuic => ShareTuic(item), - EConfigType.Wireguard => ShareWireguard(item), - _ => null, - }; - - return url; - } - catch (Exception ex) - { - Logging.SaveLog(ex.Message, ex); - return ""; - } - } - - private static string ShareVmess(ProfileItem item) - { - string url = string.Empty; - - VmessQRCode vmessQRCode = new() - { - v = item.configVersion, - ps = item.remarks.TrimEx(), - add = item.address, - port = item.port, - id = item.id, - aid = item.alterId, - scy = item.security, - net = item.network, - type = item.headerType, - host = item.requestHost, - path = item.path, - tls = item.streamSecurity, - sni = item.sni, - alpn = item.alpn, - fp = item.fingerprint - }; - - url = JsonUtils.Serialize(vmessQRCode); - url = Utils.Base64Encode(url); - url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}"; - - return url; - } - - private static string ShareShadowsocks(ProfileItem item) - { - string url = string.Empty; - - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - //url = string.Format("{0}:{1}@{2}:{3}", - // item.security, - // item.id, - // item.address, - // item.port); - //url = Utile.Base64Encode(url); - //new Sip002 - var pw = Utils.Base64Encode($"{item.security}:{item.id}"); - url = $"{pw}@{GetIpv6(item.address)}:{item.port}"; - url = $"{Global.ProtocolShares[EConfigType.Shadowsocks]}{url}{remark}"; - return url; - } - - private static string ShareSocks(ProfileItem item) - { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - //url = string.Format("{0}:{1}@{2}:{3}", - // item.security, - // item.id, - // item.address, - // item.port); - //url = Utile.Base64Encode(url); - //new - var pw = Utils.Base64Encode($"{item.security}:{item.id}"); - url = $"{pw}@{GetIpv6(item.address)}:{item.port}"; - url = $"{Global.ProtocolShares[EConfigType.Socks]}{url}{remark}"; - return url; - } - - private static string ShareTrojan(ProfileItem item) - { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - var dicQuery = new Dictionary(); - GetStdTransport(item, null, ref dicQuery); - string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); - - url = string.Format("{0}@{1}:{2}", - item.id, - GetIpv6(item.address), - item.port); - url = $"{Global.ProtocolShares[EConfigType.Trojan]}{url}{query}{remark}"; - return url; - } - - private static string ShareVLESS(ProfileItem item) - { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - var dicQuery = new Dictionary(); - if (!Utils.IsNullOrEmpty(item.security)) - { - dicQuery.Add("encryption", item.security); - } - else - { - dicQuery.Add("encryption", Global.None); - } - GetStdTransport(item, Global.None, ref dicQuery); - string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); - - url = string.Format("{0}@{1}:{2}", - item.id, - GetIpv6(item.address), - item.port); - url = $"{Global.ProtocolShares[EConfigType.VLESS]}{url}{query}{remark}"; - return url; - } - - private static string ShareHysteria2(ProfileItem item) - { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - var dicQuery = new Dictionary(); - if (!Utils.IsNullOrEmpty(item.sni)) - { - dicQuery.Add("sni", item.sni); - } - if (!Utils.IsNullOrEmpty(item.alpn)) - { - dicQuery.Add("alpn", Utils.UrlEncode(item.alpn)); - } - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("obfs", "salamander"); - dicQuery.Add("obfs-password", Utils.UrlEncode(item.path)); - } - dicQuery.Add("insecure", item.allowInsecure.ToLower() == "true" ? "1" : "0"); - - string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); - - url = string.Format("{0}@{1}:{2}", - item.id, - GetIpv6(item.address), - item.port); - url = $"{Global.ProtocolShares[EConfigType.Hysteria2]}{url}/{query}{remark}"; - return url; - } - - private static string ShareTuic(ProfileItem item) - { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - var dicQuery = new Dictionary(); - if (!Utils.IsNullOrEmpty(item.sni)) - { - dicQuery.Add("sni", item.sni); - } - if (!Utils.IsNullOrEmpty(item.alpn)) - { - dicQuery.Add("alpn", Utils.UrlEncode(item.alpn)); - } - dicQuery.Add("congestion_control", item.headerType); - - string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); - - url = string.Format("{0}@{1}:{2}", - $"{item.id}:{item.security}", - GetIpv6(item.address), - item.port); - url = $"{Global.ProtocolShares[EConfigType.Tuic]}{url}{query}{remark}"; - return url; - } - - private static string ShareWireguard(ProfileItem item) - { - string url = string.Empty; - string remark = string.Empty; - if (!Utils.IsNullOrEmpty(item.remarks)) - { - remark = "#" + Utils.UrlEncode(item.remarks); - } - - var dicQuery = new Dictionary(); - if (!Utils.IsNullOrEmpty(item.publicKey)) - { - dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey)); - } - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("reserved", Utils.UrlEncode(item.path)); - } - if (!Utils.IsNullOrEmpty(item.requestHost)) - { - dicQuery.Add("address", Utils.UrlEncode(item.requestHost)); - } - if (!Utils.IsNullOrEmpty(item.shortId)) - { - dicQuery.Add("mtu", Utils.UrlEncode(item.shortId)); - } - string query = "?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()); - - url = string.Format("{0}@{1}:{2}", - Utils.UrlEncode(item.id), - GetIpv6(item.address), - item.port); - url = $"{Global.ProtocolShares[EConfigType.Wireguard]}{url}/{query}{remark}"; - return url; - } - - private static string GetIpv6(string address) - { - if (Utils.IsIpv6(address)) - { - // 检查地址是否已经被方括号包围,如果没有,则添加方括号 - return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]"; - } - return address; // 如果不是IPv6地址,直接返回原地址 - } - - private static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary dicQuery) - { - if (!Utils.IsNullOrEmpty(item.flow)) - { - dicQuery.Add("flow", item.flow); - } - - if (!Utils.IsNullOrEmpty(item.streamSecurity)) - { - dicQuery.Add("security", item.streamSecurity); - } - else - { - if (securityDef != null) - { - dicQuery.Add("security", securityDef); - } - } - if (!Utils.IsNullOrEmpty(item.sni)) - { - dicQuery.Add("sni", item.sni); - } - if (!Utils.IsNullOrEmpty(item.alpn)) - { - dicQuery.Add("alpn", Utils.UrlEncode(item.alpn)); - } - if (!Utils.IsNullOrEmpty(item.fingerprint)) - { - dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint)); - } - if (!Utils.IsNullOrEmpty(item.publicKey)) - { - dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey)); - } - if (!Utils.IsNullOrEmpty(item.shortId)) - { - dicQuery.Add("sid", Utils.UrlEncode(item.shortId)); - } - if (!Utils.IsNullOrEmpty(item.spiderX)) - { - dicQuery.Add("spx", Utils.UrlEncode(item.spiderX)); - } - - dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp)); - - switch (item.network) - { - case nameof(ETransport.tcp): - dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None); - if (!Utils.IsNullOrEmpty(item.requestHost)) - { - dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); - } - break; - - case nameof(ETransport.kcp): - dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None); - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("seed", Utils.UrlEncode(item.path)); - } - break; - - case nameof(ETransport.ws): - case nameof(ETransport.httpupgrade): - if (!Utils.IsNullOrEmpty(item.requestHost)) - { - dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); - } - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("path", Utils.UrlEncode(item.path)); - } - break; - - case nameof(ETransport.http): - case nameof(ETransport.h2): - dicQuery["type"] = nameof(ETransport.http); - if (!Utils.IsNullOrEmpty(item.requestHost)) - { - dicQuery.Add("host", Utils.UrlEncode(item.requestHost)); - } - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("path", Utils.UrlEncode(item.path)); - } - break; - - case nameof(ETransport.quic): - dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None); - dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost)); - dicQuery.Add("key", Utils.UrlEncode(item.path)); - break; - - case nameof(ETransport.grpc): - if (!Utils.IsNullOrEmpty(item.path)) - { - dicQuery.Add("authority", Utils.UrlEncode(item.requestHost)); - dicQuery.Add("serviceName", Utils.UrlEncode(item.path)); - if (item.headerType is Global.GrpcGunMode or Global.GrpcMultiMode) - { - dicQuery.Add("mode", Utils.UrlEncode(item.headerType)); - } - } - break; - } - return 0; - } - - #endregion GetShareUrl - - #region ImportShareUrl - - /// - /// 从剪贴板导入URL - /// - /// - /// - public static ProfileItem? ImportFromClipboardConfig(string clipboardData, out string msg) - { - msg = ResUI.ConfigurationFormatIncorrect; - ProfileItem? profileItem; - - try - { - string result = clipboardData.TrimEx(); - if (Utils.IsNullOrEmpty(result)) - { - msg = ResUI.FailedReadConfiguration; - return null; - } - - if (result.StartsWith(Global.ProtocolShares[EConfigType.VMess])) - { - int indexSplit = result.IndexOf("?"); - if (indexSplit > 0) - { - profileItem = ResolveStdVmess(result) ?? ResolveVmess4Kitsunebi(result); - } - else - { - profileItem = ResolveVmess(result, out msg); - } - } - else if (result.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks])) - { - profileItem = ResolveSSLegacy(result) ?? ResolveSip002(result); - if (profileItem == null) - { - return null; - } - if (profileItem.address.Length == 0 || profileItem.port == 0 || profileItem.security.Length == 0 || profileItem.id.Length == 0) - { - return null; - } - - profileItem.configType = EConfigType.Shadowsocks; - } - else if (result.StartsWith(Global.ProtocolShares[EConfigType.Socks])) - { - profileItem = ResolveSocksNew(result) ?? ResolveSocks(result); - if (profileItem == null) - { - return null; - } - if (profileItem.address.Length == 0 || profileItem.port == 0) - { - return null; - } - - profileItem.configType = EConfigType.Socks; - } - else if (result.StartsWith(Global.ProtocolShares[EConfigType.Trojan])) - { - profileItem = ResolveTrojan(result); - } - else if (result.StartsWith(Global.ProtocolShares[EConfigType.VLESS])) - { - profileItem = ResolveStdVLESS(result); - } - else if (result.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || result.StartsWith(Global.Hysteria2ProtocolShare)) - { - profileItem = ResolveHysteria2(result); - } - else if (result.StartsWith(Global.ProtocolShares[EConfigType.Tuic])) - { - profileItem = ResolveTuic(result); - } - else if (result.StartsWith(Global.ProtocolShares[EConfigType.Wireguard])) - { - profileItem = ResolveWireguard(result); - } - else - { - msg = ResUI.NonvmessOrssProtocol; - return null; - } - } - catch (Exception ex) - { - Logging.SaveLog(ex.Message, ex); - msg = ResUI.Incorrectconfiguration; - return null; - } - - return profileItem; - } - - private static ProfileItem? ResolveVmess(string result, out string msg) - { - msg = string.Empty; - var profileItem = new ProfileItem - { - configType = EConfigType.VMess - }; - - result = result[Global.ProtocolShares[EConfigType.VMess].Length..]; - result = Utils.Base64Decode(result); - - //转成Json - VmessQRCode? vmessQRCode = JsonUtils.Deserialize(result); - if (vmessQRCode == null) - { - msg = ResUI.FailedConversionConfiguration; - return null; - } - - profileItem.network = Global.DefaultNetwork; - profileItem.headerType = Global.None; - - profileItem.configVersion = Utils.ToInt(vmessQRCode.v); - profileItem.remarks = Utils.ToString(vmessQRCode.ps); - profileItem.address = Utils.ToString(vmessQRCode.add); - profileItem.port = Utils.ToInt(vmessQRCode.port); - profileItem.id = Utils.ToString(vmessQRCode.id); - profileItem.alterId = Utils.ToInt(vmessQRCode.aid); - profileItem.security = Utils.ToString(vmessQRCode.scy); - - profileItem.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity; - if (!Utils.IsNullOrEmpty(vmessQRCode.net)) - { - profileItem.network = vmessQRCode.net; - } - if (!Utils.IsNullOrEmpty(vmessQRCode.type)) - { - profileItem.headerType = vmessQRCode.type; - } - - profileItem.requestHost = Utils.ToString(vmessQRCode.host); - profileItem.path = Utils.ToString(vmessQRCode.path); - profileItem.streamSecurity = Utils.ToString(vmessQRCode.tls); - profileItem.sni = Utils.ToString(vmessQRCode.sni); - profileItem.alpn = Utils.ToString(vmessQRCode.alpn); - profileItem.fingerprint = Utils.ToString(vmessQRCode.fp); - - return profileItem; - } - - private static ProfileItem? ResolveVmess4Kitsunebi(string result) - { - ProfileItem profileItem = new() - { - configType = EConfigType.VMess - }; - result = result[Global.ProtocolShares[EConfigType.VMess].Length..]; - int indexSplit = result.IndexOf("?"); - if (indexSplit > 0) - { - result = result[..indexSplit]; - } - result = Utils.Base64Decode(result); - - string[] arr1 = result.Split('@'); - if (arr1.Length != 2) - { - return null; - } - string[] arr21 = arr1[0].Split(':'); - string[] arr22 = arr1[1].Split(':'); - if (arr21.Length != 2 || arr22.Length != 2) - { - return null; - } - - profileItem.address = arr22[0]; - profileItem.port = Utils.ToInt(arr22[1]); - profileItem.security = arr21[0]; - profileItem.id = arr21[1]; - - profileItem.network = Global.DefaultNetwork; - profileItem.headerType = Global.None; - profileItem.remarks = "Alien"; - - return profileItem; - } - - private static ProfileItem? ResolveStdVmess(string result) - { - ProfileItem i = new() - { - configType = EConfigType.VMess, - security = "auto" - }; - - Uri u = new(result); - - i.address = u.IdnHost; - i.port = u.Port; - i.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - var query = Utils.ParseQueryString(u.Query); - - var m = StdVmessUserInfo.Match(u.UserInfo); - if (!m.Success) return null; - - i.id = m.Groups["id"].Value; - - if (m.Groups["streamSecurity"].Success) - { - i.streamSecurity = m.Groups["streamSecurity"].Value; - } - switch (i.streamSecurity) - { - case Global.StreamSecurity: - break; - - default: - if (!Utils.IsNullOrEmpty(i.streamSecurity)) - return null; - break; - } - - i.network = m.Groups["network"].Value; - switch (i.network) - { - case nameof(ETransport.tcp): - string t1 = query["type"] ?? Global.None; - i.headerType = t1; - break; - - case nameof(ETransport.kcp): - i.headerType = query["type"] ?? Global.None; - break; - - case nameof(ETransport.ws): - case nameof(ETransport.httpupgrade): - string p1 = query["path"] ?? "/"; - string h1 = query["host"] ?? ""; - i.requestHost = Utils.UrlDecode(h1); - i.path = p1; - break; - - case nameof(ETransport.http): - case nameof(ETransport.h2): - i.network = nameof(ETransport.h2); - string p2 = query["path"] ?? "/"; - string h2 = query["host"] ?? ""; - i.requestHost = Utils.UrlDecode(h2); - i.path = p2; - break; - - case nameof(ETransport.quic): - string s = query["security"] ?? Global.None; - string k = query["key"] ?? ""; - string t3 = query["type"] ?? Global.None; - i.headerType = t3; - i.requestHost = Utils.UrlDecode(s); - i.path = k; - break; - - default: - return null; - } - - return i; - } - - private static ProfileItem? ResolveSip002(string result) - { - Uri parsedUrl; - try - { - parsedUrl = new Uri(result); - } - catch (UriFormatException) - { - return null; - } - ProfileItem server = new() - { - remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), - address = parsedUrl.IdnHost, - port = parsedUrl.Port, - }; - string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.UriEscaped); - //2022-blake3 - if (rawUserInfo.Contains(':')) - { - string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); - if (userInfoParts.Length != 2) - { - return null; - } - server.security = userInfoParts[0]; - server.id = Utils.UrlDecode(userInfoParts[1]); - } - else - { - // parse base64 UserInfo - string userInfo = Utils.Base64Decode(rawUserInfo); - string[] userInfoParts = userInfo.Split(new[] { ':' }, 2); - if (userInfoParts.Length != 2) - { - return null; - } - server.security = userInfoParts[0]; - server.id = userInfoParts[1]; - } - - var queryParameters = Utils.ParseQueryString(parsedUrl.Query); - if (queryParameters["plugin"] != null) - { - //obfs-host exists - var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host")); - if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost)) - { - obfsHost = obfsHost?.Replace("obfs-host=", ""); - server.network = Global.DefaultNetwork; - server.headerType = Global.TcpHeaderHttp; - server.requestHost = obfsHost ?? ""; - } - else - { - return null; - } - } - - return server; - } - - private static readonly Regex UrlFinder = new(@"ss://(?[A-Za-z0-9+-/=_]+)(?:#(?\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex DetailsParser = new(@"^((?.+?):(?.*)@(?.+?):(?\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled); - - private static ProfileItem? ResolveSSLegacy(string result) - { - var match = UrlFinder.Match(result); - if (!match.Success) - return null; - - ProfileItem server = new(); - var base64 = match.Groups["base64"].Value.TrimEnd('/'); - var tag = match.Groups["tag"].Value; - if (!Utils.IsNullOrEmpty(tag)) - { - server.remarks = Utils.UrlDecode(tag); - } - Match details; - try - { - details = DetailsParser.Match(Utils.Base64Decode(base64)); - } - catch (FormatException) - { - return null; - } - if (!details.Success) - return null; - server.security = details.Groups["method"].Value; - server.id = details.Groups["password"].Value; - server.address = details.Groups["hostname"].Value; - server.port = Utils.ToInt(details.Groups["port"].Value); - return server; - } - - private static readonly Regex StdVmessUserInfo = new( - @"^(?[a-z]+)(\+(?[a-z]+))?:(?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", RegexOptions.Compiled); - - private static ProfileItem? ResolveSocks(string result) - { - ProfileItem profileItem = new() - { - configType = EConfigType.Socks - }; - result = result[Global.ProtocolShares[EConfigType.Socks].Length..]; - //remark - int indexRemark = result.IndexOf("#"); - if (indexRemark > 0) - { - try - { - profileItem.remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1)); - } - catch { } - result = result[..indexRemark]; - } - //part decode - int indexS = result.IndexOf("@"); - if (indexS > 0) - { - } - else - { - result = Utils.Base64Decode(result); - } - - string[] arr1 = result.Split('@'); - if (arr1.Length != 2) - { - return null; - } - string[] arr21 = arr1[0].Split(':'); - //string[] arr22 = arr1[1].Split(':'); - int indexPort = arr1[1].LastIndexOf(":"); - if (arr21.Length != 2 || indexPort < 0) - { - return null; - } - profileItem.address = arr1[1][..indexPort]; - profileItem.port = Utils.ToInt(arr1[1][(indexPort + 1)..]); - profileItem.security = arr21[0]; - profileItem.id = arr21[1]; - - return profileItem; - } - - private static ProfileItem? ResolveSocksNew(string result) - { - Uri parsedUrl; - try - { - parsedUrl = new Uri(result); - } - catch (UriFormatException) - { - return null; - } - ProfileItem server = new() - { - remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), - address = parsedUrl.IdnHost, - port = parsedUrl.Port, - }; - - // parse base64 UserInfo - string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped); - string userInfo = Utils.Base64Decode(rawUserInfo); - string[] userInfoParts = userInfo.Split(new[] { ':' }, 2); - if (userInfoParts.Length == 2) - { - server.security = userInfoParts[0]; - server.id = userInfoParts[1]; - } - - return server; - } - - private static ProfileItem ResolveTrojan(string result) - { - ProfileItem item = new() - { - configType = EConfigType.Trojan - }; - - Uri url = new(result); - - item.address = url.IdnHost; - item.port = url.Port; - item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.id = Utils.UrlDecode(url.UserInfo); - - var query = Utils.ParseQueryString(url.Query); - ResolveStdTransport(query, ref item); - - return item; - } - - private static ProfileItem ResolveStdVLESS(string result) - { - ProfileItem item = new() - { - configType = EConfigType.VLESS, - security = Global.None - }; - - Uri url = new(result); - - item.address = url.IdnHost; - item.port = url.Port; - item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.id = Utils.UrlDecode(url.UserInfo); - - var query = Utils.ParseQueryString(url.Query); - item.security = query["encryption"] ?? Global.None; - item.streamSecurity = query["security"] ?? ""; - ResolveStdTransport(query, ref item); - - return item; - } - - private static ProfileItem ResolveHysteria2(string result) - { - ProfileItem item = new() - { - configType = EConfigType.Hysteria2 - }; - - Uri url = new(result); - - item.address = url.IdnHost; - item.port = url.Port; - item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.id = Utils.UrlDecode(url.UserInfo); - - var query = Utils.ParseQueryString(url.Query); - ResolveStdTransport(query, ref item); - item.path = Utils.UrlDecode(query["obfs-password"] ?? ""); - item.allowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false"; - - return item; - } - - private static ProfileItem ResolveTuic(string result) - { - ProfileItem item = new() - { - configType = EConfigType.Tuic - }; - - Uri url = new(result); - - item.address = url.IdnHost; - item.port = url.Port; - item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - var userInfoParts = url.UserInfo.Split(new[] { ':' }, 2); - if (userInfoParts.Length == 2) - { - item.id = userInfoParts[0]; - item.security = userInfoParts[1]; - } - - var query = Utils.ParseQueryString(url.Query); - ResolveStdTransport(query, ref item); - item.headerType = query["congestion_control"] ?? ""; - - return item; - } - - private static ProfileItem ResolveWireguard(string result) - { - ProfileItem item = new() - { - configType = EConfigType.Wireguard - }; - - Uri url = new(result); - - item.address = url.IdnHost; - item.port = url.Port; - item.remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); - item.id = Utils.UrlDecode(url.UserInfo); - - var query = Utils.ParseQueryString(url.Query); - - item.publicKey = Utils.UrlDecode(query["publickey"] ?? ""); - item.path = Utils.UrlDecode(query["reserved"] ?? ""); - item.requestHost = Utils.UrlDecode(query["address"] ?? ""); - item.shortId = Utils.UrlDecode(query["mtu"] ?? ""); - - return item; - } - - private static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item) - { - item.flow = query["flow"] ?? ""; - item.streamSecurity = query["security"] ?? ""; - item.sni = query["sni"] ?? ""; - item.alpn = Utils.UrlDecode(query["alpn"] ?? ""); - item.fingerprint = Utils.UrlDecode(query["fp"] ?? ""); - item.publicKey = Utils.UrlDecode(query["pbk"] ?? ""); - item.shortId = Utils.UrlDecode(query["sid"] ?? ""); - item.spiderX = Utils.UrlDecode(query["spx"] ?? ""); - - item.network = query["type"] ?? nameof(ETransport.tcp); - switch (item.network) - { - case nameof(ETransport.tcp): - item.headerType = query["headerType"] ?? Global.None; - item.requestHost = Utils.UrlDecode(query["host"] ?? ""); - - break; - - case nameof(ETransport.kcp): - item.headerType = query["headerType"] ?? Global.None; - item.path = Utils.UrlDecode(query["seed"] ?? ""); - break; - - case nameof(ETransport.ws): - case nameof(ETransport.httpupgrade): - item.requestHost = Utils.UrlDecode(query["host"] ?? ""); - item.path = Utils.UrlDecode(query["path"] ?? "/"); - break; - - case nameof(ETransport.http): - case nameof(ETransport.h2): - item.network = nameof(ETransport.h2); - item.requestHost = Utils.UrlDecode(query["host"] ?? ""); - item.path = Utils.UrlDecode(query["path"] ?? "/"); - break; - - case nameof(ETransport.quic): - item.headerType = query["headerType"] ?? Global.None; - item.requestHost = query["quicSecurity"] ?? Global.None; - item.path = Utils.UrlDecode(query["key"] ?? ""); - break; - - case nameof(ETransport.grpc): - item.requestHost = Utils.UrlDecode(query["authority"] ?? ""); - item.path = Utils.UrlDecode(query["serviceName"] ?? ""); - item.headerType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode); - break; - - default: - break; - } - return 0; - } - - #endregion ImportShareUrl - } -} \ No newline at end of file diff --git a/v2rayN/v2rayN/Models/ConfigItems.cs b/v2rayN/v2rayN/Models/ConfigItems.cs index ad869814..a986dbf9 100644 --- a/v2rayN/v2rayN/Models/ConfigItems.cs +++ b/v2rayN/v2rayN/Models/ConfigItems.cs @@ -187,7 +187,7 @@ namespace v2rayN.Models public string domainStrategy4Singbox { get; set; } public string domainMatcher { get; set; } public string routingIndexId { get; set; } - public bool enableRoutingAdvanced { get; set; } + public bool enableRoutingAdvanced { get; set; } } [Serializable] diff --git a/v2rayN/v2rayN/Models/SingboxConfig.cs b/v2rayN/v2rayN/Models/SingboxConfig.cs index 45012e68..da4675b9 100644 --- a/v2rayN/v2rayN/Models/SingboxConfig.cs +++ b/v2rayN/v2rayN/Models/SingboxConfig.cs @@ -241,6 +241,6 @@ public string? path { get; set; } public string? url { get; set; } public string? download_detour { get; set; } - public string? update_interval { get; set; } + public string? update_interval { get; set; } } } \ No newline at end of file diff --git a/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs b/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs index e1da3a6d..0bdee0a4 100644 --- a/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/MainWindowViewModel.cs @@ -16,6 +16,7 @@ using System.Windows; using System.Windows.Media; using v2rayN.Enums; using v2rayN.Handler; +using v2rayN.Handler.Fmt; using v2rayN.Models; using v2rayN.Resx; using v2rayN.Views; @@ -1153,7 +1154,7 @@ namespace v2rayN.ViewModels _noticeHandler?.Enqueue(ResUI.PleaseSelectServer); return; } - var url = ShareHandler.GetShareUrl(item); + var url = FmtHandler.GetShareUri(item); if (Utils.IsNullOrEmpty(url)) { return; @@ -1294,7 +1295,7 @@ namespace v2rayN.ViewModels StringBuilder sb = new(); foreach (var it in lstSelecteds) { - string url = ShareHandler.GetShareUrl(it); + string url = FmtHandler.GetShareUri(it); if (Utils.IsNullOrEmpty(url)) { continue; @@ -1319,7 +1320,7 @@ namespace v2rayN.ViewModels StringBuilder sb = new(); foreach (var it in lstSelecteds) { - string? url = ShareHandler.GetShareUrl(it); + string? url = FmtHandler.GetShareUri(it); if (Utils.IsNullOrEmpty(url)) { continue; diff --git a/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs b/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs index 67fab943..77a4851d 100644 --- a/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/OptionSettingViewModel.cs @@ -264,7 +264,7 @@ namespace v2rayN.ViewModels var needReboot = (EnableStatistics != _config.guiItem.enableStatistics || EnableDragDropSort != _config.uiItem.enableDragDropSort || EnableHWA != _config.guiItem.enableHWA - || CurrentFontFamily != _config.uiItem.currentFontFamily); + || CurrentFontFamily != _config.uiItem.currentFontFamily); //if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString()) // || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString()) diff --git a/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs b/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs index aa327e8e..2d5b532f 100644 --- a/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs +++ b/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs @@ -290,7 +290,7 @@ namespace v2rayN.ViewModels private void ImportRulesFromClipboard() { - string clipboardData = Utils.GetClipboardData(); + var clipboardData = Utils.GetClipboardData(); if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0) { RefreshRulesItems(); diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs index 05761af0..53bdb32d 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs @@ -42,7 +42,7 @@ namespace v2rayN.Views _config.inbound[0].destOverride?.ForEach(it => { clbdestOverride.SelectedItems.Add(it); - }); + }); Global.IEProxyProtocols.ForEach(it => { cmbsystemProxyAdvancedProtocol.Items.Add(it);