feat: add open group v2 storage and db methods, starting on new open group v2 poller

This commit is contained in:
jubb
2021-04-13 17:17:16 +10:00
parent 201dde7412
commit 0eadc55325
12 changed files with 485 additions and 115 deletions

View File

@@ -14,6 +14,7 @@ import org.session.libsession.messaging.messages.signal.IncomingTextMessage
import org.session.libsession.messaging.messages.visible.Attachment
import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.opengroups.OpenGroup
import org.session.libsession.messaging.opengroups.OpenGroupV2
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview
@@ -221,6 +222,21 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(server, null)
}
override fun getAuthToken(room: String, server: String): String? {
val id = "$server.$room"
return DatabaseFactory.getLokiAPIDatabase(context).getAuthToken(id)
}
override fun setAuthToken(room: String, server: String, newValue: String) {
val id = "$server.$room"
DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(id, newValue)
}
override fun removeAuthToken(room: String, server: String) {
val id = "$server.$room"
DatabaseFactory.getLokiAPIDatabase(context).setAuthToken(id, null)
}
override fun getOpenGroup(threadID: String): OpenGroup? {
if (threadID.toInt() < 0) { return null }
val database = databaseHelper.readableDatabase
@@ -230,6 +246,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
}
}
override fun getV2OpenGroup(threadId: String): OpenGroupV2? {
if (threadId.toInt() < 0) { return null }
val database = databaseHelper.readableDatabase
return database.get(LokiThreadDatabase.publicChatTable, "${LokiThreadDatabase.threadID} = ?", arrayOf(threadId)) { cursor ->
val publicChatAsJson = cursor.getString(LokiThreadDatabase.publicChat)
OpenGroupV2.fromJson(publicChatAsJson)
}
}
override fun getThreadID(openGroupID: String): String {
val address = Address.fromSerialized(openGroupID)
val recipient = Recipient.from(context, address, false)
@@ -254,6 +279,18 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(groupID, publicKey)
}
override fun getLastMessageServerId(room: String, server: String): Long? {
return DatabaseFactory.getLokiAPIDatabase(context).getLastMessageServerID(room, server)
}
override fun setLastMessageServerId(room: String, server: String, newValue: Long) {
DatabaseFactory.getLokiAPIDatabase(context).setLastMessageServerID(room, server, newValue)
}
override fun removeLastMessageServerId(room: String, server: String) {
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(room, server)
}
override fun getLastMessageServerID(group: Long, server: String): Long? {
return DatabaseFactory.getLokiAPIDatabase(context).getLastMessageServerID(group, server)
}
@@ -266,6 +303,18 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(group, server)
}
override fun getLastDeletionServerId(room: String, server: String): Long? {
TODO("Not yet implemented")
}
override fun setLastDeletionServerId(room: String, server: String, newValue: Long) {
TODO("Not yet implemented")
}
override fun removeLastDeletionServerId(room: String, server: String) {
TODO("Not yet implemented")
}
override fun getLastDeletionServerID(group: Long, server: String): Long? {
return DatabaseFactory.getLokiAPIDatabase(context).getLastDeletionServerID(group, server)
}
@@ -471,6 +520,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
}
}
override fun getAllV2OpenGroups(): Map<Long, OpenGroupV2> {
return DatabaseFactory.getLokiThreadDatabase(context).getAllV2OpenGroups()
}
override fun addOpenGroup(server: String, channel: Long) {
OpenGroupUtilities.addGroup(context, server, channel)
}

View File

@@ -24,7 +24,7 @@ class PublicChatManager(private val context: Context) {
private val pollers = mutableMapOf<Long, OpenGroupPoller>()
private val observers = mutableMapOf<Long, ContentObserver>()
private var isPolling = false
private val executorService = Executors.newScheduledThreadPool(16)
private val executorService = Executors.newScheduledThreadPool(4)
public fun areAllCaughtUp(): Boolean {
var areAllCaughtUp = true

View File

@@ -286,6 +286,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
}?.toLong()
}
override fun getLastMessageServerID(room: String, server: String): Long? {
val database = databaseHelper.writableDatabase
val index = "$server.$room"
return database.get(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index)) { cursor ->
cursor.getInt(lastMessageServerID)
}?.toLong()
}
override fun setLastMessageServerID(group: Long, server: String, newValue: Long) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
@@ -293,12 +301,25 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index))
}
override fun setLastMessageServerID(room: String, server: String, newValue: Long) {
val database = databaseHelper.writableDatabase
val index = "$server.$room"
val row = wrap(mapOf( lastMessageServerIDTableIndex to index, lastMessageServerID to newValue.toString() ))
database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index))
}
fun removeLastMessageServerID(group: Long, server: String) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
database.delete(lastMessageServerIDTable,"$lastMessageServerIDTableIndex = ?", wrap(index))
}
fun removeLastMessageServerID(room: String, server:String) {
val database = databaseHelper.writableDatabase
val index = "$server.$channel"
database.delete(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index))
}
override fun getLastDeletionServerID(group: Long, server: String): Long? {
val database = databaseHelper.readableDatabase
val index = "$server.$group"
@@ -307,6 +328,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
}?.toLong()
}
override fun getLastDeletionServerID(room: String, server: String): Long? {
val database = databaseHelper.readableDatabase
val index = "$server.$room"
return database.get(lastDeletionServerIDTable, "$lastDeletionServerIDTableIndex = ?", wrap(index)) { cursor ->
cursor.getInt(lastDeletionServerID)
}?.toLong()
}
override fun setLastDeletionServerID(group: Long, server: String, newValue: Long) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
@@ -314,6 +343,13 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.insertOrUpdate(lastDeletionServerIDTable, row, "$lastDeletionServerIDTableIndex = ?", wrap(index))
}
override fun setLastDeletionServerID(room: String, server: String, newValue: Long) {
val database = databaseHelper.writableDatabase
val index = "$server.$room"
val row = wrap(mapOf(lastDeletionServerIDTableIndex to index, lastDeletionServerID to newValue.toString()))
database.insertOrUpdate(lastDeletionServerIDTable, row, "$lastDeletionServerIDTableIndex = ?", wrap(index))
}
fun removeLastDeletionServerID(group: Long, server: String) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
@@ -328,6 +364,14 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
}?.toInt()
}
fun getUserCount(room: String, server: String): Int? {
val database = databaseHelper.readableDatabase
val index = "$server.$room"
return database.get(userCountTable, "$publicChatID = ?", wrap(index)) { cursor ->
cursor.getInt(userCount)
}?.toInt()
}
override fun setUserCount(group: Long, server: String, newValue: Int) {
val database = databaseHelper.writableDatabase
val index = "$server.$group"
@@ -335,6 +379,13 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.insertOrUpdate(userCountTable, row, "$publicChatID = ?", wrap(index))
}
override fun setUserCount(room: String, server: String, newValue: Int) {
val database = databaseHelper.writableDatabase
val index = "$server.$room"
val row = wrap(mapOf( publicChatID to index, userCount to newValue.toString() ))
database.insertOrUpdate(userCountTable, row, "$publicChatID = ?", wrap(index))
}
override fun getSessionRequestSentTimestamp(publicKey: String): Long? {
val database = databaseHelper.readableDatabase
return database.get(sessionRequestSentTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor ->

View File

@@ -3,20 +3,18 @@ package org.thoughtcrime.securesms.loki.database
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import org.session.libsession.messaging.opengroups.OpenGroupV2
import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsignal.service.loki.api.opengroups.PublicChat
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol
import org.session.libsignal.utilities.JsonUtil
import org.thoughtcrime.securesms.database.Database
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.utilities.*
import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.service.loki.api.opengroups.PublicChat
import org.session.libsignal.utilities.JsonUtil
import org.session.libsignal.service.loki.database.LokiThreadDatabaseProtocol
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.loki.utilities.get
import org.thoughtcrime.securesms.loki.utilities.getString
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol {
@@ -57,6 +55,26 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa
return result
}
fun getAllV2OpenGroups(): Map<Long, OpenGroupV2> {
val database = databaseHelper.readableDatabase
var cursor: Cursor? = null
val result = mutableMapOf<Long, OpenGroupV2>()
try {
cursor = database.rawQuery("select * from $publicChatTable", null)
while (cursor != null && cursor.moveToNext()) {
val threadID = cursor.getLong(threadID)
val string = cursor.getString(publicChat)
val openGroup = OpenGroupV2.fromJson(string)
if (openGroup != null) result[threadID] = openGroup
}
} catch (e: Exception) {
// do nothing
} finally {
cursor?.close()
}
return result
}
fun getAllPublicChatServers(): Set<String> {
return getAllPublicChats().values.fold(setOf()) { set, chat -> set.plus(chat.server) }
}

View File

@@ -1,25 +1,12 @@
package org.thoughtcrime.securesms.loki.protocol
import android.content.Context
import com.google.protobuf.ByteString
import org.session.libsession.messaging.MessagingConfiguration
import org.session.libsession.messaging.messages.Destination
import org.session.libsession.messaging.messages.control.ConfigurationMessage
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.preferences.ProfileKeyUtil
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Hex
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
object MultiDeviceProtocol {
@@ -51,80 +38,4 @@ object MultiDeviceProtocol {
TextSecurePreferences.setLastConfigurationSyncTime(context, System.currentTimeMillis())
}
// TODO: remove this after we migrate to new message receiving pipeline
@JvmStatic
fun handleConfigurationMessage(context: Context, content: SignalServiceProtos.Content, senderPublicKey: String, timestamp: Long) {
synchronized(this) {
val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return
if (TextSecurePreferences.getConfigurationMessageSynced(context) && !TextSecurePreferences.shouldUpdateProfile(context, timestamp)) return
if (senderPublicKey != userPublicKey) return
TextSecurePreferences.setConfigurationMessageSynced(context, true)
TextSecurePreferences.setLastProfileUpdateTime(context, timestamp)
val configurationMessage = ConfigurationMessage.fromProto(content) ?: return
val storage = MessagingConfiguration.shared.storage
val allClosedGroupPublicKeys = storage.getAllClosedGroupPublicKeys()
val threadDatabase = DatabaseFactory.getThreadDatabase(context)
val recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
val ourRecipient = Recipient.from(context, Address.fromSerialized(userPublicKey),false)
for (closedGroup in configurationMessage.closedGroups) {
if (allClosedGroupPublicKeys.contains(closedGroup.publicKey)) continue
val closedGroupUpdate = DataMessage.ClosedGroupControlMessage.newBuilder()
closedGroupUpdate.type = DataMessage.ClosedGroupControlMessage.Type.NEW
closedGroupUpdate.publicKey = ByteString.copyFrom(Hex.fromStringCondensed(closedGroup.publicKey))
closedGroupUpdate.name = closedGroup.name
val encryptionKeyPair = SignalServiceProtos.KeyPair.newBuilder()
encryptionKeyPair.publicKey = ByteString.copyFrom(closedGroup.encryptionKeyPair!!.publicKey.serialize().removing05PrefixIfNeeded())
encryptionKeyPair.privateKey = ByteString.copyFrom(closedGroup.encryptionKeyPair!!.privateKey.serialize())
closedGroupUpdate.encryptionKeyPair = encryptionKeyPair.build()
closedGroupUpdate.addAllMembers(closedGroup.members.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
closedGroupUpdate.addAllAdmins(closedGroup.admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
ClosedGroupsProtocolV2.handleNewClosedGroup(context, closedGroupUpdate.build(), userPublicKey, timestamp)
}
val allOpenGroups = storage.getAllOpenGroups().map { it.value.server }
for (openGroup in configurationMessage.openGroups) {
if (allOpenGroups.contains(openGroup)) continue
OpenGroupUtilities.addGroup(context, openGroup, 1)
}
if (configurationMessage.displayName.isNotEmpty()) {
TextSecurePreferences.setProfileName(context, configurationMessage.displayName)
recipientDatabase.setProfileName(ourRecipient, configurationMessage.displayName)
}
if (configurationMessage.profileKey.isNotEmpty()) {
val profileKey = Base64.encodeBytes(configurationMessage.profileKey)
ProfileKeyUtil.setEncodedProfileKey(context, profileKey)
recipientDatabase.setProfileKey(ourRecipient, configurationMessage.profileKey)
if (!configurationMessage.profilePicture.isNullOrEmpty() && TextSecurePreferences.getProfilePictureURL(context) != configurationMessage.profilePicture) {
TextSecurePreferences.setProfilePictureURL(context, configurationMessage.profilePicture)
ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, configurationMessage.profilePicture))
}
}
for (contact in configurationMessage.contacts) {
val address = Address.fromSerialized(contact.publicKey)
val recipient = Recipient.from(context, address, true)
if (!contact.profilePicture.isNullOrEmpty()) {
recipientDatabase.setProfileAvatar(recipient, contact.profilePicture)
}
if (contact.profileKey?.isNotEmpty() == true) {
recipientDatabase.setProfileKey(recipient, contact.profileKey)
}
if (contact.name.isNotEmpty()) {
recipientDatabase.setProfileName(recipient, contact.name)
}
recipientDatabase.setProfileSharing(recipient, true)
recipientDatabase.setRegistered(recipient, Recipient.RegisteredState.REGISTERED)
// create Thread if needed
threadDatabase.getOrCreateThreadIdFor(recipient)
}
if (configurationMessage.contacts.isNotEmpty()) {
threadDatabase.notifyUpdatedFromConfig()
}
}
}
}