2023-06-21 02:31:35 +02:00
|
|
|
package org.thoughtcrime.securesms.notifications
|
|
|
|
|
|
|
|
import com.goterl.lazysodium.LazySodiumAndroid
|
|
|
|
import com.goterl.lazysodium.SodiumAndroid
|
|
|
|
import com.goterl.lazysodium.interfaces.Sign
|
|
|
|
import com.goterl.lazysodium.utils.KeyPair
|
|
|
|
import kotlinx.serialization.encodeToString
|
|
|
|
import kotlinx.serialization.json.Json
|
|
|
|
import kotlinx.serialization.json.decodeFromStream
|
|
|
|
import nl.komponents.kovenant.Promise
|
|
|
|
import nl.komponents.kovenant.functional.map
|
|
|
|
import okhttp3.MediaType
|
|
|
|
import okhttp3.Request
|
|
|
|
import okhttp3.RequestBody
|
2023-07-26 03:18:20 +02:00
|
|
|
import org.session.libsession.messaging.sending_receiving.notifications.*
|
2023-06-21 02:31:35 +02:00
|
|
|
import org.session.libsession.snode.OnionRequestAPI
|
|
|
|
import org.session.libsession.snode.SnodeAPI
|
|
|
|
import org.session.libsession.snode.Version
|
2023-07-26 07:06:06 +02:00
|
|
|
import org.session.libsession.utilities.Device
|
2023-06-21 02:31:35 +02:00
|
|
|
import org.session.libsignal.utilities.Base64
|
|
|
|
import org.session.libsignal.utilities.Log
|
|
|
|
import org.session.libsignal.utilities.Namespace
|
|
|
|
import org.session.libsignal.utilities.retryIfNeeded
|
2023-07-26 03:18:20 +02:00
|
|
|
import javax.inject.Inject
|
|
|
|
import javax.inject.Singleton
|
2023-06-21 02:31:35 +02:00
|
|
|
|
|
|
|
private const val TAG = "PushManagerV2"
|
2023-07-26 03:18:20 +02:00
|
|
|
private const val maxRetryCount = 4
|
2023-06-21 02:31:35 +02:00
|
|
|
|
2023-07-26 03:18:20 +02:00
|
|
|
@Singleton
|
|
|
|
class PushManagerV2 @Inject constructor(private val pushHandler: PushHandler) {
|
2023-06-21 02:31:35 +02:00
|
|
|
private val sodium = LazySodiumAndroid(SodiumAndroid())
|
|
|
|
|
|
|
|
fun register(
|
2023-07-26 07:06:06 +02:00
|
|
|
device: Device,
|
2023-06-21 02:31:35 +02:00
|
|
|
token: String,
|
|
|
|
publicKey: String,
|
|
|
|
userEd25519Key: KeyPair,
|
|
|
|
namespaces: List<Int>
|
|
|
|
): Promise<SubscriptionResponse, Exception> {
|
2023-07-25 07:19:41 +02:00
|
|
|
val pnKey = pushHandler.getOrCreateNotificationKey()
|
2023-06-21 02:31:35 +02:00
|
|
|
|
|
|
|
val timestamp = SnodeAPI.nowWithOffset / 1000 // get timestamp in ms -> s
|
|
|
|
// if we want to support passing namespace list, here is the place to do it
|
|
|
|
val sigData = "MONITOR${publicKey}${timestamp}1${namespaces.joinToString(separator = ",")}".encodeToByteArray()
|
|
|
|
val signature = ByteArray(Sign.BYTES)
|
|
|
|
sodium.cryptoSignDetached(signature, sigData, sigData.size.toLong(), userEd25519Key.secretKey.asBytes)
|
|
|
|
val requestParameters = SubscriptionRequest(
|
|
|
|
pubkey = publicKey,
|
|
|
|
session_ed25519 = userEd25519Key.publicKey.asHexString,
|
|
|
|
namespaces = listOf(Namespace.DEFAULT),
|
|
|
|
data = true, // only permit data subscription for now (?)
|
2023-07-26 07:06:06 +02:00
|
|
|
service = device.service,
|
2023-06-21 02:31:35 +02:00
|
|
|
sig_ts = timestamp,
|
|
|
|
signature = Base64.encodeBytes(signature),
|
|
|
|
service_info = mapOf("token" to token),
|
|
|
|
enc_key = pnKey.asHexString,
|
|
|
|
).let(Json::encodeToString)
|
|
|
|
|
|
|
|
return retryResponseBody<SubscriptionResponse>("subscribe", requestParameters) success {
|
2023-06-23 02:59:49 +02:00
|
|
|
Log.d(TAG, "registerV2 success")
|
2023-06-21 02:31:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun unregister(
|
2023-07-26 07:06:06 +02:00
|
|
|
device: Device,
|
2023-06-21 02:31:35 +02:00
|
|
|
token: String,
|
|
|
|
userPublicKey: String,
|
|
|
|
userEdKey: KeyPair
|
|
|
|
): Promise<UnsubscribeResponse, Exception> {
|
|
|
|
val timestamp = SnodeAPI.nowWithOffset / 1000 // get timestamp in ms -> s
|
|
|
|
// if we want to support passing namespace list, here is the place to do it
|
|
|
|
val sigData = "UNSUBSCRIBE${userPublicKey}${timestamp}".encodeToByteArray()
|
|
|
|
val signature = ByteArray(Sign.BYTES)
|
|
|
|
sodium.cryptoSignDetached(signature, sigData, sigData.size.toLong(), userEdKey.secretKey.asBytes)
|
|
|
|
|
|
|
|
val requestParameters = UnsubscriptionRequest(
|
|
|
|
pubkey = userPublicKey,
|
|
|
|
session_ed25519 = userEdKey.publicKey.asHexString,
|
2023-07-26 07:06:06 +02:00
|
|
|
service = device.service,
|
2023-06-21 02:31:35 +02:00
|
|
|
sig_ts = timestamp,
|
|
|
|
signature = Base64.encodeBytes(signature),
|
|
|
|
service_info = mapOf("token" to token),
|
|
|
|
).let(Json::encodeToString)
|
|
|
|
|
|
|
|
return retryResponseBody<UnsubscribeResponse>("unsubscribe", requestParameters) success {
|
2023-06-23 02:59:49 +02:00
|
|
|
Log.d(TAG, "unregisterV2 success")
|
2023-06-21 02:31:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private inline fun <reified T: Response> retryResponseBody(path: String, requestParameters: String): Promise<T, Exception> =
|
2023-07-26 03:18:20 +02:00
|
|
|
retryIfNeeded(maxRetryCount) { getResponseBody(path, requestParameters) }
|
2023-06-21 02:31:35 +02:00
|
|
|
|
|
|
|
private inline fun <reified T: Response> getResponseBody(path: String, requestParameters: String): Promise<T, Exception> {
|
2023-06-23 02:59:49 +02:00
|
|
|
val server = Server.LATEST
|
|
|
|
val url = "${server.url}/$path"
|
2023-06-21 02:31:35 +02:00
|
|
|
val body = RequestBody.create(MediaType.get("application/json"), requestParameters)
|
|
|
|
val request = Request.Builder().url(url).post(body).build()
|
|
|
|
|
|
|
|
return OnionRequestAPI.sendOnionRequest(
|
|
|
|
request,
|
2023-06-23 02:59:49 +02:00
|
|
|
server.url,
|
|
|
|
server.publicKey,
|
2023-06-21 02:31:35 +02:00
|
|
|
Version.V4
|
|
|
|
).map { response ->
|
|
|
|
response.body!!.inputStream()
|
|
|
|
.let { Json.decodeFromStream<T>(it) }
|
|
|
|
.also { if (it.isFailure()) throw Exception("error: ${it.message}.") }
|
|
|
|
}
|
|
|
|
}
|
2023-06-23 02:59:49 +02:00
|
|
|
}
|