mirror of
https://github.com/thelinkin3000/SonicLair.git
synced 2026-05-24 08:49:21 -04:00
New Release!
[All versions] Now the app supports using plaintext passwords for authentication, as some subsonic implementations need it to work. Please try not to use this on unsecure connections (i.e. http://) as the password travels unencrypted through the network. Closes #2. Thanks @epoupon
This commit is contained in:
@@ -163,11 +163,13 @@ class BackendPlugin : Plugin(), IBroadcastObserver {
|
||||
val username = data.getString("username") ?: throw ParameterException("username")
|
||||
val password = data.getString("password") ?: throw Exception("password")
|
||||
val url = data.getString("url") ?: throw Exception("url")
|
||||
val usePlaintext = data.getBoolean("usePlaintext")
|
||||
try {
|
||||
val account = subsonicClient!!.login(
|
||||
username,
|
||||
password,
|
||||
url
|
||||
url,
|
||||
usePlaintext
|
||||
)
|
||||
setActiveAccount(account)
|
||||
call.resolve(okResponse(account))
|
||||
|
||||
@@ -35,7 +35,7 @@ class KeyValueStorage {
|
||||
val account: Account = Gson().fromJson(activeAccount, Account::class.java)
|
||||
account
|
||||
} catch (exception: Exception) {
|
||||
Account(null, "", "", "")
|
||||
Account(null, "", "", "", false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ package tech.logica10.soniclair
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.support.v4.media.MediaBrowserCompat
|
||||
@@ -40,7 +39,7 @@ import java.util.concurrent.TimeUnit
|
||||
|
||||
class SubsonicClient(var initialAccount: Account) {
|
||||
companion object {
|
||||
var account: Account = Account(null, "", "", "")
|
||||
var account: Account = Account(null, "", "", "", false)
|
||||
var spotifyToken: String = ""
|
||||
var downloadQueue: MutableList<Song> = mutableListOf()
|
||||
var downloadQueueForce: HashMap<String, Boolean> = HashMap()
|
||||
@@ -75,11 +74,12 @@ class SubsonicClient(var initialAccount: Account) {
|
||||
BigInteger(1, md.digest(saltedPassword.toByteArray())).toString(16).padStart(32, '0')
|
||||
return BasicParams(
|
||||
account.username ?: "",
|
||||
hash,
|
||||
salt,
|
||||
if(account.usePlaintext) null else hash,
|
||||
if(account.usePlaintext) null else salt,
|
||||
"1.16.1",
|
||||
"soniclair",
|
||||
"json",
|
||||
if(account.usePlaintext) account.password else null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -646,6 +646,7 @@ class SubsonicClient(var initialAccount: Account) {
|
||||
uriBuilder.appendQueryParameter(key, map[key])
|
||||
}
|
||||
uriBuilder.appendQueryParameter("id", song.id)
|
||||
uriBuilder.appendQueryParameter("estimateContentLength", "true")
|
||||
|
||||
|
||||
if (KeyValueStorage.getSettings().transcoding != "" && connectivityManager.getNetworkCapabilities(
|
||||
@@ -654,7 +655,6 @@ class SubsonicClient(var initialAccount: Account) {
|
||||
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) == false
|
||||
) {
|
||||
uriBuilder.appendQueryParameter("format", KeyValueStorage.getSettings().transcoding)
|
||||
uriBuilder.appendQueryParameter("estimateContentLength", "true")
|
||||
|
||||
}
|
||||
return uriBuilder.build().toString()
|
||||
@@ -678,7 +678,7 @@ class SubsonicClient(var initialAccount: Account) {
|
||||
)!!.song
|
||||
}
|
||||
|
||||
fun login(username: String, password: String, url: String): Account {
|
||||
fun login(username: String, password: String, url: String, usePlaintext: Boolean): Account {
|
||||
val salt = "abcd1234"
|
||||
val saltedPassword = "${password}${salt}"
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
@@ -686,11 +686,12 @@ class SubsonicClient(var initialAccount: Account) {
|
||||
BigInteger(1, md.digest(saltedPassword.toByteArray())).toString(16).padStart(32, '0')
|
||||
val basicParams = BasicParams(
|
||||
username,
|
||||
hash,
|
||||
salt,
|
||||
if(usePlaintext) null else hash,
|
||||
if(usePlaintext) null else salt,
|
||||
"1.16.1",
|
||||
"soniclair",
|
||||
"json",
|
||||
if(usePlaintext) password else null
|
||||
)
|
||||
val uriBuilder = Uri.parse(url).buildUpon()
|
||||
.appendPath("rest")
|
||||
@@ -715,7 +716,7 @@ class SubsonicClient(var initialAccount: Account) {
|
||||
if (ret.status != "ok") {
|
||||
throw Exception(ret.error?.message)
|
||||
}
|
||||
account = Account(username, password, url, ret.type)
|
||||
account = Account(username, password, url, ret.type ?: "Unknown Server", usePlaintext)
|
||||
KeyValueStorage.setActiveAccount(account)
|
||||
val accounts = KeyValueStorage.getAccounts()
|
||||
val exists = accounts.filter { it.url == url }.size == 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package tech.logica10.soniclair.models
|
||||
|
||||
class Account(val username: String?, val password: String, val url: String, var type: String)
|
||||
class Account(val username: String?, val password: String, val url: String, var type: String, var usePlaintext: Boolean)
|
||||
|
||||
|
||||
@@ -2,17 +2,25 @@ package tech.logica10.soniclair.models
|
||||
|
||||
class BasicParams(
|
||||
val u: String,
|
||||
val t: String,
|
||||
val s: String,
|
||||
val t: String?,
|
||||
val s: String?,
|
||||
val v: String,
|
||||
val c: String,
|
||||
val f: String
|
||||
val f: String,
|
||||
val p: String?,
|
||||
) {
|
||||
fun asMap(): HashMap<String, String> {
|
||||
val ret = HashMap<String, String>()
|
||||
ret["u"] = u
|
||||
ret["t"] = t
|
||||
ret["s"] = s
|
||||
if (t != null) {
|
||||
ret["t"] = t
|
||||
}
|
||||
if (s != null) {
|
||||
ret["s"] = s
|
||||
}
|
||||
if (p != null) {
|
||||
ret["p"] = p
|
||||
}
|
||||
ret["v"] = v
|
||||
ret["c"] = c
|
||||
ret["f"] = f
|
||||
|
||||
@@ -2,5 +2,5 @@ package tech.logica10.soniclair.models
|
||||
|
||||
class Context(
|
||||
val accounts: List<Account> = emptyList(),
|
||||
val activeAccount: Account = Account(null, "", "", ""),
|
||||
val activeAccount: Account = Account(null, "", "", "", false),
|
||||
)
|
||||
@@ -3,7 +3,7 @@ package tech.logica10.soniclair.models
|
||||
open class SubsonicResponse {
|
||||
val serverVersion: String = ""
|
||||
val status: String = ""
|
||||
val type: String = ""
|
||||
val type: String? = ""
|
||||
val version: String = ""
|
||||
val error: SubsonicError? = null
|
||||
}
|
||||
@@ -270,10 +270,18 @@ export default function PlayTest() {
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
id="flexSwitchCheckDefault"
|
||||
{...register("usePlaintext", { required: true })}
|
||||
{...register("usePlaintext")}
|
||||
/>
|
||||
<label className="w-100 text-start form-label text-white"> Use plaintext password (insecure on http connections, needed for some servers)</label>
|
||||
<label className="w-100 text-start form-label text-white">
|
||||
Use plaintext password (insecure on http
|
||||
connections, needed for some servers)
|
||||
</label>
|
||||
</div>
|
||||
{errors && errors.url && (
|
||||
<div className="col-12 text-danger">
|
||||
{errors.usePlaintext?.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
ref={buttonRef}
|
||||
|
||||
Reference in New Issue
Block a user