Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Include legal hold flag when sending/receiving messages (WPB-5442) #2279

Merged
merged 19 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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 @@ -38,6 +38,7 @@ import com.wire.kalium.persistence.dao.asset.AssetMessageEntity
import com.wire.kalium.persistence.dao.message.MessageEntity
import com.wire.kalium.persistence.dao.message.MessageEntityContent
import com.wire.kalium.protobuf.messages.Asset
import com.wire.kalium.protobuf.messages.LegalHoldStatus
import com.wire.kalium.util.DateTimeUtil
import com.wire.kalium.util.KaliumDispatcher
import com.wire.kalium.util.KaliumDispatcherImpl
Expand All @@ -52,7 +53,11 @@ interface AssetMapper {
fun fromUserAssetToDaoModel(assetId: String, assetDomain: String?, dataPath: Path, dataSize: Long): AssetEntity
fun fromAssetEntityToAssetContent(assetContentEntity: MessageEntityContent.Asset): AssetContent
fun fromProtoAssetMessageToAssetContent(protoAssetMessage: Asset): AssetContent
fun fromAssetContentToProtoAssetMessage(messageContent: MessageContent.Asset, expectsReadConfirmation: Boolean): Asset
fun fromAssetContentToProtoAssetMessage(
messageContent: MessageContent.Asset,
expectsReadConfirmation: Boolean,
legalHoldStatus: LegalHoldStatus
): Asset
}

class AssetMapperImpl(
Expand Down Expand Up @@ -196,8 +201,11 @@ class AssetMapperImpl(
}
}

override fun fromAssetContentToProtoAssetMessage(messageContent: MessageContent.Asset, expectsReadConfirmation: Boolean): Asset =
with(messageContent.value) {
override fun fromAssetContentToProtoAssetMessage(
messageContent: MessageContent.Asset,
expectsReadConfirmation: Boolean,
legalHoldStatus: LegalHoldStatus
): Asset = with(messageContent.value) {
Asset(
original = Asset.Original(
mimeType = mimeType,
Expand Down Expand Up @@ -231,7 +239,8 @@ class AssetMapperImpl(
encryption = encryptionAlgorithmMapper.toProtoBufModel(remoteData.encryptionAlgorithm)
)
),
expectsReadConfirmation = expectsReadConfirmation
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = legalHoldStatus
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ data class Conversation(
enum class TypingIndicatorMode { STARTED, STOPPED }

enum class VerificationStatus { VERIFIED, NOT_VERIFIED, DEGRADED }
enum class LegalHoldStatus { ENABLED, DISABLED, DEGRADED }
enum class LegalHoldStatus { ENABLED, DISABLED, DEGRADED, UNKNOWN }

@Suppress("MagicNumber")
enum class CipherSuite(val tag: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package com.wire.kalium.logic.data.message

import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.message.ProtoContent.ExternalMessageInstructions
import com.wire.kalium.persistence.dao.conversation.ConversationEntity.ProtocolInfo.Proteus
import com.wire.kalium.protobuf.messages.GenericMessage
Expand All @@ -38,6 +39,7 @@ sealed interface ProtoContent {
override val messageUid: String,
val messageContent: MessageContent.FromProto,
val expectsReadConfirmation: Boolean,
val legalHoldStatus: Conversation.LegalHoldStatus,
val expiresAfterMillis: Long? = null
) : ProtoContent

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package com.wire.kalium.logic.data.message

import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.data.asset.AssetMapper
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.IdMapper
import com.wire.kalium.logic.data.message.mention.MessageMentionMapper
Expand All @@ -43,6 +44,7 @@ import com.wire.kalium.protobuf.messages.External
import com.wire.kalium.protobuf.messages.GenericMessage
import com.wire.kalium.protobuf.messages.Knock
import com.wire.kalium.protobuf.messages.LastRead
import com.wire.kalium.protobuf.messages.LegalHoldStatus
import com.wire.kalium.protobuf.messages.MessageDelete
import com.wire.kalium.protobuf.messages.MessageEdit
import com.wire.kalium.protobuf.messages.MessageHide
Expand Down Expand Up @@ -84,26 +86,29 @@ class ProtoContentMapperImpl(
mapEphemeralContent(
protoContent.messageContent,
protoContent.expiresAfterMillis,
protoContent.expectsReadConfirmation
protoContent.expectsReadConfirmation,
protoContent.legalHoldStatus
)
} else {
mapNormalContent(
protoContent.messageContent,
protoContent.expectsReadConfirmation
protoContent.expectsReadConfirmation,
protoContent.legalHoldStatus
)
}
}

@Suppress("ComplexMethod")
private fun mapNormalContent(
readableContent: MessageContent.FromProto,
expectsReadConfirmation: Boolean
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content<out Any> {
return when (readableContent) {
is MessageContent.Text -> packText(readableContent, expectsReadConfirmation)
is MessageContent.Text -> packText(readableContent, expectsReadConfirmation, legalHoldStatus)
is MessageContent.Calling -> packCalling(readableContent)
is MessageContent.Asset -> packAsset(readableContent, expectsReadConfirmation)
is MessageContent.Knock -> GenericMessage.Content.Knock(Knock(hotKnock = readableContent.hotKnock))
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.DeleteForMe -> packHidden(readableContent)
is MessageContent.Availability -> GenericMessage.Content.Availability(
Expand All @@ -114,7 +119,7 @@ class ProtoContentMapperImpl(

is MessageContent.LastRead -> packLastRead(readableContent)
is MessageContent.Cleared -> packCleared(readableContent)
is MessageContent.Reaction -> packReaction(readableContent)
is MessageContent.Reaction -> packReaction(readableContent, legalHoldStatus)
is MessageContent.Receipt -> packReceipt(readableContent)
is MessageContent.ClientAction -> packClientAction()
is MessageContent.TextEdited -> packEdited(readableContent)
Expand All @@ -123,7 +128,7 @@ class ProtoContentMapperImpl(
"Unexpected message content type: ${readableContent.getType()}"
)

is MessageContent.Composite -> packComposite(readableContent, expectsReadConfirmation)
is MessageContent.Composite -> packComposite(readableContent, expectsReadConfirmation, legalHoldStatus)
is MessageContent.ButtonAction -> packButtonAction(readableContent)

is MessageContent.ButtonActionConfirmation -> TODO()
Expand All @@ -142,12 +147,13 @@ class ProtoContentMapperImpl(

private fun packComposite(
readableContent: MessageContent.Composite,
expectsReadConfirmation: Boolean
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Composite {
val items: MutableList<Composite.Item> = mutableListOf()

readableContent.textContent?.let {
val text = packText(it, expectsReadConfirmation)
val text = packText(it, expectsReadConfirmation, legalHoldStatus)
Composite.Item.Content.Text(text.value).also {
items.add(Composite.Item(it))
}
Expand All @@ -159,7 +165,8 @@ class ProtoContentMapperImpl(
val composite = GenericMessage.Content.Composite(
Composite(
items = items,
expectsReadConfirmation = expectsReadConfirmation
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
)
)
return composite
Expand All @@ -168,25 +175,26 @@ class ProtoContentMapperImpl(
private fun mapEphemeralContent(
readableContent: MessageContent.FromProto,
expireAfterMillis: Long,
expectsReadConfirmation: Boolean
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content<out Any> {
val ephemeralContent = when (readableContent) {
is MessageContent.Text -> {
val text = packText(readableContent, expectsReadConfirmation)
val text = packText(readableContent, expectsReadConfirmation, legalHoldStatus)
Ephemeral.Content.Text(
text.value
)
}

is MessageContent.Asset -> {
val asset = packAsset(readableContent, expectsReadConfirmation)
val asset = packAsset(readableContent, expectsReadConfirmation, legalHoldStatus)
Ephemeral.Content.Asset(
asset.value
)
}

is MessageContent.Knock -> {
val knock = GenericMessage.Content.Knock(Knock(hotKnock = readableContent.hotKnock))
val knock = packKnock(readableContent, legalHoldStatus)
Ephemeral.Content.Knock(
knock.value
)
Expand Down Expand Up @@ -236,6 +244,7 @@ class ProtoContentMapperImpl(
is GenericMessage.Content.Asset -> content.value.expectsReadConfirmation ?: false
else -> false
}
val legalHoldStatus = getLegalHoldStatusFromProtoContent(genericMessage)
val expiresAfterMillis: Long? = when (val content = genericMessage.content) {
is GenericMessage.Content.Ephemeral -> content.value.expireAfterMillis
else -> null
Expand All @@ -244,11 +253,32 @@ class ProtoContentMapperImpl(
messageUid = genericMessage.messageId,
messageContent = getReadableContent(genericMessage, encodedContent),
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = fromProtoLegalHoldStatus(legalHoldStatus),
expiresAfterMillis = expiresAfterMillis
)
}
}

private fun getLegalHoldStatusFromProtoContent(genericMessage: GenericMessage) =
when (val content = genericMessage.content) {
is GenericMessage.Content.Text -> content.value.legalHoldStatus
is GenericMessage.Content.Asset -> content.value.legalHoldStatus
is GenericMessage.Content.Knock -> content.value.legalHoldStatus
is GenericMessage.Content.Location -> content.value.legalHoldStatus
is GenericMessage.Content.Reaction -> content.value.legalHoldStatus
is GenericMessage.Content.Composite -> content.value.legalHoldStatus
else -> null
}

private fun fromProtoLegalHoldStatus(legalHoldStatus: LegalHoldStatus?): Conversation.LegalHoldStatus =
legalHoldStatus?.let {
when (legalHoldStatus) {
is LegalHoldStatus.ENABLED -> Conversation.LegalHoldStatus.ENABLED
is LegalHoldStatus.DISABLED -> Conversation.LegalHoldStatus.DISABLED
else -> Conversation.LegalHoldStatus.UNKNOWN
}
} ?: run { Conversation.LegalHoldStatus.UNKNOWN }

@Suppress("ComplexMethod", "LongMethod")
private fun getReadableContent(
genericMessage: GenericMessage,
Expand Down Expand Up @@ -335,12 +365,20 @@ class ProtoContentMapperImpl(
)
} ?: MessageContent.Ignored

private fun packReaction(readableContent: MessageContent.Reaction) =
GenericMessage.Content.Reaction(Reaction(emoji = readableContent.emojiSet.map { it.trim() }.filter { it.isNotBlank() }
.joinToString(separator = ",") { it },
messageId = readableContent.messageId
)
private fun packReaction(
readableContent: MessageContent.Reaction,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Reaction {
val protoLegalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
return GenericMessage.Content.Reaction(
Reaction(
emoji = readableContent.emojiSet.map { it.trim() }.filter { it.isNotBlank() }
.joinToString(separator = ",") { it },
messageId = readableContent.messageId,
legalHoldStatus = protoLegalHoldStatus
)
)
}

private fun packClientAction() = GenericMessage.Content.ClientAction(ClientAction.RESET_SESSION)

Expand Down Expand Up @@ -461,17 +499,30 @@ class ProtoContentMapperImpl(
time = Instant.fromEpochMilliseconds(protoContent.value.clearedTimestamp)
)

private fun packText(readableContent: MessageContent.Text, expectsReadConfirmation: Boolean): GenericMessage.Content.Text {
private fun toProtoLegalHoldStatus(legalHoldStatus: Conversation.LegalHoldStatus): LegalHoldStatus =
when (legalHoldStatus) {
Conversation.LegalHoldStatus.ENABLED -> LegalHoldStatus.ENABLED
Conversation.LegalHoldStatus.DISABLED -> LegalHoldStatus.DISABLED
else -> LegalHoldStatus.UNKNOWN
}

private fun packText(
readableContent: MessageContent.Text,
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Text {
val mentions = readableContent.mentions.map { messageMentionMapper.fromModelToProto(it) }
val quote = readableContent.quotedMessageReference?.let {
Quote(it.quotedMessageId, it.quotedMessageSha256?.let { hash -> ByteArr(hash) })
}
val protoLegalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
return GenericMessage.Content.Text(
Text(
content = readableContent.value,
mentions = mentions,
quote = quote,
expectsReadConfirmation = expectsReadConfirmation
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = protoLegalHoldStatus
)
)
}
Expand Down Expand Up @@ -509,11 +560,30 @@ class ProtoContentMapperImpl(
}
}

private fun packAsset(readableContent: MessageContent.Asset, expectsReadConfirmation: Boolean): GenericMessage.Content.Asset {
private fun packKnock(
readableContent: MessageContent.Knock,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Knock {
val protoLegalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
return GenericMessage.Content.Knock(
Knock(
hotKnock = readableContent.hotKnock,
legalHoldStatus = protoLegalHoldStatus
)
)
}

private fun packAsset(
readableContent: MessageContent.Asset,
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Asset {
val protoLegalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
return GenericMessage.Content.Asset(
asset = assetMapper.fromAssetContentToProtoAssetMessage(
readableContent,
expectsReadConfirmation
expectsReadConfirmation,
protoLegalHoldStatus
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ fun WebEventContent.toMigratedMessage(selfUserDomain: String): MigratedMessage?
MessageContent.Text(
data.text,
),
data.expectsReadConfirmation ?: false
data.expectsReadConfirmation ?: false,
legalHoldStatus = if (data.legalHoldStatus == 2) Conversation.LegalHoldStatus.ENABLED
else Conversation.LegalHoldStatus.DISABLED
),
encryptedProto = null,
null,
Expand Down Expand Up @@ -110,7 +112,9 @@ fun WebEventContent.toMigratedMessage(selfUserDomain: String): MigratedMessage?
downloadStatus = Message.DownloadStatus.NOT_DOWNLOADED
),
),
data.expectsReadConfirmation
data.expectsReadConfirmation,
legalHoldStatus = if (data.legalHoldStatus == 2) Conversation.LegalHoldStatus.ENABLED
else Conversation.LegalHoldStatus.DISABLED
),
encryptedProto = null,
null,
Expand Down Expand Up @@ -172,7 +176,7 @@ fun WebConversationContent.toConversation(selfUserId: UserId): Conversation? {
archivedDateTime = conversationArchivedTimestamp,
mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED,
legalHoldStatus = if (legalHoldStatus == 1) Conversation.LegalHoldStatus.ENABLED
legalHoldStatus = if (legalHoldStatus == 2) Conversation.LegalHoldStatus.ENABLED
else Conversation.LegalHoldStatus.DISABLED
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package com.wire.kalium.logic.feature.message

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.client.MLSClientProvider
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.GroupID
import com.wire.kalium.logic.data.id.IdMapper
import com.wire.kalium.logic.data.message.Message
Expand Down Expand Up @@ -59,11 +60,18 @@ class MLSMessageCreatorImpl(
else -> false
}

// TODO(legalhold) - Get correct legal hold status
val legalHoldStatus = when (message) {
is Message.Regular -> Conversation.LegalHoldStatus.DISABLED
else -> Conversation.LegalHoldStatus.DISABLED
}

val content = protoContentMapper.encodeToProtobuf(
protoContent = ProtoContent.Readable(
messageUid = message.id,
messageContent = message.content,
expectsReadConfirmation = expectsReadConfirmation
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = legalHoldStatus
)
)
wrapMLSRequest { MLSMessageApi.Message(mlsClient.encryptMessage(idMapper.toCryptoModel(groupId), content.data)) }
Expand Down
Loading
Loading