Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PixelPlayer"
android:usesCleartextTraffic="false"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31">
<activity
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.lostf1sh.pixelplayeross.data.navidrome.model

import com.lostf1sh.pixelplayeross.data.stream.CloudStreamSecurity
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull

Expand Down Expand Up @@ -59,14 +60,26 @@ data class NavidromeCredentials(
/**
* Returns a validation error for connection setup, or null when the URL is acceptable.
*/
fun connectionValidationError(requireHttps: Boolean = true): String? {
fun connectionValidationError(requireHttps: Boolean = false): String? {
val httpUrl = normalizedHttpUrlOrNull ?: return "Enter a valid server URL."
if (httpUrl.username.isNotEmpty() || httpUrl.password.isNotEmpty()) {
return "Server URL must not include embedded credentials."
}
if (requireHttps && !httpUrl.isHttps) {
return "Use an https:// server URL for Navidrome/Subsonic."
}
if (!httpUrl.isHttps && !isHttpAllowedHost(httpUrl.host.lowercase())) {
return "Use https:// for remote Navidrome/Subsonic servers. HTTP is only allowed for local network addresses."
}
return null
}

private fun isHttpAllowedHost(host: String): Boolean {
return host == "localhost" ||
host == "127.0.0.1" ||
host.endsWith(".local") ||
host.endsWith(".ts.net") ||
!host.contains('.') ||
CloudStreamSecurity.isPrivateIpv4Literal(host)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ fun NavidromeLoginScreen(
value = serverUrl,
onValueChange = { serverUrl = it },
label = stringResource(R.string.auth_server_url),
placeholder = stringResource(R.string.auth_server_url_placeholder_https),
placeholder = stringResource(R.string.auth_navidrome_server_placeholder),
supportingText = stringResource(R.string.auth_navidrome_server_url_hint),
leadingIcon = Icons.Rounded.CloudQueue,
enabled = !isLoading,
Expand Down Expand Up @@ -369,15 +369,15 @@ fun NavidromeLoginScreen(
FilledTonalButton(
onClick = {
if (serverUrl.isBlank()) {
serverUrl = "https://"
serverUrl = "http://"
}
},
enabled = !isLoading && serverUrl.isBlank(),
shape = inputShape,
contentPadding = PaddingValues(horizontal = 14.dp, vertical = 10.dp)
) {
Text(
text = stringResource(R.string.auth_prefill_https),
text = stringResource(R.string.auth_prefill_http),
fontFamily = RoundedSans,
fontWeight = FontWeight.Medium
)
Expand Down
7 changes: 3 additions & 4 deletions app/src/main/res/values/strings_auth.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
<string name="auth_navidrome_title">Subsonic / Navidrome</string>
<string name="auth_navidrome_subtitle">Connect to your self-hosted music server</string>
<string name="auth_navidrome_info_card">Supports Navidrome, Airsonic, Gonic, Ampache and other servers compatible with the Subsonic API.</string>
<string name="auth_server_url_placeholder_https">https://music.example.com</string>
<string name="auth_navidrome_server_url_hint">Use the full https:// base address of your server.</string>
<string name="auth_navidrome_server_placeholder">http://192.168.1.100:4533</string>
<string name="auth_navidrome_server_url_hint">Use the full server URL, including http:// for local servers or https:// for remote servers.</string>
<string name="auth_navidrome_username_hint">This is your Subsonic or Navidrome account name.</string>
<string name="auth_navidrome_password_hint">App password also works if your server supports it.</string>
<string name="auth_navidrome_пароль_hint">App password also works if your server supports it.</string>
<string name="auth_prefill_https">Prefill https://</string>
<string name="auth_prefill_http">Prefill http://</string>
<string name="auth_navidrome_footer">Compatible with Navidrome, Gonic, Airsonic, and other Subsonic-compatible servers</string>
<string name="cd_navidrome_logo">Navidrome</string>
<string name="cd_subsonic_logo">Subsonic</string>
Expand All @@ -38,7 +38,6 @@
<string name="auth_jellyfin_server_url_hint">Full URL of your Jellyfin server, including port.</string>
<string name="auth_jellyfin_username_hint">Your Jellyfin account username.</string>
<string name="auth_jellyfin_password_hint">Your Jellyfin account password.</string>
<string name="auth_prefill_http">Prefill http://</string>
<string name="auth_jellyfin_footer">Connects to Jellyfin servers for streaming your music library</string>
<string name="cd_jellyfin_logo">Jellyfin</string>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,30 @@ class NavidromeCredentialsTest {
}

@Test
fun `connectionValidationError rejects insecure http urls`() {
fun `connectionValidationError accepts http urls for local network addresses`() {
val credentials = NavidromeCredentials(
serverUrl = "http://192.168.1.20:4533",
username = "user",
password = "pass"
)

assertNull(credentials.connectionValidationError())
assertEquals(
"http://192.168.1.20:4533",
credentials.normalizedServerUrl
)
}

@Test
fun `connectionValidationError rejects http urls for public hosts`() {
val credentials = NavidromeCredentials(
serverUrl = "http://music.example.com",
username = "user",
password = "pass"
)

assertEquals(
"Use an https:// server URL for Navidrome/Subsonic.",
"Use https:// for remote Navidrome/Subsonic servers. HTTP is only allowed for local network addresses.",
credentials.connectionValidationError()
)
}
Expand Down