mirror of
https://github.com/oxen-io/session-android.git
synced 2025-01-12 09:33:39 +00:00
Update onion response parsing
This commit is contained in:
parent
95fd2baec0
commit
9581a75268
@ -66,8 +66,6 @@ object OpenGroupManager {
|
|||||||
storage.removeLastMessageServerID(room, server)
|
storage.removeLastMessageServerID(room, server)
|
||||||
// Store the public key
|
// Store the public key
|
||||||
storage.setOpenGroupPublicKey(server,publicKey)
|
storage.setOpenGroupPublicKey(server,publicKey)
|
||||||
// Get an auth token
|
|
||||||
OpenGroupApi.getAuthToken(room, server).get()
|
|
||||||
// Get capabilities
|
// Get capabilities
|
||||||
val capabilities = OpenGroupApi.getCapabilities(room, server).get()
|
val capabilities = OpenGroupApi.getCapabilities(room, server).get()
|
||||||
// Get group info
|
// Get group info
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package org.thoughtcrime.securesms.notifications
|
package org.thoughtcrime.securesms.notifications
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import nl.komponents.kovenant.Promise
|
||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
||||||
import okhttp3.MediaType
|
import okhttp3.MediaType
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
|
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
|
import org.session.libsession.snode.Version
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.utilities.JsonUtil
|
import org.session.libsignal.utilities.JsonUtil
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
@ -43,7 +45,7 @@ object LokiPushNotificationManager {
|
|||||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||||
val request = Request.Builder().url(url).post(body)
|
val request = Request.Builder().url(url).post(body)
|
||||||
retryIfNeeded(maxRetryCount) {
|
retryIfNeeded(maxRetryCount) {
|
||||||
OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, OnionRequestAPI.Version.V2).map { json ->
|
getResponseBody(request.build()).map { json ->
|
||||||
val code = json["code"] as? Int
|
val code = json["code"] as? Int
|
||||||
if (code != null && code != 0) {
|
if (code != null && code != 0) {
|
||||||
TextSecurePreferences.setIsUsingFCM(context, false)
|
TextSecurePreferences.setIsUsingFCM(context, false)
|
||||||
@ -72,7 +74,7 @@ object LokiPushNotificationManager {
|
|||||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||||
val request = Request.Builder().url(url).post(body)
|
val request = Request.Builder().url(url).post(body)
|
||||||
retryIfNeeded(maxRetryCount) {
|
retryIfNeeded(maxRetryCount) {
|
||||||
OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, OnionRequestAPI.Version.V2).map { json ->
|
getResponseBody(request.build()).map { json ->
|
||||||
val code = json["code"] as? Int
|
val code = json["code"] as? Int
|
||||||
if (code != null && code != 0) {
|
if (code != null && code != 0) {
|
||||||
TextSecurePreferences.setIsUsingFCM(context, true)
|
TextSecurePreferences.setIsUsingFCM(context, true)
|
||||||
@ -100,7 +102,7 @@ object LokiPushNotificationManager {
|
|||||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||||
val request = Request.Builder().url(url).post(body)
|
val request = Request.Builder().url(url).post(body)
|
||||||
retryIfNeeded(maxRetryCount) {
|
retryIfNeeded(maxRetryCount) {
|
||||||
OnionRequestAPI.sendOnionRequest(request.build(), server, pnServerPublicKey, OnionRequestAPI.Version.V2).map { json ->
|
getResponseBody(request.build()).map { json ->
|
||||||
val code = json["code"] as? Int
|
val code = json["code"] as? Int
|
||||||
if (code == null || code == 0) {
|
if (code == null || code == 0) {
|
||||||
Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${json["message"] as? String ?: "null"}.")
|
Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${json["message"] as? String ?: "null"}.")
|
||||||
@ -110,4 +112,10 @@ object LokiPushNotificationManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getResponseBody(request: Request): Promise<Map<*, *>, Exception> {
|
||||||
|
return OnionRequestAPI.sendOnionRequest(request, server, pnServerPublicKey, Version.V2).map { response ->
|
||||||
|
JsonUtil.fromJson(response.body, Map::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,9 @@ object FileServerApi {
|
|||||||
HTTP.Verb.DELETE -> requestBuilder.delete(createBody(request.parameters))
|
HTTP.Verb.DELETE -> requestBuilder.delete(createBody(request.parameters))
|
||||||
}
|
}
|
||||||
return if (request.useOnionRouting) {
|
return if (request.useOnionRouting) {
|
||||||
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), server, serverPublicKey).fail { e ->
|
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), server, serverPublicKey).map {
|
||||||
|
JsonUtil.fromJson(it.body, Map::class.java)
|
||||||
|
}.fail { e ->
|
||||||
Log.e("Loki", "File server request failed.", e)
|
Log.e("Loki", "File server request failed.", e)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,6 +13,7 @@ import org.session.libsession.messaging.sending_receiving.notifications.PushNoti
|
|||||||
import org.session.libsession.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
import org.session.libsession.snode.SnodeMessage
|
import org.session.libsession.snode.SnodeMessage
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
|
import org.session.libsession.snode.Version
|
||||||
|
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.session.libsignal.utilities.JsonUtil
|
import org.session.libsignal.utilities.JsonUtil
|
||||||
@ -38,10 +39,10 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
|
|||||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||||
val request = Request.Builder().url(url).post(body)
|
val request = Request.Builder().url(url).post(body)
|
||||||
retryIfNeeded(4) {
|
retryIfNeeded(4) {
|
||||||
OnionRequestAPI.sendOnionRequest(request.build(), server, PushNotificationAPI.serverPublicKey, OnionRequestAPI.Version.V2).map { json ->
|
OnionRequestAPI.sendOnionRequest(request.build(), server, PushNotificationAPI.serverPublicKey, Version.V2).map { response ->
|
||||||
val code = json["code"] as? Int
|
val code = response.info["code"] as? Int
|
||||||
if (code == null || code == 0) {
|
if (code == null || code == 0) {
|
||||||
Log.d("Loki", "Couldn't notify PN server due to error: ${json["message"] as? String ?: "null"}.")
|
Log.d("Loki", "Couldn't notify PN server due to error: ${response.info["message"] as? String ?: "null"}.")
|
||||||
}
|
}
|
||||||
}.fail { exception ->
|
}.fail { exception ->
|
||||||
Log.d("Loki", "Couldn't notify PN server due to error: $exception.")
|
Log.d("Loki", "Couldn't notify PN server due to error: $exception.")
|
||||||
|
@ -7,9 +7,9 @@ import com.fasterxml.jackson.databind.type.TypeFactory
|
|||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
import com.goterl.lazysodium.LazySodiumAndroid
|
||||||
import com.goterl.lazysodium.SodiumAndroid
|
import com.goterl.lazysodium.SodiumAndroid
|
||||||
import com.goterl.lazysodium.interfaces.GenericHash
|
import com.goterl.lazysodium.interfaces.GenericHash
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
import nl.komponents.kovenant.functional.bind
|
|
||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
@ -19,7 +19,7 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
|
|||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod
|
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod
|
||||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsession.utilities.AESGCM
|
import org.session.libsession.snode.OnionResponse
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.utilities.Base64.decode
|
import org.session.libsignal.utilities.Base64.decode
|
||||||
import org.session.libsignal.utilities.Base64.encodeBytes
|
import org.session.libsignal.utilities.Base64.encodeBytes
|
||||||
@ -32,9 +32,7 @@ import org.session.libsignal.utilities.Hex
|
|||||||
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.removing05PrefixIfNeeded
|
import org.session.libsignal.utilities.removing05PrefixIfNeeded
|
||||||
import org.session.libsignal.utilities.toHexString
|
|
||||||
import org.whispersystems.curve25519.Curve25519
|
import org.whispersystems.curve25519.Curve25519
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
@ -109,7 +107,6 @@ object OpenGroupApi {
|
|||||||
val queryParameters: Map<String, String> = mapOf(),
|
val queryParameters: Map<String, String> = mapOf(),
|
||||||
val parameters: Any? = null,
|
val parameters: Any? = null,
|
||||||
val headers: Map<String, String> = mapOf(),
|
val headers: Map<String, String> = mapOf(),
|
||||||
val isAuthRequired: Boolean = true,
|
|
||||||
/**
|
/**
|
||||||
* Always `true` under normal circumstances. You might want to disable
|
* Always `true` under normal circumstances. You might want to disable
|
||||||
* this when running over Lokinet.
|
* this when running over Lokinet.
|
||||||
@ -124,7 +121,13 @@ object OpenGroupApi {
|
|||||||
return RequestBody.create(MediaType.get("application/json"), parametersAsJSON)
|
return RequestBody.create(MediaType.get("application/json"), parametersAsJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun send(request: Request): Promise<Map<*, *>, Exception> {
|
private fun getResponseBodyJson(request: Request): Promise<Map<*, *>, Exception> {
|
||||||
|
return send(request).map {
|
||||||
|
JsonUtil.fromJson(it.body, Map::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun send(request: Request): Promise<OnionResponse, Exception> {
|
||||||
val url = HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.InvalidURL)
|
val url = HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.InvalidURL)
|
||||||
val urlBuilder = HttpUrl.Builder()
|
val urlBuilder = HttpUrl.Builder()
|
||||||
.scheme(url.scheme())
|
.scheme(url.scheme())
|
||||||
@ -136,7 +139,7 @@ object OpenGroupApi {
|
|||||||
urlBuilder.addQueryParameter(key, value)
|
urlBuilder.addQueryParameter(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun execute(token: String?): Promise<Map<*, *>, Exception> {
|
fun execute(): Promise<OnionResponse, Exception> {
|
||||||
val publicKey =
|
val publicKey =
|
||||||
MessagingModuleConfiguration.shared.storage.getOpenGroupPublicKey(request.server)
|
MessagingModuleConfiguration.shared.storage.getOpenGroupPublicKey(request.server)
|
||||||
?: return Promise.ofFail(Error.NoPublicKey)
|
?: return Promise.ofFail(Error.NoPublicKey)
|
||||||
@ -191,15 +194,10 @@ object OpenGroupApi {
|
|||||||
headers["X-SOGS-Timestamp"] = "$timestamp"
|
headers["X-SOGS-Timestamp"] = "$timestamp"
|
||||||
headers["X-SOGS-Pubkey"] = pubKey
|
headers["X-SOGS-Pubkey"] = pubKey
|
||||||
headers["X-SOGS-Signature"] = encodeBytes(signature)
|
headers["X-SOGS-Signature"] = encodeBytes(signature)
|
||||||
headers.forEach { entry -> Log.d("Loki", "${entry.key}: ${entry.value}") }
|
|
||||||
|
|
||||||
val requestBuilder = okhttp3.Request.Builder()
|
val requestBuilder = okhttp3.Request.Builder()
|
||||||
.url(urlRequest)
|
.url(urlRequest)
|
||||||
.headers(Headers.of(headers))
|
.headers(Headers.of(headers))
|
||||||
if (request.isAuthRequired) {
|
|
||||||
if (token.isNullOrEmpty()) throw IllegalStateException("No auth token for request.")
|
|
||||||
requestBuilder.header("Authorization", token)
|
|
||||||
}
|
|
||||||
when (request.verb) {
|
when (request.verb) {
|
||||||
GET -> requestBuilder.get()
|
GET -> requestBuilder.get()
|
||||||
PUT -> requestBuilder.put(createBody(request.parameters)!!)
|
PUT -> requestBuilder.put(createBody(request.parameters)!!)
|
||||||
@ -209,31 +207,13 @@ object OpenGroupApi {
|
|||||||
if (!request.room.isNullOrEmpty()) {
|
if (!request.room.isNullOrEmpty()) {
|
||||||
requestBuilder.header("Room", request.room)
|
requestBuilder.header("Room", request.room)
|
||||||
}
|
}
|
||||||
if (request.useOnionRouting) {
|
return if (request.useOnionRouting) {
|
||||||
return OnionRequestAPI.sendOnionRequest(
|
OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey)
|
||||||
requestBuilder.build(),
|
|
||||||
request.server,
|
|
||||||
publicKey
|
|
||||||
).fail { e ->
|
|
||||||
// A 401 means that we didn't provide a (valid) auth token for a route that required one. We use this as an
|
|
||||||
// indication that the token we're using has expired. Note that a 403 has a different meaning; it means that
|
|
||||||
// we provided a valid token but it doesn't have a high enough permission level for the route in question.
|
|
||||||
if (e is OnionRequestAPI.HTTPRequestFailedAtDestinationException && e.statusCode == 401) {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
if (request.room != null) {
|
|
||||||
storage.removeAuthToken(request.room, request.server)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))
|
Promise.ofFail(IllegalStateException("It's currently not allowed to send non onion routed requests."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if (request.isAuthRequired) {
|
return execute()
|
||||||
getAuthToken(request.room!!, request.server).bind { execute(it) }
|
|
||||||
} else {
|
|
||||||
execute(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun downloadOpenGroupProfilePicture(
|
fun downloadOpenGroupProfilePicture(
|
||||||
@ -244,78 +224,14 @@ object OpenGroupApi {
|
|||||||
verb = GET,
|
verb = GET,
|
||||||
room = roomID,
|
room = roomID,
|
||||||
server = server,
|
server = server,
|
||||||
endpoint = "rooms/$roomID/image",
|
endpoint = "rooms/$roomID/image"
|
||||||
isAuthRequired = false
|
|
||||||
)
|
)
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
val result = json["result"] as? String ?: throw Error.ParsingFailed
|
val result = json["result"] as? String ?: throw Error.ParsingFailed
|
||||||
decode(result)
|
decode(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Authorization
|
|
||||||
fun getAuthToken(room: String, server: String): Promise<String, Exception> {
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
|
||||||
return storage.getAuthToken(room, server)?.let {
|
|
||||||
Promise.of(it)
|
|
||||||
} ?: run {
|
|
||||||
requestNewAuthToken(room, server)
|
|
||||||
.bind { claimAuthToken(it, room, server) }
|
|
||||||
.success { authToken ->
|
|
||||||
storage.setAuthToken(room, server, authToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun requestNewAuthToken(room: String, server: String): Promise<String, Exception> {
|
|
||||||
val (publicKey, privateKey) = MessagingModuleConfiguration.shared.storage.getUserX25519KeyPair()
|
|
||||||
.let { it.publicKey.serialize() to it.privateKey.serialize() }
|
|
||||||
?: return Promise.ofFail(Error.Generic)
|
|
||||||
val queryParameters = mutableMapOf("public_key" to publicKey.toHexString())
|
|
||||||
val request = Request(
|
|
||||||
GET,
|
|
||||||
room,
|
|
||||||
server,
|
|
||||||
"auth_token_challenge",
|
|
||||||
queryParameters,
|
|
||||||
isAuthRequired = false,
|
|
||||||
parameters = null
|
|
||||||
)
|
|
||||||
return send(request).map { json ->
|
|
||||||
val challenge = json["challenge"] as? Map<*, *> ?: throw Error.ParsingFailed
|
|
||||||
val base64EncodedCiphertext =
|
|
||||||
challenge["ciphertext"] as? String ?: throw Error.ParsingFailed
|
|
||||||
val base64EncodedEphemeralPublicKey =
|
|
||||||
challenge["ephemeral_public_key"] as? String ?: throw Error.ParsingFailed
|
|
||||||
val ciphertext = decode(base64EncodedCiphertext)
|
|
||||||
val ephemeralPublicKey = decode(base64EncodedEphemeralPublicKey)
|
|
||||||
val symmetricKey = AESGCM.generateSymmetricKey(ephemeralPublicKey, privateKey)
|
|
||||||
val tokenAsData = try {
|
|
||||||
AESGCM.decrypt(ciphertext, symmetricKey)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw Error.DecryptionFailed
|
|
||||||
}
|
|
||||||
tokenAsData.toHexString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun claimAuthToken(
|
|
||||||
authToken: String,
|
|
||||||
room: String,
|
|
||||||
server: String
|
|
||||||
): Promise<String, Exception> {
|
|
||||||
val parameters =
|
|
||||||
mapOf("public_key" to MessagingModuleConfiguration.shared.storage.getUserPublicKey()!!)
|
|
||||||
val headers = mapOf("Authorization" to authToken)
|
|
||||||
val request = Request(
|
|
||||||
verb = POST, room = room, server = server, endpoint = "claim_auth_token",
|
|
||||||
parameters = parameters, headers = headers, isAuthRequired = false
|
|
||||||
)
|
|
||||||
return send(request).map { authToken }
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Upload/Download
|
// region Upload/Download
|
||||||
fun upload(file: ByteArray, room: String, server: String): Promise<Long, Exception> {
|
fun upload(file: ByteArray, room: String, server: String): Promise<Long, Exception> {
|
||||||
val base64EncodedFile = encodeBytes(file)
|
val base64EncodedFile = encodeBytes(file)
|
||||||
@ -327,14 +243,14 @@ object OpenGroupApi {
|
|||||||
endpoint = "files",
|
endpoint = "files",
|
||||||
parameters = parameters
|
parameters = parameters
|
||||||
)
|
)
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
(json["result"] as? Number)?.toLong() ?: throw Error.ParsingFailed
|
(json["result"] as? Number)?.toLong() ?: throw Error.ParsingFailed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun download(file: Long, room: String, server: String): Promise<ByteArray, Exception> {
|
fun download(file: Long, room: String, server: String): Promise<ByteArray, Exception> {
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "files/$file")
|
val request = Request(verb = GET, room = room, server = server, endpoint = "files/$file")
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
val base64EncodedFile = json["result"] as? String ?: throw Error.ParsingFailed
|
val base64EncodedFile = json["result"] as? String ?: throw Error.ParsingFailed
|
||||||
decode(base64EncodedFile) ?: throw Error.ParsingFailed
|
decode(base64EncodedFile) ?: throw Error.ParsingFailed
|
||||||
}
|
}
|
||||||
@ -356,7 +272,7 @@ object OpenGroupApi {
|
|||||||
endpoint = "messages",
|
endpoint = "messages",
|
||||||
parameters = jsonMessage
|
parameters = jsonMessage
|
||||||
)
|
)
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
@Suppress("UNCHECKED_CAST") val rawMessage = json["message"] as? Map<String, Any>
|
@Suppress("UNCHECKED_CAST") val rawMessage = json["message"] as? Map<String, Any>
|
||||||
?: throw Error.ParsingFailed
|
?: throw Error.ParsingFailed
|
||||||
val result = OpenGroupMessageV2.fromJSON(rawMessage) ?: throw Error.ParsingFailed
|
val result = OpenGroupMessageV2.fromJSON(rawMessage) ?: throw Error.ParsingFailed
|
||||||
@ -381,7 +297,7 @@ object OpenGroupApi {
|
|||||||
endpoint = "messages",
|
endpoint = "messages",
|
||||||
queryParameters = queryParameters
|
queryParameters = queryParameters
|
||||||
)
|
)
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
@Suppress("UNCHECKED_CAST") val rawMessages =
|
@Suppress("UNCHECKED_CAST") val rawMessages =
|
||||||
json["messages"] as? List<Map<String, Any>>
|
json["messages"] as? List<Map<String, Any>>
|
||||||
?: throw Error.ParsingFailed
|
?: throw Error.ParsingFailed
|
||||||
@ -443,7 +359,8 @@ object OpenGroupApi {
|
|||||||
endpoint = "deleted_messages",
|
endpoint = "deleted_messages",
|
||||||
queryParameters = queryParameters
|
queryParameters = queryParameters
|
||||||
)
|
)
|
||||||
return send(request).map { json ->
|
return send(request).map { response ->
|
||||||
|
val json = JsonUtil.fromJson(response.body, Map::class.java)
|
||||||
val type = TypeFactory.defaultInstance()
|
val type = TypeFactory.defaultInstance()
|
||||||
.constructCollectionType(List::class.java, MessageDeletion::class.java)
|
.constructCollectionType(List::class.java, MessageDeletion::class.java)
|
||||||
val idsAsString = JsonUtil.toJson(json["ids"])
|
val idsAsString = JsonUtil.toJson(json["ids"])
|
||||||
@ -466,7 +383,7 @@ object OpenGroupApi {
|
|||||||
|
|
||||||
fun getModerators(room: String, server: String): Promise<List<String>, Exception> {
|
fun getModerators(room: String, server: String): Promise<List<String>, Exception> {
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "moderators")
|
val request = Request(verb = GET, room = room, server = server, endpoint = "moderators")
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
@Suppress("UNCHECKED_CAST") val moderatorsJson = json["moderators"] as? List<String>
|
@Suppress("UNCHECKED_CAST") val moderatorsJson = json["moderators"] as? List<String>
|
||||||
?: throw Error.ParsingFailed
|
?: throw Error.ParsingFailed
|
||||||
val id = "$server.$room"
|
val id = "$server.$room"
|
||||||
@ -519,11 +436,10 @@ object OpenGroupApi {
|
|||||||
|
|
||||||
// region General
|
// region General
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun batch(
|
fun poll(
|
||||||
rooms: List<String>,
|
rooms: List<String>,
|
||||||
server: String
|
server: String
|
||||||
): Promise<Map<String, BatchResult>, Exception> {
|
): Promise<Map<String, BatchResult>, Exception> {
|
||||||
val authTokenRequests = rooms.associateWith { room -> getAuthToken(room, server) }
|
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val context = MessagingModuleConfiguration.shared.context
|
val context = MessagingModuleConfiguration.shared.context
|
||||||
val timeSinceLastOpen = this.timeSinceLastOpen
|
val timeSinceLastOpen = this.timeSinceLastOpen
|
||||||
@ -535,15 +451,9 @@ object OpenGroupApi {
|
|||||||
TextSecurePreferences.setLastOpenDate(context)
|
TextSecurePreferences.setLastOpenDate(context)
|
||||||
}
|
}
|
||||||
val requests = rooms.mapNotNull { room ->
|
val requests = rooms.mapNotNull { room ->
|
||||||
val authToken = try {
|
|
||||||
authTokenRequests[room]?.get()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Loki", "Failed to get auth token for $room.", e)
|
|
||||||
null
|
|
||||||
} ?: return@mapNotNull null
|
|
||||||
BatchRequest(
|
BatchRequest(
|
||||||
roomID = room,
|
roomID = room,
|
||||||
authToken = authToken,
|
authToken = "",
|
||||||
fromDeletionServerID = if (useMessageLimit) null else storage.getLastDeletionServerID(
|
fromDeletionServerID = if (useMessageLimit) null else storage.getLastDeletionServerID(
|
||||||
room,
|
room,
|
||||||
server
|
server
|
||||||
@ -559,22 +469,13 @@ object OpenGroupApi {
|
|||||||
room = null,
|
room = null,
|
||||||
server = server,
|
server = server,
|
||||||
endpoint = "batch",
|
endpoint = "batch",
|
||||||
isAuthRequired = false,
|
|
||||||
parameters = mapOf("requests" to requests)
|
parameters = mapOf("requests" to requests)
|
||||||
)
|
)
|
||||||
return send(request = request).map { json ->
|
return getResponseBodyJson(request = request).map { json ->
|
||||||
val results = json["results"] as? List<*> ?: throw Error.ParsingFailed
|
val results = json["results"] as? List<*> ?: throw Error.ParsingFailed
|
||||||
results.mapNotNull { json ->
|
results.mapNotNull { json ->
|
||||||
if (json !is Map<*, *>) return@mapNotNull null
|
if (json !is Map<*, *>) return@mapNotNull null
|
||||||
val roomID = json["room_id"] as? String ?: return@mapNotNull null
|
val roomID = json["room_id"] as? String ?: return@mapNotNull null
|
||||||
// A 401 means that we didn't provide a (valid) auth token for a route that required one. We use this as an
|
|
||||||
// indication that the token we're using has expired. Note that a 403 has a different meaning; it means that
|
|
||||||
// we provided a valid token but it doesn't have a high enough permission level for the route in question.
|
|
||||||
val statusCode = json["status_code"] as? Int ?: return@mapNotNull null
|
|
||||||
if (statusCode == 401) {
|
|
||||||
// delete auth token and return null
|
|
||||||
storage.removeAuthToken(roomID, server)
|
|
||||||
}
|
|
||||||
// Moderators
|
// Moderators
|
||||||
val moderators = json["moderators"] as? List<String> ?: return@mapNotNull null
|
val moderators = json["moderators"] as? List<String> ?: return@mapNotNull null
|
||||||
handleModerators("$server.$roomID", moderators)
|
handleModerators("$server.$roomID", moderators)
|
||||||
@ -632,10 +533,9 @@ object OpenGroupApi {
|
|||||||
verb = GET,
|
verb = GET,
|
||||||
room = null,
|
room = null,
|
||||||
server = server,
|
server = server,
|
||||||
endpoint = "rooms/$room",
|
endpoint = "rooms/$room"
|
||||||
isAuthRequired = false
|
|
||||||
)
|
)
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
val rawRoom = json["room"] as? Map<*, *> ?: throw Error.ParsingFailed
|
val rawRoom = json["room"] as? Map<*, *> ?: throw Error.ParsingFailed
|
||||||
val id = rawRoom["id"] as? String ?: throw Error.ParsingFailed
|
val id = rawRoom["id"] as? String ?: throw Error.ParsingFailed
|
||||||
val name = rawRoom["name"] as? String ?: throw Error.ParsingFailed
|
val name = rawRoom["name"] as? String ?: throw Error.ParsingFailed
|
||||||
@ -650,14 +550,13 @@ object OpenGroupApi {
|
|||||||
room = null,
|
room = null,
|
||||||
server = server,
|
server = server,
|
||||||
endpoint = "rooms",
|
endpoint = "rooms",
|
||||||
isAuthRequired = false,
|
|
||||||
isBlinded = true
|
isBlinded = true
|
||||||
)
|
)
|
||||||
return send(request).map { json ->
|
return send(request).map { response ->
|
||||||
val rawRooms = json["rooms"] as? List<Map<*, *>> ?: throw Error.ParsingFailed
|
val rawRooms = JsonUtil.fromJson(response.body, List::class.java) ?: throw Error.ParsingFailed
|
||||||
rawRooms.mapNotNull {
|
rawRooms.mapNotNull {
|
||||||
val roomJson = it as? Map<*, *> ?: return@mapNotNull null
|
val roomJson = it as? Map<*, *> ?: return@mapNotNull null
|
||||||
val id = roomJson["id"] as? String ?: return@mapNotNull null
|
val id = roomJson["token"] as? String ?: return@mapNotNull null
|
||||||
val name = roomJson["name"] as? String ?: return@mapNotNull null
|
val name = roomJson["name"] as? String ?: return@mapNotNull null
|
||||||
val imageID = roomJson["image_id"] as? String
|
val imageID = roomJson["image_id"] as? String
|
||||||
Info(id, name, imageID)
|
Info(id, name, imageID)
|
||||||
@ -667,7 +566,7 @@ object OpenGroupApi {
|
|||||||
|
|
||||||
fun getMemberCount(room: String, server: String): Promise<Int, Exception> {
|
fun getMemberCount(room: String, server: String): Promise<Int, Exception> {
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "active_users")
|
val request = Request(verb = GET, room = room, server = server, endpoint = "active_users")
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
val activeUserCount = json["active_users"] as? Int ?: throw Error.ParsingFailed
|
val activeUserCount = json["active_users"] as? Int ?: throw Error.ParsingFailed
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
storage.setUserCount(room, server, activeUserCount)
|
storage.setUserCount(room, server, activeUserCount)
|
||||||
@ -677,7 +576,7 @@ object OpenGroupApi {
|
|||||||
|
|
||||||
fun getCapabilities(room: String, server: String): Promise<List<String>, Exception> {
|
fun getCapabilities(room: String, server: String): Promise<List<String>, Exception> {
|
||||||
val request = Request(verb = GET, room = room, server = server, endpoint = "capabilities")
|
val request = Request(verb = GET, room = room, server = server, endpoint = "capabilities")
|
||||||
return send(request).map { json ->
|
return getResponseBodyJson(request).map { json ->
|
||||||
json["capabilities"] as? List<String> ?: throw Error.ParsingFailed
|
json["capabilities"] as? List<String> ?: throw Error.ParsingFailed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,12 @@ import org.session.libsession.messaging.messages.control.ClosedGroupControlMessa
|
|||||||
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
||||||
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
||||||
import org.session.libsession.messaging.messages.control.UnsendRequest
|
import org.session.libsession.messaging.messages.control.UnsendRequest
|
||||||
import org.session.libsession.messaging.messages.visible.*
|
import org.session.libsession.messaging.messages.visible.LinkPreview
|
||||||
import org.session.libsession.messaging.open_groups.*
|
import org.session.libsession.messaging.messages.visible.Profile
|
||||||
|
import org.session.libsession.messaging.messages.visible.Quote
|
||||||
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||||
|
import org.session.libsession.messaging.open_groups.OpenGroupMessageV2
|
||||||
import org.session.libsession.messaging.utilities.MessageWrapper
|
import org.session.libsession.messaging.utilities.MessageWrapper
|
||||||
import org.session.libsession.snode.RawResponsePromise
|
import org.session.libsession.snode.RawResponsePromise
|
||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
|
@ -7,6 +7,7 @@ import okhttp3.Request
|
|||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
|
import org.session.libsession.snode.Version
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.utilities.retryIfNeeded
|
import org.session.libsignal.utilities.retryIfNeeded
|
||||||
import org.session.libsignal.utilities.JsonUtil
|
import org.session.libsignal.utilities.JsonUtil
|
||||||
@ -38,12 +39,12 @@ object PushNotificationAPI {
|
|||||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||||
val request = Request.Builder().url(url).post(body)
|
val request = Request.Builder().url(url).post(body)
|
||||||
retryIfNeeded(maxRetryCount) {
|
retryIfNeeded(maxRetryCount) {
|
||||||
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, OnionRequestAPI.Version.V2).map { json ->
|
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response ->
|
||||||
val code = json["code"] as? Int
|
val code = response.info["code"] as? Int
|
||||||
if (code != null && code != 0) {
|
if (code != null && code != 0) {
|
||||||
TextSecurePreferences.setIsUsingFCM(context, false)
|
TextSecurePreferences.setIsUsingFCM(context, false)
|
||||||
} else {
|
} else {
|
||||||
Log.d("Loki", "Couldn't disable FCM due to error: ${json["message"] as? String ?: "null"}.")
|
Log.d("Loki", "Couldn't disable FCM due to error: ${response.info["message"] as? String ?: "null"}.")
|
||||||
}
|
}
|
||||||
}.fail { exception ->
|
}.fail { exception ->
|
||||||
Log.d("Loki", "Couldn't disable FCM due to error: ${exception}.")
|
Log.d("Loki", "Couldn't disable FCM due to error: ${exception}.")
|
||||||
@ -66,14 +67,14 @@ object PushNotificationAPI {
|
|||||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||||
val request = Request.Builder().url(url).post(body)
|
val request = Request.Builder().url(url).post(body)
|
||||||
retryIfNeeded(maxRetryCount) {
|
retryIfNeeded(maxRetryCount) {
|
||||||
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, OnionRequestAPI.Version.V2).map { json ->
|
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response ->
|
||||||
val code = json["code"] as? Int
|
val code = response.info["code"] as? Int
|
||||||
if (code != null && code != 0) {
|
if (code != null && code != 0) {
|
||||||
TextSecurePreferences.setIsUsingFCM(context, true)
|
TextSecurePreferences.setIsUsingFCM(context, true)
|
||||||
TextSecurePreferences.setFCMToken(context, token)
|
TextSecurePreferences.setFCMToken(context, token)
|
||||||
TextSecurePreferences.setLastFCMUploadTime(context, System.currentTimeMillis())
|
TextSecurePreferences.setLastFCMUploadTime(context, System.currentTimeMillis())
|
||||||
} else {
|
} else {
|
||||||
Log.d("Loki", "Couldn't register for FCM due to error: ${json["message"] as? String ?: "null"}.")
|
Log.d("Loki", "Couldn't register for FCM due to error: ${response.info["message"] as? String ?: "null"}.")
|
||||||
}
|
}
|
||||||
}.fail { exception ->
|
}.fail { exception ->
|
||||||
Log.d("Loki", "Couldn't register for FCM due to error: ${exception}.")
|
Log.d("Loki", "Couldn't register for FCM due to error: ${exception}.")
|
||||||
@ -93,10 +94,10 @@ object PushNotificationAPI {
|
|||||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||||
val request = Request.Builder().url(url).post(body)
|
val request = Request.Builder().url(url).post(body)
|
||||||
retryIfNeeded(maxRetryCount) {
|
retryIfNeeded(maxRetryCount) {
|
||||||
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, OnionRequestAPI.Version.V2).map { json ->
|
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response ->
|
||||||
val code = json["code"] as? Int
|
val code = response.info["code"] as? Int
|
||||||
if (code == null || code == 0) {
|
if (code == null || code == 0) {
|
||||||
Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${json["message"] as? String ?: "null"}.")
|
Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${response.info["message"] as? String ?: "null"}.")
|
||||||
}
|
}
|
||||||
}.fail { exception ->
|
}.fail { exception ->
|
||||||
Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${exception}.")
|
Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${exception}.")
|
||||||
|
@ -45,7 +45,7 @@ class OpenGroupPoller(private val server: String, private val executorService: S
|
|||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val rooms = storage.getAllV2OpenGroups().values.filter { it.server == server }.map { it.room }
|
val rooms = storage.getAllV2OpenGroups().values.filter { it.server == server }.map { it.room }
|
||||||
rooms.forEach { downloadGroupAvatarIfNeeded(it) }
|
rooms.forEach { downloadGroupAvatarIfNeeded(it) }
|
||||||
return OpenGroupApi.batch(rooms, server).successBackground { responses ->
|
return OpenGroupApi.poll(rooms, server).successBackground { responses ->
|
||||||
responses.forEach { (room, response) ->
|
responses.forEach { (room, response) ->
|
||||||
val openGroupID = "$server.$room"
|
val openGroupID = "$server.$room"
|
||||||
handleNewMessages(room, openGroupID, response.messages)
|
handleNewMessages(room, openGroupID, response.messages)
|
||||||
|
@ -348,8 +348,12 @@ object OnionRequestAPI {
|
|||||||
/**
|
/**
|
||||||
* Sends an onion request to `destination`. Builds new paths as needed.
|
* Sends an onion request to `destination`. Builds new paths as needed.
|
||||||
*/
|
*/
|
||||||
private fun sendOnionRequest(destination: Destination, payload: ByteArray, version: Version): Promise<Map<*, *>, Exception> {
|
private fun sendOnionRequest(
|
||||||
val deferred = deferred<Map<*, *>, Exception>()
|
destination: Destination,
|
||||||
|
payload: ByteArray,
|
||||||
|
version: Version
|
||||||
|
): Promise<OnionResponse, Exception> {
|
||||||
|
val deferred = deferred<OnionResponse, Exception>()
|
||||||
lateinit var guardSnode: Snode
|
lateinit var guardSnode: Snode
|
||||||
buildOnionForDestination(payload, destination, version).success { result ->
|
buildOnionForDestination(payload, destination, version).success { result ->
|
||||||
guardSnode = result.guardSnode
|
guardSnode = result.guardSnode
|
||||||
@ -439,7 +443,13 @@ object OnionRequestAPI {
|
|||||||
/**
|
/**
|
||||||
* Sends an onion request to `snode`. Builds new paths as needed.
|
* Sends an onion request to `snode`. Builds new paths as needed.
|
||||||
*/
|
*/
|
||||||
internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, version: Version, publicKey: String? = null): Promise<Map<*, *>, Exception> {
|
internal fun sendOnionRequest(
|
||||||
|
method: Snode.Method,
|
||||||
|
parameters: Map<*, *>,
|
||||||
|
snode: Snode,
|
||||||
|
version: Version,
|
||||||
|
publicKey: String? = null
|
||||||
|
): Promise<OnionResponse, Exception> {
|
||||||
val payload = mapOf(
|
val payload = mapOf(
|
||||||
"method" to method.rawValue,
|
"method" to method.rawValue,
|
||||||
"params" to parameters
|
"params" to parameters
|
||||||
@ -461,7 +471,12 @@ object OnionRequestAPI {
|
|||||||
*
|
*
|
||||||
* `publicKey` is the hex encoded public key of the user the call is associated with. This is needed for swarm cache maintenance.
|
* `publicKey` is the hex encoded public key of the user the call is associated with. This is needed for swarm cache maintenance.
|
||||||
*/
|
*/
|
||||||
fun sendOnionRequest(request: Request, server: String, x25519PublicKey: String, version: Version = Version.V4): Promise<Map<*, *>, Exception> {
|
fun sendOnionRequest(
|
||||||
|
request: Request,
|
||||||
|
server: String,
|
||||||
|
x25519PublicKey: String,
|
||||||
|
version: Version = Version.V4
|
||||||
|
): Promise<OnionResponse, Exception> {
|
||||||
val url = request.url()
|
val url = request.url()
|
||||||
val payload = generatePayload(request, server, version)
|
val payload = generatePayload(request, server, version)
|
||||||
val destination = Destination.Server(url.host(), version.value, x25519PublicKey, url.scheme(), url.port())
|
val destination = Destination.Server(url.host(), version.value, x25519PublicKey, url.scheme(), url.port())
|
||||||
@ -518,7 +533,7 @@ object OnionRequestAPI {
|
|||||||
destinationSymmetricKey: ByteArray,
|
destinationSymmetricKey: ByteArray,
|
||||||
destination: Destination,
|
destination: Destination,
|
||||||
version: Version,
|
version: Version,
|
||||||
deferred: Deferred<Map<*, *>, Exception>
|
deferred: Deferred<OnionResponse, Exception>
|
||||||
) {
|
) {
|
||||||
if (version == Version.V4) {
|
if (version == Version.V4) {
|
||||||
try {
|
try {
|
||||||
@ -563,25 +578,24 @@ object OnionRequestAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If there is no data in the response then just return the ResponseInfo
|
// If there is no data in the response then just return the ResponseInfo
|
||||||
if (info.length < "l${infoLength}${info}e".length) {
|
if (plaintextString.length <= "l${infoLength}${info}e".length) {
|
||||||
return deferred.resolve(JsonUtil.fromJson(info, Map::class.java))
|
return deferred.resolve(OnionResponse(responseInfo, null))
|
||||||
}
|
}
|
||||||
// Extract the response data as well
|
// Extract the response data as well
|
||||||
val data = plaintextString.substring(infoEndIndex)
|
val data = plaintextString.substring(infoEndIndex)
|
||||||
val dataParts = data.split(":")
|
val dataParts = data.split(":".toRegex(), 2)
|
||||||
val dataLength = dataParts.firstOrNull()?.length
|
val dataLength = dataParts.firstOrNull()?.toIntOrNull()
|
||||||
if (dataParts.size <= 1 || dataLength == null) return deferred.reject(Exception("Invalid JSON"))
|
if (dataParts.size <= 1 || dataLength == null) return deferred.reject(Exception("Invalid response"))
|
||||||
val dataString = dataParts.last().dropLast(1)
|
val dataString = dataParts.last().dropLast(1)
|
||||||
return deferred.resolve(JsonUtil.fromJson(dataString, Map::class.java))
|
return deferred.resolve(OnionResponse(responseInfo, dataString.encodeToByteArray()))
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
deferred.reject(exception)
|
deferred.reject(exception)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val bodyAsString = response.decodeToString()
|
|
||||||
val json = try {
|
val json = try {
|
||||||
JsonUtil.fromJson(bodyAsString, Map::class.java)
|
JsonUtil.fromJson(response, Map::class.java)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
mapOf( "result" to bodyAsString)
|
mapOf( "result" to response.decodeToString())
|
||||||
}
|
}
|
||||||
val base64EncodedIVAndCiphertext = json["result"] as? String ?: return deferred.reject(Exception("Invalid JSON"))
|
val base64EncodedIVAndCiphertext = json["result"] as? String ?: return deferred.reject(Exception("Invalid JSON"))
|
||||||
val ivAndCiphertext = Base64.decode(base64EncodedIVAndCiphertext)
|
val ivAndCiphertext = Base64.decode(base64EncodedIVAndCiphertext)
|
||||||
@ -624,7 +638,7 @@ object OnionRequestAPI {
|
|||||||
)
|
)
|
||||||
return deferred.reject(exception)
|
return deferred.reject(exception)
|
||||||
}
|
}
|
||||||
deferred.resolve(body)
|
deferred.resolve(OnionResponse(body, JsonUtil.toJson(body).toByteArray()))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (statusCode != 200) {
|
if (statusCode != 200) {
|
||||||
@ -635,7 +649,7 @@ object OnionRequestAPI {
|
|||||||
)
|
)
|
||||||
return deferred.reject(exception)
|
return deferred.reject(exception)
|
||||||
}
|
}
|
||||||
deferred.resolve(json)
|
deferred.resolve(OnionResponse(json, JsonUtil.toJson(json).toByteArray()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
@ -647,11 +661,15 @@ object OnionRequestAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
enum class Version(val value: String) {
|
|
||||||
V2("/loki/v2/lsrpc"),
|
|
||||||
V3("/loki/v3/lsrpc"),
|
|
||||||
V4("/oxen/v4/lsrpc");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class Version(val value: String) {
|
||||||
|
V2("/loki/v2/lsrpc"),
|
||||||
|
V3("/loki/v3/lsrpc"),
|
||||||
|
V4("/oxen/v4/lsrpc");
|
||||||
|
}
|
||||||
|
|
||||||
|
data class OnionResponse(
|
||||||
|
val info: Map<*, *>,
|
||||||
|
val body: ByteArray? = null
|
||||||
|
)
|
||||||
|
@ -35,12 +35,12 @@ object OnionRequestEncryption {
|
|||||||
internal fun encryptPayloadForDestination(
|
internal fun encryptPayloadForDestination(
|
||||||
payload: ByteArray,
|
payload: ByteArray,
|
||||||
destination: Destination,
|
destination: Destination,
|
||||||
version: OnionRequestAPI.Version
|
version: Version
|
||||||
): Promise<EncryptionResult, Exception> {
|
): Promise<EncryptionResult, Exception> {
|
||||||
val deferred = deferred<EncryptionResult, Exception>()
|
val deferred = deferred<EncryptionResult, Exception>()
|
||||||
ThreadUtils.queue {
|
ThreadUtils.queue {
|
||||||
try {
|
try {
|
||||||
val plaintext = if (version == OnionRequestAPI.Version.V4) {
|
val plaintext = if (version == Version.V4) {
|
||||||
payload
|
payload
|
||||||
} else {
|
} else {
|
||||||
// Wrapping isn't needed for file server or open group onion requests
|
// Wrapping isn't needed for file server or open group onion requests
|
||||||
|
@ -79,13 +79,15 @@ object SnodeAPI {
|
|||||||
snode: Snode,
|
snode: Snode,
|
||||||
parameters: Map<String, Any>,
|
parameters: Map<String, Any>,
|
||||||
publicKey: String? = null,
|
publicKey: String? = null,
|
||||||
version: OnionRequestAPI.Version = OnionRequestAPI.Version.V3
|
version: Version = Version.V3
|
||||||
): RawResponsePromise {
|
): RawResponsePromise {
|
||||||
val url = "${snode.address}:${snode.port}/storage_rpc/v1"
|
val url = "${snode.address}:${snode.port}/storage_rpc/v1"
|
||||||
|
val deferred = deferred<Map<*, *>, Exception>()
|
||||||
if (useOnionRequests) {
|
if (useOnionRequests) {
|
||||||
return OnionRequestAPI.sendOnionRequest(method, parameters, snode, version, publicKey)
|
OnionRequestAPI.sendOnionRequest(method, parameters, snode, version, publicKey).map {
|
||||||
|
deferred.resolve(JsonUtil.fromJson(it.body, Map::class.java))
|
||||||
|
}.fail { deferred.reject(it) }
|
||||||
} else {
|
} else {
|
||||||
val deferred = deferred<Map<*, *>, Exception>()
|
|
||||||
ThreadUtils.queue {
|
ThreadUtils.queue {
|
||||||
val payload = mapOf( "method" to method.rawValue, "params" to parameters )
|
val payload = mapOf( "method" to method.rawValue, "params" to parameters )
|
||||||
try {
|
try {
|
||||||
@ -102,8 +104,8 @@ object SnodeAPI {
|
|||||||
deferred.reject(exception)
|
deferred.reject(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return deferred.promise
|
|
||||||
}
|
}
|
||||||
|
return deferred.promise
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getRandomSnode(): Promise<Snode, Exception> {
|
internal fun getRandomSnode(): Promise<Snode, Exception> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user