Fix control messages

This commit is contained in:
andrew 2023-11-28 12:47:27 +10:30
parent 8cc26b8fb6
commit 7bd43b1b3c
18 changed files with 110 additions and 192 deletions

View File

@ -61,7 +61,7 @@ class DisappearingMessagesViewModel(
init { init {
viewModelScope.launch { 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 recipient = threadDb.getRecipientForThreadId(threadId)
val groupRecord = recipient?.takeIf { it.isClosedGroupRecipient } val groupRecord = recipient?.takeIf { it.isClosedGroupRecipient }
?.run { groupDb.getGroup(address.toGroupString()).orNull() } ?.run { groupDb.getGroup(address.toGroupString()).orNull() }
@ -83,7 +83,7 @@ class DisappearingMessagesViewModel(
override fun onSetClick() = viewModelScope.launch { override fun onSetClick() = viewModelScope.launch {
val state = _state.value val state = _state.value
val mode = state.expiryMode val mode = state.expiryMode?.coerceLegacyToAfterSend()
val address = state.address val address = state.address
if (address == null || mode == null) { if (address == null || mode == null) {
_event.send(Event.FAIL) _event.send(Event.FAIL)
@ -93,8 +93,9 @@ class DisappearingMessagesViewModel(
val expiryChangeTimestampMs = SnodeAPI.nowWithOffset val expiryChangeTimestampMs = SnodeAPI.nowWithOffset
storage.setExpirationConfiguration(ExpirationConfiguration(threadId, mode, expiryChangeTimestampMs)) storage.setExpirationConfiguration(ExpirationConfiguration(threadId, mode, expiryChangeTimestampMs))
val message = ExpirationTimerUpdate(mode.expirySeconds.toInt()).apply { val message = ExpirationTimerUpdate(mode).apply {
sender = textSecurePreferences.getLocalNumber() sender = textSecurePreferences.getLocalNumber()
isSenderSelf = true
recipient = address.serialize() recipient = address.serialize()
sentTimestamp = expiryChangeTimestampMs sentTimestamp = expiryChangeTimestampMs
} }
@ -106,6 +107,8 @@ class DisappearingMessagesViewModel(
_event.send(Event.SUCCESS) _event.send(Event.SUCCESS)
} }
private fun ExpiryMode.coerceLegacyToAfterSend() = takeUnless { it is ExpiryMode.Legacy } ?: ExpiryMode.AfterSend(expirySeconds)
@dagger.assisted.AssistedFactory @dagger.assisted.AssistedFactory
interface AssistedFactory { interface AssistedFactory {
fun create(threadId: Long): Factory fun create(threadId: Long): Factory
@ -135,3 +138,5 @@ class DisappearingMessagesViewModel(
) as T ) as T
} }
} }
private fun ExpiryMode.maybeConvertToLegacy(isNewConfigEnabled: Boolean): ExpiryMode = takeIf { isNewConfigEnabled } ?: ExpiryMode.Legacy(expirySeconds)

View File

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.conversation.v2.components
import android.content.Context import android.content.Context
import android.graphics.drawable.AnimationDrawable import android.graphics.drawable.AnimationDrawable
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.widget.ImageView
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import network.loki.messenger.R import network.loki.messenger.R

View File

@ -30,6 +30,7 @@ import network.loki.messenger.databinding.ViewVisibleMessageBinding
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.contacts.Contact.ContactContext import org.session.libsession.messaging.contacts.Contact.ContactContext
import org.session.libsession.messaging.open_groups.OpenGroupApi 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.Address
import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.ViewUtil
import org.session.libsession.utilities.getColorFromAttr import org.session.libsession.utilities.getColorFromAttr
@ -348,23 +349,18 @@ class VisibleMessageView : LinearLayout {
} }
private fun updateExpirationTimer(message: MessageRecord) { private fun updateExpirationTimer(message: MessageRecord) {
val expirationTimerView = binding.expirationTimerView
if (!message.isOutgoing) binding.messageStatusTextView.bringToFront() if (!message.isOutgoing) binding.messageStatusTextView.bringToFront()
if (message.expiresIn > 0) { val expireStarted = message.expireStarted.takeIf { it > 0 } ?: SnodeAPI.nowWithOffset
if (message.expireStarted > 0) {
expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn) val id = message.getId()
ApplicationContext.getInstance(context).expiringMessageManager.checkSchedule() val mms = message.isMms
} else { binding.expirationTimerView.setExpirationTime(expireStarted, message.expiresIn)
ThreadUtils.queue { ThreadUtils.queue {
val expirationManager = ApplicationContext.getInstance(context).expiringMessageManager val db = if (mms) mmsDb else smsDb
val id = message.getId() db.markExpireStarted(id, expireStarted)
val mms = message.isMms ApplicationContext.getInstance(context).expiringMessageManager
if (mms) mmsDb.markExpireStarted(id) else smsDb.markExpireStarted(id) .scheduleDeletion(id, mms, expireStarted, message.expiresIn)
expirationManager.scheduleDeletion(id, mms, message.expiresIn)
}
}
} }
} }

View File

@ -0,0 +1,3 @@
package org.thoughtcrime.securesms.database
data class ExpirationInfo(val id: Long, val expiresIn: Long, val expireStarted: Long, val isMms: Boolean)

View File

@ -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
}
}
}

View File

@ -12,7 +12,6 @@ import org.session.libsession.utilities.Document;
import org.session.libsession.utilities.IdentityKeyMismatch; import org.session.libsession.utilities.IdentityKeyMismatch;
import org.session.libsession.utilities.IdentityKeyMismatchList; import org.session.libsession.utilities.IdentityKeyMismatchList;
import org.session.libsignal.crypto.IdentityKey; import org.session.libsignal.crypto.IdentityKey;
import org.session.libsignal.protos.SignalServiceProtos;
import org.session.libsignal.utilities.JsonUtil; import org.session.libsignal.utilities.JsonUtil;
import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType; 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 { public static class InsertResult {
private final long messageId; private final long messageId;
private final long threadId; private final long threadId;

View File

@ -1718,9 +1718,7 @@ open class Storage(
?.run { disappearingTimer.takeIf { it != 0L }?.let(ExpiryMode::AfterSend) ?: ExpiryMode.NONE } ?.run { disappearingTimer.takeIf { it != 0L }?.let(ExpiryMode::AfterSend) ?: ExpiryMode.NONE }
} }
else -> null else -> null
} }?.let { ExpirationConfiguration(threadId, it, dbExpirationMetadata.updatedTimestampMs) }
?.run { takeIf { isNewConfigEnabled || it is ExpiryMode.NONE } ?: ExpiryMode.Legacy(expirySeconds) }
?.let { ExpirationConfiguration(threadId, it, dbExpirationMetadata.updatedTimestampMs) }
} }
override fun setExpirationConfiguration(config: ExpirationConfiguration) { override fun setExpirationConfiguration(config: ExpirationConfiguration) {
@ -1729,12 +1727,7 @@ open class Storage(
val expirationDb = DatabaseComponent.get(context).expirationConfigurationDatabase() val expirationDb = DatabaseComponent.get(context).expirationConfigurationDatabase()
val currentConfig = expirationDb.getExpirationConfiguration(config.threadId) val currentConfig = expirationDb.getExpirationConfiguration(config.threadId)
if (currentConfig != null && currentConfig.updatedTimestampMs >= config.updatedTimestampMs) return if (currentConfig != null && currentConfig.updatedTimestampMs >= config.updatedTimestampMs) return
val expiryMode = config.expiryMode
val expiryMode = config.expiryMode.run {
takeUnless { it is ExpiryMode.Legacy }
?: if (recipient.isContactRecipient) ExpiryMode.AfterRead(expirySeconds)
else ExpiryMode.AfterSend(expirySeconds)
}
if (recipient.isClosedGroupRecipient) { if (recipient.isClosedGroupRecipient) {
val userGroups = configFactory.userGroups ?: return val userGroups = configFactory.userGroups ?: return

View File

@ -51,7 +51,6 @@ import org.session.libsignal.utilities.Pair;
import org.session.libsignal.utilities.guava.Optional; import org.session.libsignal.utilities.guava.Optional;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.contacts.ContactUtil; 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.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;

View File

@ -27,7 +27,7 @@ import androidx.core.app.NotificationManagerCompat;
import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.ApplicationContext; 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.dependencies.DatabaseComponent;
import java.util.LinkedList; import java.util.LinkedList;

View File

@ -36,7 +36,7 @@ import org.session.libsession.utilities.Address;
import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.ApplicationContext; 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.dependencies.DatabaseComponent;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;

View File

@ -18,8 +18,8 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo import org.thoughtcrime.securesms.database.ExpirationInfo
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo import org.thoughtcrime.securesms.database.MarkedMessageInfo
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.util.SessionMetaProtocol.shouldSendReadReceipt import org.thoughtcrime.securesms.util.SessionMetaProtocol.shouldSendReadReceipt
@ -129,16 +129,18 @@ class MarkReadReceiver : BroadcastReceiver() {
expirationInfo: ExpirationInfo, expirationInfo: ExpirationInfo,
expiresIn: Long = expirationInfo.expiresIn expiresIn: Long = expirationInfo.expiresIn
) { ) {
if (expiresIn > 0 && expirationInfo.expireStarted <= 0) { if (expiresIn <= 0 || expirationInfo.expireStarted > 0) return
if (expirationInfo.isMms) DatabaseComponent.get(context!!).mmsDatabase().markExpireStarted(expirationInfo.id)
else DatabaseComponent.get(context!!).smsDatabase().markExpireStarted(expirationInfo.id)
ApplicationContext.getInstance(context).expiringMessageManager.scheduleDeletion( val now = SnodeAPI.nowWithOffset
expirationInfo.id, val db = DatabaseComponent.get(context!!).run { if (expirationInfo.isMms) mmsDatabase() else smsDatabase() }
expirationInfo.isMms, db.markExpireStarted(expirationInfo.id, now)
expiresIn
) ApplicationContext.getInstance(context).expiringMessageManager.scheduleDeletion(
} expirationInfo.id,
expirationInfo.isMms,
now,
expiresIn
)
} }
} }
} }

View File

@ -36,7 +36,7 @@ import org.session.libsession.utilities.Address;
import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.ApplicationContext; 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.MmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.Storage; import org.thoughtcrime.securesms.database.Storage;

View File

@ -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.control.ExpirationTimerUpdate;
import org.session.libsession.messaging.messages.signal.IncomingMediaMessage; import org.session.libsession.messaging.messages.signal.IncomingMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingExpirationUpdateMessage; 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.Address;
import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.SSKEnvironment; import org.session.libsession.utilities.SSKEnvironment;
@ -54,10 +55,6 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
executor.execute(new ProcessTask()); 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) { public void scheduleDeletion(long id, boolean mms, long startedAtTimestamp, long expiresInMillis) {
long expiresAtMillis = startedAtTimestamp + expiresInMillis; long expiresAtMillis = startedAtTimestamp + expiresInMillis;
@ -78,8 +75,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
String userPublicKey = TextSecurePreferences.getLocalNumber(context); String userPublicKey = TextSecurePreferences.getLocalNumber(context);
String senderPublicKey = message.getSender(); String senderPublicKey = message.getSender();
long sentTimestamp = message.getSentTimestamp() == null ? 0 : message.getSentTimestamp(); long sentTimestamp = message.getSentTimestamp() == null ? 0 : message.getSentTimestamp();
long expireStartedAt = (expiryMode instanceof ExpiryMode.AfterSend) long expireStartedAt = (expiryMode instanceof ExpiryMode.AfterSend || message.isSenderSelf()) ? sentTimestamp : 0;
? sentTimestamp : 0;
// Notify the user // Notify the user
if (senderPublicKey == null || userPublicKey.equals(senderPublicKey)) { if (senderPublicKey == null || userPublicKey.equals(senderPublicKey)) {
@ -88,7 +84,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
} else { } else {
insertIncomingExpirationTimerMessage(message, expireStartedAt); 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); startAnyExpiration(message.getSentTimestamp(), senderPublicKey, expireStartedAt);
} }
} }
@ -98,7 +94,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
String senderPublicKey = message.getSender(); String senderPublicKey = message.getSender();
Long sentTimestamp = message.getSentTimestamp(); Long sentTimestamp = message.getSentTimestamp();
String groupId = message.getGroupPublicKey(); String groupId = message.getGroupPublicKey();
long expiresInMillis = message.getDuration() * 1000L; long expiresInMillis = message.getExpiryMode().getExpiryMillis();
Optional<SignalServiceGroup> groupInfo = Optional.absent(); Optional<SignalServiceGroup> groupInfo = Optional.absent();
Address address = Address.fromSerialized(senderPublicKey); Address address = Address.fromSerialized(senderPublicKey);
@ -144,7 +140,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
Long sentTimestamp = message.getSentTimestamp(); Long sentTimestamp = message.getSentTimestamp();
String groupId = message.getGroupPublicKey(); String groupId = message.getGroupPublicKey();
int duration = message.getDuration(); long duration = message.getExpiryMode().getExpiryMillis();
Address address; Address address;
@ -159,7 +155,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
StorageProtocol storage = MessagingModuleConfiguration.getShared().getStorage(); StorageProtocol storage = MessagingModuleConfiguration.getShared().getStorage();
message.setThreadID(storage.getOrCreateThreadIdFor(address)); 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); mmsDatabase.insertSecureDecryptedMessageOutbox(timerUpdateMessage, message.getThreadID(), sentTimestamp, true);
} catch (MmsException | IOException ioe) { } catch (MmsException | IOException ioe) {
Log.e("Loki", "Failed to insert expiration update message.", ioe); Log.e("Loki", "Failed to insert expiration update message.", ioe);
@ -169,18 +165,17 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
@Override @Override
public void startAnyExpiration(long timestamp, @NotNull String author, long expireStartedAt) { public void startAnyExpiration(long timestamp, @NotNull String author, long expireStartedAt) {
MessageRecord messageRecord = mmsSmsDatabase.getMessageFor(timestamp, author); MessageRecord messageRecord = mmsSmsDatabase.getMessageFor(timestamp, author);
if (messageRecord != null) { if (messageRecord == null) return;
boolean mms = messageRecord.isMms(); boolean mms = messageRecord.isMms();
ExpirationConfiguration config = DatabaseComponent.get(context).storage().getExpirationConfiguration(messageRecord.getThreadId()); ExpirationConfiguration config = DatabaseComponent.get(context).storage().getExpirationConfiguration(messageRecord.getThreadId());
if (config == null || !config.isEnabled()) return; if (config == null || !config.isEnabled()) return;
ExpiryMode mode = config.getExpiryMode(); ExpiryMode mode = config.getExpiryMode();
if (mms) { if (mms) {
mmsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt); mmsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt);
} else { } else {
smsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt); smsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt);
}
scheduleDeletion(messageRecord.getId(), mms, expireStartedAt, (mode != null ? mode.getExpiryMillis() : 0));
} }
scheduleDeletion(messageRecord.getId(), mms, expireStartedAt, (mode != null ? mode.getExpiryMillis() : 0));
} }
private class LoadTask implements Runnable { private class LoadTask implements Runnable {
@ -219,7 +214,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
while (expiringMessageReferences.isEmpty()) expiringMessageReferences.wait(); while (expiringMessageReferences.isEmpty()) expiringMessageReferences.wait();
ExpiringMessageReference nextReference = expiringMessageReferences.first(); ExpiringMessageReference nextReference = expiringMessageReferences.first();
long waitTime = nextReference.expiresAtMillis - System.currentTimeMillis(); long waitTime = nextReference.expiresAtMillis - SnodeAPI.getNowWithOffset();
if (waitTime > 0) { if (waitTime > 0) {
ExpirationListener.setAlarm(context, waitTime); ExpirationListener.setAlarm(context, waitTime);

View File

@ -64,13 +64,11 @@ abstract class Message {
} }
expirationTimer = config.expiryMode.expirySeconds.toInt() expirationTimer = config.expiryMode.expirySeconds.toInt()
lastDisappearingMessageChangeTimestamp = config.updatedTimestampMs lastDisappearingMessageChangeTimestamp = config.updatedTimestampMs
if (ExpirationConfiguration.isNewConfigEnabled) { config.expiryMode.let { expiryMode ->
config.expiryMode.let { expiryMode -> expirationType = when (expiryMode) {
when (expiryMode) { is ExpiryMode.Legacy, is ExpiryMode.AfterSend -> ExpirationType.DELETE_AFTER_SEND
is ExpiryMode.AfterSend -> expirationType = ExpirationType.DELETE_AFTER_SEND is ExpiryMode.AfterRead -> ExpirationType.DELETE_AFTER_READ
is ExpiryMode.AfterRead -> expirationType = ExpirationType.DELETE_AFTER_READ ExpiryMode.NONE -> ExpirationType.UNKNOWN
is ExpiryMode.Legacy, ExpiryMode.NONE -> { /* do nothing */ }
}
} }
} }
return this return this

View File

@ -1,5 +1,6 @@
package org.session.libsession.messaging.messages.control 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.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.messages.ExpirationConfiguration
import org.session.libsession.messaging.messages.visible.VisibleMessage 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. * **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 val isSelfSendValid: Boolean = true
override fun isValid(): Boolean {
if (!super.isValid()) return false
return duration != null || ExpirationConfiguration.isNewConfigEnabled
}
companion object { companion object {
const val TAG = "ExpirationTimerUpdate" const val TAG = "ExpirationTimerUpdate"
@ -29,15 +25,22 @@ data class ExpirationTimerUpdate(var duration: Int? = 0, var syncTarget: String?
) != 0 ) != 0
if (!isExpirationTimerUpdate) return null if (!isExpirationTimerUpdate) return null
val syncTarget = dataMessageProto.syncTarget val syncTarget = dataMessageProto.syncTarget
val duration = if (proto.hasExpirationTimer()) proto.expirationTimer else dataMessageProto.expireTimer val duration: Int = if (proto.hasExpirationTimer()) proto.expirationTimer else dataMessageProto.expireTimer
return ExpirationTimerUpdate(duration, syncTarget) 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? { override fun toProto(): SignalServiceProtos.Content? {
val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder() val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder()
dataMessageProto.flags = SignalServiceProtos.DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE dataMessageProto.flags = SignalServiceProtos.DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE
duration?.let { dataMessageProto.expireTimer = it } dataMessageProto.expireTimer = expiryMode.expirySeconds.toInt()
// Sync target // Sync target
if (syncTarget != null) { if (syncTarget != null) {
dataMessageProto.syncTarget = syncTarget dataMessageProto.syncTarget = syncTarget
@ -53,6 +56,8 @@ data class ExpirationTimerUpdate(var duration: Int? = 0, var syncTarget: String?
} }
return try { return try {
SignalServiceProtos.Content.newBuilder().apply { SignalServiceProtos.Content.newBuilder().apply {
expirationType
expirationTimer
dataMessage = dataMessageProto.build() dataMessage = dataMessageProto.build()
setExpirationConfigurationIfNeeded(threadID) setExpirationConfigurationIfNeeded(threadID)
}.build() }.build()

View File

@ -148,12 +148,14 @@ object MessageReceiver {
VisibleMessage.fromProto(proto) ?: run { VisibleMessage.fromProto(proto) ?: run {
throw Error.UnknownMessage throw Error.UnknownMessage
} }
val isUserBlindedSender = sender == openGroupPublicKey?.let { SodiumUtilities.blindedKeyPair(it, MessagingModuleConfiguration.shared.getUserED25519KeyPair()!!) }?.let { SessionId(IdPrefix.BLINDED, it.publicKey.asBytes).hexString } 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 // Ignore self send if needed
if (!message.isSelfSendValid && (sender == userPublicKey || isUserBlindedSender)) { if (!message.isSelfSendValid && (isUserSender || isUserBlindedSender)) {
throw Error.SelfSend throw Error.SelfSend
} }
if (sender == userPublicKey || isUserBlindedSender) { if (isUserSender || isUserBlindedSender) {
message.isSenderSelf = true message.isSenderSelf = true
} }
// Guard against control messages in open groups // Guard against control messages in open groups

View File

@ -40,7 +40,6 @@ import org.session.libsession.utilities.GroupUtil.doubleEncodeGroupID
import org.session.libsession.utilities.ProfileKeyUtil import org.session.libsession.utilities.ProfileKeyUtil
import org.session.libsession.utilities.SSKEnvironment import org.session.libsession.utilities.SSKEnvironment
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.expiryMode
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.crypto.ecc.DjbECPrivateKey import org.session.libsignal.crypto.ecc.DjbECPrivateKey
import org.session.libsignal.crypto.ecc.DjbECPublicKey import org.session.libsignal.crypto.ecc.DjbECPublicKey
@ -156,28 +155,21 @@ fun MessageReceiver.cancelTypingIndicatorsIfNeeded(senderPublicKey: String) {
private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimerUpdate) { private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimerUpdate) {
if (ExpirationConfiguration.isNewConfigEnabled) return if (ExpirationConfiguration.isNewConfigEnabled) return
val module = MessagingModuleConfiguration.shared 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 { try {
var threadId: Long = module.storage.getOrCreateThreadIdFor(fromSerialized(message.sender!!)) val threadId = fromSerialized(message.groupPublicKey?.let(::doubleEncodeGroupID) ?: message.sender!!)
if (message.groupPublicKey != null) { .let(module.storage::getOrCreateThreadIdFor)
threadId = module.storage.getOrCreateThreadIdFor(fromSerialized(doubleEncodeGroupID(message.groupPublicKey!!)))
}
module.storage.setExpirationConfiguration( module.storage.setExpirationConfiguration(
ExpirationConfiguration( ExpirationConfiguration(
threadId, threadId,
type, message.expiryMode,
message.sentTimestamp!! message.sentTimestamp!!
) )
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Loki", "Failed to update expiration configuration.") 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) { private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) {
@ -317,10 +309,18 @@ fun MessageReceiver.updateExpiryIfNeeded(
if (message is ExpirationTimerUpdate) { 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( fun MessageReceiver.handleVisibleMessage(
message: VisibleMessage, message: VisibleMessage,
proto: SignalServiceProtos.Content, proto: SignalServiceProtos.Content,
@ -583,8 +583,8 @@ private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMess
val groupPublicKey = kind.publicKey.toByteArray().toHexString() val groupPublicKey = kind.publicKey.toByteArray().toHexString()
val members = kind.members.map { it.toByteArray().toHexString() } val members = kind.members.map { it.toByteArray().toHexString() }
val admins = kind.admins.map { it.toByteArray().toHexString() } val admins = kind.admins.map { it.toByteArray().toHexString() }
val expireTimer = kind.expirationTimer val expirationTimer = kind.expirationTimer
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!, expireTimer) 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<String>, admins: List<String>, formationTimestamp: Long, expireTimer: Int) { private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: List<String>, admins: List<String>, formationTimestamp: Long, expireTimer: Int) {

View File

@ -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<out ExpiryMode>? {
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
}
}