diff --git a/core/src/androidTest/java/com/github/shadowsocks/database/ProfileTest.kt b/core/src/androidTest/java/com/github/shadowsocks/database/ProfileTest.kt index 61aefccfd2..3a0b9dafd2 100644 --- a/core/src/androidTest/java/com/github/shadowsocks/database/ProfileTest.kt +++ b/core/src/androidTest/java/com/github/shadowsocks/database/ProfileTest.kt @@ -33,4 +33,18 @@ class ProfileTest { Assert.assertEquals("ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888#example-server".toUri(), results.single().toUri()) } + + @Test + fun parsingPlainUserInfo() { + val results = Profile.findAllUrls( + "ss://2022-blake3-chacha20-poly1305:F6BY4IwbXB5Juobo2aofoJ6P86G%2FyBYJO3pJzcMIUOU%3D@1.2.3.4:8400") + .toList() + Assert.assertEquals(1, results.size) + results.single().also { + Assert.assertEquals("2022-blake3-chacha20-poly1305", it.method) + Assert.assertEquals("F6BY4IwbXB5Juobo2aofoJ6P86G/yBYJO3pJzcMIUOU=", it.password) + Assert.assertEquals("1.2.3.4", it.host) + Assert.assertEquals(8400, it.remotePort) + } + } } diff --git a/core/src/main/java/com/github/shadowsocks/database/Profile.kt b/core/src/main/java/com/github/shadowsocks/database/Profile.kt index 01a2540ad2..8f5e82f5b3 100644 --- a/core/src/main/java/com/github/shadowsocks/database/Profile.kt +++ b/core/src/main/java/com/github/shadowsocks/database/Profile.kt @@ -109,6 +109,15 @@ data class Profile( private val userInfoPattern = "^(.+?):(.*)$".toRegex() private val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)$".toRegex() + private fun userInfoCandidates(uri: Uri) = sequence { + uri.userInfo?.also { userInfo -> + try { + yield(String(Base64.decode(userInfo, Base64.NO_PADDING or Base64.NO_WRAP or Base64.URL_SAFE))) + } catch (_: IllegalArgumentException) { } + yield(userInfo) + } + } + fun findAllUrls(data: CharSequence?, feature: Profile? = null) = pattern.findAll(data ?: "").map { val uri = it.value.toUri() try { @@ -129,8 +138,7 @@ data class Profile( null } } else { - val match = userInfoPattern.matchEntire(String(Base64.decode(uri.userInfo, - Base64.NO_PADDING or Base64.NO_WRAP or Base64.URL_SAFE))) + val match = userInfoCandidates(uri).mapNotNull(userInfoPattern::matchEntire).firstOrNull() if (match != null) { val profile = Profile() feature?.copyFeatureSettingsTo(profile)