From 7bd43b1b3cd0c1c217161728dac4c735218c672a Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 28 Nov 2023 12:47:27 +1030 Subject: [PATCH] Fix control messages --- .../DisappearingMessagesViewModel.kt | 11 +++- .../v2/components/ExpirationTimerView.kt | 2 - .../v2/messages/VisibleMessageView.kt | 26 ++++---- .../securesms/database/ExpirationInfo.kt | 3 + .../securesms/database/MarkedMessageInfo.kt | 14 +++++ .../securesms/database/MessagingDatabase.java | 61 ------------------- .../securesms/database/Storage.kt | 11 +--- .../securesms/database/ThreadDatabase.java | 1 - .../AndroidAutoHeardReceiver.java | 2 +- .../AndroidAutoReplyReceiver.java | 2 +- .../notifications/MarkReadReceiver.kt | 24 ++++---- .../notifications/RemoteReplyReceiver.java | 2 +- .../service/ExpiringMessageManager.java | 39 ++++++------ .../libsession/messaging/messages/Message.kt | 12 ++-- .../messages/control/ExpirationTimerUpdate.kt | 23 ++++--- .../sending_receiving/MessageReceiver.kt | 6 +- .../ReceivedMessageHandler.kt | 32 +++++----- .../utilities/ExpirationUtilities.kt | 31 ---------- 18 files changed, 110 insertions(+), 192 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/ExpirationInfo.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/MarkedMessageInfo.kt delete mode 100644 libsession/src/main/java/org/session/libsession/utilities/ExpirationUtilities.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/DisappearingMessagesViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/DisappearingMessagesViewModel.kt index 761d6cd07..fb9ed095c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/DisappearingMessagesViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/disappearingmessages/DisappearingMessagesViewModel.kt @@ -61,7 +61,7 @@ class DisappearingMessagesViewModel( init { viewModelScope.launch { - val expiryMode = storage.getExpirationConfiguration(threadId)?.expiryMode ?: ExpiryMode.NONE + val expiryMode = storage.getExpirationConfiguration(threadId)?.expiryMode?.maybeConvertToLegacy(isNewConfigEnabled) ?: ExpiryMode.NONE val recipient = threadDb.getRecipientForThreadId(threadId) val groupRecord = recipient?.takeIf { it.isClosedGroupRecipient } ?.run { groupDb.getGroup(address.toGroupString()).orNull() } @@ -83,7 +83,7 @@ class DisappearingMessagesViewModel( override fun onSetClick() = viewModelScope.launch { val state = _state.value - val mode = state.expiryMode + val mode = state.expiryMode?.coerceLegacyToAfterSend() val address = state.address if (address == null || mode == null) { _event.send(Event.FAIL) @@ -93,8 +93,9 @@ class DisappearingMessagesViewModel( val expiryChangeTimestampMs = SnodeAPI.nowWithOffset storage.setExpirationConfiguration(ExpirationConfiguration(threadId, mode, expiryChangeTimestampMs)) - val message = ExpirationTimerUpdate(mode.expirySeconds.toInt()).apply { + val message = ExpirationTimerUpdate(mode).apply { sender = textSecurePreferences.getLocalNumber() + isSenderSelf = true recipient = address.serialize() sentTimestamp = expiryChangeTimestampMs } @@ -106,6 +107,8 @@ class DisappearingMessagesViewModel( _event.send(Event.SUCCESS) } + private fun ExpiryMode.coerceLegacyToAfterSend() = takeUnless { it is ExpiryMode.Legacy } ?: ExpiryMode.AfterSend(expirySeconds) + @dagger.assisted.AssistedFactory interface AssistedFactory { fun create(threadId: Long): Factory @@ -135,3 +138,5 @@ class DisappearingMessagesViewModel( ) as T } } + +private fun ExpiryMode.maybeConvertToLegacy(isNewConfigEnabled: Boolean): ExpiryMode = takeIf { isNewConfigEnabled } ?: ExpiryMode.Legacy(expirySeconds) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/ExpirationTimerView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/ExpirationTimerView.kt index 634937236..257d30866 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/ExpirationTimerView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/ExpirationTimerView.kt @@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.conversation.v2.components import android.content.Context import android.graphics.drawable.AnimationDrawable import android.util.AttributeSet -import android.util.Log -import android.widget.ImageView import androidx.appcompat.widget.AppCompatImageView import androidx.core.content.ContextCompat import network.loki.messenger.R diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index 3f128652d..d3925197f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -30,6 +30,7 @@ import network.loki.messenger.databinding.ViewVisibleMessageBinding import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact.ContactContext import org.session.libsession.messaging.open_groups.OpenGroupApi +import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.Address import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.getColorFromAttr @@ -348,23 +349,18 @@ class VisibleMessageView : LinearLayout { } private fun updateExpirationTimer(message: MessageRecord) { - val expirationTimerView = binding.expirationTimerView - if (!message.isOutgoing) binding.messageStatusTextView.bringToFront() - if (message.expiresIn > 0) { - if (message.expireStarted > 0) { - expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn) - ApplicationContext.getInstance(context).expiringMessageManager.checkSchedule() - } else { - ThreadUtils.queue { - val expirationManager = ApplicationContext.getInstance(context).expiringMessageManager - val id = message.getId() - val mms = message.isMms - if (mms) mmsDb.markExpireStarted(id) else smsDb.markExpireStarted(id) - expirationManager.scheduleDeletion(id, mms, message.expiresIn) - } - } + val expireStarted = message.expireStarted.takeIf { it > 0 } ?: SnodeAPI.nowWithOffset + + val id = message.getId() + val mms = message.isMms + binding.expirationTimerView.setExpirationTime(expireStarted, message.expiresIn) + ThreadUtils.queue { + val db = if (mms) mmsDb else smsDb + db.markExpireStarted(id, expireStarted) + ApplicationContext.getInstance(context).expiringMessageManager + .scheduleDeletion(id, mms, expireStarted, message.expiresIn) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ExpirationInfo.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ExpirationInfo.kt new file mode 100644 index 000000000..f9adad892 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ExpirationInfo.kt @@ -0,0 +1,3 @@ +package org.thoughtcrime.securesms.database + +data class ExpirationInfo(val id: Long, val expiresIn: Long, val expireStarted: Long, val isMms: Boolean) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MarkedMessageInfo.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MarkedMessageInfo.kt new file mode 100644 index 000000000..4f32077d4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MarkedMessageInfo.kt @@ -0,0 +1,14 @@ +package org.thoughtcrime.securesms.database + +import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType +import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId + +data class MarkedMessageInfo(val syncMessageId: SyncMessageId, val expirationInfo: ExpirationInfo) { + fun guessExpiryType(): ExpiryType = expirationInfo.run { + when { + syncMessageId.timetamp == expireStarted -> ExpiryType.AFTER_SEND + expiresIn > 0 -> ExpiryType.AFTER_READ + else -> ExpiryType.NONE + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java index dec5f0045..1c998ff06 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessagingDatabase.java @@ -12,7 +12,6 @@ import org.session.libsession.utilities.Document; import org.session.libsession.utilities.IdentityKeyMismatch; import org.session.libsession.utilities.IdentityKeyMismatchList; import org.session.libsignal.crypto.IdentityKey; -import org.session.libsignal.protos.SignalServiceProtos; import org.session.libsignal.utilities.JsonUtil; import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType; @@ -227,66 +226,6 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn } } - public static class ExpirationInfo { - - private final long id; - private final long expiresIn; - private final long expireStarted; - private final boolean mms; - - public ExpirationInfo(long id, long expiresIn, long expireStarted, boolean mms) { - this.id = id; - this.expiresIn = expiresIn; - this.expireStarted = expireStarted; - this.mms = mms; - } - - public long getId() { - return id; - } - - public long getExpiresIn() { - return expiresIn; - } - - public long getExpireStarted() { - return expireStarted; - } - - public boolean isMms() { - return mms; - } - } - - public static class MarkedMessageInfo { - - private final SyncMessageId syncMessageId; - private final ExpirationInfo expirationInfo; - - public MarkedMessageInfo(SyncMessageId syncMessageId, ExpirationInfo expirationInfo) { - this.syncMessageId = syncMessageId; - this.expirationInfo = expirationInfo; - } - - public SyncMessageId getSyncMessageId() { - return syncMessageId; - } - - public ExpirationInfo getExpirationInfo() { - return expirationInfo; - } - - public ExpiryType guessExpiryType() { - long expireStarted = expirationInfo.expireStarted; - long expiresIn = expirationInfo.expiresIn; - long timestamp = syncMessageId.timetamp; - - if (timestamp == expireStarted) return ExpiryType.AFTER_SEND; - if (expiresIn > 0) return ExpiryType.AFTER_READ; - return ExpiryType.NONE; - } - } - public static class InsertResult { private final long messageId; private final long threadId; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 9f692c6f4..cdca00b08 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -1718,9 +1718,7 @@ open class Storage( ?.run { disappearingTimer.takeIf { it != 0L }?.let(ExpiryMode::AfterSend) ?: ExpiryMode.NONE } } else -> null - } - ?.run { takeIf { isNewConfigEnabled || it is ExpiryMode.NONE } ?: ExpiryMode.Legacy(expirySeconds) } - ?.let { ExpirationConfiguration(threadId, it, dbExpirationMetadata.updatedTimestampMs) } + }?.let { ExpirationConfiguration(threadId, it, dbExpirationMetadata.updatedTimestampMs) } } override fun setExpirationConfiguration(config: ExpirationConfiguration) { @@ -1729,12 +1727,7 @@ open class Storage( val expirationDb = DatabaseComponent.get(context).expirationConfigurationDatabase() val currentConfig = expirationDb.getExpirationConfiguration(config.threadId) if (currentConfig != null && currentConfig.updatedTimestampMs >= config.updatedTimestampMs) return - - val expiryMode = config.expiryMode.run { - takeUnless { it is ExpiryMode.Legacy } - ?: if (recipient.isContactRecipient) ExpiryMode.AfterRead(expirySeconds) - else ExpiryMode.AfterSend(expirySeconds) - } + val expiryMode = config.expiryMode if (recipient.isClosedGroupRecipient) { val userGroups = configFactory.userGroups ?: return diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index 9c3db8446..fd5042086 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -51,7 +51,6 @@ import org.session.libsignal.utilities.Pair; import org.session.libsignal.utilities.guava.Optional; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.contacts.ContactUtil; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord; diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java index 21157d0f5..88f92ecb4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java @@ -27,7 +27,7 @@ import androidx.core.app.NotificationManagerCompat; import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.MarkedMessageInfo; import org.thoughtcrime.securesms.dependencies.DatabaseComponent; import java.util.LinkedList; diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java index fb10a4bd7..0bfa2b089 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java @@ -36,7 +36,7 @@ import org.session.libsession.utilities.Address; import org.session.libsession.utilities.recipients.Recipient; import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.MarkedMessageInfo; import org.thoughtcrime.securesms.dependencies.DatabaseComponent; import org.thoughtcrime.securesms.mms.MmsException; diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.kt index b15670183..c9aaf27fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.kt @@ -18,8 +18,8 @@ import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType -import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo +import org.thoughtcrime.securesms.database.ExpirationInfo +import org.thoughtcrime.securesms.database.MarkedMessageInfo import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.util.SessionMetaProtocol.shouldSendReadReceipt @@ -129,16 +129,18 @@ class MarkReadReceiver : BroadcastReceiver() { expirationInfo: ExpirationInfo, expiresIn: Long = expirationInfo.expiresIn ) { - if (expiresIn > 0 && expirationInfo.expireStarted <= 0) { - if (expirationInfo.isMms) DatabaseComponent.get(context!!).mmsDatabase().markExpireStarted(expirationInfo.id) - else DatabaseComponent.get(context!!).smsDatabase().markExpireStarted(expirationInfo.id) + if (expiresIn <= 0 || expirationInfo.expireStarted > 0) return - ApplicationContext.getInstance(context).expiringMessageManager.scheduleDeletion( - expirationInfo.id, - expirationInfo.isMms, - expiresIn - ) - } + val now = SnodeAPI.nowWithOffset + val db = DatabaseComponent.get(context!!).run { if (expirationInfo.isMms) mmsDatabase() else smsDatabase() } + db.markExpireStarted(expirationInfo.id, now) + + ApplicationContext.getInstance(context).expiringMessageManager.scheduleDeletion( + expirationInfo.id, + expirationInfo.isMms, + now, + expiresIn + ) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java index 95ef513cd..cf0e04ddf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java @@ -36,7 +36,7 @@ import org.session.libsession.utilities.Address; import org.session.libsession.utilities.recipients.Recipient; import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; +import org.thoughtcrime.securesms.database.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.Storage; 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 662f7b8d4..96fa5e4db 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java @@ -9,6 +9,7 @@ import org.session.libsession.messaging.messages.ExpirationConfiguration; import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate; import org.session.libsession.messaging.messages.signal.IncomingMediaMessage; import org.session.libsession.messaging.messages.signal.OutgoingExpirationUpdateMessage; +import org.session.libsession.snode.SnodeAPI; import org.session.libsession.utilities.Address; import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.SSKEnvironment; @@ -54,10 +55,6 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM executor.execute(new ProcessTask()); } - public void scheduleDeletion(long id, boolean mms, long expiresInMillis) { - scheduleDeletion(id, mms, System.currentTimeMillis(), expiresInMillis); - } - public void scheduleDeletion(long id, boolean mms, long startedAtTimestamp, long expiresInMillis) { long expiresAtMillis = startedAtTimestamp + expiresInMillis; @@ -78,8 +75,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM String userPublicKey = TextSecurePreferences.getLocalNumber(context); String senderPublicKey = message.getSender(); long sentTimestamp = message.getSentTimestamp() == null ? 0 : message.getSentTimestamp(); - long expireStartedAt = (expiryMode instanceof ExpiryMode.AfterSend) - ? sentTimestamp : 0; + long expireStartedAt = (expiryMode instanceof ExpiryMode.AfterSend || message.isSenderSelf()) ? sentTimestamp : 0; // Notify the user if (senderPublicKey == null || userPublicKey.equals(senderPublicKey)) { @@ -88,7 +84,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM } else { insertIncomingExpirationTimerMessage(message, expireStartedAt); } - if (expiryMode instanceof ExpiryMode.AfterSend && message.getSentTimestamp() != null && senderPublicKey != null) { + if (expiryMode.getExpirySeconds() > 0 && message.getSentTimestamp() != null && senderPublicKey != null) { startAnyExpiration(message.getSentTimestamp(), senderPublicKey, expireStartedAt); } } @@ -98,7 +94,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM String senderPublicKey = message.getSender(); Long sentTimestamp = message.getSentTimestamp(); String groupId = message.getGroupPublicKey(); - long expiresInMillis = message.getDuration() * 1000L; + long expiresInMillis = message.getExpiryMode().getExpiryMillis(); Optional groupInfo = Optional.absent(); Address address = Address.fromSerialized(senderPublicKey); @@ -144,7 +140,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM Long sentTimestamp = message.getSentTimestamp(); String groupId = message.getGroupPublicKey(); - int duration = message.getDuration(); + long duration = message.getExpiryMode().getExpiryMillis(); Address address; @@ -159,7 +155,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM StorageProtocol storage = MessagingModuleConfiguration.getShared().getStorage(); message.setThreadID(storage.getOrCreateThreadIdFor(address)); - OutgoingExpirationUpdateMessage timerUpdateMessage = new OutgoingExpirationUpdateMessage(recipient, sentTimestamp, duration * 1000L, expireStartedAt, groupId); + OutgoingExpirationUpdateMessage timerUpdateMessage = new OutgoingExpirationUpdateMessage(recipient, sentTimestamp, duration, expireStartedAt, groupId); mmsDatabase.insertSecureDecryptedMessageOutbox(timerUpdateMessage, message.getThreadID(), sentTimestamp, true); } catch (MmsException | IOException ioe) { Log.e("Loki", "Failed to insert expiration update message.", ioe); @@ -169,18 +165,17 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM @Override public void startAnyExpiration(long timestamp, @NotNull String author, long expireStartedAt) { MessageRecord messageRecord = mmsSmsDatabase.getMessageFor(timestamp, author); - if (messageRecord != null) { - boolean mms = messageRecord.isMms(); - ExpirationConfiguration config = DatabaseComponent.get(context).storage().getExpirationConfiguration(messageRecord.getThreadId()); - if (config == null || !config.isEnabled()) return; - ExpiryMode mode = config.getExpiryMode(); - if (mms) { - mmsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt); - } else { - smsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt); - } - scheduleDeletion(messageRecord.getId(), mms, expireStartedAt, (mode != null ? mode.getExpiryMillis() : 0)); + if (messageRecord == null) return; + boolean mms = messageRecord.isMms(); + ExpirationConfiguration config = DatabaseComponent.get(context).storage().getExpirationConfiguration(messageRecord.getThreadId()); + if (config == null || !config.isEnabled()) return; + ExpiryMode mode = config.getExpiryMode(); + if (mms) { + mmsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt); + } else { + smsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt); } + scheduleDeletion(messageRecord.getId(), mms, expireStartedAt, (mode != null ? mode.getExpiryMillis() : 0)); } private class LoadTask implements Runnable { @@ -219,7 +214,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM while (expiringMessageReferences.isEmpty()) expiringMessageReferences.wait(); ExpiringMessageReference nextReference = expiringMessageReferences.first(); - long waitTime = nextReference.expiresAtMillis - System.currentTimeMillis(); + long waitTime = nextReference.expiresAtMillis - SnodeAPI.getNowWithOffset(); if (waitTime > 0) { ExpirationListener.setAlarm(context, waitTime); diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt index 2ead594de..2fa04aee6 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt @@ -64,13 +64,11 @@ abstract class Message { } expirationTimer = config.expiryMode.expirySeconds.toInt() lastDisappearingMessageChangeTimestamp = config.updatedTimestampMs - if (ExpirationConfiguration.isNewConfigEnabled) { - config.expiryMode.let { expiryMode -> - when (expiryMode) { - is ExpiryMode.AfterSend -> expirationType = ExpirationType.DELETE_AFTER_SEND - is ExpiryMode.AfterRead -> expirationType = ExpirationType.DELETE_AFTER_READ - is ExpiryMode.Legacy, ExpiryMode.NONE -> { /* do nothing */ } - } + config.expiryMode.let { expiryMode -> + expirationType = when (expiryMode) { + is ExpiryMode.Legacy, is ExpiryMode.AfterSend -> ExpirationType.DELETE_AFTER_SEND + is ExpiryMode.AfterRead -> ExpirationType.DELETE_AFTER_READ + ExpiryMode.NONE -> ExpirationType.UNKNOWN } } return this diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt index bcb71a483..18a0472c5 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt @@ -1,5 +1,6 @@ package org.session.libsession.messaging.messages.control +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.messages.visible.VisibleMessage @@ -10,15 +11,10 @@ import org.session.libsignal.utilities.Log * * **Note:** `nil` if this isn't a sync message. */ -data class ExpirationTimerUpdate(var duration: Int? = 0, var syncTarget: String? = null) : ControlMessage() { +data class ExpirationTimerUpdate(var expiryMode: ExpiryMode, var syncTarget: String? = null) : ControlMessage() { override val isSelfSendValid: Boolean = true - override fun isValid(): Boolean { - if (!super.isValid()) return false - return duration != null || ExpirationConfiguration.isNewConfigEnabled - } - companion object { const val TAG = "ExpirationTimerUpdate" @@ -29,15 +25,22 @@ data class ExpirationTimerUpdate(var duration: Int? = 0, var syncTarget: String? ) != 0 if (!isExpirationTimerUpdate) return null val syncTarget = dataMessageProto.syncTarget - val duration = if (proto.hasExpirationTimer()) proto.expirationTimer else dataMessageProto.expireTimer - return ExpirationTimerUpdate(duration, syncTarget) + val duration: Int = if (proto.hasExpirationTimer()) proto.expirationTimer else dataMessageProto.expireTimer + val type = proto.expirationType.takeIf { duration > 0 } + val expiryMode = when (type) { + SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong()) + SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(duration.toLong()) + else -> ExpiryMode.NONE + } + + return ExpirationTimerUpdate(expiryMode, syncTarget) } } override fun toProto(): SignalServiceProtos.Content? { val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder() dataMessageProto.flags = SignalServiceProtos.DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE - duration?.let { dataMessageProto.expireTimer = it } + dataMessageProto.expireTimer = expiryMode.expirySeconds.toInt() // Sync target if (syncTarget != null) { dataMessageProto.syncTarget = syncTarget @@ -53,6 +56,8 @@ data class ExpirationTimerUpdate(var duration: Int? = 0, var syncTarget: String? } return try { SignalServiceProtos.Content.newBuilder().apply { + expirationType + expirationTimer dataMessage = dataMessageProto.build() setExpirationConfigurationIfNeeded(threadID) }.build() 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 ed174e78f..9fb55b249 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 @@ -148,12 +148,14 @@ object MessageReceiver { VisibleMessage.fromProto(proto) ?: run { throw Error.UnknownMessage } + val isUserBlindedSender = sender == openGroupPublicKey?.let { SodiumUtilities.blindedKeyPair(it, MessagingModuleConfiguration.shared.getUserED25519KeyPair()!!) }?.let { SessionId(IdPrefix.BLINDED, it.publicKey.asBytes).hexString } + val isUserSender = sender == userPublicKey // Ignore self send if needed - if (!message.isSelfSendValid && (sender == userPublicKey || isUserBlindedSender)) { + if (!message.isSelfSendValid && (isUserSender || isUserBlindedSender)) { throw Error.SelfSend } - if (sender == userPublicKey || isUserBlindedSender) { + if (isUserSender || isUserBlindedSender) { message.isSenderSelf = true } // Guard against control messages in open groups 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 29f02845c..75aa66285 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 @@ -40,7 +40,6 @@ import org.session.libsession.utilities.GroupUtil.doubleEncodeGroupID import org.session.libsession.utilities.ProfileKeyUtil import org.session.libsession.utilities.SSKEnvironment import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.expiryMode import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.crypto.ecc.DjbECPrivateKey import org.session.libsignal.crypto.ecc.DjbECPublicKey @@ -156,28 +155,21 @@ fun MessageReceiver.cancelTypingIndicatorsIfNeeded(senderPublicKey: String) { private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimerUpdate) { if (ExpirationConfiguration.isNewConfigEnabled) return val module = MessagingModuleConfiguration.shared - val recipient = Recipient.from(module.context, Address.fromSerialized(message.sender!!), false) - val type = when { - recipient.isContactRecipient -> ExpiryMode.AfterRead(message.duration!!.toLong()) - recipient.isGroupRecipient -> ExpiryMode.AfterSend(message.duration!!.toLong()) - else -> ExpiryMode.NONE - } try { - var threadId: Long = module.storage.getOrCreateThreadIdFor(fromSerialized(message.sender!!)) - if (message.groupPublicKey != null) { - threadId = module.storage.getOrCreateThreadIdFor(fromSerialized(doubleEncodeGroupID(message.groupPublicKey!!))) - } + val threadId = fromSerialized(message.groupPublicKey?.let(::doubleEncodeGroupID) ?: message.sender!!) + .let(module.storage::getOrCreateThreadIdFor) + module.storage.setExpirationConfiguration( ExpirationConfiguration( threadId, - type, + message.expiryMode, message.sentTimestamp!! ) ) } catch (e: Exception) { Log.e("Loki", "Failed to update expiration configuration.") } - SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, type) + SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, message.expiryMode) } private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) { @@ -317,10 +309,18 @@ fun MessageReceiver.updateExpiryIfNeeded( if (message is ExpirationTimerUpdate) { - SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, type?.expiryMode(durationSeconds.toLong())) + SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, expiryMode) } } +private fun SignalServiceProtos.Content.ExpirationType.expiryMode(durationSeconds: Long) = takeIf { durationSeconds > 0 }?.let { + when (it) { + SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(durationSeconds) + SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND, SignalServiceProtos.Content.ExpirationType.UNKNOWN -> ExpiryMode.AfterSend(durationSeconds) + else -> ExpiryMode.NONE + } +} ?: ExpiryMode.NONE + fun MessageReceiver.handleVisibleMessage( message: VisibleMessage, proto: SignalServiceProtos.Content, @@ -583,8 +583,8 @@ private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMess val groupPublicKey = kind.publicKey.toByteArray().toHexString() val members = kind.members.map { it.toByteArray().toHexString() } val admins = kind.admins.map { it.toByteArray().toHexString() } - val expireTimer = kind.expirationTimer - handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!, expireTimer) + val expirationTimer = kind.expirationTimer + handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!, expirationTimer) } private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: List, admins: List, formationTimestamp: Long, expireTimer: Int) { diff --git a/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtilities.kt b/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtilities.kt deleted file mode 100644 index 77287b4dc..000000000 --- a/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtilities.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.session.libsession.utilities - -import network.loki.messenger.libsession_util.util.ExpiryMode -import org.session.libsignal.protos.SignalServiceProtos -import kotlin.reflect.KClass - -fun ExpiryMode?.typeRadioIndex(): Int { - return when (this) { - is ExpiryMode.AfterRead -> SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ_VALUE - is ExpiryMode.AfterSend -> SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND_VALUE - is ExpiryMode.Legacy -> SignalServiceProtos.Content.ExpirationType.UNKNOWN_VALUE - else -> -1 - } -} - -fun SignalServiceProtos.Content.ExpirationType?.expiryMode(durationSeconds: Long): ExpiryMode? = when (this) { - null -> null - SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(durationSeconds) - SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(durationSeconds) - SignalServiceProtos.Content.ExpirationType.UNKNOWN -> ExpiryMode.Legacy(durationSeconds) -} - -fun Int.expiryType(): KClass? { - if (this == -1) return null - return when (this) { - SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ_VALUE -> ExpiryMode.AfterSend::class - SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND_VALUE -> ExpiryMode.AfterRead::class - SignalServiceProtos.Content.ExpirationType.UNKNOWN_VALUE -> ExpiryMode.Legacy::class - else -> ExpiryMode.NONE::class - } -} \ No newline at end of file