diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index e735ea9c74..c961be7264 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -572,7 +572,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe private fun setUpOutdatedClientBanner() { val recipient = viewModel.recipient ?: return - if (!ExpirationConfiguration.isNewConfigEnabled && recipient.isContactRecipient && + if (ExpirationConfiguration.isNewConfigEnabled && recipient.isContactRecipient && recipient.disappearingState == DisappearingState.LEGACY && viewModel.expirationConfiguration?.isEnabled == true ) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java index d7eb563e8f..57ac533e65 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java @@ -84,9 +84,6 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM if (expiryType == ExpirationType.DELETE_AFTER_SEND_VALUE && message.getSentTimestamp() != null && senderPublicKey != null) { startAnyExpiration(message.getSentTimestamp(), senderPublicKey, expireStartedAt); } - if (message.getId() != null) { - DatabaseComponent.get(context).smsDatabase().deleteMessage(message.getId()); - } } private void insertIncomingExpirationTimerMessage(ExpirationTimerUpdate message, long expireStartedAt) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt index 84af6ebdec..5a756d8b64 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt @@ -18,7 +18,7 @@ import org.session.libsession.messaging.sending_receiving.MessageReceiver import org.session.libsession.messaging.sending_receiving.handle import org.session.libsession.messaging.sending_receiving.handleOpenGroupReactions import org.session.libsession.messaging.sending_receiving.handleVisibleMessage -import org.session.libsession.messaging.sending_receiving.updateExpirationConfigurationIfNeeded +import org.session.libsession.messaging.sending_receiving.updateExpiryIfNeeded import org.session.libsession.messaging.utilities.Data import org.session.libsession.messaging.utilities.SessionId import org.session.libsession.messaging.utilities.SodiumUtilities @@ -113,7 +113,7 @@ class BatchMessageReceiveJob( messages.forEach { (parameters, message, proto) -> try { if (message is VisibleMessage) { - MessageReceiver.updateExpirationConfigurationIfNeeded(message, proto, openGroupID) + MessageReceiver.updateExpiryIfNeeded(message, proto, openGroupID) val messageId = MessageReceiver.handleVisibleMessage(message, proto, openGroupID, runIncrement = false, runThreadUpdate = false, diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/ExpirationConfiguration.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/ExpirationConfiguration.kt index 8254b64c64..cfaa2bd084 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/ExpirationConfiguration.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/ExpirationConfiguration.kt @@ -13,5 +13,6 @@ class ExpirationConfiguration( companion object { val isNewConfigEnabled = false /* TODO: System.currentTimeMillis() > 1_676_851_200_000 // 13/02/2023 */ + const val LAST_READ_TEST = 1673587663000L } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt index 6a38a551f8..6e0ea6e775 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt @@ -35,11 +35,12 @@ object MessageReceiver { object InvalidGroupPublicKey: Error("Invalid group public key.") object NoGroupKeyPair: Error("Missing group key pair.") object NoUserED25519KeyPair : Error("Couldn't find user ED25519 key pair.") + object ExpiredMessage: Error("Message has already expired, prevent adding") internal val isRetryable: Boolean = when (this) { is DuplicateMessage, is InvalidMessage, is UnknownMessage, is UnknownEnvelopeType, is InvalidSignature, is NoData, - is SenderBlocked, is SelfSend -> false + is SenderBlocked, is SelfSend, is ExpiredMessage -> false else -> true } } @@ -49,7 +50,7 @@ object MessageReceiver { openGroupServerID: Long?, isOutgoing: Boolean? = null, otherBlindedPublicKey: String? = null, - openGroupPublicKey: String? = null, + openGroupPublicKey: String? = null ): Pair { val storage = MessagingModuleConfiguration.shared.storage val userPublicKey = storage.getUserPublicKey() @@ -179,4 +180,5 @@ object MessageReceiver { // Return return Pair(message, proto) } + } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index 5052ab1530..ec0452bedd 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -39,7 +39,6 @@ import org.session.libsession.utilities.ProfileKeyUtil import org.session.libsession.utilities.SSKEnvironment import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient -import org.session.libsession.utilities.recipients.Recipient.DisappearingState import org.session.libsignal.crypto.ecc.DjbECPrivateKey import org.session.libsignal.crypto.ecc.DjbECPublicKey import org.session.libsignal.crypto.ecc.ECKeyPair @@ -63,7 +62,7 @@ internal fun MessageReceiver.isBlocked(publicKey: String): Boolean { } fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content, openGroupID: String?) { - updateExpirationConfigurationIfNeeded(message, proto, openGroupID) + MessageReceiver.updateExpiryIfNeeded(message, proto, openGroupID) when (message) { is ReadReceipt -> handleReadReceipt(message) is TypingIndicator -> handleTypingIndicator(message) @@ -82,29 +81,6 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content, } } -fun MessageReceiver.updateExpirationConfigurationIfNeeded(message: Message, proto: SignalServiceProtos.Content, openGroupID: String?) { - val storage = MessagingModuleConfiguration.shared.storage - val disappearingState = if (proto.hasExpirationTimer()) DisappearingState.UPDATED else DisappearingState.LEGACY - if (!proto.hasLastDisappearingMessageChangeTimestamp() || !ExpirationConfiguration.isNewConfigEnabled) return - val threadID = storage.getOrCreateThreadIdFor(message.sender!!, message.groupPublicKey, openGroupID) - if (threadID <= 0) return - storage.updateDisappearingState(threadID, disappearingState) - val localConfig = storage.getExpirationConfiguration(threadID) - if (localConfig != null && localConfig.updatedTimestampMs > proto.lastDisappearingMessageChangeTimestamp) return - val durationSeconds = if (proto.hasExpirationTimer()) proto.expirationTimer else 0 - val type = if (proto.hasExpirationType()) proto.expirationType else null - val remoteConfig = ExpirationConfiguration( - threadID, - durationSeconds, - type?.number ?: -1, - proto.lastDisappearingMessageChangeTimestamp - ) - storage.setExpirationConfiguration(remoteConfig) - if (message is ExpirationTimerUpdate) { - SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, type?.number ?: -1) - } -} - // region Control Messages private fun MessageReceiver.handleReadReceipt(message: ReadReceipt) { val context = MessagingModuleConfiguration.shared.context @@ -256,6 +232,63 @@ fun handleMessageRequestResponse(message: MessageRequestResponse) { } //endregion +fun MessageReceiver.updateExpiryIfNeeded(message: Message, proto: SignalServiceProtos.Content, openGroupID: String?) { + val storage = MessagingModuleConfiguration.shared.storage + val sentTime = message.sentTimestamp ?: throw MessageReceiver.Error.InvalidMessage + if (!proto.hasLastDisappearingMessageChangeTimestamp()) return + val threadID = storage.getOrCreateThreadIdFor(message.sender!!, message.groupPublicKey, openGroupID) + if (threadID <= 0) throw MessageReceiver.Error.NoThread + + val localConfig = storage.getExpirationConfiguration(threadID) + + val durationSeconds = if (proto.hasExpirationTimer()) proto.expirationTimer else 0 + val type = if (proto.hasExpirationType()) proto.expirationType else null + val remoteConfig = ExpirationConfiguration( + threadID, + durationSeconds, + type?.number ?: -1, + proto.lastDisappearingMessageChangeTimestamp + ) + + val (shouldUpdateConfig, configToUse) = + if (localConfig != null && localConfig.updatedTimestampMs > proto.lastDisappearingMessageChangeTimestamp) { + false to localConfig + } else { + true to remoteConfig + } + + val recipient = storage.getRecipientForThread(threadID) ?: throw MessageReceiver.Error.NoThread + + // don't update any values for open groups + if (recipient.isOpenGroupRecipient && type != null) throw MessageReceiver.Error.InvalidMessage + if ((recipient.isGroupRecipient || recipient.isLocalNumber) + && type == ExpirationType.DELETE_AFTER_READ) { + // don't allow deleteAfterRead if we are sending to note to self or a group, treat the entire message as invalid + throw MessageReceiver.Error.InvalidMessage + } + + // handle a delete after send expired fetch + if (type == ExpirationType.DELETE_AFTER_SEND + && sentTime + configToUse.durationSeconds <= SnodeAPI.nowWithClockOffset) { + throw MessageReceiver.Error.ExpiredMessage + } + // handle a delete after read last known config value (test) TODO: actually implement this with shared config library + if (type == ExpirationType.DELETE_AFTER_READ + && sentTime + configToUse.durationSeconds <= ExpirationConfiguration.LAST_READ_TEST) { + throw MessageReceiver.Error.ExpiredMessage + } + + if (shouldUpdateConfig) { + val disappearingState = if (proto.hasExpirationType()) Recipient.DisappearingState.UPDATED else Recipient.DisappearingState.LEGACY + storage.updateDisappearingState(threadID, disappearingState) + storage.setExpirationConfiguration(configToUse) + } + + if (message is ExpirationTimerUpdate) { + SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, type?.number ?: -1) + } +} + fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalServiceProtos.Content, openGroupID: String?, diff --git a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt index f93a7b243e..3f9fc0c225 100644 --- a/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/OnionRequestAPI.kt @@ -25,7 +25,6 @@ import org.session.libsignal.utilities.Snode import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.recover import org.session.libsignal.utilities.toHexString -import java.util.Date import kotlin.collections.set private typealias Path = List @@ -594,7 +593,7 @@ object OnionRequestAPI { } if (body["t"] != null) { val timestamp = body["t"] as Long - val offset = timestamp - Date().time + val offset = timestamp - System.currentTimeMillis() SnodeAPI.clockOffset = offset } if (body.containsKey("hf")) { diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 6915c3d9a4..f373714349 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -56,6 +56,10 @@ object SnodeAPI { * user's clock is incorrect. */ var clockOffset = 0L + + val nowWithClockOffset + get() = System.currentTimeMillis() + clockOffset + internal var forkInfo by observable(database.getForkInfo()) { _, oldValue, newValue -> if (newValue > oldValue) { Log.d("Loki", "Setting new fork info new: $newValue, old: $oldValue")