From f015339fc57dc7ef13cf02acdcca3082e89fffc0 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Fri, 22 May 2020 10:41:31 +1000 Subject: [PATCH] Fix simultaneous session request bug --- .../securesms/ApplicationContext.java | 1 + .../database/helpers/SQLCipherOpenHelper.java | 12 ++++++-- .../loki/database/LokiAPIDatabase.kt | 28 +++++++++++++++---- .../loki/protocol/ClosedGroupsProtocol.kt | 2 ++ .../protocol/SessionManagementProtocol.kt | 5 ++++ 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 3af959795f..3d533ddb72 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -567,6 +567,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc @Override public void sendSessionRequest(@NotNull String publicKey) { + DatabaseFactory.getLokiAPIDatabase(this).setSessionRequestTimestamp(publicKey, new Date().getTime()); EphemeralMessage sessionRequest = EphemeralMessage.createSessionRequest(publicKey); jobManager.add(new PushEphemeralMessageSendJob(sessionRequest)); } diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index f922df9c7f..e47442c04c 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -35,11 +35,11 @@ import org.thoughtcrime.securesms.database.StickerDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; -import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; +import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase; import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase; +import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.service.KeyCachingService; @@ -81,8 +81,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV5 = 26; private static final int lokiV6 = 27; private static final int lokiV7 = 28; + private static final int lokiV8 = 29; - private static final int DATABASE_VERSION = lokiV7; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes + private static final int DATABASE_VERSION = lokiV8; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -138,6 +139,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); db.execSQL(LokiAPIDatabase.getCreateDeviceLinkTableCommand()); db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand()); + db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampTableCommand()); db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand()); db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand()); db.execSQL(LokiMessageDatabase.getCreateMessageFriendRequestTableCommand()); @@ -576,6 +578,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand()); } + if (oldVersion < lokiV8) { + db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampTableCommand()); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt index 14eee137f6..eee56c34d8 100644 --- a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt +++ b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt @@ -32,10 +32,10 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( private val receivedMessageHashValues = "received_message_hash_values" @JvmStatic val createReceivedMessageHashValuesTableCommand = "CREATE TABLE $receivedMessageHashValuesCache ($userID TEXT PRIMARY KEY, $receivedMessageHashValues TEXT);" // Group chat auth token cache - private val groupChatAuthTokenTable = "loki_api_group_chat_auth_token_database" + private val groupChatAuthTokenCache = "loki_api_group_chat_auth_token_database" private val server = "server" private val token = "token" - @JvmStatic val createGroupChatAuthTokenTableCommand = "CREATE TABLE $groupChatAuthTokenTable ($server TEXT PRIMARY KEY, $token TEXT);" + @JvmStatic val createGroupChatAuthTokenTableCommand = "CREATE TABLE $groupChatAuthTokenCache ($server TEXT PRIMARY KEY, $token TEXT);" // Last message server ID cache private val lastMessageServerIDCache = "loki_api_last_message_server_id_cache" private val lastMessageServerIDCacheIndex = "loki_api_last_message_server_id_cache_index" @@ -59,6 +59,11 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( private val publicChatID = "public_chat_id" private val userCount = "user_count" @JvmStatic val createUserCountTableCommand = "CREATE TABLE $userCountCache ($publicChatID STRING PRIMARY KEY, $userCount INTEGER DEFAULT 0);" + // Session request timestamp cache + private val sessionRequestTimestampCache = "session_request_timestamp_cache" + private val publicKey = "public_key" + private val timestamp = "timestamp" + @JvmStatic val createSessionRequestTimestampTableCommand = "CREATE TABLE $sessionRequestTimestampCache ($publicKey STRING PRIMARY KEY, $timestamp INTEGER DEFAULT 0);" } override fun getSwarmCache(hexEncodedPublicKey: String): Set? { @@ -120,7 +125,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( override fun getAuthToken(server: String): String? { val database = databaseHelper.readableDatabase - return database.get(groupChatAuthTokenTable, "${Companion.server} = ?", wrap(server)) { cursor -> + return database.get(groupChatAuthTokenCache, "${Companion.server} = ?", wrap(server)) { cursor -> cursor.getString(cursor.getColumnIndexOrThrow(token)) } } @@ -129,9 +134,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( val database = databaseHelper.writableDatabase if (newValue != null) { val row = wrap(mapOf(Companion.server to server, token to newValue)) - database.insertOrUpdate(groupChatAuthTokenTable, row, "${Companion.server} = ?", wrap(server)) + database.insertOrUpdate(groupChatAuthTokenCache, row, "${Companion.server} = ?", wrap(server)) } else { - database.delete(groupChatAuthTokenTable, "${Companion.server} = ?", wrap(server)) + database.delete(groupChatAuthTokenCache, "${Companion.server} = ?", wrap(server)) } } @@ -222,6 +227,19 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( val row = wrap(mapOf(publicChatID to index, Companion.userCount to userCount.toString())) database.insertOrUpdate(userCountCache, row, "$publicChatID = ?", wrap(index)) } + + fun getSessionRequestTimestamp(publicKey: String): Long? { + val database = databaseHelper.readableDatabase + return database.get(sessionRequestTimestampCache, "$LokiAPIDatabase.publicKey = ?", wrap(publicKey)) { cursor -> + cursor.getInt(LokiAPIDatabase.timestamp) + }?.toLong() + } + + fun setSessionRequestTimestamp(publicKey: String, timestamp: Long) { + val database = databaseHelper.writableDatabase + val row = wrap(mapOf(LokiAPIDatabase.publicKey to publicKey, LokiAPIDatabase.timestamp to timestamp.toString())) + database.insertOrUpdate(sessionRequestTimestampCache, row, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) + } } // region Convenience diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index b381b45a2a..05ea5ee6eb 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -15,6 +15,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceGroup import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol +import java.util.* object ClosedGroupsProtocol { @@ -96,6 +97,7 @@ object ClosedGroupsProtocol { val deviceAsAddress = SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID) val hasSession = TextSecureSessionStore(context).containsSession(deviceAsAddress) if (hasSession) { continue } + DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestTimestamp(device, Date().time) val sessionRequest = EphemeralMessage.createSessionRequest(device) ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRequest)) } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt index 0e96f90c49..61a1679883 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt @@ -76,6 +76,11 @@ object SessionManagementProtocol { @JvmStatic fun handleSessionRequestIfNeeded(context: Context, content: SignalServiceContent) { if (!content.dataMessage.isPresent || !content.dataMessage.get().isSessionRequest) { return } + val sentSessionRequestTimestamp = DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestTimestamp(content.sender) + if (sentSessionRequestTimestamp != null && content.timestamp < sentSessionRequestTimestamp) { + // We sent a session request after this one was sent + return + } // Auto-accept all session requests val ephemeralMessage = EphemeralMessage.create(content.sender) ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))