mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-17 13:48:26 +00:00
Handle SessionRequest messages.
This commit is contained in:
parent
538cd39d00
commit
93a9f4c1dc
@ -497,7 +497,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
for (Recipient recipient : recipients) {
|
||||
boolean isPush = isActiveInDirectory(recipient);
|
||||
|
||||
if (failIfNotPush && !isPush) {
|
||||
if (failIfNotPush && !isPush && !recipient.getAddress().isPhone()) {
|
||||
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
|
||||
recipient.toShortString())));
|
||||
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getAddress().serialize())) {
|
||||
|
@ -8,6 +8,7 @@ import android.support.annotation.Nullable;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
@ -23,15 +24,18 @@ import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||
|
||||
@ -41,6 +45,8 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import kotlin.Unit;
|
||||
|
||||
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer;
|
||||
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
@ -114,6 +120,10 @@ public class GroupMessageProcessor {
|
||||
database.create(id, group.getName().orNull(), members,
|
||||
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins);
|
||||
|
||||
if (group.getMembers().isPresent()) {
|
||||
establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
|
||||
}
|
||||
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
}
|
||||
|
||||
@ -183,6 +193,10 @@ public class GroupMessageProcessor {
|
||||
|
||||
if (!groupRecord.isActive()) database.setActive(id, true);
|
||||
|
||||
if (group.getMembers().isPresent()) {
|
||||
establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
|
||||
}
|
||||
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
}
|
||||
|
||||
@ -309,4 +323,20 @@ public class GroupMessageProcessor {
|
||||
return hexEncodedPublicKey;
|
||||
}
|
||||
}
|
||||
|
||||
private static void establishSessionsWithMembersIfNeeded(Context context, List<String> members) {
|
||||
String ourNumber = TextSecurePreferences.getLocalNumber(context);
|
||||
for (String member : members) {
|
||||
// Make sure we have session with all of the members secondary devices
|
||||
LokiStorageAPI.shared.getAllDevicePublicKeys(member).success(devices -> {
|
||||
if (devices.contains(ourNumber)) { return Unit.INSTANCE; }
|
||||
for (String device : devices) {
|
||||
SignalProtocolAddress protocolAddress = new SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID);
|
||||
boolean haveSession = new TextSecureSessionStore(context).containsSession(protocolAddress);
|
||||
if (!haveSession) { MessageSender.sendBackgroundSessionRequest(context, device); }
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.Database;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
|
||||
@ -296,6 +295,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
// Loki - Handle friend request acceptance if needed
|
||||
acceptFriendRequestIfNeeded(envelope, content);
|
||||
|
||||
// Loki - Session requests
|
||||
handleSessionRequestIfNeeded(envelope, content);
|
||||
|
||||
// Loki - Store pre key bundle
|
||||
// We shouldn't store it if it's a pairing message
|
||||
if (!content.getPairingAuthorisation().isPresent()) {
|
||||
@ -340,7 +342,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
}
|
||||
} else {
|
||||
// Loki - We shouldn't process session restore message any further
|
||||
if (message.isSessionRestore()) { return; }
|
||||
if (message.isSessionRestore() || message.isSessionRequest()) { return; }
|
||||
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
|
||||
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
|
||||
else if (message.isExpirationUpdate())
|
||||
@ -697,7 +699,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
Log.d("Loki", "Sent friend request to " + pubKey);
|
||||
} else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
|
||||
// Accept the incoming friend request
|
||||
becomeFriendsWithContact(pubKey, false);
|
||||
becomeFriendsWithContact(pubKey, false, false);
|
||||
// Send them an accept message back
|
||||
MessageSender.sendBackgroundMessage(context, pubKey);
|
||||
Log.d("Loki", "Became friends with " + deviceContact.getNumber());
|
||||
@ -1225,10 +1227,21 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
private void acceptFriendRequestIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
||||
// If we get anything other than a friend request, we can assume that we have a session with the other user
|
||||
if (envelope.isFriendRequest() || isGroupChatMessage(content)) { return; }
|
||||
becomeFriendsWithContact(content.getSender(), true);
|
||||
becomeFriendsWithContact(content.getSender(), true, false);
|
||||
}
|
||||
|
||||
private void becomeFriendsWithContact(String pubKey, boolean syncContact) {
|
||||
private void handleSessionRequestIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
||||
if (envelope.isFriendRequest() && isSessionRequest(content)) {
|
||||
// TODO: Check if member is in one of our private groups
|
||||
boolean isInOneOfOurGroups = false;
|
||||
if (isInOneOfOurGroups) {
|
||||
// Send a background message to acknowledge session request
|
||||
MessageSender.sendBackgroundMessage(context, content.getSender());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void becomeFriendsWithContact(String pubKey, boolean syncContact, boolean force) {
|
||||
LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
|
||||
Recipient contactID = Recipient.from(context, Address.fromSerialized(pubKey), false);
|
||||
if (contactID.isGroupRecipient()) return;
|
||||
@ -1236,6 +1249,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(contactID);
|
||||
LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID);
|
||||
if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS) { return; }
|
||||
|
||||
// We shouldn't be able to skip from None -> Friends in normal circumstances.
|
||||
// Multi-device is the exception to this rule because we want to automatically be friends with a secondary device
|
||||
if (!force && threadFriendRequestStatus == LokiThreadFriendRequestStatus.NONE) { return; }
|
||||
|
||||
// If the thread's friend request status is not `FRIENDS`, but we're receiving a message,
|
||||
// it must be a friend request accepted message. Declining a friend request doesn't send a message.
|
||||
lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
|
||||
@ -1256,13 +1274,13 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
}
|
||||
|
||||
private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
|
||||
if (!envelope.isFriendRequest() || message.isGroupUpdate()) { return; }
|
||||
if (!envelope.isFriendRequest() || message.isGroupUpdate() || message.isSessionRequest()) { return; }
|
||||
// This handles the case where another user sends us a regular message without authorisation
|
||||
Promise<Boolean, Exception> promise = PromiseUtil.timeout(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), 8000);
|
||||
boolean shouldBecomeFriends = PromiseUtil.get(promise, false);
|
||||
if (shouldBecomeFriends) {
|
||||
// Become friends AND update the message they sent
|
||||
becomeFriendsWithContact(content.getSender(), true);
|
||||
becomeFriendsWithContact(content.getSender(), true, true);
|
||||
// Send them an accept message back
|
||||
MessageSender.sendBackgroundMessage(context, content.getSender());
|
||||
} else {
|
||||
@ -1843,6 +1861,10 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isSessionRequest(SignalServiceContent content) {
|
||||
return content.getDataMessage().isPresent() && content.getDataMessage().get().isSessionRequest();
|
||||
}
|
||||
|
||||
private boolean isGroupChatMessage(SignalServiceContent content) {
|
||||
return content.getDataMessage().isPresent() && content.getDataMessage().get().getGroupInfo().isPresent();
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
|
||||
@ -30,9 +31,11 @@ import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
@ -160,7 +163,23 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList();
|
||||
else target = getGroupMessageRecipients(message.getRecipient().getAddress().toGroupString(), messageId);
|
||||
|
||||
List<SendMessageResult> results = deliver(message, target);
|
||||
// Only send messages to the contacts we have sessions with
|
||||
List<Address> validTargets = Stream.of(target).filter(member -> {
|
||||
SignalProtocolAddress protocolAddress = new SignalProtocolAddress(member.toPhoneString(), SignalServiceAddress.DEFAULT_DEVICE_ID);
|
||||
boolean hasSession = new TextSecureSessionStore(context).containsSession(protocolAddress);
|
||||
if (hasSession) { return true; }
|
||||
|
||||
// We should allow sending if we have a prekeybundle for the contact
|
||||
return DatabaseFactory.getLokiPreKeyBundleDatabase(context).hasPreKeyBundle(member.toPhoneString());
|
||||
}).toList();
|
||||
|
||||
// Send a session request to the other devices
|
||||
List<Address> others = Stream.of(target).filter(t -> !validTargets.contains(t)).toList();
|
||||
for (Address device : others) {
|
||||
MessageSender.sendBackgroundSessionRequest(context, device.toPhoneString());
|
||||
}
|
||||
|
||||
List<SendMessageResult> results = deliver(message, validTargets);
|
||||
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList();
|
||||
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList();
|
||||
Set<Address> successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet());
|
||||
|
@ -13,7 +13,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||
import java.io.IOException
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
data class BackgroundMessage private constructor(val data: Map<String, Any>) {
|
||||
@ -37,6 +36,12 @@ data class BackgroundMessage private constructor(val data: Map<String, Any>) {
|
||||
"friendRequest" to true,
|
||||
"sessionRestore" to true
|
||||
))
|
||||
@JvmStatic
|
||||
fun createSessionRequest(recipient: String) = BackgroundMessage(mapOf(
|
||||
"recipient" to recipient,
|
||||
"friendRequest" to true,
|
||||
"sessionRequest" to true
|
||||
))
|
||||
|
||||
internal fun parse(serialized: String): BackgroundMessage {
|
||||
val data = JsonUtil.fromJson(serialized, Map::class.java) as? Map<String, Any> ?: throw AssertionError("JSON parsing failed")
|
||||
@ -103,6 +108,10 @@ class PushBackgroundMessageSendJob private constructor(
|
||||
dataMessage.asSessionRestore(true)
|
||||
}
|
||||
|
||||
if (message.get("sessionRequest", false)) {
|
||||
dataMessage.asSessionRequest(true)
|
||||
}
|
||||
|
||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||
val address = SignalServiceAddress(recipient)
|
||||
try {
|
||||
|
@ -17,7 +17,6 @@
|
||||
package org.thoughtcrime.securesms.sms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
@ -60,11 +59,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
|
||||
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
|
||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||
@ -143,6 +138,10 @@ public class MessageSender {
|
||||
public static void sendRestoreSessionMessage(Context context, String contactHexEncodedPublicKey) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createSessionRestore(contactHexEncodedPublicKey)));
|
||||
}
|
||||
|
||||
public static void sendBackgroundSessionRequest(Context context, String contactHexEncodedPublicKey) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createSessionRequest(contactHexEncodedPublicKey)));
|
||||
}
|
||||
// endregion
|
||||
|
||||
public static long send(final Context context,
|
||||
|
Loading…
x
Reference in New Issue
Block a user