Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/
package com.wire.kalium.conversation.history.data

import com.wire.kalium.conversation.history.HistoryClient
import com.wire.kalium.logic.data.history.HistoryClient
import com.wire.kalium.persistence.dao.QualifiedIDEntity
import kotlinx.coroutines.flow.Flow
import kotlinx.datetime.Instant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ package com.wire.kalium.conversation.history.data

import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import com.wire.kalium.conversation.history.HistoryClient
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.history.HistoryClient
import com.wire.kalium.persistence.HistoryClientQueries
import com.wire.kalium.persistence.dao.QualifiedIDEntity
import kotlinx.coroutines.Dispatchers
Expand All @@ -44,12 +43,10 @@ internal class SQLiteHistoryClientDAO internal constructor(
* Maps a database entity to a domain model.
*/
private fun mapToHistoryClient(
conversationId: QualifiedIDEntity,
id: String,
secret: ByteArray,
creationDate: Instant
): HistoryClient = HistoryClient(
conversationId = ConversationId(conversationId.value, conversationId.domain),
id = id,
creationTime = creationDate,
secret = HistoryClient.Secret(secret)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package com.wire.kalium.conversation.history.data

import app.cash.turbine.test
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.persistence.TestUserDatabase
import com.wire.kalium.persistence.dao.QualifiedIDEntity
import com.wire.kalium.persistence.dao.UserIDEntity
Expand Down Expand Up @@ -121,7 +120,6 @@ class SQLiteHistoryClientDAOTest {
// Then
assertEquals(1, result.size)
with(result.first()) {
assertEquals(ConversationId(conversationId.value, conversationId.domain), this.conversationId)
assertEquals(clientId, this.id)
assertEquals(creationDate, this.creationTime)
assertTrue(this.secret.value.contentEquals(secret))
Expand Down Expand Up @@ -166,12 +164,10 @@ class SQLiteHistoryClientDAOTest {
assertEquals(1, result2.size)

with(result1.first()) {
assertEquals(ConversationId(conversationId1.value, conversationId1.domain), this.conversationId)
assertEquals(clientId1, this.id)
}

with(result2.first()) {
assertEquals(ConversationId(conversationId2.value, conversationId2.domain), this.conversationId)
assertEquals(clientId2, this.id)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.conversation.history
package com.wire.kalium.logic.data.history

import com.wire.kalium.logic.data.id.ConversationId
import kotlinx.datetime.Instant
import kotlin.time.ExperimentalTime

public data class HistoryClient @OptIn(ExperimentalTime::class) constructor(
val conversationId: ConversationId,
data class HistoryClient @OptIn(ExperimentalTime::class) constructor(
val id: String,
val creationTime: Instant,
val secret: Secret,
Expand All @@ -45,6 +43,5 @@ public data class HistoryClient @OptIn(ExperimentalTime::class) constructor(
override fun hashCode(): Int {
return value.contentHashCode()
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,19 @@ sealed interface Message {
is MessageContent.CompositeEdited -> mutableMapOf(
typeKey to "compositeEdited"
)

MessageContent.History.ClientsRequest -> mutableMapOf(
typeKey to "historyClientsRequest",
)

is MessageContent.History.ClientsResponse -> mutableMapOf(
typeKey to "historyClientsResponse",
"count" to content.clients.size
)

is MessageContent.History.NewClientAvailable -> mutableMapOf(
typeKey to "historyNewClientAvailable",
)
}

val standardProperties = mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package com.wire.kalium.logic.data.message
import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.data.conversation.ClientId
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.history.HistoryClient
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.MessageButtonId
import com.wire.kalium.logic.data.id.MessageId
Expand Down Expand Up @@ -401,6 +402,12 @@ sealed interface MessageContent {
val emojis: Map<String, Int>
) : Signaling

sealed interface History : Signaling {
data class NewClientAvailable(val client: HistoryClient) : History
data class ClientsResponse(val clients: List<HistoryClient>) : History
data object ClientsRequest : History
}

data class Multipart(
val value: String?,
val linkPreviews: List<MessageLinkPreview> = emptyList(),
Expand All @@ -421,7 +428,7 @@ sealed interface MessageContent {
* @return A string representing the type of content.
* Useful for logging. Plain strings must be used, otherwise it may be affected by code minification.
*/
@Suppress("ComplexMethod")
@Suppress("ComplexMethod", "LongMethod")
fun MessageContent?.getType() = when (this) {
is MessageContent.Asset -> "Asset"
is MessageContent.FailedDecryption -> "FailedDecryption"
Expand Down Expand Up @@ -478,6 +485,9 @@ fun MessageContent?.getType() = when (this) {
is MessageContent.InCallEmoji -> "InCallEmoji"
is MessageContent.Multipart -> "Multipart"
is MessageContent.CompositeEdited -> "CompositeEdited"
MessageContent.History.ClientsRequest -> "History.ClientsRequest"
is MessageContent.History.ClientsResponse -> "History.ClientsResponse"
is MessageContent.History.NewClientAvailable -> "History.NewClientAvailable"
null -> "null"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ inline fun MessageContent.FromProto.typeDescription(): String = when (this) {
is MessageContent.InCallEmoji -> "InCallEmoji"
is MessageContent.Multipart -> "Multipart"
is MessageContent.CompositeEdited -> "CompositeEdited"
MessageContent.History.ClientsRequest -> "History.ClientsRequest"
is MessageContent.History.ClientsResponse -> "History.ClientsResponse"
is MessageContent.History.NewClientAvailable -> "History.NewClientAvailable"
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ internal class PersistMessageUseCaseImpl(
is MessageContent.DataTransfer -> false
is MessageContent.InCallEmoji -> false
is MessageContent.Multipart -> true
is MessageContent.History -> false
}

@Suppress("ComplexMethod")
Expand Down Expand Up @@ -188,6 +189,7 @@ internal class PersistMessageUseCaseImpl(
is MessageContent.MemberChange.RemovedFromTeam,
is MessageContent.TeamMemberRemoved,
is MessageContent.DataTransfer,
is MessageContent.InCallEmoji -> false
is MessageContent.InCallEmoji,
is MessageContent.History -> false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.wire.kalium.logic.data.asset.AssetTransferStatus
import com.wire.kalium.logic.data.asset.toModel
import com.wire.kalium.logic.data.asset.toProto
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.history.HistoryClient
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.IdMapper
import com.wire.kalium.logic.data.message.composite.CompositeButton
Expand All @@ -51,7 +52,12 @@ import com.wire.kalium.protobuf.messages.DataTransfer
import com.wire.kalium.protobuf.messages.Ephemeral
import com.wire.kalium.protobuf.messages.External
import com.wire.kalium.protobuf.messages.GenericMessage
import com.wire.kalium.protobuf.messages.GenericMessage.Content.Availability
import com.wire.kalium.protobuf.messages.GenericMessage.Content.Deleted
import com.wire.kalium.protobuf.messages.GenericMessage.UnknownStrategy
import com.wire.kalium.protobuf.messages.HistoryClientAvailable
import com.wire.kalium.protobuf.messages.HistoryClientRequest
import com.wire.kalium.protobuf.messages.HistoryClientResponse
import com.wire.kalium.protobuf.messages.InCallEmoji
import com.wire.kalium.protobuf.messages.Knock
import com.wire.kalium.protobuf.messages.LastRead
Expand All @@ -66,9 +72,11 @@ import com.wire.kalium.protobuf.messages.Quote
import com.wire.kalium.protobuf.messages.Reaction
import com.wire.kalium.protobuf.messages.Text
import com.wire.kalium.protobuf.messages.TrackingIdentifier
import com.wire.kalium.util.DateTimeUtil.toIsoDateTimeString
import io.mockative.Mockable
import kotlinx.datetime.Instant
import pbandk.ByteArr
import com.wire.kalium.protobuf.messages.HistoryClient as ProtoHistoryClient

@Mockable
interface ProtoContentMapper {
Expand Down Expand Up @@ -130,9 +138,9 @@ class ProtoContentMapperImpl(
is MessageContent.Calling -> packCalling(readableContent)
is MessageContent.Asset -> packAsset(readableContent, expectsReadConfirmation, legalHoldStatus)
is MessageContent.Knock -> packKnock(readableContent, legalHoldStatus)
is MessageContent.DeleteMessage -> GenericMessage.Content.Deleted(MessageDelete(messageId = readableContent.messageId))
is MessageContent.DeleteMessage -> Deleted(MessageDelete(messageId = readableContent.messageId))
is MessageContent.DeleteForMe -> packHidden(readableContent)
is MessageContent.Availability -> GenericMessage.Content.Availability(
is MessageContent.Availability -> Availability(
availabilityMapper.fromModelAvailabilityToProto(
readableContent.status
)
Expand Down Expand Up @@ -161,6 +169,27 @@ class ProtoContentMapperImpl(
is MessageContent.DataTransfer -> packDataTransfer(readableContent)
is MessageContent.InCallEmoji -> packInCallEmoji(readableContent)
is MessageContent.Multipart -> packMultipart(readableContent, expectsReadConfirmation, legalHoldStatus)
is MessageContent.History -> packHistoryMessage(readableContent)
}
}

private fun packHistoryMessage(readableContent: MessageContent.History): GenericMessage.Content<out Any> {
fun mapHistoryClientToProto(historyClient: HistoryClient): ProtoHistoryClient {
return ProtoHistoryClient(
clientId = historyClient.id,
createdAt = historyClient.creationTime.toIsoDateTimeString(),
secret = ByteArr(historyClient.secret.value)
)
}
return when (readableContent) {
MessageContent.History.ClientsRequest -> GenericMessage.Content.HistoryClientRequest(HistoryClientRequest())
is MessageContent.History.ClientsResponse -> GenericMessage.Content.HistoryClientResponse(
HistoryClientResponse(readableContent.clients.map { mapHistoryClientToProto(it) })
)

is MessageContent.History.NewClientAvailable -> GenericMessage.Content.HistoryClientAvailable(
HistoryClientAvailable(mapHistoryClientToProto(readableContent.client))
)
}
}

Expand Down Expand Up @@ -337,6 +366,7 @@ class ProtoContentMapperImpl(
is MessageContent.CompositeEdited,
is MessageContent.DataTransfer,
is MessageContent.InCallEmoji,
is MessageContent.History,
is MessageContent.Multipart -> throw IllegalArgumentException(
"Unexpected message content type: ${readableContent.getType()}"
)
Expand All @@ -349,7 +379,9 @@ class ProtoContentMapperImpl(
External(
ByteArr(protoContent.otrKey),
protoContent.sha256?.let { ByteArr(it) },
protoContent.encryptionAlgorithm?.let { encryptionAlgorithmMapper.toProtoBufModel(it) }
protoContent.encryptionAlgorithm?.let {
encryptionAlgorithmMapper.toProtoBufModel(it)
}
)
)

Expand Down Expand Up @@ -450,6 +482,10 @@ class ProtoContentMapperImpl(

is GenericMessage.Content.InCallEmoji -> unpackInCallEmoji(protoContent)

is GenericMessage.Content.HistoryClientAvailable -> unpackHistoryClientAvailable(protoContent)
is GenericMessage.Content.HistoryClientRequest -> unpackHistoryClientRequest()
is GenericMessage.Content.HistoryClientResponse -> unpackHistoryClientResponse(protoContent)

null -> {
kaliumLogger.w(
"Null content when parsing protobuf. Message UUID = ${genericMessage.messageId.obfuscateId()}" +
Expand Down Expand Up @@ -889,6 +925,21 @@ class ProtoContentMapperImpl(
)
}

private fun unpackHistoryClientRequest(): MessageContent.History =
MessageContent.History.ClientsRequest

private fun protoHistoryClientToHistoryClient(protoClient: ProtoHistoryClient) = HistoryClient(
id = protoClient.clientId,
secret = HistoryClient.Secret(protoClient.secret.array),
creationTime = Instant.parse(protoClient.createdAt),
)

private fun unpackHistoryClientResponse(protoContent: GenericMessage.Content.HistoryClientResponse): MessageContent.History =
MessageContent.History.ClientsResponse(protoContent.value.clients.map(::protoHistoryClientToHistoryClient))

private fun unpackHistoryClientAvailable(protoContent: GenericMessage.Content.HistoryClientAvailable): MessageContent.History =
MessageContent.History.NewClientAvailable(protoHistoryClientToHistoryClient(protoContent.value.client))

private fun extractConversationId(
qualifiedConversationID: QualifiedConversationId?,
unqualifiedConversationID: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,23 +184,7 @@ internal class ApplicationMessageHandlerImpl(
userRepository.updateOtherUserAvailabilityStatus(signaling.senderUserId, content.status)
}

is MessageContent.ClientAction -> {
logger.i(message = "ClientAction status update received: ")

val message = Message.System(
id = signaling.id,
content = MessageContent.CryptoSessionReset,
conversationId = signaling.conversationId,
date = signaling.date,
senderUserId = signaling.senderUserId,
status = signaling.status,
senderUserName = signaling.senderUserName,
expirationData = null
)

logger.i(message = "Persisting crypto session reset system message..")
persistMessage(message)
}
is MessageContent.ClientAction -> handleClientAction(signaling)

is MessageContent.Reaction -> persistReaction(content, signaling.conversationId, signaling.senderUserId, signaling.date)
is MessageContent.DeleteMessage -> deleteMessageHandler(content, signaling.conversationId, signaling.senderUserId)
Expand Down Expand Up @@ -240,9 +224,31 @@ internal class ApplicationMessageHandlerImpl(
)

is MessageContent.CompositeEdited -> messageCompositeEditHandler.handle(signaling, content)

is MessageContent.History -> TODO("HISTORY CLIENTS ARE NOT HANDLED YET")
}
}

private suspend fun handleClientAction(
signaling: Message.Signaling,
) {
logger.i(message = "ClientAction status update received: ")

val message = Message.System(
id = signaling.id,
content = MessageContent.CryptoSessionReset,
conversationId = signaling.conversationId,
date = signaling.date,
senderUserId = signaling.senderUserId,
status = signaling.status,
senderUserName = signaling.senderUserName,
expirationData = null
)

logger.i(message = "Persisting crypto session reset system message..")
persistMessage(message)
}

private suspend fun processMessage(message: Message.Regular) {
logger.i(message = "Message received: { \"message\" : ${message.toLogString()} }")
when (val content = message.content) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ CREATE INDEX history_client_id ON HistoryClient(id);
CREATE INDEX history_client_conversation_date ON HistoryClient(conversation_id, creation_date);

selectAllForConversation:
SELECT * FROM HistoryClient WHERE conversation_id = ?;
SELECT id, secret, creation_date FROM HistoryClient WHERE conversation_id = ?;

selectAllForConversationFromDateOnwards:
SELECT * FROM HistoryClient WHERE conversation_id = ? AND creation_date >= ?;
SELECT id, secret, creation_date FROM HistoryClient WHERE conversation_id = ? AND creation_date >= ?;

insertClient:
INSERT OR IGNORE INTO HistoryClient(conversation_id, id, secret, creation_date) VALUES (?, ?, ?, ?);
Loading
Loading