mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
fix: uri handling, ci test setup (#4556)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
5061dc8262
commit
0f03492ac6
10 changed files with 115 additions and 34 deletions
|
|
@ -20,7 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.model.util.URL_PREFIX
|
||||
import org.meshtastic.core.model.util.CHANNEL_URL_PREFIX
|
||||
import org.meshtastic.core.model.util.getChannelUrl
|
||||
import org.meshtastic.core.model.util.toChannelSet
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
|
|
@ -33,7 +33,7 @@ class ChannelTest {
|
|||
val ch = ChannelSet(settings = listOf(Channel.default.settings), lora_config = Channel.default.loraConfig)
|
||||
val channelUrl = ch.getChannelUrl()
|
||||
|
||||
Assert.assertTrue(channelUrl.toString().startsWith(URL_PREFIX))
|
||||
Assert.assertTrue(channelUrl.toString().startsWith(CHANNEL_URL_PREFIX))
|
||||
Assert.assertEquals(channelUrl.toChannelSet(), ch)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,21 +40,27 @@ class SharedContactTest {
|
|||
|
||||
@Test
|
||||
fun testWwwHostIsAccepted() {
|
||||
val url = Uri.parse("https://www.meshtastic.org/v/#CggKBVN1enVtZRICU1oaBTEyMzQ1")
|
||||
val original = SharedContact(user = User(long_name = "Suzume"), node_num = 12345)
|
||||
val urlStr = original.getSharedContactUrl().toString().replace("meshtastic.org", "www.meshtastic.org")
|
||||
val url = Uri.parse(urlStr)
|
||||
val contact = url.toSharedContact()
|
||||
assertEquals("Suzume", contact.user?.long_name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLongPathIsAccepted() {
|
||||
val url = Uri.parse("https://meshtastic.org/contact/v/#CggKBVN1enVtZRICU1oaBTEyMzQ1")
|
||||
val original = SharedContact(user = User(long_name = "Suzume"), node_num = 12345)
|
||||
val urlStr = original.getSharedContactUrl().toString().replace("/v/", "/contact/v/")
|
||||
val url = Uri.parse(urlStr)
|
||||
val contact = url.toSharedContact()
|
||||
assertEquals("Suzume", contact.user?.long_name)
|
||||
}
|
||||
|
||||
@Test(expected = java.net.MalformedURLException::class)
|
||||
fun testInvalidHostThrows() {
|
||||
val url = Uri.parse("https://example.com/v/#CggKBVN1enVtZRICU1oaBTEyMzQ1")
|
||||
val original = SharedContact(user = User(long_name = "Suzume"), node_num = 12345)
|
||||
val urlStr = original.getSharedContactUrl().toString().replace("meshtastic.org", "example.com")
|
||||
val url = Uri.parse(urlStr)
|
||||
url.toSharedContact()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import org.meshtastic.proto.SharedContact
|
|||
import org.meshtastic.proto.User
|
||||
import java.net.MalformedURLException
|
||||
|
||||
private const val BASE64FLAGS = Base64.URL_SAFE + Base64.NO_WRAP + Base64.NO_PADDING
|
||||
private const val BASE64FLAGS = Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING
|
||||
|
||||
/**
|
||||
* Return a [SharedContact] that represents the contact encoded by the URL.
|
||||
|
|
@ -33,16 +33,48 @@ private const val BASE64FLAGS = Base64.URL_SAFE + Base64.NO_WRAP + Base64.NO_PAD
|
|||
*/
|
||||
@Throws(MalformedURLException::class)
|
||||
fun Uri.toSharedContact(): SharedContact {
|
||||
val h = host ?: ""
|
||||
val isCorrectHost =
|
||||
h.equals(MESHTASTIC_HOST, ignoreCase = true) || h.equals("www.$MESHTASTIC_HOST", ignoreCase = true)
|
||||
checkSharedContactUrl()
|
||||
val data = fragment!!.substringBefore('?')
|
||||
return decodeSharedContactData(data)
|
||||
}
|
||||
|
||||
@Throws(MalformedURLException::class)
|
||||
private fun Uri.checkSharedContactUrl() {
|
||||
val h = host?.lowercase() ?: ""
|
||||
val isCorrectHost = h == MESHTASTIC_HOST || h == "www.$MESHTASTIC_HOST"
|
||||
val segments = pathSegments
|
||||
val isCorrectPath = segments.any { it.equals("v", ignoreCase = true) }
|
||||
|
||||
if (fragment.isNullOrBlank() || !isCorrectHost || !isCorrectPath) {
|
||||
throw MalformedURLException("Not a valid Meshtastic URL")
|
||||
val frag = fragment
|
||||
if (frag.isNullOrBlank() || !isCorrectHost || !isCorrectPath) {
|
||||
throw MalformedURLException(
|
||||
"Not a valid Meshtastic URL: host=$h, segments=$segments, hasFragment=${!frag.isNullOrBlank()}",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(MalformedURLException::class)
|
||||
private fun decodeSharedContactData(data: String): SharedContact {
|
||||
val decodedBytes =
|
||||
try {
|
||||
// We use a more lenient decoding for the input to handle variations from different clients
|
||||
Base64.decode(data, Base64.DEFAULT or Base64.URL_SAFE)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
val ex =
|
||||
MalformedURLException(
|
||||
"Failed to Base64 decode SharedContact data ($data): ${e.javaClass.simpleName}: ${e.message}",
|
||||
)
|
||||
ex.initCause(e)
|
||||
throw ex
|
||||
}
|
||||
|
||||
return try {
|
||||
SharedContact.ADAPTER.decode(decodedBytes.toByteString())
|
||||
} catch (e: java.io.IOException) {
|
||||
val ex = MalformedURLException("Failed to proto decode SharedContact: ${e.javaClass.simpleName}: ${e.message}")
|
||||
ex.initCause(e)
|
||||
throw ex
|
||||
}
|
||||
return SharedContact.ADAPTER.decode(Base64.decode(fragment!!, BASE64FLAGS).toByteString())
|
||||
}
|
||||
|
||||
/** Converts a [SharedContact] to its corresponding URI representation. */
|
||||
|
|
|
|||
|
|
@ -65,4 +65,36 @@ class SharedContactTest {
|
|||
val url = Uri.parse(urlStr)
|
||||
url.toSharedContact()
|
||||
}
|
||||
|
||||
@Test(expected = java.net.MalformedURLException::class)
|
||||
fun testInvalidPathThrows() {
|
||||
val original = SharedContact(user = User(long_name = "Suzume"), node_num = 12345)
|
||||
val urlStr = original.getSharedContactUrl().toString().replace("/v/", "/wrong/")
|
||||
val url = Uri.parse(urlStr)
|
||||
url.toSharedContact()
|
||||
}
|
||||
|
||||
@Test(expected = java.net.MalformedURLException::class)
|
||||
fun testMissingFragmentThrows() {
|
||||
val urlStr = "https://meshtastic.org/v/"
|
||||
val url = Uri.parse(urlStr)
|
||||
url.toSharedContact()
|
||||
}
|
||||
|
||||
@Test(expected = java.net.MalformedURLException::class)
|
||||
fun testInvalidBase64Throws() {
|
||||
val urlStr = "https://meshtastic.org/v/#InvalidBase64!!!!"
|
||||
val url = Uri.parse(urlStr)
|
||||
url.toSharedContact()
|
||||
}
|
||||
|
||||
@Test(expected = java.net.MalformedURLException::class)
|
||||
fun testInvalidProtoThrows() {
|
||||
// Tag 0 is invalid in Protobuf
|
||||
// 0x00 -> Tag 0, Type 0.
|
||||
// Base64 for 0x00 is "AA=="
|
||||
val urlStr = "https://meshtastic.org/v/#AA=="
|
||||
val url = Uri.parse(urlStr)
|
||||
url.toSharedContact()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue