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 @@ -10,7 +10,6 @@ import org.session.libsession.messaging.jobs.MessageSendJob
import org.session.libsession.messaging.messages.Message
import org.session.libsession.messaging.messages.control.GroupUpdated
import org.session.libsession.messaging.messages.visible.Attachment
import org.session.libsession.messaging.messages.visible.Profile
import org.session.libsession.messaging.messages.visible.Reaction
import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
Expand Down Expand Up @@ -38,7 +37,6 @@ interface StorageProtocol {
fun getUserED25519KeyPair(): KeyPair?
fun getUserX25519KeyPair(): KeyPair
fun getUserBlindedAccountId(serverPublicKey: String): AccountId?
fun getUserProfile(): Profile

// Jobs
fun persistJob(job: Job)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.session.libsession.messaging.messages

import network.loki.messenger.libsession_util.util.ExpiryMode
import org.session.libsession.database.MessageDataProvider
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
import org.session.libsession.messaging.messages.visible.VisibleMessage
Expand Down Expand Up @@ -47,18 +48,32 @@ abstract class Message {
&& sender != null
&& recipient != null

abstract fun toProto(): SignalServiceProtos.Content?
protected abstract fun buildProto(
builder: SignalServiceProtos.Content.Builder,
messageDataProvider: MessageDataProvider
)

abstract fun shouldDiscardIfBlocked(): Boolean

fun SignalServiceProtos.Content.Builder.applyExpiryMode() = apply {
expirationTimerSeconds = expiryMode.expirySeconds.toInt()
expirationType = when (expiryMode) {
fun toProto(
builder: SignalServiceProtos.Content.Builder,
messageDataProvider: MessageDataProvider
) {
// First apply common message data
// * Expiry mode
builder.expirationTimerSeconds = expiryMode.expirySeconds.toInt()
builder.expirationType = when (expiryMode) {
is ExpiryMode.AfterSend -> ExpirationType.DELETE_AFTER_SEND
is ExpiryMode.AfterRead -> ExpirationType.DELETE_AFTER_READ
else -> ExpirationType.UNKNOWN
}

// * Timestamps
builder.setSigTimestampMs(sentTimestamp!!)

// Then ask the subclasses to build their specific proto
buildProto(builder, messageDataProvider)
}

abstract fun shouldDiscardIfBlocked(): Boolean
}

inline fun <reified M: Message> M.copyExpiration(proto: SignalServiceProtos.Content): M = apply {
Expand All @@ -71,20 +86,10 @@ inline fun <reified M: Message> M.copyExpiration(proto: SignalServiceProtos.Cont
}
}

fun SignalServiceProtos.Content.expiryMode(): ExpiryMode =
(takeIf { it.hasExpirationTimerSeconds() }?.expirationTimerSeconds ?: dataMessage?.expireTimerSeconds)?.let { duration ->
when (expirationType.takeIf { duration > 0 }) {
ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong())
ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(duration.toLong())
else -> ExpiryMode.NONE
}
} ?: ExpiryMode.NONE

/**
* Apply ExpiryMode from the current setting.
*/
inline fun <reified M: Message> M.applyExpiryMode(recipientAddress: Address): M = apply {
expiryMode = MessagingModuleConfiguration.shared.recipientRepository.getRecipientSync(recipientAddress)
?.expiryMode?.coerceSendToRead(coerceDisappearAfterSendToRead)
?: ExpiryMode.NONE
.expiryMode.coerceSendToRead(coerceDisappearAfterSendToRead)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.session.libsession.messaging.messages

import com.google.protobuf.ByteString
import network.loki.messenger.libsession_util.util.BaseCommunityInfo
import network.loki.messenger.libsession_util.util.UserPic
import org.session.libsession.messaging.messages.visible.Profile
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.Address.Companion.toAddress
import org.session.libsession.utilities.ConfigFactoryProtocol
import org.session.libsession.utilities.updateContact
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.AccountId
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.database.BlindMappingRepository
Expand Down Expand Up @@ -164,44 +165,69 @@ class ProfileUpdateHandler @Inject constructor(
}

companion object {
fun create(
name: String? = null,
picUrl: String?,
picKey: ByteArray?,
blocksCommunityMessageRequests: Boolean? = null,
proStatus: Boolean? = null,
profileUpdateTime: Instant?
): Updates? {
val hasNameUpdate = !name.isNullOrBlank()
val pic = when {
picUrl == null -> null
picUrl.isBlank() || picKey == null || picKey.size !in VALID_PROFILE_KEY_LENGTH -> UserPic.DEFAULT
else -> UserPic(picUrl, picKey)
fun create(content: SignalServiceProtos.Content): Updates? {
val profile: SignalServiceProtos.DataMessage.LokiProfile
val profilePicKey: ByteString?

when {
content.hasDataMessage() && content.dataMessage.hasProfile() -> {
profile = content.dataMessage.profile
profilePicKey =
if (content.dataMessage.hasProfileKey()) content.dataMessage.profileKey else null
}

content.hasMessageRequestResponse() && content.messageRequestResponse.hasProfile() -> {
profile = content.messageRequestResponse.profile
profilePicKey =
if (content.messageRequestResponse.hasProfileKey()) content.messageRequestResponse.profileKey else null
}

else -> {
// No profile found, not updating.
// This is different from having an empty profile, which is a valid update.
return null
}
}

if (!hasNameUpdate && pic == null && blocksCommunityMessageRequests == null && proStatus == null) {
val pic = if (profile.hasProfilePicture()) {
if (!profile.profilePicture.isNullOrBlank() && profilePicKey != null &&
profilePicKey.size() in VALID_PROFILE_KEY_LENGTH) {
UserPic(
url = profile.profilePicture,
key = profilePicKey.toByteArray()
)
} else {
UserPic.DEFAULT // Clear the profile picture
}
} else {
null // No update to profile picture
}

val name = if (profile.hasDisplayName()) profile.displayName else null
val blocksCommunityMessageRequests = if (content.hasDataMessage() &&
content.dataMessage.hasBlocksCommunityMessageRequests()) {
content.dataMessage.blocksCommunityMessageRequests
} else {
null
}

if (name == null && pic == null && blocksCommunityMessageRequests == null) {
// Nothing is updated..
return null
}

return Updates(
name = if (hasNameUpdate) name else null,
name = name,
pic = pic,
blocksCommunityMessageRequests = blocksCommunityMessageRequests,
profileUpdateTime = profileUpdateTime
profileUpdateTime = if (profile.hasLastProfileUpdateSeconds()) {
Instant.ofEpochSecond(profile.lastProfileUpdateSeconds)
} else {
null
}
)
}

fun Profile.toUpdates(
blocksCommunityMessageRequests: Boolean? = null,
): Updates? {
return create(
name = this.displayName,
picUrl = this.profilePictureURL,
picKey = this.profileKey,
blocksCommunityMessageRequests = blocksCommunityMessageRequests,
profileUpdateTime = this.profileUpdated
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package org.session.libsession.messaging.messages.control

import org.session.libsession.messaging.messages.applyExpiryMode
import org.session.libsession.database.MessageDataProvider
import org.session.libsession.messaging.messages.copyExpiration
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.ANSWER
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.END_CALL
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.OFFER
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.PRE_OFFER
import org.session.libsignal.utilities.Log
import java.util.UUID

class CallMessage(): ControlMessage() {
Expand Down Expand Up @@ -77,23 +76,16 @@ class CallMessage(): ControlMessage() {
}
}

override fun toProto(): SignalServiceProtos.Content? {
val nonNullType = type ?: run {
Log.w(TAG,"Couldn't construct call message request proto from: $this")
return null
}

val callMessage = SignalServiceProtos.CallMessage.newBuilder()
.setType(nonNullType)
protected override fun buildProto(
builder: SignalServiceProtos.Content.Builder,
messageDataProvider: MessageDataProvider
) {
builder.callMessageBuilder
.setType(type!!)
.addAllSdps(sdps)
.addAllSdpMLineIndexes(sdpMLineIndexes)
.addAllSdpMids(sdpMids)
.setUuid(callId!!.toString())

return SignalServiceProtos.Content.newBuilder()
.applyExpiryMode()
.setCallMessage(callMessage)
.build()
}

override fun equals(other: Any?): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.session.libsession.messaging.messages.control

import org.session.libsession.database.MessageDataProvider
import org.session.libsession.messaging.messages.copyExpiration
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.Log

class DataExtractionNotification() : ControlMessage() {
var kind: Kind? = null
Expand Down Expand Up @@ -53,28 +53,17 @@ class DataExtractionNotification() : ControlMessage() {
}
}

override fun toProto(): SignalServiceProtos.Content? {
val kind = kind
if (kind == null) {
Log.w(TAG, "Couldn't construct data extraction notification proto from: $this")
return null
}
try {
val dataExtractionNotification = SignalServiceProtos.DataExtractionNotification.newBuilder()
when(kind) {
is Kind.Screenshot -> dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT
is Kind.MediaSaved -> {
dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED
dataExtractionNotification.timestampMs = kind.timestamp
}
protected override fun buildProto(
builder: SignalServiceProtos.Content.Builder,
messageDataProvider: MessageDataProvider
) {
val dataExtractionNotification = builder.dataExtractionNotificationBuilder
when (val kind = kind!!) {
is Kind.Screenshot -> dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT
is Kind.MediaSaved -> {
dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED
dataExtractionNotification.timestampMs = kind.timestamp
}
return SignalServiceProtos.Content.newBuilder()
.setDataExtractionNotification(dataExtractionNotification.build())
.applyExpiryMode()
.build()
} catch (e: Exception) {
Log.w(TAG, "Couldn't construct data extraction notification proto from: $this")
return null
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.session.libsession.messaging.messages.control

import org.session.libsession.database.MessageDataProvider
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.copyExpiration
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE
import org.session.libsignal.utilities.Log

/** In the case of a sync message, the public key of the person the message was targeted at.
*
Expand All @@ -25,21 +25,16 @@ data class ExpirationTimerUpdate(var syncTarget: String? = null, val isGroup: Bo
}
}

override fun toProto(): SignalServiceProtos.Content? {
val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder().apply {
flags = EXPIRATION_TIMER_UPDATE_VALUE
expireTimerSeconds = expiryMode.expirySeconds.toInt()
}
// Sync target
syncTarget?.let { dataMessageProto.syncTarget = it }
return try {
SignalServiceProtos.Content.newBuilder()
.setDataMessage(dataMessageProto)
.applyExpiryMode()
.build()
} catch (e: Exception) {
Log.w(TAG, "Couldn't construct expiration timer update proto from: $this", e)
null
}
protected override fun buildProto(
builder: SignalServiceProtos.Content.Builder,
messageDataProvider: MessageDataProvider
) {
builder.dataMessageBuilder
.setFlags(EXPIRATION_TIMER_UPDATE_VALUE)
.setExpireTimerSeconds(expiryMode.expirySeconds.toInt())
.also { builder ->
// Sync target
syncTarget?.let { builder.syncTarget = it }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package org.session.libsession.messaging.messages.control

import org.session.libsession.messaging.messages.visible.Profile
import org.session.libsession.database.MessageDataProvider
import org.session.libsignal.protos.SignalServiceProtos.Content
import org.session.libsignal.protos.SignalServiceProtos.DataMessage
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateMessage

class GroupUpdated @JvmOverloads constructor(
val inner: GroupUpdateMessage = GroupUpdateMessage.getDefaultInstance(),
val profile: Profile? = null
): ControlMessage() {

override fun isValid(): Boolean {
Expand All @@ -26,18 +24,13 @@ class GroupUpdated @JvmOverloads constructor(
if (message.hasDataMessage() && message.dataMessage.hasGroupUpdateMessage())
GroupUpdated(
inner = message.dataMessage.groupUpdateMessage,
profile = Profile.fromProto(message.dataMessage)
)
else null
}

override fun toProto(): Content {
val dataMessage = DataMessage.newBuilder()
override fun buildProto(builder: Content.Builder, messageDataProvider: MessageDataProvider) {
builder.dataMessageBuilder
.setGroupUpdateMessage(inner)
.apply { profile?.let(this::setProfile) }
.build()
return Content.newBuilder()
.setDataMessage(dataMessage)
.build()
}
}
Loading