owenclave / app/src/main/java/io/nekohasekai/sagernet/fmt/mieru/MieruFmt.kt

github - dev - back to tree - upstream
/****************************************************************************** * Copyright (C) 2022 by nekohasekai * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ******************************************************************************/ package io.nekohasekai.sagernet.fmt.mieru import io.nekohasekai.sagernet.ktx.listByLineOrComma import io.nekohasekai.sagernet.ktx.queryParameter import libexclavecore.Libexclavecore fun parseMieru(link: String): List { val beans = mutableListOf() val url = Libexclavecore.parseURL(link) if (url.username.isNullOrEmpty() || url.password.isNullOrEmpty()) { error("empty username or password") } val portCount = url.countQueryParameter("port") if (portCount != url.countQueryParameter("protocol")) { error("port count and protocol count mismatch") } if (portCount == 0L) { error("missing port and protocol") } val tcpPorts = mutableListOf() val udpPorts = mutableListOf() for (i in 0.. udpPorts.add(url.getQueryParameterAt("port", i)) "TCP" -> tcpPorts.add(url.getQueryParameterAt("port", i)) else -> error("unknown protocol: $protocol") } } val profileName = url.queryParameter("profile") val multiplexing = url.queryParameter("multiplexing")?.let { when (it) { "MULTIPLEXING_OFF" -> MieruBean.MULTIPLEXING_OFF "MULTIPLEXING_LOW" -> MieruBean.MULTIPLEXING_LOW "MULTIPLEXING_MIDDLE" -> MieruBean.MULTIPLEXING_MIDDLE "MULTIPLEXING_HIGH" -> MieruBean.MULTIPLEXING_HIGH else -> MieruBean.MULTIPLEXING_DEFAULT } } val handshakemode = url.queryParameter("handshake-mode")?.let { when (it) { "HANDSHAKE_STANDARD" -> MieruBean.HANDSHAKE_STANDARD "HANDSHAKE_NO_WAIT" -> MieruBean.HANDSHAKE_NO_WAIT else -> MieruBean.HANDSHAKE_DEFAULT } } val trafficpattern = url.queryParameter("traffic-pattern") if (tcpPorts.isNotEmpty()) { beans.add(MieruBean().apply { serverAddress = url.host.ifEmpty { error("empty host") } serverPort = if (tcpPorts.size == 1 && tcpPorts[0].toIntOrNull() != null) { tcpPorts[0].toInt() } else 0 portRange = if (tcpPorts.size != 1 || tcpPorts[0].toIntOrNull() == null) { tcpPorts.joinToString("\n") } else "" username = url.username password = url.password name = profileName multiplexingLevel = multiplexing handshakeMode = handshakemode trafficPattern = trafficpattern protocol = MieruBean.PROTOCOL_TCP }) } if (udpPorts.isNotEmpty()) { beans.add(MieruBean().apply { serverAddress = url.host.ifEmpty { error("empty host") } serverPort = if (udpPorts.size == 1 && udpPorts[0].toIntOrNull() != null) { udpPorts[0].toInt() } else 0 portRange = if (udpPorts.size != 1 || udpPorts[0].toIntOrNull() == null) { udpPorts.joinToString("\n") } else "" username = url.username password = url.password name = profileName multiplexingLevel = multiplexing handshakeMode = handshakemode trafficPattern = trafficpattern protocol = MieruBean.PROTOCOL_UDP // mtu = url.queryParameter("mtu")?.toIntOrNull() ?: 1400 }) } return beans } fun MieruBean.toUri(): String? { val builder = Libexclavecore.newURL("mierus").apply { host = serverAddress.ifEmpty { error("empty server address") } } if (username.isNotEmpty()) { builder.username = username } else { error("empty username") } if (password.isNotEmpty()) { builder.password = password } else { error("empty password") } if (name.isNotEmpty()) { builder.addQueryParameter("profile", name) } if (portRange.isNotEmpty()) { for (range in portRange.listByLineOrComma()) { builder.addQueryParameter("port", range) builder.addQueryParameter("protocol", when (protocol) { MieruBean.PROTOCOL_TCP -> "TCP" MieruBean.PROTOCOL_UDP -> "UDP" else -> error("impossible") }) } } else { builder.addQueryParameter("port", serverPort.toString()) builder.addQueryParameter("protocol", when (protocol) { MieruBean.PROTOCOL_TCP -> "TCP" MieruBean.PROTOCOL_UDP -> "UDP" else -> error("impossible") }) } /*if (protocol == PROTOCOL_UDP && mtu > 0) { builder.addQueryParameter("mtu", mtu.toString()) }*/ when (multiplexingLevel) { MieruBean.MULTIPLEXING_OFF -> { builder.addQueryParameter("multiplexing", "MULTIPLEXING_OFF") } MieruBean.MULTIPLEXING_LOW -> { builder.addQueryParameter("multiplexing", "MULTIPLEXING_LOW") } MieruBean.MULTIPLEXING_MIDDLE -> { builder.addQueryParameter("multiplexing", "MULTIPLEXING_MIDDLE") } MieruBean.MULTIPLEXING_HIGH -> { builder.addQueryParameter("multiplexing", "MULTIPLEXING_HIGH") } } when (handshakeMode) { MieruBean.HANDSHAKE_STANDARD -> { builder.addQueryParameter("handshake-mode", "HANDSHAKE_STANDARD") } MieruBean.HANDSHAKE_NO_WAIT -> { builder.addQueryParameter("handshake-mode", "HANDSHAKE_NO_WAIT") } } if (trafficPattern.isNotEmpty()) { builder.addQueryParameter("traffic-pattern", trafficPattern) } return builder.string }