From 443cdfa4ebc41675bfb1905c2024f3b6ee288f87 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Fri, 4 Sep 2020 15:55:32 +1000 Subject: [PATCH] enable push notification for shared sender keys --- .../loki/api/LokiPushNotificationManager.kt | 89 ++++++++++++++++++- .../loki/protocol/ClosedGroupsProtocol.kt | 13 ++- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt b/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt index 39a9c46078..e59502174d 100644 --- a/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt +++ b/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt @@ -2,11 +2,18 @@ package org.thoughtcrime.securesms.loki.api import android.content.Context import okhttp3.* +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.BaseJob +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob import org.thoughtcrime.securesms.util.TextSecurePreferences import org.whispersystems.libsignal.logging.Log import org.whispersystems.signalservice.internal.util.JsonUtil import org.whispersystems.signalservice.loki.api.PushNotificationAcknowledgement import java.io.IOException +import java.lang.Exception +import java.util.concurrent.TimeUnit object LokiPushNotificationManager { private val connection = OkHttpClient() @@ -15,11 +22,14 @@ object LokiPushNotificationManager { PushNotificationAcknowledgement.shared.server } + public const val subscribe = "subscribe_closed_group" + public const val unsubscribe = "unsubscribe_closed_group" + private const val tokenExpirationInterval = 12 * 60 * 60 * 1000 @JvmStatic fun unregister(token: String, context: Context?) { - val parameters = mapOf( "token" to token ) + val parameters = mapOf("token" to token) val url = "$server/register" val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) val request = Request.Builder().url(url).post(body).build() @@ -30,7 +40,7 @@ object LokiPushNotificationManager { 200 -> { val bodyAsString = response.body()!!.string() val json = JsonUtil.fromJson(bodyAsString, Map::class.java) - val code = json?.get("code") as? Int + val code = json?.get("code") as? Int if (code != null && code != 0) { TextSecurePreferences.setIsUsingFCM(context, false) } else { @@ -44,6 +54,10 @@ object LokiPushNotificationManager { Log.d("Loki", "Couldn't disable FCM.") } }) + + for (closedGroup: String in DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys()) { + operateClosedGroup(closedGroup, TextSecurePreferences.getLocalNumber(context), context, unsubscribe) + } } @JvmStatic @@ -51,7 +65,7 @@ object LokiPushNotificationManager { val oldToken = TextSecurePreferences.getFCMToken(context) val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context) if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return } - val parameters = mapOf( "token" to token, "pubKey" to publicKey ) + val parameters = mapOf("token" to token, "pubKey" to publicKey) val url = "$server/register" val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) val request = Request.Builder().url(url).post(body).build() @@ -62,7 +76,7 @@ object LokiPushNotificationManager { 200 -> { val bodyAsString = response.body()!!.string() val json = JsonUtil.fromJson(bodyAsString, Map::class.java) - val code = json?.get("code") as? Int + val code = json?.get("code") as? Int if (code != null && code != 0) { TextSecurePreferences.setIsUsingFCM(context, true) TextSecurePreferences.setFCMToken(context, token) @@ -78,5 +92,72 @@ object LokiPushNotificationManager { Log.d("Loki", "Couldn't register for FCM.") } }) + + for (closedGroup: String in DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys()) { + operateClosedGroup(closedGroup, publicKey, context, subscribe) + } + + } + + @JvmStatic + fun operateClosedGroup(closedGroupPublicKey: String, publicKey: String, context: Context?, operation: String) { + Log.d("Loki", "Start to notify PN server of closed group.") + if (!TextSecurePreferences.isUsingFCM(context)) { return } + val parameters = mapOf("closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey) + val url = "$server/$operation" + val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) + val request = Request.Builder().url(url).post(body).build() + connection.newCall(request).enqueue(object : Callback { + + override fun onResponse(call: Call, response: Response) { + when (response.code()) { + 200 -> { + val bodyAsString = response.body()!!.string() + val json = JsonUtil.fromJson(bodyAsString, Map::class.java) + val code = json?.get("code") as? Int + if (code == null || code == 0) { + Log.d("Loki", "Couldn't subscribe/unsubscribe $closedGroupPublicKey due to error: ${json?.get("message") as? String ?: "null"}.") + } else { + Log.d("Loki", "Subscribe/unsubscribe success.") + } + } + } + } + + override fun onFailure(call: Call, exception: IOException) { + Log.d("Loki", "Couldn't subscribe/unsubscribe $closedGroupPublicKey.") + } + }) } } + +class ClosedGroupSubscribeJob private constructor(parameters: Parameters, private val closedGroupPublicKey: String) : BaseJob(parameters) { + + companion object { + const val KEY = "ClosedGroupSubscribeJob" + } + + constructor(closedGroupPublicKey: String) : this(Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue(KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(1) + .build(), + closedGroupPublicKey) + + override fun serialize(): Data { + val builder = Data.Builder() + builder.putString("closedGroupPublicKey", closedGroupPublicKey) + return builder.build() + } + + override fun getFactoryKey(): String { return KEY } + + override fun onCanceled() { } + + public override fun onRun() { + LokiPushNotificationManager.operateClosedGroup(closedGroupPublicKey, TextSecurePreferences.getLocalNumber(context), context, LokiPushNotificationManager.subscribe) + } + + override fun onShouldRetry(e: Exception): Boolean { return false } +} diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index 0841aaf2a6..aa5d282792 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -8,6 +8,8 @@ import nl.komponents.kovenant.deferred import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.api.ClosedGroupSubscribeJob +import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager import org.thoughtcrime.securesms.loki.utilities.recipient import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage import org.thoughtcrime.securesms.recipients.Recipient @@ -33,7 +35,7 @@ import java.io.IOException import java.util.* object ClosedGroupsProtocol { - val isSharedSenderKeysEnabled = false + val isSharedSenderKeysEnabled = true val groupSizeLimit = 10 public fun createClosedGroup(context: Context, name: String, members: Collection): Promise { @@ -70,6 +72,10 @@ object ClosedGroupsProtocol { } // Add the group to the user's set of public keys to poll for DatabaseFactory.getSSKDatabase(context).setClosedGroupPrivateKey(groupPublicKey, groupKeyPair.hexEncodedPrivateKey) + // Notify PN server + val jobForPN = ClosedGroupSubscribeJob(groupPublicKey) + jobForPN.setContext(context) + jobForPN.onRun() // Notify the user val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID) @@ -137,6 +143,8 @@ object ClosedGroupsProtocol { sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) groupDB.setActive(groupID, false) groupDB.remove(groupID, Address.fromSerialized(userPublicKey)) + // Notify PN server + LokiPushNotificationManager.operateClosedGroup(groupPublicKey, userPublicKey, context, LokiPushNotificationManager.unsubscribe) } else { // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, members) @@ -231,6 +239,7 @@ object ClosedGroupsProtocol { public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { // Prepare + val userPublicKey = TextSecurePreferences.getLocalNumber(context) val sskDatabase = DatabaseFactory.getSSKDatabase(context) // Unwrap the message val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() @@ -258,6 +267,8 @@ object ClosedGroupsProtocol { insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, members) + // Notify PN server + LokiPushNotificationManager.operateClosedGroup(groupPublicKey, userPublicKey, context, LokiPushNotificationManager.subscribe) } public fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) {