Add delivery receipts to the MSL.

fork-5.53.8
Greyson Parrelli 2021-06-30 14:36:00 -04:00 zatwierdzone przez Alex Hart
rodzic 5372f79c40
commit 3d0e15e2b8
15 zmienionych plików z 182 dodań i 88 usunięć

Wyświetl plik

@ -300,6 +300,9 @@ class ConversationSettingsFragment : DSLSettingsFragment(
recipient = state.recipient, recipient = state.recipient,
onDisableProfileSharingClick = { onDisableProfileSharingClick = {
viewModel.disableProfileSharing() viewModel.disableProfileSharing()
},
onDeleteSessionClick = {
viewModel.deleteSession()
} }
) )
) )

Wyświetl plik

@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.recipients.RecipientUtil import org.thoughtcrime.securesms.recipients.RecipientUtil
import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.FeatureFlags
import org.whispersystems.libsignal.util.guava.Optional import org.whispersystems.libsignal.util.guava.Optional
import org.whispersystems.libsignal.util.guava.Preconditions
import java.io.IOException import java.io.IOException
private val TAG = Log.tag(ConversationSettingsRepository::class.java) private val TAG = Log.tag(ConversationSettingsRepository::class.java)
@ -185,12 +186,22 @@ class ConversationSettingsRepository(
} }
} }
fun disableProfileSharing(recipientId: RecipientId) { fun disableProfileSharingForInternalUser(recipientId: RecipientId) {
Preconditions.checkArgument(FeatureFlags.internalUser(), "Internal users only!");
SignalExecutors.BOUNDED.execute { SignalExecutors.BOUNDED.execute {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipientId, false) DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipientId, false)
} }
} }
fun deleteSessionForInternalUser(recipientId: RecipientId) {
Preconditions.checkArgument(FeatureFlags.internalUser(), "Internal users only!");
SignalExecutors.BOUNDED.execute {
DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId)
}
}
@WorkerThread @WorkerThread
fun isMessageRequestAccepted(recipient: Recipient): Boolean { fun isMessageRequestAccepted(recipient: Recipient): Boolean {
return RecipientUtil.isMessageRequestAccepted(context, recipient) return RecipientUtil.isMessageRequestAccepted(context, recipient)

Wyświetl plik

@ -116,6 +116,8 @@ sealed class ConversationSettingsViewModel(
open fun disableProfileSharing(): Unit = error("This ViewModel does not support this interaction") open fun disableProfileSharing(): Unit = error("This ViewModel does not support this interaction")
open fun deleteSession(): Unit = error("This ViewModel does not support this interaction")
open fun initiateGroupUpgrade(): Unit = error("This ViewModel does not support this interaction") open fun initiateGroupUpgrade(): Unit = error("This ViewModel does not support this interaction")
private class RecipientSettingsViewModel( private class RecipientSettingsViewModel(
@ -237,7 +239,11 @@ sealed class ConversationSettingsViewModel(
} }
override fun disableProfileSharing() { override fun disableProfileSharing() {
repository.disableProfileSharing(recipientId) repository.disableProfileSharingForInternalUser(recipientId)
}
override fun deleteSession() {
repository.deleteSessionForInternalUser(recipientId)
} }
} }

Wyświetl plik

@ -19,7 +19,8 @@ object InternalPreference {
class Model( class Model(
private val recipient: Recipient, private val recipient: Recipient,
val onDisableProfileSharingClick: () -> Unit val onDisableProfileSharingClick: () -> Unit,
val onDeleteSessionClick: () -> Unit
) : PreferenceModel<Model>() { ) : PreferenceModel<Model>() {
val body: String get() { val body: String get() {
@ -58,10 +59,12 @@ object InternalPreference {
private val body: TextView = itemView.findViewById(R.id.internal_preference_body) private val body: TextView = itemView.findViewById(R.id.internal_preference_body)
private val disableProfileSharing: View = itemView.findViewById(R.id.internal_disable_profile_sharing) private val disableProfileSharing: View = itemView.findViewById(R.id.internal_disable_profile_sharing)
private val deleteSession: View = itemView.findViewById(R.id.internal_delete_session)
override fun bind(model: Model) { override fun bind(model: Model) {
body.text = model.body body.text = model.body
disableProfileSharing.setOnClickListener { model.onDisableProfileSharingClick() } disableProfileSharing.setOnClickListener { model.onDisableProfileSharingClick() }
deleteSession.setOnClickListener { model.onDeleteSessionClick() }
} }
} }
} }

Wyświetl plik

@ -131,7 +131,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
public abstract List<MarkedMessageInfo> setEntireThreadRead(long threadId); public abstract List<MarkedMessageInfo> setEntireThreadRead(long threadId);
public abstract List<MarkedMessageInfo> setMessagesReadSince(long threadId, long timestamp); public abstract List<MarkedMessageInfo> setMessagesReadSince(long threadId, long timestamp);
public abstract List<MarkedMessageInfo> setAllMessagesRead(); public abstract List<MarkedMessageInfo> setAllMessagesRead();
public abstract Pair<Long, Long> updateBundleMessageBody(long messageId, String body); public abstract InsertResult updateBundleMessageBody(long messageId, String body);
public abstract @NonNull List<MarkedMessageInfo> getViewedIncomingMessages(long threadId); public abstract @NonNull List<MarkedMessageInfo> getViewedIncomingMessages(long threadId);
public abstract @Nullable MarkedMessageInfo setIncomingMessageViewed(long messageId); public abstract @Nullable MarkedMessageInfo setIncomingMessageViewed(long messageId);
public abstract @NonNull List<MarkedMessageInfo> setIncomingMessagesViewed(@NonNull List<Long> messageIds); public abstract @NonNull List<MarkedMessageInfo> setIncomingMessagesViewed(@NonNull List<Long> messageIds);

Wyświetl plik

@ -142,12 +142,12 @@ class MessageSendLogDatabase constructor(context: Context?, databaseHelper: SQLC
) )
} }
fun insertIfPossible(recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, relatedMessageId: Long, isRelatedMessageMms: Boolean) { fun insertIfPossible(recipientId: RecipientId, sentTimestamp: Long, sendMessageResult: SendMessageResult, contentHint: ContentHint, messageId: MessageId) {
if (!FeatureFlags.senderKey()) return if (!FeatureFlags.senderKey()) return
if (sendMessageResult.isSuccess && sendMessageResult.success.content.isPresent) { if (sendMessageResult.isSuccess && sendMessageResult.success.content.isPresent) {
val recipientDevice = listOf(RecipientDevice(recipientId, sendMessageResult.success.devices)) val recipientDevice = listOf(RecipientDevice(recipientId, sendMessageResult.success.devices))
insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, listOf(MessageId(relatedMessageId, isRelatedMessageMms))) insert(recipientDevice, sentTimestamp, sendMessageResult.success.content.get(), contentHint, listOf(messageId))
} }
} }

Wyświetl plik

@ -380,7 +380,7 @@ public class MmsDatabase extends MessageDatabase {
} }
@Override @Override
public Pair<Long, Long> updateBundleMessageBody(long messageId, String body) { public InsertResult updateBundleMessageBody(long messageId, String body) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

Wyświetl plik

@ -664,7 +664,7 @@ public class SmsDatabase extends MessageDatabase {
} }
@Override @Override
public Pair<Long, Long> updateBundleMessageBody(long messageId, String body) { public InsertResult updateBundleMessageBody(long messageId, String body) {
long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT; long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT;
return updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, type); return updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, type);
} }
@ -684,7 +684,7 @@ public class SmsDatabase extends MessageDatabase {
return Collections.emptyList(); return Collections.emptyList();
} }
private Pair<Long, Long> updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) { private InsertResult updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " + db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " +
TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " +
@ -697,7 +697,7 @@ public class SmsDatabase extends MessageDatabase {
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
notifyConversationListListeners(); notifyConversationListListeners();
return new Pair<>(messageId, threadId); return new InsertResult(messageId, threadId);
} }
@Override @Override

Wyświetl plik

@ -7,4 +7,16 @@ package org.thoughtcrime.securesms.database.model
data class MessageId( data class MessageId(
val id: Long, val id: Long,
@get:JvmName("isMms") val mms: Boolean @get:JvmName("isMms") val mms: Boolean
) ) {
fun serialize(): String {
return "$id|$mms"
}
companion object {
@JvmStatic
fun deserialize(serialized: String): MessageId {
val parts: List<String> = serialized.split("|")
return MessageId(parts[0].toLong(), parts[1].toBoolean())
}
}
}

Wyświetl plik

@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
@ -224,11 +225,11 @@ public class PushMediaSendJob extends PushSendJob {
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess); SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess);
SendMessageResult result = messageSender.sendSyncMessage(syncMessage, syncAccess); SendMessageResult result = messageSender.sendSyncMessage(syncMessage, syncAccess);
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, messageId, true); DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true));
return syncAccess.isPresent(); return syncAccess.isPresent();
} else { } else {
SendMessageResult result = messageSender.sendDataMessage(address, UnidentifiedAccessUtil.getAccessFor(context, messageRecipient), ContentHint.RESENDABLE, mediaMessage); SendMessageResult result = messageSender.sendDataMessage(address, UnidentifiedAccessUtil.getAccessFor(context, messageRecipient), ContentHint.RESENDABLE, mediaMessage);
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, messageId, true); DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getSentTimeMillis(), result, ContentHint.RESENDABLE, new MessageId(messageId, true));
return result.getSuccess().isUnidentified(); return result.getSuccess().isUnidentified();
} }
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {

Wyświetl plik

@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode; import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
@ -176,11 +177,11 @@ public class PushTextSendJob extends PushSendJob {
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess); SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess);
SendMessageResult result = messageSender.sendSyncMessage(syncMessage, syncAccess); SendMessageResult result = messageSender.sendSyncMessage(syncMessage, syncAccess);
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, messageId, false); DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false));
return syncAccess.isPresent(); return syncAccess.isPresent();
} else { } else {
SendMessageResult result = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, textSecureMessage); SendMessageResult result = messageSender.sendDataMessage(address, unidentifiedAccess, ContentHint.RESENDABLE, textSecureMessage);
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, messageId, false); DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(messageRecipient.getId(), message.getDateSent(), result, ContentHint.RESENDABLE, new MessageId(messageId, false));
return result.getSuccess().isUnidentified(); return result.getSuccess().isUnidentified();
} }
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {

Wyświetl plik

@ -2,9 +2,12 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
@ -15,7 +18,9 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -29,17 +34,21 @@ public class SendDeliveryReceiptJob extends BaseJob {
public static final String KEY = "SendDeliveryReceiptJob"; public static final String KEY = "SendDeliveryReceiptJob";
private static final String KEY_RECIPIENT = "recipient"; private static final String KEY_RECIPIENT = "recipient";
private static final String KEY_MESSAGE_ID = "message_id"; private static final String KEY_MESSAGE_SENT_TIMESTAMP = "message_id";
private static final String KEY_TIMESTAMP = "timestamp"; private static final String KEY_TIMESTAMP = "timestamp";
private static final String KEY_MESSAGE_ID = "message_db_id";
private static final String TAG = Log.tag(SendReadReceiptJob.class); private static final String TAG = Log.tag(SendReadReceiptJob.class);
private RecipientId recipientId; private final RecipientId recipientId;
private long messageId; private final long messageSentTimestamp;
private long timestamp; private final long timestamp;
public SendDeliveryReceiptJob(@NonNull RecipientId recipientId, long messageId) { @Nullable
private final MessageId messageId;
public SendDeliveryReceiptJob(@NonNull RecipientId recipientId, long messageSentTimestamp, @NonNull MessageId messageId) {
this(new Job.Parameters.Builder() this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
@ -47,28 +56,36 @@ public class SendDeliveryReceiptJob extends BaseJob {
.setQueue(recipientId.toQueueKey()) .setQueue(recipientId.toQueueKey())
.build(), .build(),
recipientId, recipientId,
messageSentTimestamp,
messageId, messageId,
System.currentTimeMillis()); System.currentTimeMillis());
} }
private SendDeliveryReceiptJob(@NonNull Job.Parameters parameters, private SendDeliveryReceiptJob(@NonNull Job.Parameters parameters,
@NonNull RecipientId recipientId, @NonNull RecipientId recipientId,
long messageId, long messageSentTimestamp,
@Nullable MessageId messageId,
long timestamp) long timestamp)
{ {
super(parameters); super(parameters);
this.recipientId = recipientId; this.recipientId = recipientId;
this.messageId = messageId; this.messageSentTimestamp = messageSentTimestamp;
this.timestamp = timestamp; this.messageId = messageId;
this.timestamp = timestamp;
} }
@Override @Override
public @NonNull Data serialize() { public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_RECIPIENT, recipientId.serialize()) Data.Builder builder = new Data.Builder().putString(KEY_RECIPIENT, recipientId.serialize())
.putLong(KEY_MESSAGE_ID, messageId) .putLong(KEY_MESSAGE_SENT_TIMESTAMP, messageSentTimestamp)
.putLong(KEY_TIMESTAMP, timestamp) .putLong(KEY_TIMESTAMP, timestamp);
.build();
if (messageId != null) {
builder.putString(KEY_MESSAGE_ID, messageId.serialize());
}
return builder.build();
} }
@Override @Override
@ -86,12 +103,16 @@ public class SendDeliveryReceiptJob extends BaseJob {
Recipient recipient = Recipient.resolved(recipientId); Recipient recipient = Recipient.resolved(recipientId);
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient); SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY,
Collections.singletonList(messageId), Collections.singletonList(messageSentTimestamp),
timestamp); timestamp);
messageSender.sendReceipt(remoteAddress, SendMessageResult result = messageSender.sendReceipt(remoteAddress,
UnidentifiedAccessUtil.getAccessFor(context, recipient), UnidentifiedAccessUtil.getAccessFor(context, recipient),
receiptMessage); receiptMessage);
if (messageId != null) {
DatabaseFactory.getMessageLogDatabase(context).insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageId);
}
} }
@Override @Override
@ -109,9 +130,16 @@ public class SendDeliveryReceiptJob extends BaseJob {
public static final class Factory implements Job.Factory<SendDeliveryReceiptJob> { public static final class Factory implements Job.Factory<SendDeliveryReceiptJob> {
@Override @Override
public @NonNull SendDeliveryReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull SendDeliveryReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) {
MessageId messageId = null;
if (data.hasString(KEY_MESSAGE_ID)) {
messageId = MessageId.deserialize(data.getString(KEY_MESSAGE_ID));
}
return new SendDeliveryReceiptJob(parameters, return new SendDeliveryReceiptJob(parameters,
RecipientId.from(data.getString(KEY_RECIPIENT)), RecipientId.from(data.getString(KEY_RECIPIENT)),
data.getLong(KEY_MESSAGE_ID), data.getLong(KEY_MESSAGE_SENT_TIMESTAMP),
messageId,
data.getLong(KEY_TIMESTAMP)); data.getLong(KEY_TIMESTAMP));
} }
} }

Wyświetl plik

@ -253,15 +253,17 @@ public final class MessageContentProcessor {
} }
} }
MessageId messageId = null;
if (isInvalidMessage(message)) handleInvalidMessage(content.getSender(), content.getSenderDevice(), groupId, content.getTimestamp(), smsMessageId); if (isInvalidMessage(message)) handleInvalidMessage(content.getSender(), content.getSenderDevice(), groupId, content.getTimestamp(), smsMessageId);
else if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId); else if (message.isEndSession()) messageId = handleEndSessionMessage(content, smsMessageId);
else if (message.isGroupV1Update()) handleGroupV1Message(content, message, smsMessageId, groupId.get().requireV1(), receivedTime); else if (message.isGroupV1Update()) handleGroupV1Message(content, message, smsMessageId, groupId.get().requireV1(), receivedTime);
else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId, groupId, receivedTime); else if (message.isExpirationUpdate()) messageId = handleExpirationUpdate(content, message, smsMessageId, groupId, receivedTime);
else if (message.getReaction().isPresent()) handleReaction(content, message); else if (message.getReaction().isPresent()) messageId = handleReaction(content, message);
else if (message.getRemoteDelete().isPresent()) handleRemoteDelete(content, message); else if (message.getRemoteDelete().isPresent()) messageId = handleRemoteDelete(content, message);
else if (message.getPayment().isPresent()) handlePayment(content, message); else if (message.getPayment().isPresent()) handlePayment(content, message);
else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId, receivedTime); else if (isMediaMessage) messageId = handleMediaMessage(content, message, smsMessageId, receivedTime);
else if (message.getBody().isPresent()) handleTextMessage(content, message, smsMessageId, groupId, receivedTime); else if (message.getBody().isPresent()) messageId = handleTextMessage(content, message, smsMessageId, groupId, receivedTime);
else if (Build.VERSION.SDK_INT > 19 && message.getGroupCallUpdate().isPresent()) handleGroupCallUpdateMessage(content, message, groupId); else if (Build.VERSION.SDK_INT > 19 && message.getGroupCallUpdate().isPresent()) handleGroupCallUpdateMessage(content, message, groupId);
if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) { if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) {
@ -272,9 +274,9 @@ public final class MessageContentProcessor {
handleProfileKey(content, message.getProfileKey().get()); handleProfileKey(content, message.getProfileKey().get());
} }
if (content.isNeedsReceipt()) { if (content.isNeedsReceipt() && messageId != null) {
handleNeedsDeliveryReceipt(content, message); handleNeedsDeliveryReceipt(content, message, messageId);
} else { } else if (!content.isNeedsReceipt()) {
Recipient sender = getMessageDestination(content, message); Recipient sender = getMessageDestination(content, message);
if (RecipientUtil.shouldHaveProfileKey(context, sender)) { if (RecipientUtil.shouldHaveProfileKey(context, sender)) {
@ -643,8 +645,8 @@ public final class MessageContentProcessor {
GroupCallPeekJob.enqueue(groupRecipientId); GroupCallPeekJob.enqueue(groupRecipientId);
} }
private void handleEndSessionMessage(@NonNull SignalServiceContent content, private @Nullable MessageId handleEndSessionMessage(@NonNull SignalServiceContent content,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalHighTrustPush(context, content.getSender()).getId(), IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Recipient.externalHighTrustPush(context, content.getSender()).getId(),
@ -658,25 +660,27 @@ public final class MessageContentProcessor {
content.isNeedsReceipt(), content.isNeedsReceipt(),
content.getServerUuid()); content.getServerUuid());
Long threadId; Optional<InsertResult> insertResult;
if (!smsMessageId.isPresent()) { if (!smsMessageId.isPresent()) {
IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage); IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(incomingEndSessionMessage);
if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); insertResult = smsDatabase.insertMessageInbox(incomingEndSessionMessage);
else threadId = null;
} else { } else {
smsDatabase.markAsEndSession(smsMessageId.get()); smsDatabase.markAsEndSession(smsMessageId.get());
threadId = smsDatabase.getThreadIdForMessage(smsMessageId.get()); insertResult = Optional.of(new InsertResult(smsMessageId.get(), smsDatabase.getThreadIdForMessage(smsMessageId.get())));
} }
if (threadId != null) { if (insertResult.isPresent()) {
SessionStore sessionStore = new TextSecureSessionStore(context); SessionStore sessionStore = new TextSecureSessionStore(context);
sessionStore.deleteAllSessions(content.getSender().getIdentifier()); sessionStore.deleteAllSessions(content.getSender().getIdentifier());
SecurityEvent.broadcastSecurityUpdateEvent(context); SecurityEvent.broadcastSecurityUpdateEvent(context);
ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId); ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
return new MessageId(insertResult.get().getMessageId(), true);
} else {
return null;
} }
} }
@ -742,16 +746,16 @@ public final class MessageContentProcessor {
} }
} }
private void handleExpirationUpdate(@NonNull SignalServiceContent content, private @Nullable MessageId handleExpirationUpdate(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId, @NonNull Optional<Long> smsMessageId,
@NonNull Optional<GroupId> groupId, @NonNull Optional<GroupId> groupId,
long receivedTime) long receivedTime)
throws StorageFailedException, BadGroupIdException throws StorageFailedException, BadGroupIdException
{ {
if (groupId.isPresent() && groupId.get().isV2()) { if (groupId.isPresent() && groupId.get().isV2()) {
warn(String.valueOf(content.getTimestamp()), "Expiration update received for GV2. Ignoring."); warn(String.valueOf(content.getTimestamp()), "Expiration update received for GV2. Ignoring.");
return; return null;
} }
int expiresInSeconds = message.getExpiresInSeconds(); int expiresInSeconds = message.getExpiresInSeconds();
@ -760,7 +764,7 @@ public final class MessageContentProcessor {
if (recipient.getExpireMessages() == expiresInSeconds) { if (recipient.getExpireMessages() == expiresInSeconds) {
log(String.valueOf(content.getTimestamp()), "No change in message expiry for group. Ignoring."); log(String.valueOf(content.getTimestamp()), "No change in message expiry for group. Ignoring.");
return; return null;
} }
try { try {
@ -785,24 +789,30 @@ public final class MessageContentProcessor {
Optional.absent(), Optional.absent(),
content.getServerUuid()); content.getServerUuid());
database.insertSecureDecryptedMessageInbox(mediaMessage, -1); Optional<InsertResult> insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient.getId(), expiresInSeconds); DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient.getId(), expiresInSeconds);
if (smsMessageId.isPresent()) { if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
} }
if (insertResult.isPresent()) {
return new MessageId(insertResult.get().getMessageId(), true);
}
} catch (MmsException e) { } catch (MmsException e) {
throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice()); throw new StorageFailedException(e, content.getSender().getIdentifier(), content.getSenderDevice());
} }
return null;
} }
private void handleReaction(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { private @Nullable MessageId handleReaction(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
SignalServiceDataMessage.Reaction reaction = message.getReaction().get(); SignalServiceDataMessage.Reaction reaction = message.getReaction().get();
if (!EmojiUtil.isEmoji(reaction.getEmoji())) { if (!EmojiUtil.isEmoji(reaction.getEmoji())) {
Log.w(TAG, "Reaction text is not a valid emoji! Ignoring the message."); Log.w(TAG, "Reaction text is not a valid emoji! Ignoring the message.");
return; return null;
} }
Recipient reactionAuthor = Recipient.externalHighTrustPush(context, content.getSender()); Recipient reactionAuthor = Recipient.externalHighTrustPush(context, content.getSender());
@ -812,31 +822,31 @@ public final class MessageContentProcessor {
if (targetMessage == null) { if (targetMessage == null) {
warn(String.valueOf(content.getTimestamp()), "[handleReaction] Could not find matching message! Putting it in the early message cache. timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); warn(String.valueOf(content.getTimestamp()), "[handleReaction] Could not find matching message! Putting it in the early message cache. timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId());
ApplicationDependencies.getEarlyMessageCache().store(targetAuthor.getId(), reaction.getTargetSentTimestamp(), content); ApplicationDependencies.getEarlyMessageCache().store(targetAuthor.getId(), reaction.getTargetSentTimestamp(), content);
return; return null;
} }
if (targetMessage.isRemoteDelete()) { if (targetMessage.isRemoteDelete()) {
warn(String.valueOf(content.getTimestamp()), "[handleReaction] Found a matching message, but it's flagged as remotely deleted. timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); warn(String.valueOf(content.getTimestamp()), "[handleReaction] Found a matching message, but it's flagged as remotely deleted. timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId());
return; return null;
} }
ThreadRecord targetThread = DatabaseFactory.getThreadDatabase(context).getThreadRecord(targetMessage.getThreadId()); ThreadRecord targetThread = DatabaseFactory.getThreadDatabase(context).getThreadRecord(targetMessage.getThreadId());
if (targetThread == null) { if (targetThread == null) {
warn(String.valueOf(content.getTimestamp()), "[handleReaction] Could not find a thread for the message! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); warn(String.valueOf(content.getTimestamp()), "[handleReaction] Could not find a thread for the message! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId());
return; return null;
} }
Recipient threadRecipient = targetThread.getRecipient().resolve(); Recipient threadRecipient = targetThread.getRecipient().resolve();
if (threadRecipient.isGroup() && !threadRecipient.getParticipants().contains(reactionAuthor)) { if (threadRecipient.isGroup() && !threadRecipient.getParticipants().contains(reactionAuthor)) {
warn(String.valueOf(content.getTimestamp()), "[handleReaction] Reaction author is not in the group! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); warn(String.valueOf(content.getTimestamp()), "[handleReaction] Reaction author is not in the group! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId());
return; return null;
} }
if (!threadRecipient.isGroup() && !reactionAuthor.equals(threadRecipient) && !reactionAuthor.isSelf()) { if (!threadRecipient.isGroup() && !reactionAuthor.equals(threadRecipient) && !reactionAuthor.isSelf()) {
warn(String.valueOf(content.getTimestamp()), "[handleReaction] Reaction author is not a part of the 1:1 thread! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId()); warn(String.valueOf(content.getTimestamp()), "[handleReaction] Reaction author is not a part of the 1:1 thread! timestamp: " + reaction.getTargetSentTimestamp() + " author: " + targetAuthor.getId());
return; return null;
} }
MessageDatabase db = targetMessage.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context); MessageDatabase db = targetMessage.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context);
@ -849,9 +859,11 @@ public final class MessageContentProcessor {
db.addReaction(targetMessage.getId(), reactionRecord); db.addReaction(targetMessage.getId(), reactionRecord);
ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false); ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false);
} }
return new MessageId(targetMessage.getId(), targetMessage.isMms());
} }
private void handleRemoteDelete(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { private @Nullable MessageId handleRemoteDelete(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
SignalServiceDataMessage.RemoteDelete delete = message.getRemoteDelete().get(); SignalServiceDataMessage.RemoteDelete delete = message.getRemoteDelete().get();
Recipient sender = Recipient.externalHighTrustPush(context, content.getSender()); Recipient sender = Recipient.externalHighTrustPush(context, content.getSender());
@ -861,12 +873,15 @@ public final class MessageContentProcessor {
MessageDatabase db = targetMessage.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context); MessageDatabase db = targetMessage.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context);
db.markAsRemoteDelete(targetMessage.getId()); db.markAsRemoteDelete(targetMessage.getId());
ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false); ApplicationDependencies.getMessageNotifier().updateNotification(context, targetMessage.getThreadId(), false);
return new MessageId(targetMessage.getId(), targetMessage.isMms());
} else if (targetMessage == null) { } else if (targetMessage == null) {
warn(String.valueOf(content.getTimestamp()), "[handleRemoteDelete] Could not find matching message! timestamp: " + delete.getTargetSentTimestamp() + " author: " + sender.getId()); warn(String.valueOf(content.getTimestamp()), "[handleRemoteDelete] Could not find matching message! timestamp: " + delete.getTargetSentTimestamp() + " author: " + sender.getId());
ApplicationDependencies.getEarlyMessageCache().store(sender.getId(), delete.getTargetSentTimestamp(), content); ApplicationDependencies.getEarlyMessageCache().store(sender.getId(), delete.getTargetSentTimestamp(), content);
return null;
} else { } else {
warn(String.valueOf(content.getTimestamp()), String.format(Locale.ENGLISH, "[handleRemoteDelete] Invalid remote delete! deleteTime: %d, targetTime: %d, deleteAuthor: %s, targetAuthor: %s", warn(String.valueOf(content.getTimestamp()), String.format(Locale.ENGLISH, "[handleRemoteDelete] Invalid remote delete! deleteTime: %d, targetTime: %d, deleteAuthor: %s, targetAuthor: %s",
content.getServerReceivedTimestamp(), targetMessage.getServerTimestamp(), sender.getId(), targetMessage.getRecipient().getId())); content.getServerReceivedTimestamp(), targetMessage.getServerTimestamp(), sender.getId(), targetMessage.getRecipient().getId()));
return null;
} }
} }
@ -1192,10 +1207,10 @@ public final class MessageContentProcessor {
messageNotifier.updateNotification(context); messageNotifier.updateNotification(context);
} }
private void handleMediaMessage(@NonNull SignalServiceContent content, private @Nullable MessageId handleMediaMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId, @NonNull Optional<Long> smsMessageId,
long receivedTime) long receivedTime)
throws StorageFailedException, BadGroupIdException throws StorageFailedException, BadGroupIdException
{ {
notifyTypingStoppedFromIncomingMessage(getMessageDestination(content, message), content.getSender(), content.getSenderDevice()); notifyTypingStoppedFromIncomingMessage(getMessageDestination(content, message), content.getSender(), content.getSenderDevice());
@ -1263,6 +1278,10 @@ public final class MessageContentProcessor {
if (message.isViewOnce()) { if (message.isViewOnce()) {
ApplicationDependencies.getViewOnceMessageManager().scheduleIfNecessary(); ApplicationDependencies.getViewOnceMessageManager().scheduleIfNecessary();
} }
return new MessageId(insertResult.get().getMessageId(), true);
} else {
return null;
} }
} }
@ -1416,11 +1435,11 @@ public final class MessageContentProcessor {
receiptDatabase.setUnidentified(unidentifiedStatus, messageId); receiptDatabase.setUnidentified(unidentifiedStatus, messageId);
} }
private void handleTextMessage(@NonNull SignalServiceContent content, private @Nullable MessageId handleTextMessage(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId, @NonNull Optional<Long> smsMessageId,
@NonNull Optional<GroupId> groupId, @NonNull Optional<GroupId> groupId,
long receivedTime) long receivedTime)
throws StorageFailedException, BadGroupIdException throws StorageFailedException, BadGroupIdException
{ {
MessageDatabase database = DatabaseFactory.getSmsDatabase(context); MessageDatabase database = DatabaseFactory.getSmsDatabase(context);
@ -1431,10 +1450,10 @@ public final class MessageContentProcessor {
handleExpirationUpdate(content, message, Optional.absent(), groupId, receivedTime); handleExpirationUpdate(content, message, Optional.absent(), groupId, receivedTime);
} }
Long threadId; Optional<InsertResult> insertResult;
if (smsMessageId.isPresent() && !message.getGroupContext().isPresent()) { if (smsMessageId.isPresent() && !message.getGroupContext().isPresent()) {
threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second(); insertResult = Optional.of(database.updateBundleMessageBody(smsMessageId.get(), body));
} else { } else {
notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice()); notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice());
@ -1450,16 +1469,16 @@ public final class MessageContentProcessor {
content.getServerUuid()); content.getServerUuid());
textMessage = new IncomingEncryptedMessage(textMessage, body); textMessage = new IncomingEncryptedMessage(textMessage, body);
Optional<InsertResult> insertResult = database.insertMessageInbox(textMessage); insertResult = database.insertMessageInbox(textMessage);
if (insertResult.isPresent()) threadId = insertResult.get().getThreadId();
else threadId = null;
if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get()); if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get());
} }
if (threadId != null) { if (insertResult.isPresent()) {
ApplicationDependencies.getMessageNotifier().updateNotification(context, threadId); ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
return new MessageId(insertResult.get().getMessageId(), false);
} else {
return null;
} }
} }
@ -1638,9 +1657,10 @@ public final class MessageContentProcessor {
} }
private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content, private void handleNeedsDeliveryReceipt(@NonNull SignalServiceContent content,
@NonNull SignalServiceDataMessage message) @NonNull SignalServiceDataMessage message,
@NonNull MessageId messageId)
{ {
ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(RecipientId.fromHighTrust(content.getSender()), message.getTimestamp())); ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(RecipientId.fromHighTrust(content.getSender()), message.getTimestamp(), messageId));
} }
private void handleViewedReceipt(@NonNull SignalServiceContent content, private void handleViewedReceipt(@NonNull SignalServiceContent content,

Wyświetl plik

@ -23,4 +23,12 @@
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:text="@string/preferences__internal_disable_profile_sharing" /> android:text="@string/preferences__internal_disable_profile_sharing" />
<com.google.android.material.button.MaterialButton
android:id="@+id/internal_delete_session"
style="@style/Signal.Widget.Button.Small.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/preferences__internal_delete_session" />
</LinearLayout> </LinearLayout>

Wyświetl plik

@ -2453,6 +2453,7 @@
<string name="preferences__internal_delete_all_dynamic_shortcuts" translatable="false">Delete all dynamic shortcuts</string> <string name="preferences__internal_delete_all_dynamic_shortcuts" translatable="false">Delete all dynamic shortcuts</string>
<string name="preferences__internal_click_to_delete_all_dynamic_shortcuts" translatable="false">Click to delete all dynamic shortcuts</string> <string name="preferences__internal_click_to_delete_all_dynamic_shortcuts" translatable="false">Click to delete all dynamic shortcuts</string>
<string name="preferences__internal_disable_profile_sharing" translatable="false">Disable Profile Sharing</string> <string name="preferences__internal_disable_profile_sharing" translatable="false">Disable Profile Sharing</string>
<string name="preferences__internal_delete_session" translatable="false">Delete Session</string>
<string name="preferences__internal_sender_key" translatable="false">Sender Key</string> <string name="preferences__internal_sender_key" translatable="false">Sender Key</string>
<string name="preferences__internal_clear_all_state" translatable="false">Clear all state</string> <string name="preferences__internal_clear_all_state" translatable="false">Clear all state</string>
<string name="preferences__internal_click_to_delete_all_sender_key_state" translatable="false">Click to delete all sender key state</string> <string name="preferences__internal_click_to_delete_all_sender_key_state" translatable="false">Click to delete all sender key state</string>