mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-28 20:45:17 +00:00
Version fetching API added
This commit is contained in:
parent
d23d8f3b07
commit
35a9f9fbbe
@ -0,0 +1,44 @@
|
|||||||
|
package org.thoughtcrime.securesms.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
|
||||||
|
class VersionUtil(
|
||||||
|
private val context: Context,
|
||||||
|
private val prefs: TextSecurePreferences
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
private val runnable: Runnable
|
||||||
|
|
||||||
|
init {
|
||||||
|
runnable = Runnable {
|
||||||
|
// Task to be executed every 4 hours
|
||||||
|
fetchVersionData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-schedule the task
|
||||||
|
handler.postDelayed(runnable, FOUR_HOURS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startTimedVersionCheck() {
|
||||||
|
handler.post(runnable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopTimedVersionCheck() {
|
||||||
|
handler.removeCallbacks(runnable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchVersionData() {
|
||||||
|
// only perform this if at least 4h has elapsed since th last successful check
|
||||||
|
if(prefs.getLastVersionCheck() < FOUR_HOURS) return
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val FOUR_HOURS = 4 * 60 * 60 * 1000L // 4 hours in milliseconds
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,22 @@
|
|||||||
package org.session.libsession.messaging.file_server
|
package org.session.libsession.messaging.file_server
|
||||||
|
|
||||||
|
import android.util.Base64
|
||||||
|
import network.loki.messenger.libsession_util.util.BlindKeyAPI
|
||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.Headers.Companion.toHeaders
|
import okhttp3.Headers.Companion.toHeaders
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.MediaType
|
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
|
import org.session.libsession.snode.utilities.await
|
||||||
import org.session.libsignal.utilities.HTTP
|
import org.session.libsignal.utilities.HTTP
|
||||||
import org.session.libsignal.utilities.JsonUtil
|
import org.session.libsignal.utilities.JsonUtil
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
|
import org.session.libsignal.utilities.toHexString
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
object FileServerApi {
|
object FileServerApi {
|
||||||
|
|
||||||
@ -23,6 +27,7 @@ object FileServerApi {
|
|||||||
sealed class Error(message: String) : Exception(message) {
|
sealed class Error(message: String) : Exception(message) {
|
||||||
object ParsingFailed : Error("Invalid response.")
|
object ParsingFailed : Error("Invalid response.")
|
||||||
object InvalidURL : Error("Invalid URL.")
|
object InvalidURL : Error("Invalid URL.")
|
||||||
|
object NoEd25519KeyPair : Error("Couldn't find ed25519 key pair.")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Request(
|
data class Request(
|
||||||
@ -105,4 +110,52 @@ object FileServerApi {
|
|||||||
val request = Request(verb = HTTP.Verb.GET, endpoint = "file/$file")
|
val request = Request(verb = HTTP.Verb.GET, endpoint = "file/$file")
|
||||||
return send(request)
|
return send(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current version of session
|
||||||
|
* This is effectively proxying (and caching) the response from the github release
|
||||||
|
* page.
|
||||||
|
*
|
||||||
|
* Note that the value is cached and can be up to 30 minutes out of date normally, and up to 24
|
||||||
|
* hours out of date if we cannot reach the Github API for some reason.
|
||||||
|
*
|
||||||
|
* https://github.com/oxen-io/session-file-server/blob/dev/doc/api.yaml#L119
|
||||||
|
*/
|
||||||
|
suspend fun getClientVersion(): VersionData {
|
||||||
|
// Generate the auth signature
|
||||||
|
val secretKey = MessagingModuleConfiguration.shared.getUserED25519KeyPair()?.secretKey?.asBytes
|
||||||
|
?: throw (Error.NoEd25519KeyPair)
|
||||||
|
|
||||||
|
val blindedKeys = BlindKeyAPI.blindVersionKeyPair(secretKey)
|
||||||
|
val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) // The current timestamp in seconds
|
||||||
|
val signature = BlindKeyAPI.blindVersionSign(secretKey, timestamp)
|
||||||
|
|
||||||
|
// The hex encoded version-blinded public key with a 07 prefix
|
||||||
|
val blindedPkHex = buildString {
|
||||||
|
append("07")
|
||||||
|
append(blindedKeys.pubKey.toHexString())
|
||||||
|
}
|
||||||
|
|
||||||
|
val request = Request(
|
||||||
|
verb = HTTP.Verb.GET,
|
||||||
|
endpoint = "session_version",
|
||||||
|
queryParameters = mapOf("platform" to "android"),
|
||||||
|
headers = mapOf(
|
||||||
|
"X-FS-Pubkey" to blindedPkHex,
|
||||||
|
"X-FS-Timestamp" to timestamp.toString(),
|
||||||
|
"X-FS-Signature" to Base64.encodeToString(signature, Base64.NO_WRAP)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// transform the promise into a coroutine
|
||||||
|
val result = send(request).await()
|
||||||
|
|
||||||
|
// map out the result
|
||||||
|
val json = JsonUtil.fromJson(result, Map::class.java)
|
||||||
|
val statusCode = json.getOrDefault("status_code", 0) as Int
|
||||||
|
val version = json.getOrDefault("result", "") as String
|
||||||
|
val updated = json.getOrDefault("updated", 0.0) as Double
|
||||||
|
|
||||||
|
return VersionData(statusCode, version, updated)
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package org.session.libsession.messaging.file_server
|
||||||
|
|
||||||
|
data class VersionData(
|
||||||
|
val statusCode: Int, // The value 200. Included for backwards compatibility, and may be removed someday.
|
||||||
|
val version: String, // The Session version.
|
||||||
|
val updated: Double // The unix timestamp when this version was retrieved from Github; this can be up to 24 hours ago in case of consistent fetch errors, though normally will be within the last 30 minutes.
|
||||||
|
)
|
@ -0,0 +1,13 @@
|
|||||||
|
package org.session.libsession.snode.utilities
|
||||||
|
|
||||||
|
import nl.komponents.kovenant.Promise
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
|
suspend fun <T, E: Throwable> Promise<T, E>.await(): T {
|
||||||
|
return suspendCoroutine { cont ->
|
||||||
|
success { cont.resume(it) }
|
||||||
|
fail { cont.resumeWithException(it) }
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import kotlinx.coroutines.channels.BufferOverflow
|
|||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import org.session.libsession.R
|
import org.session.libsession.R
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences.Companion
|
||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.AUTOPLAY_AUDIO_MESSAGES
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.AUTOPLAY_AUDIO_MESSAGES
|
||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.CALL_NOTIFICATIONS_ENABLED
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.CALL_NOTIFICATIONS_ENABLED
|
||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK
|
||||||
@ -20,6 +21,7 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_
|
|||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.FOLLOW_SYSTEM_SETTINGS
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.FOLLOW_SYSTEM_SETTINGS
|
||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.HIDE_PASSWORD
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.HIDE_PASSWORD
|
||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.LAST_VACUUM_TIME
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.LAST_VACUUM_TIME
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.LAST_VERSION_CHECK
|
||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.LEGACY_PREF_KEY_SELECTED_UI_MODE
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.LEGACY_PREF_KEY_SELECTED_UI_MODE
|
||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_DARK
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_DARK
|
||||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_LIGHT
|
import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_LIGHT
|
||||||
@ -186,6 +188,8 @@ interface TextSecurePreferences {
|
|||||||
fun clearAll()
|
fun clearAll()
|
||||||
fun getHidePassword(): Boolean
|
fun getHidePassword(): Boolean
|
||||||
fun setHidePassword(value: Boolean)
|
fun setHidePassword(value: Boolean)
|
||||||
|
fun getLastVersionCheck(): Long
|
||||||
|
fun setLastVersionCheck()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = TextSecurePreferences::class.simpleName
|
val TAG = TextSecurePreferences::class.simpleName
|
||||||
@ -272,6 +276,7 @@ interface TextSecurePreferences {
|
|||||||
const val AUTOPLAY_AUDIO_MESSAGES = "pref_autoplay_audio"
|
const val AUTOPLAY_AUDIO_MESSAGES = "pref_autoplay_audio"
|
||||||
const val FINGERPRINT_KEY_GENERATED = "fingerprint_key_generated"
|
const val FINGERPRINT_KEY_GENERATED = "fingerprint_key_generated"
|
||||||
const val SELECTED_ACCENT_COLOR = "selected_accent_color"
|
const val SELECTED_ACCENT_COLOR = "selected_accent_color"
|
||||||
|
const val LAST_VERSION_CHECK = "pref_last_version_check"
|
||||||
|
|
||||||
const val HAS_RECEIVED_LEGACY_CONFIG = "has_received_legacy_config"
|
const val HAS_RECEIVED_LEGACY_CONFIG = "has_received_legacy_config"
|
||||||
const val HAS_FORCED_NEW_CONFIG = "has_forced_new_config"
|
const val HAS_FORCED_NEW_CONFIG = "has_forced_new_config"
|
||||||
@ -1541,6 +1546,14 @@ class AppTextSecurePreferences @Inject constructor(
|
|||||||
setLongPreference(LAST_VACUUM_TIME, System.currentTimeMillis())
|
setLongPreference(LAST_VACUUM_TIME, System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getLastVersionCheck(): Long {
|
||||||
|
return getLongPreference(LAST_VERSION_CHECK, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setLastVersionCheck() {
|
||||||
|
setLongPreference(LAST_VERSION_CHECK, System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
|
||||||
override fun setShownCallNotification(): Boolean {
|
override fun setShownCallNotification(): Boolean {
|
||||||
val previousValue = getBooleanPreference(SHOWN_CALL_NOTIFICATION, false)
|
val previousValue = getBooleanPreference(SHOWN_CALL_NOTIFICATION, false)
|
||||||
if (previousValue) return false
|
if (previousValue) return false
|
||||||
|
Loading…
Reference in New Issue
Block a user