fix: uri handling, ci test setup (#4556)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-02-14 10:07:03 -06:00 committed by GitHub
parent 5061dc8262
commit 0f03492ac6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 115 additions and 34 deletions

View file

@ -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. */