Browse Source

Element call: Pass in custom call URL query parameters

pull/1434/head
Benoit Marty 12 months ago
parent
commit
8cb7c3211c
  1. 33
      features/call/src/main/kotlin/io/element/android/features/call/CallIntentDataParser.kt
  2. 50
      features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTests.kt

33
features/call/src/main/kotlin/io/element/android/features/call/CallIntentDataParser.kt

@ -27,7 +27,7 @@ class CallIntentDataParser @Inject constructor() {
val parsedUrl = data?.let { Uri.parse(data) } ?: return null val parsedUrl = data?.let { Uri.parse(data) } ?: return null
val scheme = parsedUrl.scheme val scheme = parsedUrl.scheme
return when { return when {
scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> data scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> parsedUrl
scheme == "element" && parsedUrl.host == "call" -> { scheme == "element" && parsedUrl.host == "call" -> {
// We use this custom scheme to load arbitrary URLs for other instances of Element Call, // We use this custom scheme to load arbitrary URLs for other instances of Element Call,
// so we can only verify it's an HTTP/HTTPs URL with a non-empty host // so we can only verify it's an HTTP/HTTPs URL with a non-empty host
@ -40,14 +40,35 @@ class CallIntentDataParser @Inject constructor() {
} }
// This should never be possible, but we still need to take into account the possibility // This should never be possible, but we still need to take into account the possibility
else -> null else -> null
} }?.withCustomParameters()
} }
private fun Uri.getUrlParameter(): String? { private fun Uri.getUrlParameter(): Uri? {
return getQueryParameter("url") return getQueryParameter("url")
?.takeIf { ?.let { urlParameter ->
val internalUri = Uri.parse(it) Uri.parse(urlParameter).takeIf { uri ->
internalUri.scheme in validHttpSchemes && !internalUri.host.isNullOrBlank() uri.scheme in validHttpSchemes && !uri.host.isNullOrBlank()
}
} }
} }
} }
/**
* Ensure the uri has the following parameters and value:
* - appPrompt=false
* - confineToRoom=true
*/
private fun Uri.withCustomParameters(): String {
val builder = buildUpon()
builder.clearQuery()
queryParameterNames.forEach {
if (it == APP_PROMPT_PARAMETER || it == CONFINE_TO_ROOM_PARAMETER) return@forEach
builder.appendQueryParameter(it, getQueryParameter(it))
}
builder.appendQueryParameter(APP_PROMPT_PARAMETER, "false")
builder.appendQueryParameter(CONFINE_TO_ROOM_PARAMETER, "true")
return builder.build().toString()
}
private const val APP_PROMPT_PARAMETER = "appPrompt"
private const val CONFINE_TO_ROOM_PARAMETER = "confineToRoom"

50
features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTests.kt

@ -62,9 +62,9 @@ class CallIntentDataParserTests {
@Test @Test
fun `Element Call urls will be returned as is`() { fun `Element Call urls will be returned as is`() {
val httpsBaseUrl = "https://call.element.io" val httpsBaseUrl = "https://call.element.io"
val httpsCallUrl = "https://call.element.io/some-actual-call?with=parameters" val httpsCallUrl = VALID_CALL_URL_WITH_PARAM
assertThat(callIntentDataParser.parse(httpsBaseUrl)).isEqualTo(httpsBaseUrl) assertThat(callIntentDataParser.parse(httpsBaseUrl)).isEqualTo("$httpsBaseUrl?$EXTRA_PARAMS")
assertThat(callIntentDataParser.parse(httpsCallUrl)).isEqualTo(httpsCallUrl) assertThat(callIntentDataParser.parse(httpsCallUrl)).isEqualTo("$httpsCallUrl&$EXTRA_PARAMS")
} }
@Test @Test
@ -89,10 +89,10 @@ class CallIntentDataParserTests {
@Test @Test
fun `element scheme with call host and url param gets url extracted`() { fun `element scheme with call host and url param gets url extracted`() {
val embeddedUrl = "https://call.element.io/some-actual-call?with=parameters" val embeddedUrl = VALID_CALL_URL_WITH_PARAM
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "element://call?url=$encodedUrl" val url = "element://call?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isEqualTo(embeddedUrl) assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
} }
@Test @Test
@ -105,10 +105,10 @@ class CallIntentDataParserTests {
@Test @Test
fun `element scheme 2 with url param gets url extracted`() { fun `element scheme 2 with url param gets url extracted`() {
val embeddedUrl = "https://call.element.io/some-actual-call?with=parameters" val embeddedUrl = VALID_CALL_URL_WITH_PARAM
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "io.element.call:/?url=$encodedUrl" val url = "io.element.call:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isEqualTo(embeddedUrl) assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
} }
@Test @Test
@ -121,7 +121,7 @@ class CallIntentDataParserTests {
@Test @Test
fun `element scheme 2 with no url returns null`() { fun `element scheme 2 with no url returns null`() {
val embeddedUrl = "https://call.element.io/some-actual-call?with=parameters" val embeddedUrl = VALID_CALL_URL_WITH_PARAM
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "io.element.call:/?no_url=$encodedUrl" val url = "io.element.call:/?no_url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isNull() assertThat(callIntentDataParser.parse(url)).isNull()
@ -129,7 +129,7 @@ class CallIntentDataParserTests {
@Test @Test
fun `element scheme with no call host returns null`() { fun `element scheme with no call host returns null`() {
val embeddedUrl = "https://call.element.io/some-actual-call?with=parameters" val embeddedUrl = VALID_CALL_URL_WITH_PARAM
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "element://no-call?url=$encodedUrl" val url = "element://no-call?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isNull() assertThat(callIntentDataParser.parse(url)).isNull()
@ -149,9 +149,39 @@ class CallIntentDataParserTests {
@Test @Test
fun `element invalid scheme returns null`() { fun `element invalid scheme returns null`() {
val embeddedUrl = "https://call.element.io/some-actual-call?with=parameters" val embeddedUrl = VALID_CALL_URL_WITH_PARAM
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "bad.scheme:/?url=$encodedUrl" val url = "bad.scheme:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isNull() assertThat(callIntentDataParser.parse(url)).isNull()
} }
@Test
fun `element scheme 2 with url extra param appPrompt gets url extracted`() {
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}&appPrompt=true"
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "io.element.call:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
}
@Test
fun `element scheme 2 with url extra param confineToRoom gets url extracted`() {
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}&confineToRoom=false"
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "io.element.call:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
}
@Test
fun `element scheme 2 with url fragment gets url extracted`() {
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}#fragment"
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "io.element.call:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS#fragment")
}
companion object {
const val VALID_CALL_URL_WITH_PARAM = "https://call.element.io/some-actual-call?with=parameters"
const val EXTRA_PARAMS = "appPrompt=false&confineToRoom=true"
}
} }

Loading…
Cancel
Save