Fix control messages

pull/1313/head
andrew 7 months ago
parent 8cc26b8fb6
commit 7bd43b1b3c

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

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

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

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

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

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

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

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

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

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

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

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

@ -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);

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

@ -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()

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

@ -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) {

@ -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
}
}
Loading…
Cancel
Save