Update batch endpoint responses

This commit is contained in:
charles 2022-04-26 09:46:23 +10:00
parent 1a82238a44
commit 373a61b560
6 changed files with 139 additions and 105 deletions

View File

@ -73,7 +73,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
}
override fun setUserProfilePictureURL(newValue: String) {
val ourRecipient = Address.fromSerialized(getUserPublicKey()!!).let {
val ourRecipient = fromSerialized(getUserPublicKey()!!).let {
Recipient.from(context, it, false)
}
TextSecurePreferences.setProfilePictureURL(context, newValue)
@ -103,7 +103,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List<LinkPreview?>, groupPublicKey: String?, openGroupID: String?, attachments: List<Attachment>): Long? {
var messageID: Long? = null
val senderAddress = Address.fromSerialized(message.sender!!)
val senderAddress = fromSerialized(message.sender!!)
val isUserSender = (message.sender!! == getUserPublicKey())
val group: Optional<SignalServiceGroup> = when {
openGroupID != null -> Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT))
@ -117,9 +117,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
it.toSignalAttachment()
}
val targetAddress = if (isUserSender && !message.syncTarget.isNullOrEmpty()) {
Address.fromSerialized(message.syncTarget!!)
fromSerialized(message.syncTarget!!)
} else if (group.isPresent) {
Address.fromSerialized(GroupUtil.getEncodedId(group.get()))
fromSerialized(GroupUtil.getEncodedId(group.get()))
} else {
senderAddress
}
@ -291,6 +291,16 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return getAllOpenGroups().values.firstOrNull { it.server == server && it.room == room }
}
override fun updateOpenGroupCapabilities(server: String, capabilities: List<String>) {
getAllOpenGroups().values.filter { it.server == server }
.map { it.copy(capabilities = it.capabilities) }
.forEach(this::updateOpenGroup)
}
override fun getOpenGroupServer(server: String): List<String> {
return getAllOpenGroups().values.firstOrNull { it.server == server }?.capabilities ?: emptyList()
}
override fun isDuplicateMessage(timestamp: Long): Boolean {
return getReceivedMessageTimestamps().contains(timestamp)
}
@ -317,7 +327,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun getMessageIdInDatabase(timestamp: Long, author: String): Long? {
val database = DatabaseComponent.get(context).mmsSmsDatabase()
val address = Address.fromSerialized(author)
val address = fromSerialized(author)
return database.getMessageFor(timestamp, address)?.getId()
}
@ -435,7 +445,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, sentTimestamp: Long) {
val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList())
val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true)
val m = IncomingTextMessage(fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true)
val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON()
val infoMessage = IncomingGroupMessage(m, groupID, updateData, true)
val smsDB = DatabaseComponent.get(context).smsDatabase()
@ -444,7 +454,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, threadID: Long, sentTimestamp: Long) {
val userPublicKey = getUserPublicKey()
val recipient = Recipient.from(context, Address.fromSerialized(groupID), false)
val recipient = Recipient.from(context, fromSerialized(groupID), false)
val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() ?: ""
val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, 0, true, null, listOf(), listOf())
@ -457,7 +467,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun isClosedGroup(publicKey: String): Boolean {
val isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(publicKey)
val address = Address.fromSerialized(publicKey)
val address = fromSerialized(publicKey)
return address.isClosedGroup || isClosedGroup
}
@ -514,6 +524,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return DatabaseComponent.get(context).lokiThreadDatabase().getAllOpenGroups()
}
override fun updateOpenGroup(openGroup: OpenGroup) {
OpenGroupManager.updateOpenGroup(openGroup, context)
}
override fun getAllGroups(): List<GroupRecord> {
return DatabaseComponent.get(context).groupDatabase().allGroups
}
@ -534,20 +548,20 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun getOrCreateThreadIdFor(publicKey: String, groupPublicKey: String?, openGroupID: String?): Long {
val database = DatabaseComponent.get(context).threadDatabase()
if (!openGroupID.isNullOrEmpty()) {
val recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedOpenGroupID(openGroupID.toByteArray())), false)
return database.getThreadIdIfExistsFor(recipient)
return if (!openGroupID.isNullOrEmpty()) {
val recipient = Recipient.from(context, fromSerialized(GroupUtil.getEncodedOpenGroupID(openGroupID.toByteArray())), false)
database.getThreadIdIfExistsFor(recipient)
} else if (!groupPublicKey.isNullOrEmpty()) {
val recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(groupPublicKey)), false)
return database.getOrCreateThreadIdFor(recipient)
val recipient = Recipient.from(context, fromSerialized(GroupUtil.doubleEncodeGroupID(groupPublicKey)), false)
database.getOrCreateThreadIdFor(recipient)
} else {
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
return database.getOrCreateThreadIdFor(recipient)
val recipient = Recipient.from(context, fromSerialized(publicKey), false)
database.getOrCreateThreadIdFor(recipient)
}
}
override fun getThreadId(publicKeyOrOpenGroupID: String): Long? {
val address = Address.fromSerialized(publicKeyOrOpenGroupID)
val address = fromSerialized(publicKeyOrOpenGroupID)
return getThreadId(address)
}
@ -595,7 +609,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
val recipientDatabase = DatabaseComponent.get(context).recipientDatabase()
val threadDatabase = DatabaseComponent.get(context).threadDatabase()
for (contact in contacts) {
val address = Address.fromSerialized(contact.publicKey)
val address = fromSerialized(contact.publicKey)
val recipient = Recipient.from(context, address, true)
if (!contact.profilePicture.isNullOrEmpty()) {
recipientDatabase.setProfileAvatar(recipient, contact.profilePicture)
@ -725,11 +739,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
}
override fun setLastInboxMessageId(server: String, messageId: Long) {
// TODO("Not yet implemented")
}
override fun removeLastInboxMessageId(server: String) {
// TODO("Not yet implemented")
}
override fun getLastOutboxMessageId(server: String): Long? {
@ -737,11 +751,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
}
override fun setLastOutboxMessageId(server: String, messageId: Long) {
// TODO("Not yet implemented")
}
override fun removeLastOutboxMessageId(server: String) {
// TODO("Not yet implemented")
}
}

View File

@ -124,4 +124,11 @@ object OpenGroupManager {
val publicKey = url.queryParameter("public_key") ?: return
add(server.toString().removeSuffix("/"), room, publicKey, context)
}
fun updateOpenGroup(openGroup: OpenGroup, context: Context) {
val threadDB = DatabaseComponent.get(context).lokiThreadDatabase()
val openGroupID = "${openGroup.server}.${openGroup.room}"
val threadID = GroupManager.getOpenGroupThreadID(openGroupID, context)
threadDB.setOpenGroupChat(openGroup, threadID)
}
}

View File

@ -55,10 +55,13 @@ interface StorageProtocol {
// Open Groups
fun getAllOpenGroups(): Map<Long, OpenGroup>
fun updateOpenGroup(openGroup: OpenGroup)
fun getOpenGroup(threadId: Long): OpenGroup?
fun addOpenGroup(urlAsString: String)
fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean)
fun getOpenGroup(room: String, server: String): OpenGroup?
fun updateOpenGroupCapabilities(server: String, capabilities: List<String>)
fun getOpenGroupServer(server: String): List<String>
// Open Group Public Keys
fun getOpenGroupPublicKey(server: String): String?

View File

@ -16,8 +16,7 @@ sealed class Endpoint(val value: String) {
// Messages
data class RoomMessage(val roomToken: String) :
Endpoint("room/$roomToken/message")
data class RoomMessage(val roomToken: String) : Endpoint("room/$roomToken/message")
data class RoomMessageIndividual(val roomToken: String, val messageId: Long) :
Endpoint("room/$roomToken/message/$messageId")
@ -42,8 +41,7 @@ sealed class Endpoint(val value: String) {
data class RoomUnpinMessage(val roomToken: String, val messageId: Long) :
Endpoint("room/$roomToken/unpin/$messageId")
data class RoomUnpinAll(val roomToken: String) :
Endpoint("room/$roomToken/unpin/all")
data class RoomUnpinAll(val roomToken: String) : Endpoint("room/$roomToken/unpin/all")
// Files

View File

@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.type.TypeFactory
import com.goterl.lazysodium.LazySodiumAndroid
import com.goterl.lazysodium.SodiumAndroid
import com.goterl.lazysodium.interfaces.GenericHash
import com.goterl.lazysodium.interfaces.Sign
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.flow.MutableSharedFlow
import nl.komponents.kovenant.Promise
@ -194,8 +195,7 @@ object OpenGroupApi {
* Always `true` under normal circumstances. You might want to disable
* this when running over Lokinet.
*/
val useOnionRouting: Boolean = true,
val isBlinded: Boolean = true
val useOnionRouting: Boolean = true
)
private fun createBody(parameters: Any?): RequestBody? {
@ -223,6 +223,7 @@ object OpenGroupApi {
}
}
fun execute(): Promise<OnionResponse, Exception> {
val serverCapabilities = MessagingModuleConfiguration.shared.storage.getOpenGroupServer(request.server)
val publicKey =
MessagingModuleConfiguration.shared.storage.getOpenGroupPublicKey(request.server)
?: return Promise.ofFail(Error.NoPublicKey)
@ -233,13 +234,7 @@ object OpenGroupApi {
val nonce = sodium.nonce(16)
val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
var pubKey = ""
var signature = ByteArray(0)
if (request.isBlinded) {
SodiumUtilities.blindedKeyPair(publicKey, ed25519KeyPair)?.let { keyPair ->
pubKey = SodiumUtilities.SessionId(
SodiumUtilities.IdPrefix.BLINDED,
keyPair.publicKey.asBytes
).hexString
var signature = ByteArray(Sign.BYTES)
var bodyHash = ByteArray(0)
if (request.parameters != null) {
val parameterBytes = JsonUtil.toJson(request.parameters).toByteArray()
@ -259,6 +254,13 @@ object OpenGroupApi {
.plus(request.verb.rawValue.toByteArray())
.plus(urlRequest.encodedPath().toByteArray())
.plus(bodyHash)
if (serverCapabilities.contains("blind")) {
SodiumUtilities.blindedKeyPair(publicKey, ed25519KeyPair)?.let { keyPair ->
pubKey = SodiumUtilities.SessionId(
SodiumUtilities.IdPrefix.BLINDED,
keyPair.publicKey.asBytes
).hexString
signature = SodiumUtilities.sogsSignature(
messageBytes,
ed25519KeyPair.secretKey.asBytes,
@ -271,7 +273,7 @@ object OpenGroupApi {
SodiumUtilities.IdPrefix.UN_BLINDED,
ed25519KeyPair.publicKey.asBytes
).hexString
signature = ByteArray(0)
sodium.cryptoSignDetached(signature, messageBytes, messageBytes.size.toLong(), ed25519KeyPair.secretKey.asBytes)
}
headers["X-SOGS-Nonce"] = encodeBytes(nonce)
headers["X-SOGS-Timestamp"] = "$timestamp"
@ -415,7 +417,7 @@ object OpenGroupApi {
@JvmStatic
fun deleteMessage(serverID: Long, room: String, server: String): Promise<Unit, Exception> {
val request =
Request(verb = DELETE, room = room, server = server, endpoint = "messages/$serverID")
Request(verb = DELETE, room = room, server = server, endpoint = Endpoint.RoomMessageIndividual(room, serverID))
return send(request).map {
Log.d("Loki", "Message deletion successful.")
}
@ -568,6 +570,8 @@ object OpenGroupApi {
}
)
}
val serverCapabilities = storage.getOpenGroupServer(server)
if (serverCapabilities.contains("blind")) {
requests.add(
if (lastInboxMessageId == null) {
BatchRequestInfo(
@ -610,7 +614,7 @@ object OpenGroupApi {
)
}
)
}
return parallelBatch(server, requests)
}
@ -666,7 +670,7 @@ object OpenGroupApi {
fun getDefaultRoomsIfNeeded(): Promise<List<DefaultGroup>, Exception> {
val storage = MessagingModuleConfiguration.shared.storage
storage.setOpenGroupPublicKey(defaultServer, defaultServerPublicKey)
return getAllRooms(defaultServer).map { groups ->
return getAllRooms().map { groups ->
val earlyGroups = groups.map { group ->
DefaultGroup(group.token, group.name, null)
}
@ -705,11 +709,11 @@ object OpenGroupApi {
}
}
private fun getAllRooms(server: String): Promise<List<RoomInfo>, Exception> {
private fun getAllRooms(): Promise<List<RoomInfo>, Exception> {
val request = Request(
verb = GET,
room = null,
server = server,
server = defaultServer,
endpoint = Endpoint.Rooms
)
return send(request).map { response ->

View File

@ -61,9 +61,14 @@ class OpenGroupPoller(private val server: String, private val executorService: S
is Endpoint.RoomMessagesRecent -> {
handleMessages(server, response.endpoint.roomToken, response.body as List<OpenGroupApi.Message>)
}
is Endpoint.Inbox, Endpoint.Outbox -> {
val fromOutbox = response.endpoint.value.startsWith("outbox", ignoreCase = true)
handleDirectMessages(server, fromOutbox, response.body as List<OpenGroupApi.DirectMessage>)
is Endpoint.RoomMessagesSince -> {
handleMessages(server, response.endpoint.roomToken, response.body as List<OpenGroupApi.Message>)
}
is Endpoint.Inbox, is Endpoint.InboxSince -> {
handleDirectMessages(server, false, response.body as List<OpenGroupApi.DirectMessage>)
}
is Endpoint.Outbox, is Endpoint.OutboxSince -> {
handleDirectMessages(server, true, response.body as List<OpenGroupApi.DirectMessage>)
}
}
if (secondToLastJob == null && !isCaughtUp) {
@ -77,7 +82,7 @@ class OpenGroupPoller(private val server: String, private val executorService: S
private fun handleCapabilities(server: String, capabilities: OpenGroupApi.Capabilities) {
val storage = MessagingModuleConfiguration.shared.storage
storage.setOpenGroupSever(server, capabilities.capabilities)
storage.updateOpenGroupCapabilities(server, capabilities.capabilities)
}
private fun handleRoomPollInfo(
@ -102,7 +107,7 @@ class OpenGroupPoller(private val server: String, private val executorService: S
capabilities = listOf()
)
// - Open Group changes
storage.setOpenGroup(openGroup)
storage.updateOpenGroup(openGroup)
// - User Count
storage.setUserCount(roomToken, server, pollInfo.active_users)
@ -123,7 +128,8 @@ class OpenGroupPoller(private val server: String, private val executorService: S
messages: List<OpenGroupApi.Message>
) {
val openGroupId = "$server.$roomToken"
val msgs = messages.map {
val (additions, deletions) = messages.sortedBy { it.seqno }.partition { it.data.isNotBlank() }
handleNewMessages(roomToken, openGroupId, additions.map {
OpenGroupMessageV2(
serverID = it.id,
sender = it.session_id,
@ -131,8 +137,10 @@ class OpenGroupPoller(private val server: String, private val executorService: S
base64EncodedData = it.data,
base64EncodedSignature = it.signature
)
}
handleNewMessages(roomToken, openGroupId, msgs)
})
handleDeletedMessages(roomToken, openGroupId, deletions.map {
OpenGroupApi.MessageDeletion(it.id, it.seqno)
})
}
private fun handleDirectMessages(