From 35063db3e6fc296bbce433dc7ea4132edcb800bd Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 17 Oct 2024 10:21:13 +0800 Subject: [PATCH] Improvement share uri --- .../kotlin/com/v2ray/ang/util/fmt/FmtBase.kt | 104 ++++++++++++++++++ .../com/v2ray/ang/util/fmt/Hysteria2Fmt.kt | 16 +-- .../com/v2ray/ang/util/fmt/ShadowsocksFmt.kt | 16 +-- .../kotlin/com/v2ray/ang/util/fmt/SocksFmt.kt | 13 +-- .../com/v2ray/ang/util/fmt/TrojanFmt.kt | 93 +--------------- .../kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt | 94 +--------------- .../kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt | 2 +- .../com/v2ray/ang/util/fmt/WireguardFmt.kt | 31 ++---- 8 files changed, 135 insertions(+), 234 deletions(-) create mode 100644 V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/FmtBase.kt diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/FmtBase.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/FmtBase.kt new file mode 100644 index 00000000..3b96adab --- /dev/null +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/FmtBase.kt @@ -0,0 +1,104 @@ +package com.v2ray.ang.util.fmt + +import android.text.TextUtils +import com.v2ray.ang.dto.V2rayConfig +import com.v2ray.ang.util.Utils + +open class FmtBase { + fun toUri(address: String?, port: Int?, userInfo: String?, dicQuery: HashMap?, remark: String): String { + val query = if (dicQuery != null) + ("?" + dicQuery.toList().joinToString( + separator = "&", + transform = { it.first + "=" + Utils.urlEncode(it.second) })) + else "" + + val url = String.format( + "%s@%s:%s", + Utils.urlEncode(userInfo ?: ""), + Utils.getIpv6Address(address), + port + ) + + return "${url}${query}#${Utils.urlEncode(remark)}" + } + + fun getStdTransport(outbound: V2rayConfig.OutboundBean, streamSetting: V2rayConfig.OutboundBean.StreamSettingsBean): HashMap { + val dicQuery = HashMap() + + dicQuery["security"] = streamSetting.security.ifEmpty { "none" } + (streamSetting.tlsSettings + ?: streamSetting.realitySettings)?.let { tlsSetting -> + if (!TextUtils.isEmpty(tlsSetting.serverName)) { + dicQuery["sni"] = tlsSetting.serverName + } + if (!tlsSetting.alpn.isNullOrEmpty() && tlsSetting.alpn.isNotEmpty()) { + dicQuery["alpn"] = + Utils.removeWhiteSpace(tlsSetting.alpn.joinToString(",")).orEmpty() + } + if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { + dicQuery["fp"] = tlsSetting.fingerprint.orEmpty() + } + if (!TextUtils.isEmpty(tlsSetting.publicKey)) { + dicQuery["pbk"] = tlsSetting.publicKey.orEmpty() + } + if (!TextUtils.isEmpty(tlsSetting.shortId)) { + dicQuery["sid"] = tlsSetting.shortId.orEmpty() + } + if (!TextUtils.isEmpty(tlsSetting.spiderX)) { + dicQuery["spx"] = tlsSetting.spiderX.orEmpty() + } + } + dicQuery["type"] = + streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK } + + outbound.getTransportSettingDetails()?.let { transportDetails -> + when (streamSetting.network) { + "tcp" -> { + dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } + if (!TextUtils.isEmpty(transportDetails[1])) { + dicQuery["host"] = transportDetails[1] + } + } + + "kcp" -> { + dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } + if (!TextUtils.isEmpty(transportDetails[2])) { + dicQuery["seed"] = transportDetails[2] + } + } + + "ws", "httpupgrade", "splithttp" -> { + if (!TextUtils.isEmpty(transportDetails[1])) { + dicQuery["host"] = transportDetails[1] + } + if (!TextUtils.isEmpty(transportDetails[2])) { + dicQuery["path"] = transportDetails[2] + } + } + + "http", "h2" -> { + dicQuery["type"] = "http" + if (!TextUtils.isEmpty(transportDetails[1])) { + dicQuery["host"] = transportDetails[1] + } + if (!TextUtils.isEmpty(transportDetails[2])) { + dicQuery["path"] = transportDetails[2] + } + } + + "quic" -> { + dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } + dicQuery["quicSecurity"] = transportDetails[1] + dicQuery["key"] = transportDetails[2] + } + + "grpc" -> { + dicQuery["mode"] = transportDetails[0] + dicQuery["authority"] = transportDetails[1] + dicQuery["serviceName"] = transportDetails[2] + } + } + } + return dicQuery + } +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/Hysteria2Fmt.kt index d0d7bae7..880f2db1 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/Hysteria2Fmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/Hysteria2Fmt.kt @@ -12,7 +12,7 @@ import com.v2ray.ang.util.MmkvManager.settingsStorage import com.v2ray.ang.util.Utils import java.net.URI -object Hysteria2Fmt { +object Hysteria2Fmt : FmtBase() { fun parse(str: String): ServerConfig { var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false @@ -51,7 +51,7 @@ object Hysteria2Fmt { val outbound = config.getProxyOutbound() ?: return "" val streamSetting = outbound.streamSettings ?: V2rayConfig.OutboundBean.StreamSettingsBean() - val remark = "#" + Utils.urlEncode(config.remarks) + val dicQuery = HashMap() dicQuery["security"] = streamSetting.security.ifEmpty { "none" } streamSetting.tlsSettings?.let { tlsSetting -> @@ -68,17 +68,7 @@ object Hysteria2Fmt { dicQuery["obfs-password"] = outbound.settings?.obfsPassword ?: "" } - val query = "?" + dicQuery.toList().joinToString( - separator = "&", - transform = { it.first + "=" + it.second }) - - val url = String.format( - "%s@%s:%s", - outbound.getPassword(), - Utils.getIpv6Address(outbound.getServerAddress()), - outbound.getServerPort() - ) - return url + query + remark + return toUri(outbound.getServerAddress(), outbound.getServerPort(), outbound.getPassword(), dicQuery, config.remarks) } fun toNativeConfig(config: ServerConfig, socksPort: Int): Hysteria2Bean? { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt index afd2d350..b85dfde5 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt @@ -8,7 +8,7 @@ import com.v2ray.ang.extension.idnHost import com.v2ray.ang.util.Utils import java.net.URI -object ShadowsocksFmt { +object ShadowsocksFmt : FmtBase() { fun parse(str: String): ServerConfig? { val config = ServerConfig.create(EConfigType.SHADOWSOCKS) if (!tryResolveResolveSip002(str, config)) { @@ -52,16 +52,10 @@ object ShadowsocksFmt { fun toUri(config: ServerConfig): String { val outbound = config.getProxyOutbound() ?: return "" - val remark = "#" + Utils.urlEncode(config.remarks) - val pw = - Utils.encode("${outbound.getSecurityEncryption()}:${outbound.getPassword()}") - val url = String.format( - "%s@%s:%s", - pw, - Utils.getIpv6Address(outbound.getServerAddress()), - outbound.getServerPort() - ) - return url + remark + + val pw = Utils.encode("${outbound.getSecurityEncryption()}:${outbound.getPassword()}") + + return toUri(outbound.getServerAddress(), outbound.getServerPort(), pw, null, config.remarks) } private fun tryResolveResolveSip002(str: String, config: ServerConfig): Boolean { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/SocksFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/SocksFmt.kt index 106541f8..c928d79e 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/SocksFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/SocksFmt.kt @@ -5,7 +5,7 @@ import com.v2ray.ang.dto.ServerConfig import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.util.Utils -object SocksFmt { +object SocksFmt : FmtBase() { fun parse(str: String): ServerConfig? { val config = ServerConfig.create(EConfigType.SOCKS) var result = str.replace(EConfigType.SOCKS.protocolScheme, "") @@ -52,18 +52,13 @@ object SocksFmt { fun toUri(config: ServerConfig): String { val outbound = config.getProxyOutbound() ?: return "" - val remark = "#" + Utils.urlEncode(config.remarks) + val pw = if (outbound.settings?.servers?.get(0)?.users?.get(0)?.user != null) "${outbound.settings?.servers?.get(0)?.users?.get(0)?.user}:${outbound.getPassword()}" else ":" - val url = String.format( - "%s@%s:%s", - Utils.encode(pw), - Utils.getIpv6Address(outbound.getServerAddress()), - outbound.getServerPort() - ) - return url + remark + + return toUri(outbound.getServerAddress(), outbound.getServerPort(), pw, null, config.remarks) } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt index db3a2a34..0be4bb8c 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt @@ -10,7 +10,7 @@ import com.v2ray.ang.util.MmkvManager.settingsStorage import com.v2ray.ang.util.Utils import java.net.URI -object TrojanFmt { +object TrojanFmt : FmtBase() { fun parse(str: String): ServerConfig { var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false @@ -76,98 +76,15 @@ object TrojanFmt { val outbound = config.getProxyOutbound() ?: return "" val streamSetting = outbound.streamSettings ?: V2rayConfig.OutboundBean.StreamSettingsBean() - val remark = "#" + Utils.urlEncode(config.remarks) - val dicQuery = HashMap() + + val dicQuery = getStdTransport(outbound, streamSetting) + config.outboundBean?.settings?.servers?.get(0)?.flow?.let { if (!TextUtils.isEmpty(it)) { dicQuery["flow"] = it } } - dicQuery["security"] = streamSetting.security.ifEmpty { "none" } - (streamSetting.tlsSettings - ?: streamSetting.realitySettings)?.let { tlsSetting -> - if (!TextUtils.isEmpty(tlsSetting.serverName)) { - dicQuery["sni"] = tlsSetting.serverName - } - if (!tlsSetting.alpn.isNullOrEmpty() && tlsSetting.alpn.isNotEmpty()) { - dicQuery["alpn"] = - Utils.removeWhiteSpace(tlsSetting.alpn.joinToString(",")).orEmpty() - } - if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { - dicQuery["fp"] = tlsSetting.fingerprint.orEmpty() - } - if (!TextUtils.isEmpty(tlsSetting.publicKey)) { - dicQuery["pbk"] = tlsSetting.publicKey.orEmpty() - } - if (!TextUtils.isEmpty(tlsSetting.shortId)) { - dicQuery["sid"] = tlsSetting.shortId.orEmpty() - } - if (!TextUtils.isEmpty(tlsSetting.spiderX)) { - dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX.orEmpty()) - } - } - dicQuery["type"] = - streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK } - - outbound.getTransportSettingDetails()?.let { transportDetails -> - when (streamSetting.network) { - "tcp" -> { - dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } - if (!TextUtils.isEmpty(transportDetails[1])) { - dicQuery["host"] = Utils.urlEncode(transportDetails[1]) - } - } - - "kcp" -> { - dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } - if (!TextUtils.isEmpty(transportDetails[2])) { - dicQuery["seed"] = Utils.urlEncode(transportDetails[2]) - } - } - - "ws", "httpupgrade", "splithttp" -> { - if (!TextUtils.isEmpty(transportDetails[1])) { - dicQuery["host"] = Utils.urlEncode(transportDetails[1]) - } - if (!TextUtils.isEmpty(transportDetails[2])) { - dicQuery["path"] = Utils.urlEncode(transportDetails[2]) - } - } - - "http", "h2" -> { - dicQuery["type"] = "http" - if (!TextUtils.isEmpty(transportDetails[1])) { - dicQuery["host"] = Utils.urlEncode(transportDetails[1]) - } - if (!TextUtils.isEmpty(transportDetails[2])) { - dicQuery["path"] = Utils.urlEncode(transportDetails[2]) - } - } - - "quic" -> { - dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } - dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1]) - dicQuery["key"] = Utils.urlEncode(transportDetails[2]) - } - - "grpc" -> { - dicQuery["mode"] = transportDetails[0] - dicQuery["authority"] = Utils.urlEncode(transportDetails[1]) - dicQuery["serviceName"] = Utils.urlEncode(transportDetails[2]) - } - } - } - val query = "?" + dicQuery.toList().joinToString( - separator = "&", - transform = { it.first + "=" + it.second }) - - val url = String.format( - "%s@%s:%s", - outbound.getPassword(), - Utils.getIpv6Address(outbound.getServerAddress()), - outbound.getServerPort() - ) - return url + query + remark + return toUri(outbound.getServerAddress(), outbound.getServerPort(), outbound.getPassword(), dicQuery, config.remarks) } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt index ea37d310..1a5cfbf6 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt @@ -10,7 +10,7 @@ import com.v2ray.ang.util.MmkvManager.settingsStorage import com.v2ray.ang.util.Utils import java.net.URI -object VlessFmt { +object VlessFmt : FmtBase() { fun parse(str: String): ServerConfig? { var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false @@ -63,8 +63,9 @@ object VlessFmt { val outbound = config.getProxyOutbound() ?: return "" val streamSetting = outbound.streamSettings ?: V2rayConfig.OutboundBean.StreamSettingsBean() - val remark = "#" + Utils.urlEncode(config.remarks) - val dicQuery = HashMap() + + val dicQuery = getStdTransport(outbound, streamSetting) + outbound.settings?.vnext?.get(0)?.users?.get(0)?.flow?.let { if (!TextUtils.isEmpty(it)) { dicQuery["flow"] = it @@ -74,91 +75,6 @@ object VlessFmt { if (outbound.getSecurityEncryption().isNullOrEmpty()) "none" else outbound.getSecurityEncryption().orEmpty() - - dicQuery["security"] = streamSetting.security.ifEmpty { "none" } - (streamSetting.tlsSettings - ?: streamSetting.realitySettings)?.let { tlsSetting -> - if (!TextUtils.isEmpty(tlsSetting.serverName)) { - dicQuery["sni"] = tlsSetting.serverName - } - if (!tlsSetting.alpn.isNullOrEmpty() && tlsSetting.alpn.isNotEmpty()) { - dicQuery["alpn"] = - Utils.removeWhiteSpace(tlsSetting.alpn.joinToString(",")).orEmpty() - } - if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { - dicQuery["fp"] = tlsSetting.fingerprint.orEmpty() - } - if (!TextUtils.isEmpty(tlsSetting.publicKey)) { - dicQuery["pbk"] = tlsSetting.publicKey.orEmpty() - } - if (!TextUtils.isEmpty(tlsSetting.shortId)) { - dicQuery["sid"] = tlsSetting.shortId.orEmpty() - } - if (!TextUtils.isEmpty(tlsSetting.spiderX)) { - dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX.orEmpty()) - } - } - dicQuery["type"] = - streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK } - - outbound.getTransportSettingDetails()?.let { transportDetails -> - when (streamSetting.network) { - "tcp" -> { - dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } - if (!TextUtils.isEmpty(transportDetails[1])) { - dicQuery["host"] = Utils.urlEncode(transportDetails[1]) - } - } - - "kcp" -> { - dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } - if (!TextUtils.isEmpty(transportDetails[2])) { - dicQuery["seed"] = Utils.urlEncode(transportDetails[2]) - } - } - - "ws", "httpupgrade", "splithttp" -> { - if (!TextUtils.isEmpty(transportDetails[1])) { - dicQuery["host"] = Utils.urlEncode(transportDetails[1]) - } - if (!TextUtils.isEmpty(transportDetails[2])) { - dicQuery["path"] = Utils.urlEncode(transportDetails[2]) - } - } - - "http", "h2" -> { - dicQuery["type"] = "http" - if (!TextUtils.isEmpty(transportDetails[1])) { - dicQuery["host"] = Utils.urlEncode(transportDetails[1]) - } - if (!TextUtils.isEmpty(transportDetails[2])) { - dicQuery["path"] = Utils.urlEncode(transportDetails[2]) - } - } - - "quic" -> { - dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" } - dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1]) - dicQuery["key"] = Utils.urlEncode(transportDetails[2]) - } - - "grpc" -> { - dicQuery["mode"] = transportDetails[0] - dicQuery["authority"] = Utils.urlEncode(transportDetails[1]) - dicQuery["serviceName"] = Utils.urlEncode(transportDetails[2]) - } - } - } - val query = "?" + dicQuery.toList().joinToString( - separator = "&", - transform = { it.first + "=" + it.second }) - - val url = String.format( - "%s@%s:%s", - outbound.getPassword(), - Utils.getIpv6Address(outbound.getServerAddress()), - outbound.getServerPort() - ) - return url + query + remark + return toUri(outbound.getServerAddress(), outbound.getServerPort(), outbound.getPassword(), dicQuery, config.remarks) } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt index 1efad50b..7261effa 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt @@ -14,7 +14,7 @@ import com.v2ray.ang.util.MmkvManager.settingsStorage import com.v2ray.ang.util.Utils import java.net.URI -object VmessFmt { +object VmessFmt : FmtBase() { fun parse(str: String): ServerConfig? { if (str.indexOf('?') > 0 && str.indexOf('&') > 0) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt index a198d3c8..24bc686b 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt @@ -8,7 +8,7 @@ import com.v2ray.ang.extension.removeWhiteSpace import com.v2ray.ang.util.Utils import java.net.URI -object WireguardFmt { +object WireguardFmt : FmtBase() { fun parse(str: String): ServerConfig? { val uri = URI(Utils.fixIllegalUrl(str)) if (uri.rawQuery != null) { @@ -71,37 +71,22 @@ object WireguardFmt { } - fun toUri(config: ServerConfig): String { val outbound = config.getProxyOutbound() ?: return "" - val remark = "#" + Utils.urlEncode(config.remarks) + val dicQuery = HashMap() - dicQuery["publickey"] = - Utils.urlEncode(outbound.settings?.peers?.get(0)?.publicKey.toString()) + dicQuery["publickey"] = outbound.settings?.peers?.get(0)?.publicKey.toString() if (outbound.settings?.reserved != null) { - dicQuery["reserved"] = Utils.urlEncode( - Utils.removeWhiteSpace(outbound.settings?.reserved?.joinToString(",")) - .toString() - ) + dicQuery["reserved"] = Utils.removeWhiteSpace(outbound.settings?.reserved?.joinToString(",")).toString() + } - dicQuery["address"] = Utils.urlEncode( - Utils.removeWhiteSpace((outbound.settings?.address as List<*>).joinToString(",")) - .toString() - ) + dicQuery["address"] = Utils.removeWhiteSpace((outbound.settings?.address as List<*>).joinToString(",")).toString() + if (outbound.settings?.mtu != null) { dicQuery["mtu"] = outbound.settings?.mtu.toString() } - val query = "?" + dicQuery.toList().joinToString( - separator = "&", - transform = { it.first + "=" + it.second }) - val url = String.format( - "%s@%s:%s", - Utils.urlEncode(outbound.getPassword().toString()), - Utils.getIpv6Address(outbound.getServerAddress()), - outbound.getServerPort() - ) - return url + query + remark + return toUri(outbound.getServerAddress(), outbound.getServerPort(), outbound.getPassword(), dicQuery, config.remarks) } } \ No newline at end of file