fix: fixing up LokiMessageDatabase.kt table structure, deletion closer to finished

This commit is contained in:
jubb 2021-04-29 17:13:42 +10:00
parent 51554f1cdf
commit ef19c0d10e
15 changed files with 138 additions and 52 deletions

View File

@ -137,9 +137,19 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
return openGroupMessagingDatabase.getMessageID(serverID) return openGroupMessagingDatabase.getMessageID(serverID)
} }
override fun deleteMessage(messageID: Long) { override fun getMessageID(serverId: Long, threadId: Long): Pair<Long, Boolean>? {
val messagingDatabase = DatabaseFactory.getSmsDatabase(context) val messageDB = DatabaseFactory.getLokiMessageDatabase(context)
messagingDatabase.deleteMessage(messageID) return messageDB.getMessageID(serverId, threadId)
}
override fun deleteMessage(messageID: Long, isSms: Boolean) {
if (isSms) {
val db = DatabaseFactory.getSmsDatabase(context)
db.deleteMessage(messageID)
} else {
val db = DatabaseFactory.getMmsDatabase(context)
db.delete(messageID)
}
} }
override fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? { override fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? {

View File

@ -1718,7 +1718,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
boolean initiating = threadId == -1; boolean initiating = threadId == -1;
boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize; boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize;
boolean isMediaMessage = attachmentManager.isAttachmentPresent() || boolean isMediaMessage = attachmentManager.isAttachmentPresent() ||
recipient.isGroupRecipient() || // recipient.isGroupRecipient() ||
inputPanel.getQuote().isPresent() || inputPanel.getQuote().isPresent() ||
linkPreviewViewModel.hasLinkPreview() || linkPreviewViewModel.hasLinkPreview() ||
LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages

View File

@ -533,7 +533,7 @@ public class ConversationFragment extends Fragment
boolean isSentByUser = true; boolean isSentByUser = true;
for (MessageRecord messageRecord : messageRecords) { for (MessageRecord messageRecord : messageRecords) {
isSentByUser = isSentByUser && messageRecord.isOutgoing(); isSentByUser = isSentByUser && messageRecord.isOutgoing();
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms());
if (serverID != null) { if (serverID != null) {
serverIDs.add(serverID); serverIDs.add(serverID);
} else { } else {
@ -545,7 +545,7 @@ public class ConversationFragment extends Fragment
.deleteMessages(serverIDs, publicChat.getChannel(), publicChat.getServer(), isSentByUser) .deleteMessages(serverIDs, publicChat.getChannel(), publicChat.getServer(), isSentByUser)
.success(l -> { .success(l -> {
for (MessageRecord messageRecord : messageRecords) { for (MessageRecord messageRecord : messageRecords) {
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms());
if (l.contains(serverID)) { if (l.contains(serverID)) {
if (messageRecord.isMms()) { if (messageRecord.isMms()) {
DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId()); DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId());
@ -568,7 +568,7 @@ public class ConversationFragment extends Fragment
.deleteMessage(serverId, openGroupChat.getRoom(), openGroupChat.getServer()) .deleteMessage(serverId, openGroupChat.getRoom(), openGroupChat.getServer())
.success(l -> { .success(l -> {
for (MessageRecord messageRecord : messageRecords) { for (MessageRecord messageRecord : messageRecords) {
Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms());
if (serverID != null && serverID.equals(serverId)) { if (serverID != null && serverID.equals(serverId)) {
if (messageRecord.isMms()) { if (messageRecord.isMms()) {
DatabaseFactory.getMmsDatabase(getContext()).delete(messageRecord.getId()); DatabaseFactory.getMmsDatabase(getContext()).delete(messageRecord.getId());

View File

@ -388,8 +388,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return database.getMessageFor(timestamp, address)?.getId() return database.getMessageFor(timestamp, address)?.getId()
} }
override fun setOpenGroupServerMessageID(messageID: Long, serverID: Long) { override fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean) {
DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, serverID) DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, serverID, isSms)
DatabaseFactory.getLokiMessageDatabase(context).setOriginalThreadID(messageID, serverID, threadID)
} }
override fun getQuoteServerID(quoteID: Long, publicKey: String): Long? { override fun getQuoteServerID(quoteID: Long, publicKey: String): Long? {

View File

@ -54,9 +54,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int lokiV20 = 41; private static final int lokiV20 = 41;
private static final int lokiV21 = 42; private static final int lokiV21 = 42;
private static final int lokiV22 = 43; private static final int lokiV22 = 43;
private static final int lokiV23 = 44;
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
private static final int DATABASE_VERSION = lokiV22; private static final int DATABASE_VERSION = lokiV23;
private static final String DATABASE_NAME = "signal.db"; private static final String DATABASE_NAME = "signal.db";
private final Context context; private final Context context;
@ -124,6 +125,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand());
db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand());
db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand()); db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand());
db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType());
db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable());
executeStatements(db, SmsDatabase.CREATE_INDEXS); executeStatements(db, SmsDatabase.CREATE_INDEXS);
executeStatements(db, MmsDatabase.CREATE_INDEXS); executeStatements(db, MmsDatabase.CREATE_INDEXS);
@ -272,6 +275,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
"SendDeliveryReceiptJob"); "SendDeliveryReceiptJob");
} }
if (oldVersion < lokiV23) {
db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType());
db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable());
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();

View File

@ -6,11 +6,8 @@ import org.session.libsession.messaging.threads.Address
import org.thoughtcrime.securesms.database.Database import org.thoughtcrime.securesms.database.Database
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.utilities.get
import org.thoughtcrime.securesms.loki.utilities.getInt
import org.thoughtcrime.securesms.loki.utilities.getString
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol
import org.thoughtcrime.securesms.loki.utilities.*
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol { class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
@ -23,14 +20,21 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
private val friendRequestStatus = "friend_request_status" private val friendRequestStatus = "friend_request_status"
private val threadID = "thread_id" private val threadID = "thread_id"
private val errorMessage = "error_message" private val errorMessage = "error_message"
private val messageType = "message_type"
@JvmStatic val createMessageIDTableCommand = "CREATE TABLE $messageIDTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);" @JvmStatic val createMessageIDTableCommand = "CREATE TABLE $messageIDTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);"
@JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);" @JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);"
@JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);" @JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);"
@JvmStatic val updateMessageIDTableForType = "ALTER TABLE $messageIDTable ADD COLUMN $messageType INTEGER DEFAULT 0; ALTER TABLE $messageIDTable ADD CONSTRAINT PK_$messageIDTable PRIMARY KEY ($messageID, $serverID);"
@JvmStatic val updateMessageMappingTable = "ALTER TABLE $messageThreadMappingTable ADD COLUMN $serverID INTEGER DEFAULT 0; ALTER TABLE $messageThreadMappingTable ADD CONSTRAINT PK_$messageThreadMappingTable PRIMARY KEY ($messageID, $serverID);"
const val SMS_TYPE = 0
const val MMS_TYPE = 1
} }
override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? { override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? {
val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, quoteePublicKey) val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, quoteePublicKey)
return if (message != null) getServerID(message.getId()) else null return if (message != null) getServerID(message.getId(), !message.isMms) else null
} }
fun getServerID(messageID: Long): Long? { fun getServerID(messageID: Long): Long? {
@ -40,6 +44,13 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
}?.toLong() }?.toLong()
} }
fun getServerID(messageID: Long, isSms: Boolean): Long? {
val database = databaseHelper.readableDatabase
return database.get(messageIDTable, "${Companion.messageID} = ? AND $messageType = ?", arrayOf( messageID.toString(), if (isSms) SMS_TYPE.toString() else MMS_TYPE.toString() )) { cursor ->
cursor.getInt(serverID)
}?.toLong()
}
fun getMessageID(serverID: Long): Long? { fun getMessageID(serverID: Long): Long? {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor -> return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor ->
@ -47,12 +58,22 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
}?.toLong() }?.toLong()
} }
override fun setServerID(messageID: Long, serverID: Long) { fun getMessageID(serverID: Long, threadID: Long): Pair<Long,Boolean>? {
val database = databaseHelper.readableDatabase
return database.get("$messageIDTable INNER JOIN $messageThreadMappingTable ON $messageIDTable.$messageID = $messageThreadMappingTable.$messageID",
"${Companion.serverID} = ? AND ${Companion.threadID} = ?",
arrayOf(serverID.toString(),threadID.toString())) { cursor ->
cursor.getLong(messageID) to (cursor.getInt(messageType) == SMS_TYPE)
}
}
override fun setServerID(messageID: Long, serverID: Long, isSms: Boolean) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID) contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.serverID, serverID) contentValues.put(Companion.serverID, serverID)
database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) contentValues.put(messageType, if (isSms) SMS_TYPE else MMS_TYPE)
database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf( messageID.toString(), serverID.toString() ))
} }
fun getOriginalThreadID(messageID: Long): Long { fun getOriginalThreadID(messageID: Long): Long {
@ -62,12 +83,13 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
}?.toLong() ?: -1L }?.toLong() ?: -1L
} }
fun setOriginalThreadID(messageID: Long, threadID: Long) { fun setOriginalThreadID(messageID: Long, serverID: Long, threadID: Long) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2) val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID) contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.serverID, serverID)
contentValues.put(Companion.threadID, threadID) contentValues.put(Companion.threadID, threadID)
database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf( messageID.toString(), serverID.toString() ))
} }
fun getErrorMessage(messageID: Long): String? { fun getErrorMessage(messageID: Long): String? {

View File

@ -11,7 +11,8 @@ import java.io.InputStream
interface MessageDataProvider { interface MessageDataProvider {
fun getMessageID(serverID: Long): Long? fun getMessageID(serverID: Long): Long?
fun deleteMessage(messageID: Long) fun getMessageID(serverId: Long, threadId: Long): Pair<Long, Boolean>?
fun deleteMessage(messageID: Long, isSms: Boolean)
fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment?

View File

@ -64,7 +64,7 @@ interface StorageProtocol {
// Open Groups // Open Groups
fun getThreadID(openGroupID: String): String? fun getThreadID(openGroupID: String): String?
fun addOpenGroup(server: String, channel: Long) fun addOpenGroup(server: String, channel: Long)
fun setOpenGroupServerMessageID(messageID: Long, serverID: Long) fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean)
fun getQuoteServerID(quoteID: Long, publicKey: String): Long? fun getQuoteServerID(quoteID: Long, publicKey: String): Long?
// Open Group Public Keys // Open Group Public Keys
@ -137,7 +137,7 @@ interface StorageProtocol {
fun getOrCreateThreadIdFor(address: Address): Long fun getOrCreateThreadIdFor(address: Address): Long
fun getOrCreateThreadIdFor(publicKey: String, groupPublicKey: String?, openGroupID: String?): Long fun getOrCreateThreadIdFor(publicKey: String, groupPublicKey: String?, openGroupID: String?): Long
fun getThreadIdFor(address: Address): Long? fun getThreadIdFor(address: Address): Long?
fun getThreadIdForMms(messageId: Long): Long fun getThreadIdForMms(mmsId: Long): Long
// Session Request // Session Request
fun getSessionRequestSentTimestamp(publicKey: String): Long? fun getSessionRequestSentTimestamp(publicKey: String): Long?

View File

@ -2,6 +2,7 @@ package org.session.libsession.messaging.open_groups
import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.PropertyNamingStrategy
import com.fasterxml.jackson.databind.annotation.JsonNaming import com.fasterxml.jackson.databind.annotation.JsonNaming
import com.fasterxml.jackson.databind.type.TypeFactory
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import nl.komponents.kovenant.Kovenant import nl.komponents.kovenant.Kovenant
@ -28,6 +29,8 @@ import org.session.libsignal.utilities.logging.Log
import org.whispersystems.curve25519.Curve25519 import org.whispersystems.curve25519.Curve25519
import java.util.* import java.util.*
typealias DeletionList = List<OpenGroupAPIV2.MessageDeletion>
object OpenGroupAPIV2 { object OpenGroupAPIV2 {
private val moderators: HashMap<String, Set<String>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs) private val moderators: HashMap<String, Set<String>> = hashMapOf() // Server URL to (channel ID to set of moderator IDs)
@ -60,7 +63,7 @@ object OpenGroupAPIV2 {
val imageID: String? val imageID: String?
) )
@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy::class) @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class)
data class CompactPollRequest(val roomId: String, data class CompactPollRequest(val roomId: String,
val authToken: String, val authToken: String,
val fromDeletionServerId: Long?, val fromDeletionServerId: Long?,
@ -72,6 +75,11 @@ object OpenGroupAPIV2 {
val moderators: List<String> val moderators: List<String>
) )
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class)
data class MessageDeletion @JvmOverloads constructor(val id: Long = 0,
val deletedMessageId: Long = 0
)
data class Request( data class Request(
val verb: HTTP.Verb, val verb: HTTP.Verb,
val room: String?, val room: String?,
@ -93,7 +101,7 @@ object OpenGroupAPIV2 {
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 send(request: Request, isJsonRequired: Boolean = true): Promise<Map<*, *>, Exception> {
val parsed = HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.INVALID_URL) val parsed = HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.INVALID_URL)
val urlBuilder = HttpUrl.Builder() val urlBuilder = HttpUrl.Builder()
.scheme(parsed.scheme()) .scheme(parsed.scheme())
@ -128,7 +136,7 @@ object OpenGroupAPIV2 {
if (request.useOnionRouting) { if (request.useOnionRouting) {
val publicKey = MessagingModuleConfiguration.shared.storage.getOpenGroupPublicKey(request.server) val publicKey = MessagingModuleConfiguration.shared.storage.getOpenGroupPublicKey(request.server)
?: return Promise.ofFail(Error.NO_PUBLIC_KEY) ?: return Promise.ofFail(Error.NO_PUBLIC_KEY)
return OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey) return OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey, isJSONRequired = isJsonRequired)
.fail { e -> .fail { e ->
if (e is OnionRequestAPI.HTTPRequestFailedAtDestinationException && e.statusCode == 401) { if (e is OnionRequestAPI.HTTPRequestFailedAtDestinationException && e.statusCode == 401) {
val storage = MessagingModuleConfiguration.shared.storage val storage = MessagingModuleConfiguration.shared.storage
@ -289,7 +297,7 @@ object OpenGroupAPIV2 {
} }
} }
fun getDeletedMessages(room: String, server: String): Promise<List<Long>, Exception> { fun getDeletedMessages(room: String, server: String): Promise<List<MessageDeletion>, Exception> {
val storage = MessagingModuleConfiguration.shared.storage val storage = MessagingModuleConfiguration.shared.storage
val queryParameters = mutableMapOf<String, String>() val queryParameters = mutableMapOf<String, String>()
storage.getLastDeletionServerId(room, server)?.let { last -> storage.getLastDeletionServerId(room, server)?.let { last ->
@ -297,12 +305,13 @@ object OpenGroupAPIV2 {
} }
val request = Request(verb = GET, room = room, server = server, endpoint = "deleted_messages", queryParameters = queryParameters) val request = Request(verb = GET, room = room, server = server, endpoint = "deleted_messages", queryParameters = queryParameters)
return send(request).map(sharedContext) { json -> return send(request).map(sharedContext) { json ->
@Suppress("UNCHECKED_CAST") val serverIDs = (json["ids"] as? List<Int>)?.map { it.toLong() } val type = TypeFactory.defaultInstance().constructCollectionType(List::class.java, MessageDeletion::class.java)
?: throw Error.PARSING_FAILED val idsAsString = JsonUtil.toJson(json["ids"])
val serverIDs = JsonUtil.fromJson<List<MessageDeletion>>(idsAsString, type) ?: throw Error.PARSING_FAILED
val lastMessageServerId = storage.getLastDeletionServerId(room, server) ?: 0 val lastMessageServerId = storage.getLastDeletionServerId(room, server) ?: 0
val serverID = serverIDs.maxOrNull() ?: 0 val serverID = serverIDs.maxByOrNull {it.id } ?: serverIDs.first()
if (serverID > lastMessageServerId) { if (serverID.id > lastMessageServerId) {
storage.setLastDeletionServerId(room, server, serverID) storage.setLastDeletionServerId(room, server, serverID.id)
} }
serverIDs serverIDs
} }
@ -343,14 +352,14 @@ object OpenGroupAPIV2 {
// endregion // endregion
// region General // region General
fun getCompactPoll(rooms: List<String>, server: String): Promise<Map<String,CompactPollResult>, Exception> { fun getCompactPoll(rooms: List<String>, server: String): Promise<Map<String, CompactPollResult>, Exception> {
val requestAuths = rooms.associateWith { room -> getAuthToken(room,server) } val requestAuths = rooms.associateWith { room -> getAuthToken(room, server) }
val storage = MessagingModuleConfiguration.shared.storage val storage = MessagingModuleConfiguration.shared.storage
val requests = rooms.mapNotNull { room -> val requests = rooms.mapNotNull { room ->
val authToken = try { val authToken = try {
requestAuths[room]?.get() requestAuths[room]?.get()
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Loki", "Failed to get auth token for $room",e) Log.e("Loki", "Failed to get auth token for $room", e)
null null
} ?: return@mapNotNull null } ?: return@mapNotNull null
@ -363,7 +372,7 @@ object OpenGroupAPIV2 {
val request = Request(verb = POST, room = null, server = server, endpoint = "compact_poll", isAuthRequired = false, parameters = mapOf("requests" to requests)) val request = Request(verb = POST, room = null, server = server, endpoint = "compact_poll", isAuthRequired = false, parameters = mapOf("requests" to requests))
// build a request for all rooms // build a request for all rooms
return send(request = request).map(sharedContext) { json -> return send(request = request).map(sharedContext) { json ->
val results = json["results"] as? Map<*,*> ?: throw Error.PARSING_FAILED val results = json["results"] as? Map<*, *> ?: throw Error.PARSING_FAILED
TODO() TODO()
} }
} }

View File

@ -12,16 +12,15 @@ 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.visible.* import org.session.libsession.messaging.messages.visible.*
import org.session.libsession.messaging.open_groups.OpenGroupAPI import org.session.libsession.messaging.open_groups.*
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.messaging.open_groups.OpenGroupMessage
import org.session.libsession.messaging.open_groups.OpenGroupMessageV2
import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.recipients.Recipient
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
import org.session.libsession.snode.SnodeModule import org.session.libsession.snode.SnodeModule
import org.session.libsession.snode.SnodeMessage import org.session.libsession.snode.SnodeMessage
import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.SSKEnvironment import org.session.libsession.utilities.SSKEnvironment
import org.session.libsignal.service.internal.push.PushTransportDetails import org.session.libsignal.service.internal.push.PushTransportDetails
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
@ -290,8 +289,12 @@ object MessageSender {
// Ignore future self-sends // Ignore future self-sends
storage.addReceivedMessageTimestamp(message.sentTimestamp!!) storage.addReceivedMessageTimestamp(message.sentTimestamp!!)
// Track the open group server message ID // Track the open group server message ID
if (message.openGroupServerMessageID != null) { if (message.openGroupServerMessageID != null && destination is Destination.OpenGroupV2) {
storage.setOpenGroupServerMessageID(messageId, message.openGroupServerMessageID!!) val encoded = GroupUtil.getEncodedOpenGroupID("${destination.server}.${destination.room}".toByteArray())
val threadID = storage.getThreadIdFor(Address.fromSerialized(encoded))
if (threadID != null && threadID >= 0) {
storage.setOpenGroupServerMessageID(messageId, message.openGroupServerMessageID!!, threadID, !(message as VisibleMessage).isMediaMessage())
}
} }
// Mark the message as sent // Mark the message as sent
storage.markAsSent(message.sentTimestamp!!, message.sender?:userPublicKey) storage.markAsSent(message.sentTimestamp!!, message.sender?:userPublicKey)

View File

@ -151,9 +151,19 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS
val storage = MessagingModuleConfiguration.shared.storage val storage = MessagingModuleConfiguration.shared.storage
val context = MessagingModuleConfiguration.shared.context val context = MessagingModuleConfiguration.shared.context
val userPublicKey = storage.getUserPublicKey() val userPublicKey = storage.getUserPublicKey()
// Get or create thread
val threadID = storage.getOrCreateThreadIdFor(message.syncTarget
?: message.sender!!, message.groupPublicKey, openGroupID)
val openGroup = threadID.let {
storage.getOpenGroup(it.toString())
}
// Update profile if needed // Update profile if needed
val newProfile = message.profile val newProfile = message.profile
if (newProfile != null && openGroupID.isNullOrEmpty() && userPublicKey != message.sender) {
if (newProfile != null && userPublicKey != message.sender && openGroup == null) {
val profileManager = SSKEnvironment.shared.profileManager val profileManager = SSKEnvironment.shared.profileManager
val recipient = Recipient.from(context, Address.fromSerialized(message.sender!!), false) val recipient = Recipient.from(context, Address.fromSerialized(message.sender!!), false)
val displayName = newProfile.displayName!! val displayName = newProfile.displayName!!
@ -172,9 +182,6 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS
} }
} }
} }
// Get or create thread
val threadID = storage.getOrCreateThreadIdFor(message.syncTarget
?: message.sender!!, message.groupPublicKey, openGroupID)
// Parse quote if needed // Parse quote if needed
var quoteModel: QuoteModel? = null var quoteModel: QuoteModel? = null
if (message.quote != null && proto.dataMessage.hasQuote()) { if (message.quote != null && proto.dataMessage.hasQuote()) {
@ -224,6 +231,10 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS
JobQueue.shared.add(downloadJob) JobQueue.shared.add(downloadJob)
} }
} }
val openGroupServerID = message.openGroupServerMessageID
if (openGroupServerID != null) {
storage.setOpenGroupServerMessageID(messageID, openGroupServerID, threadID, !(message.isMediaMessage() || attachments.isNotEmpty()))
}
// Cancel any typing indicators if needed // Cancel any typing indicators if needed
cancelTypingIndicatorsIfNeeded(message.sender!!) cancelTypingIndicatorsIfNeeded(message.sender!!)
//Notify the user if needed //Notify the user if needed

View File

@ -9,6 +9,8 @@ import org.session.libsession.messaging.jobs.MessageReceiveJob
import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.messaging.open_groups.OpenGroupAPI import org.session.libsession.messaging.open_groups.OpenGroupAPI
import org.session.libsession.messaging.open_groups.OpenGroupMessage import org.session.libsession.messaging.open_groups.OpenGroupMessage
import org.session.libsession.messaging.threads.Address
import org.session.libsession.utilities.GroupUtil
import org.session.libsignal.service.internal.push.SignalServiceProtos.* import org.session.libsignal.service.internal.push.SignalServiceProtos.*
import org.session.libsignal.utilities.logging.Log import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.successBackground import org.session.libsignal.utilities.successBackground
@ -210,10 +212,13 @@ class OpenGroupPoller(private val openGroup: OpenGroup, private val executorServ
} }
private fun pollForDeletedMessages() { private fun pollForDeletedMessages() {
val messagingModule = MessagingModuleConfiguration.shared
val address = GroupUtil.getEncodedOpenGroupID(openGroup.id.toByteArray())
val threadId = messagingModule.storage.getThreadIdFor(Address.fromSerialized(address)) ?: return
OpenGroupAPI.getDeletedMessageServerIDs(openGroup.channel, openGroup.server).success { deletedMessageServerIDs -> OpenGroupAPI.getDeletedMessageServerIDs(openGroup.channel, openGroup.server).success { deletedMessageServerIDs ->
val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { MessagingModuleConfiguration.shared.messageDataProvider.getMessageID(it) } val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { messagingModule.messageDataProvider.getMessageID(it, threadId) }
deletedMessageIDs.forEach { deletedMessageIDs.forEach { (messageId, isSms) ->
MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(it) MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(messageId, isSms)
} }
}.fail { }.fail {
Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${openGroup.channel} on server: ${openGroup.server}.") Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${openGroup.channel} on server: ${openGroup.server}.")

View File

@ -7,7 +7,10 @@ import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.jobs.MessageReceiveJob import org.session.libsession.messaging.jobs.MessageReceiveJob
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.messaging.open_groups.OpenGroupV2 import org.session.libsession.messaging.open_groups.OpenGroupV2
import org.session.libsession.messaging.threads.Address
import org.session.libsession.utilities.GroupUtil
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol
import org.session.libsignal.utilities.logging.Log import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.successBackground import org.session.libsignal.utilities.successBackground
import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.ScheduledExecutorService
@ -101,10 +104,17 @@ class OpenGroupV2Poller(private val openGroup: OpenGroupV2, private val executor
} }
private fun pollForDeletedMessages() { private fun pollForDeletedMessages() {
val messagingModule = MessagingModuleConfiguration.shared
val address = GroupUtil.getEncodedOpenGroupID(openGroup.id.toByteArray())
val threadId = messagingModule.storage.getThreadIdFor(Address.fromSerialized(address)) ?: return
OpenGroupAPIV2.getDeletedMessages(openGroup.room, openGroup.server).success { deletedMessageServerIDs -> OpenGroupAPIV2.getDeletedMessages(openGroup.room, openGroup.server).success { deletedMessageServerIDs ->
val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { MessagingModuleConfiguration.shared.messageDataProvider.getMessageID(it) }
deletedMessageIDs.forEach { val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { serverId ->
MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(it) messagingModule.messageDataProvider.getMessageID(serverId.deletedMessageId, threadId)
}
deletedMessageIDs.forEach { (messageId, isSms) ->
MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(messageId, isSms)
} }
}.fail { }.fail {
Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${openGroup.room} on server: ${openGroup.server}.") Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${openGroup.room} on server: ${openGroup.server}.")

View File

@ -3,5 +3,5 @@ package org.session.libsignal.service.loki.database
interface LokiMessageDatabaseProtocol { interface LokiMessageDatabaseProtocol {
fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long?
fun setServerID(messageID: Long, serverID: Long) fun setServerID(messageID: Long, serverID: Long, isSms: Boolean)
} }

View File

@ -3,8 +3,10 @@ package org.session.libsignal.utilities;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.ResolvedType;
import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.JsonSerializer;
@ -42,6 +44,10 @@ public class JsonUtil {
return objectMapper.readValue(serialized, clazz); return objectMapper.readValue(serialized, clazz);
} }
public static<T> T fromJson(String serialized, JavaType clazz) throws IOException {
return objectMapper.readValue(serialized, clazz);
}
public static <T> T fromJson(InputStream serialized, Class<T> clazz) throws IOException { public static <T> T fromJson(InputStream serialized, Class<T> clazz) throws IOException {
return objectMapper.readValue(serialized, clazz); return objectMapper.readValue(serialized, clazz);
} }