Merge pull request #90 from loki-project/robustness

Refactor
This commit is contained in:
gmbnt 2020-02-12 13:40:16 +11:00 committed by GitHub
commit fc2a7bb5a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 117 additions and 125 deletions

View File

@ -96,14 +96,13 @@ import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol; import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol;
import org.whispersystems.signalservice.loki.api.LokiDotNetAPI; import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
import org.whispersystems.signalservice.loki.api.LokiLongPoller; import org.whispersystems.signalservice.loki.api.LokiLongPoller;
import org.whispersystems.signalservice.loki.api.LokiP2PAPI; import org.whispersystems.signalservice.loki.api.LokiP2PAPI;
import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate; import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate;
import org.whispersystems.signalservice.loki.api.LokiPublicChat; import org.whispersystems.signalservice.loki.api.LokiPublicChat;
import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI; import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI;
import org.whispersystems.signalservice.loki.api.LokiRSSFeed; import org.whispersystems.signalservice.loki.api.LokiRSSFeed;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
import java.security.Security; import java.security.Security;
import java.util.ArrayList; import java.util.ArrayList;
@ -115,7 +114,6 @@ import java.util.concurrent.TimeUnit;
import dagger.ObjectGraph; import dagger.ObjectGraph;
import kotlin.Unit; import kotlin.Unit;
import network.loki.messenger.BuildConfig; import network.loki.messenger.BuildConfig;
import okhttp3.Cache;
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant; import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant; import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
@ -184,11 +182,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
ProcessLifecycleOwner.get().getLifecycle().addObserver(this); ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
// Loki - Set up P2P API if needed // Loki - Set up P2P API if needed
setUpP2PAPI(); setUpP2PAPI();
// Loki - Set the cache
LokiDotNetAPI.setCache(new Cache(this.getCacheDir(), OK_HTTP_CACHE_SIZE));
// Loki - Update device mappings // Loki - Update device mappings
if (setUpStorageAPIIfNeeded()) { if (setUpStorageAPIIfNeeded()) {
LokiFileServerAPI.Companion.getShared().updateUserDeviceMappings(); LokiFileServerAPI.Companion.getShared().updateUserDeviceLinks();
if (TextSecurePreferences.needsRevocationCheck(this)) { if (TextSecurePreferences.needsRevocationCheck(this)) {
checkNeedsRevocation(); checkNeedsRevocation();
} }

View File

@ -190,7 +190,7 @@ public class DeviceListFragment extends ListFragment
private void updateAddDeviceButtonVisibility() { private void updateAddDeviceButtonVisibility() {
if (addDeviceButton != null) { if (addDeviceButton != null) {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext());
boolean isDeviceLinkingEnabled = DatabaseFactory.getLokiAPIDatabase(getContext()).getPairingAuthorisations(userHexEncodedPublicKey).isEmpty(); boolean isDeviceLinkingEnabled = DatabaseFactory.getLokiAPIDatabase(getContext()).getDeviceLinks(userHexEncodedPublicKey).isEmpty();
addDeviceButton.setVisibility(isDeviceLinkingEnabled ? View.VISIBLE : View.INVISIBLE); addDeviceButton.setVisibility(isDeviceLinkingEnabled ? View.VISIBLE : View.INVISIBLE);
} }
} }

View File

@ -9,14 +9,13 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.TypingSendJob; import org.thoughtcrime.securesms.jobs.TypingSendJob;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import kotlin.Unit; import kotlin.Unit;
@ -91,7 +90,7 @@ public class TypingStatusSender {
ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(threadId, typingStarted)); ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(threadId, typingStarted));
return; return;
} }
LokiFileServerAPI.shared.getAllDevicePublicKeys(recipient.getAddress().serialize()).success(devices -> { LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(recipient.getAddress().serialize()).success(devices -> {
for (String device : devices) { for (String device : devices) {
Recipient deviceRecipient = Recipient.from(context, Address.fromSerialized(device), false); Recipient deviceRecipient = Recipient.from(context, Address.fromSerialized(device), false);
long deviceThreadID = threadDatabase.getThreadIdIfExistsFor(deviceRecipient); long deviceThreadID = threadDatabase.getThreadIdIfExistsFor(deviceRecipient);

View File

@ -232,10 +232,10 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.thoughtcrime.securesms.util.views.Stub; import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.loki.api.DeviceLink;
import org.whispersystems.signalservice.loki.api.LokiAPI; import org.whispersystems.signalservice.loki.api.LokiAPI;
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.api.LokiPublicChat; import org.whispersystems.signalservice.loki.api.LokiPublicChat;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
import org.whispersystems.signalservice.loki.api.PairingAuthorisation;
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.messaging.Mention; import org.whispersystems.signalservice.loki.messaging.Mention;
@ -2290,7 +2290,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (threadID != this.threadId) { if (threadID != this.threadId) {
Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID); Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
if (threadRecipient != null && !threadRecipient.isGroupRecipient()) { if (threadRecipient != null && !threadRecipient.isGroupRecipient()) {
LokiFileServerAPI.shared.getAllDevicePublicKeys(threadRecipient.getAddress().serialize()).success(devices -> { LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(threadRecipient.getAddress().serialize()).success(devices -> {
// We should update our input if this thread is a part of the other threads device // We should update our input if this thread is a part of the other threads device
if (devices.contains(recipient.getAddress().serialize())) { if (devices.contains(recipient.getAddress().serialize())) {
this.updateInputPanel(); this.updateInputPanel();
@ -3162,11 +3162,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// region Loki // region Loki
private void updateTitleTextView(GlideRequests glide, Recipient recipient) { private void updateTitleTextView(GlideRequests glide, Recipient recipient) {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this); String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
List<PairingAuthorisation> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getPairingAuthorisations(userHexEncodedPublicKey); Set<DeviceLink> deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userHexEncodedPublicKey);
HashSet<String> userLinkedDeviceHexEncodedPublicKeys = new HashSet<>(); HashSet<String> userLinkedDeviceHexEncodedPublicKeys = new HashSet<>();
for (PairingAuthorisation deviceLink : deviceLinks) { for (DeviceLink deviceLink : deviceLinks) {
userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getPrimaryDevicePublicKey().toLowerCase()); userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getMasterHexEncodedPublicKey().toLowerCase());
userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getSecondaryDevicePublicKey().toLowerCase()); userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getSlaveHexEncodedPublicKey().toLowerCase());
} }
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase()); userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase());
if (recipient == null) { if (recipient == null) {

View File

@ -11,13 +11,14 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities;
import org.thoughtcrime.securesms.util.AsyncLoader; import org.thoughtcrime.securesms.util.AsyncLoader;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
import java.io.File; import java.io.File;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Set;
public class DeviceListLoader extends AsyncLoader<List<Device>> { public class DeviceListLoader extends AsyncLoader<List<Device>> {
@ -33,7 +34,7 @@ public class DeviceListLoader extends AsyncLoader<List<Device>> {
public List<Device> loadInBackground() { public List<Device> loadInBackground() {
try { try {
String ourPublicKey = TextSecurePreferences.getLocalNumber(getContext()); String ourPublicKey = TextSecurePreferences.getLocalNumber(getContext());
List<String> secondaryDevicePublicKeys = LokiFileServerAPI.shared.getSecondaryDevicePublicKeys(ourPublicKey).get(); Set<String> secondaryDevicePublicKeys = LokiDeviceLinkUtilities.INSTANCE.getSlaveHexEncodedPublicKeys(ourPublicKey).get();
List<Device> devices = Stream.of(secondaryDevicePublicKeys).map(this::mapToDevice).toList(); List<Device> devices = Stream.of(secondaryDevicePublicKeys).map(this::mapToDevice).toList();
Collections.sort(devices, new DeviceComparator()); Collections.sort(devices, new DeviceComparator());
return devices; return devices;

View File

@ -36,7 +36,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type; import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil; import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.util.Collections; import java.util.Collections;
@ -318,7 +318,7 @@ public class GroupMessageProcessor {
try { try {
String masterHexEncodedPublicKey = hexEncodedPublicKey.equalsIgnoreCase(ourPublicKey) String masterHexEncodedPublicKey = hexEncodedPublicKey.equalsIgnoreCase(ourPublicKey)
? TextSecurePreferences.getMasterHexEncodedPublicKey(context) ? TextSecurePreferences.getMasterHexEncodedPublicKey(context)
: PromiseUtil.timeout(LokiFileServerAPI.shared.getPrimaryDevicePublicKey(hexEncodedPublicKey), 5000).get(); : PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey), 5000).get();
return masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : hexEncodedPublicKey; return masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : hexEncodedPublicKey;
} catch (Exception e) { } catch (Exception e) {
return hexEncodedPublicKey; return hexEncodedPublicKey;
@ -329,7 +329,7 @@ public class GroupMessageProcessor {
String ourNumber = TextSecurePreferences.getLocalNumber(context); String ourNumber = TextSecurePreferences.getLocalNumber(context);
for (String member : members) { for (String member : members) {
// Make sure we have session with all of the members secondary devices // Make sure we have session with all of the members secondary devices
LokiFileServerAPI.shared.getAllDevicePublicKeys(member).success(devices -> { LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(member).success(devices -> {
if (devices.contains(ourNumber)) { return Unit.INSTANCE; } if (devices.contains(ourNumber)) { return Unit.INSTANCE; }
for (String device : devices) { for (String device : devices) {
SignalProtocolAddress protocolAddress = new SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID); SignalProtocolAddress protocolAddress = new SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID);

View File

@ -68,13 +68,13 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.FriendRequestHandler; import org.thoughtcrime.securesms.loki.FriendRequestHandler;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities;
import org.thoughtcrime.securesms.loki.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyRecordDatabase;
import org.thoughtcrime.securesms.loki.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase;
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyRecordDatabase;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage; import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
@ -129,10 +129,11 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.loki.api.DeviceLink;
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession; import org.whispersystems.signalservice.loki.api.DeviceLinkingSession;
import org.whispersystems.signalservice.loki.api.LokiAPI; import org.whispersystems.signalservice.loki.api.LokiAPI;
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
import org.whispersystems.signalservice.loki.api.PairingAuthorisation;
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage; import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage;
@ -1102,20 +1103,20 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
} }
private boolean isValidPairingMessage(@NonNull PairingAuthorisation authorisation) { private boolean isValidPairingMessage(@NonNull DeviceLink authorisation) {
boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null; boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context); String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
boolean isRequest = (authorisation.getType() == PairingAuthorisation.Type.REQUEST); boolean isRequest = (authorisation.getType() == DeviceLink.Type.REQUEST);
if (authorisation.getRequestSignature() == null) { if (authorisation.getRequestSignature() == null) {
Log.d("Loki", "Ignoring pairing request message without a request signature."); Log.d("Loki", "Ignoring pairing request message without a request signature.");
return false; return false;
} else if (isRequest && isSecondaryDevice) { } else if (isRequest && isSecondaryDevice) {
Log.d("Loki", "Ignoring unexpected pairing request message (the device is already paired as a secondary device)."); Log.d("Loki", "Ignoring unexpected pairing request message (the device is already paired as a secondary device).");
return false; return false;
} else if (isRequest && !authorisation.getPrimaryDevicePublicKey().equals(userHexEncodedPublicKey)) { } else if (isRequest && !authorisation.getMasterHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
Log.d("Loki", "Ignoring pairing request message addressed to another user."); Log.d("Loki", "Ignoring pairing request message addressed to another user.");
return false; return false;
} else if (isRequest && authorisation.getSecondaryDevicePublicKey().equals(userHexEncodedPublicKey)) { } else if (isRequest && authorisation.getSlaveHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
Log.d("Loki", "Ignoring pairing request message from self."); Log.d("Loki", "Ignoring pairing request message from self.");
return false; return false;
} }
@ -1127,16 +1128,16 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(primaryDevice, url)); ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(primaryDevice, url));
} }
private void handlePairingMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) { private void handlePairingMessage(@NonNull DeviceLink authorisation, @NonNull SignalServiceContent content) {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context); String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
if (authorisation.getType() == PairingAuthorisation.Type.REQUEST) { if (authorisation.getType() == DeviceLink.Type.REQUEST) {
handlePairingRequestMessage(authorisation, content); handlePairingRequestMessage(authorisation, content);
} else if (authorisation.getSecondaryDevicePublicKey().equals(userHexEncodedPublicKey)) { } else if (authorisation.getSlaveHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
handlePairingAuthorisationMessage(authorisation, content); handlePairingAuthorisationMessage(authorisation, content);
} }
} }
private void handlePairingRequestMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) { private void handlePairingRequestMessage(@NonNull DeviceLink authorisation, @NonNull SignalServiceContent content) {
boolean isValid = isValidPairingMessage(authorisation); boolean isValid = isValidPairingMessage(authorisation);
DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared(); DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared();
if (isValid && linkingSession.isListeningForLinkingRequests()) { if (isValid && linkingSession.isListeningForLinkingRequests()) {
@ -1146,7 +1147,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
} }
private void handlePairingAuthorisationMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) { private void handlePairingAuthorisationMessage(@NonNull DeviceLink authorisation, @NonNull SignalServiceContent content) {
// Prepare // Prepare
boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null; boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
if (isSecondaryDevice) { if (isSecondaryDevice) {
@ -1162,23 +1163,23 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
Log.d("Loki", "Ignoring pairing authorisation message."); Log.d("Loki", "Ignoring pairing authorisation message.");
return; return;
} }
if (authorisation.getType() != PairingAuthorisation.Type.GRANT) { return; } if (authorisation.getType() != DeviceLink.Type.AUTHORIZATION) { return; }
Log.d("Loki", "Received pairing authorisation message from: " + authorisation.getPrimaryDevicePublicKey() + "."); Log.d("Loki", "Received pairing authorisation message from: " + authorisation.getMasterHexEncodedPublicKey() + ".");
// Save PreKeyBundle if for whatever reason we got one // Save PreKeyBundle if for whatever reason we got one
storePreKeyBundleIfNeeded(content); storePreKeyBundleIfNeeded(content);
// Process // Process
DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(authorisation); DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(authorisation);
// Store the primary device's public key // Store the primary device's public key
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context); String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
DatabaseFactory.getLokiAPIDatabase(context).removePairingAuthorisations(userHexEncodedPublicKey); DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(userHexEncodedPublicKey);
DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(authorisation); DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(authorisation);
TextSecurePreferences.setMasterHexEncodedPublicKey(context, authorisation.getPrimaryDevicePublicKey()); TextSecurePreferences.setMasterHexEncodedPublicKey(context, authorisation.getMasterHexEncodedPublicKey());
TextSecurePreferences.setMultiDevice(context, true); TextSecurePreferences.setMultiDevice(context, true);
// Send a background message to the primary device // Send a background message to the primary device
MessageSender.sendBackgroundMessage(context, authorisation.getPrimaryDevicePublicKey()); MessageSender.sendBackgroundMessage(context, authorisation.getMasterHexEncodedPublicKey());
// Propagate the updates to the file server // Propagate the updates to the file server
LokiFileServerAPI storageAPI = LokiFileServerAPI.Companion.getShared(); LokiFileServerAPI storageAPI = LokiFileServerAPI.Companion.getShared();
storageAPI.updateUserDeviceMappings(); storageAPI.updateUserDeviceLinks();
// Update display names // Update display names
if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) { if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) {
TextSecurePreferences.setProfileName(context, content.senderDisplayName.get()); TextSecurePreferences.setProfileName(context, content.senderDisplayName.get());
@ -1245,7 +1246,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
private void handleSessionRequestIfNeeded(@NonNull SignalServiceContent content) { private void handleSessionRequestIfNeeded(@NonNull SignalServiceContent content) {
if (content.isFriendRequest() && isSessionRequest(content)) { if (content.isFriendRequest() && isSessionRequest(content)) {
// Check if the session request from a member in one of our groups or our friend // Check if the session request from a member in one of our groups or our friend
LokiFileServerAPI.shared.getPrimaryDevicePublicKey(content.getSender()).success(primaryDevicePublicKey -> { LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(content.getSender()).success(primaryDevicePublicKey -> {
String sender = primaryDevicePublicKey != null ? primaryDevicePublicKey : content.getSender(); String sender = primaryDevicePublicKey != null ? primaryDevicePublicKey : content.getSender();
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(sender), false)); long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(sender), false));
LokiThreadFriendRequestStatus threadFriendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID); LokiThreadFriendRequestStatus threadFriendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
@ -1284,7 +1285,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// Allow profile sharing with contact // Allow profile sharing with contact
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(contactID, true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(contactID, true);
// Update the last message if needed // Update the last message if needed
LokiFileServerAPI.shared.getPrimaryDevicePublicKey(pubKey).success(primaryDevice -> { LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(pubKey).success(primaryDevice -> {
Util.runOnMain(() -> { Util.runOnMain(() -> {
long primaryDeviceThreadID = primaryDevice == null ? threadID : DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(primaryDevice), false)); long primaryDeviceThreadID = primaryDevice == null ? threadID : DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(primaryDevice), false));
FriendRequestHandler.updateLastFriendRequestMessage(context, primaryDeviceThreadID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED); FriendRequestHandler.updateLastFriendRequestMessage(context, primaryDeviceThreadID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
@ -1799,7 +1800,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
*/ */
private Recipient getPrimaryDeviceRecipient(String pubKey) { private Recipient getPrimaryDeviceRecipient(String pubKey) {
try { try {
String primaryDevice = PromiseUtil.timeout(LokiFileServerAPI.shared.getPrimaryDevicePublicKey(pubKey), 5000).get(); String primaryDevice = PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(pubKey), 5000).get();
String publicKey = (primaryDevice != null) ? primaryDevice : pubKey; String publicKey = (primaryDevice != null) ? primaryDevice : pubKey;
// If the public key matches our primary device then we need to forward the message to ourselves (Note to self) // If the public key matches our primary device then we need to forward the message to ourselves (Note to self)
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);

View File

@ -50,8 +50,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.api.LokiPublicChat; import org.whispersystems.signalservice.loki.api.LokiPublicChat;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil; import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.io.IOException; import java.io.IOException;
@ -368,7 +368,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
if (!member.isPhone() || member.serialize().equalsIgnoreCase(localNumber)) { continue; } if (!member.isPhone() || member.serialize().equalsIgnoreCase(localNumber)) { continue; }
try { try {
List<String> secondaryDevices = PromiseUtil.timeout(LokiFileServerAPI.shared.getSecondaryDevicePublicKeys(member.serialize()), 5000).get(); Set<String> secondaryDevices = PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getSlaveHexEncodedPublicKeys(member.serialize()), 5000).get();
memberSet.addAll(Stream.of(secondaryDevices).map(string -> { memberSet.addAll(Stream.of(secondaryDevices).map(string -> {
// Loki - Calling .map(Address::fromSerialized) is causing errors, thus we use the long method :( // Loki - Calling .map(Address::fromSerialized) is causing errors, thus we use the long method :(
return Address.fromSerialized(string); return Address.fromSerialized(string);

View File

@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -43,13 +42,12 @@ import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSy
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage; import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil; import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -292,7 +290,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
LokiSyncMessage syncMessage = null; LokiSyncMessage syncMessage = null;
if (shouldSendSyncMessage) { if (shouldSendSyncMessage) {
// Set the sync message destination the primary device, this way it will show that we sent a message to the primary device and not a secondary device // Set the sync message destination the primary device, this way it will show that we sent a message to the primary device and not a secondary device
String primaryDevice = PromiseUtil.get(LokiFileServerAPI.shared.getPrimaryDevicePublicKey(address.getNumber()), null); String primaryDevice = PromiseUtil.get(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(address.getNumber()), null);
SignalServiceAddress primaryAddress = primaryDevice == null ? address : new SignalServiceAddress(primaryDevice); SignalServiceAddress primaryAddress = primaryDevice == null ? address : new SignalServiceAddress(primaryDevice);
// We also need to use the original message id and not -1 // We also need to use the original message id and not -1
syncMessage = new LokiSyncMessage(primaryAddress, templateMessageId); syncMessage = new LokiSyncMessage(primaryAddress, templateMessageId);

View File

@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.service.ExpiringMessageManager;
@ -30,7 +29,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage; import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil; import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
@ -237,7 +236,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
LokiSyncMessage syncMessage = null; LokiSyncMessage syncMessage = null;
if (shouldSendSyncMessage) { if (shouldSendSyncMessage) {
// Set the sync message destination to the primary device, this way it will show that we sent a message to the primary device and not a secondary device // Set the sync message destination to the primary device, this way it will show that we sent a message to the primary device and not a secondary device
String primaryDevice = PromiseUtil.get(LokiFileServerAPI.shared.getPrimaryDevicePublicKey(address.getNumber()), null); String primaryDevice = PromiseUtil.get(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(address.getNumber()), null);
SignalServiceAddress primaryAddress = primaryDevice == null ? address : new SignalServiceAddress(primaryDevice); SignalServiceAddress primaryAddress = primaryDevice == null ? address : new SignalServiceAddress(primaryDevice);
// We also need to use the original message id and not -1 // We also need to use the original message id and not -1
syncMessage = new LokiSyncMessage(primaryAddress, templateMessageId); syncMessage = new LokiSyncMessage(primaryAddress, templateMessageId);

View File

@ -20,8 +20,9 @@ import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.api.DeviceLink
import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.recover import org.whispersystems.signalservice.loki.utilities.recover
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
@ -32,12 +33,12 @@ fun checkForRevocation(context: Context) {
val primaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return val primaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return
val ourDevice = TextSecurePreferences.getLocalNumber(context) val ourDevice = TextSecurePreferences.getLocalNumber(context)
LokiFileServerAPI.shared.fetchDeviceMappings(primaryDevice).bind { mappings -> LokiFileServerAPI.shared.getDeviceLinks(primaryDevice, true).bind { mappings ->
val ourMapping = mappings.find { it.secondaryDevicePublicKey == ourDevice } val ourMapping = mappings.find { it.slaveHexEncodedPublicKey == ourDevice }
if (ourMapping != null) throw Error("Device has not been revoked") if (ourMapping != null) throw Error("Device has not been revoked")
// remove pairing authorisations for our device // remove pairing authorisations for our device
DatabaseFactory.getLokiAPIDatabase(context).removePairingAuthorisations(ourDevice) DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(ourDevice)
LokiFileServerAPI.shared.updateUserDeviceMappings() LokiFileServerAPI.shared.updateUserDeviceLinks()
}.successUi { }.successUi {
TextSecurePreferences.setNeedsRevocationCheck(context, false) TextSecurePreferences.setNeedsRevocationCheck(context, false)
ApplicationContext.getInstance(context).clearData() ApplicationContext.getInstance(context).clearData()
@ -49,7 +50,7 @@ fun checkForRevocation(context: Context) {
fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> { fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
return LokiFileServerAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys -> return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(hexEncodedPublicKey).map { keys ->
val map = mutableMapOf<String, LokiThreadFriendRequestStatus>() val map = mutableMapOf<String, LokiThreadFriendRequestStatus>()
for (devicePublicKey in keys) { for (devicePublicKey in keys) {
val device = Recipient.from(context, Address.fromSerialized(devicePublicKey), false) val device = Recipient.from(context, Address.fromSerialized(devicePublicKey), false)
@ -63,7 +64,7 @@ fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: Str
fun getAllDevicePublicKeysWithFriendStatus(context: Context, hexEncodedPublicKey: String): Promise<Map<String, Boolean>, Unit> { fun getAllDevicePublicKeysWithFriendStatus(context: Context, hexEncodedPublicKey: String): Promise<Map<String, Boolean>, Unit> {
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
return LokiFileServerAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys -> return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(hexEncodedPublicKey).map { keys ->
val devices = keys.toMutableSet() val devices = keys.toMutableSet()
if (hexEncodedPublicKey != userHexEncodedPublicKey) { if (hexEncodedPublicKey != userHexEncodedPublicKey) {
devices.remove(userHexEncodedPublicKey) devices.remove(userHexEncodedPublicKey)
@ -92,7 +93,7 @@ fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Conte
return Promise.of(true) return Promise.of(true)
} }
return LokiFileServerAPI.shared.getPrimaryDevicePublicKey(publicKey).bind { primaryDevicePublicKey -> return LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(publicKey).bind { primaryDevicePublicKey ->
// If the public key doesn't have any other devices then go through regular friend request logic // If the public key doesn't have any other devices then go through regular friend request logic
if (primaryDevicePublicKey == null) { if (primaryDevicePublicKey == null) {
return@bind Promise.of(false) return@bind Promise.of(false)
@ -108,12 +109,12 @@ fun shouldAutomaticallyBecomeFriendsWithDevice(publicKey: String, context: Conte
} }
} }
fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey: String, authorisation: PairingAuthorisation): Promise<Unit, Exception> { fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey: String, authorisation: DeviceLink): Promise<Unit, Exception> {
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
val address = SignalServiceAddress(contactHexEncodedPublicKey) val address = SignalServiceAddress(contactHexEncodedPublicKey)
val message = SignalServiceDataMessage.newBuilder().withPairingAuthorisation(authorisation) val message = SignalServiceDataMessage.newBuilder().withPairingAuthorisation(authorisation)
// A REQUEST should always act as a friend request. A GRANT should always be replying back as a normal message. // A REQUEST should always act as a friend request. A GRANT should always be replying back as a normal message.
if (authorisation.type == PairingAuthorisation.Type.REQUEST) { if (authorisation.type == DeviceLink.Type.REQUEST) {
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number) val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number)
message.asFriendRequest(true).withPreKeyBundle(preKeyBundle) message.asFriendRequest(true).withPreKeyBundle(preKeyBundle)
} else { } else {
@ -139,17 +140,17 @@ fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey
} }
} }
fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisation: PairingAuthorisation) { fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisation: DeviceLink) {
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
val signedPairingAuthorisation = pairingAuthorisation.sign(PairingAuthorisation.Type.GRANT, userPrivateKey) val signedPairingAuthorisation = pairingAuthorisation.sign(DeviceLink.Type.AUTHORIZATION, userPrivateKey)
if (signedPairingAuthorisation == null || signedPairingAuthorisation.type != PairingAuthorisation.Type.GRANT) { if (signedPairingAuthorisation == null || signedPairingAuthorisation.type != DeviceLink.Type.AUTHORIZATION) {
Log.d("Loki", "Failed to sign pairing authorization.") Log.d("Loki", "Failed to sign pairing authorization.")
return return
} }
DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(signedPairingAuthorisation) DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(signedPairingAuthorisation)
TextSecurePreferences.setMultiDevice(context, true) TextSecurePreferences.setMultiDevice(context, true)
val address = Address.fromSerialized(pairingAuthorisation.secondaryDevicePublicKey); val address = Address.fromSerialized(pairingAuthorisation.slaveHexEncodedPublicKey)
val sendPromise = retryIfNeeded(8) { val sendPromise = retryIfNeeded(8) {
sendPairingAuthorisationMessage(context, address.serialize(), signedPairingAuthorisation) sendPairingAuthorisationMessage(context, address.serialize(), signedPairingAuthorisation)
@ -157,7 +158,7 @@ fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisatio
Log.d("Loki", "Failed to send pairing authorization message to ${address.serialize()}.") Log.d("Loki", "Failed to send pairing authorization message to ${address.serialize()}.")
} }
val updatePromise = LokiFileServerAPI.shared.updateUserDeviceMappings().fail { val updatePromise = LokiFileServerAPI.shared.updateUserDeviceLinks().fail {
Log.d("Loki", "Failed to update device mapping") Log.d("Loki", "Failed to update device mapping")
} }
@ -177,7 +178,7 @@ fun isOneOfOurDevices(context: Context, address: Address): Promise<Boolean, Exce
} }
val ourPublicKey = TextSecurePreferences.getLocalNumber(context) val ourPublicKey = TextSecurePreferences.getLocalNumber(context)
return LokiFileServerAPI.shared.getAllDevicePublicKeys(ourPublicKey).map { devices -> return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(ourPublicKey).map { devices ->
devices.contains(address.serialize()) devices.contains(address.serialize())
} }
} }

View File

@ -12,9 +12,9 @@ class CreateClosedGroupLoader(context: Context) : AsyncLoader<List<String>>(cont
val threadDatabase = DatabaseFactory.getThreadDatabase(context) val threadDatabase = DatabaseFactory.getThreadDatabase(context)
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val deviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getPairingAuthorisations(userHexEncodedPublicKey) val deviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(userHexEncodedPublicKey)
val userLinkedDeviceHexEncodedPublicKeys = deviceLinks.flatMap { val userLinkedDeviceHexEncodedPublicKeys = deviceLinks.flatMap {
listOf( it.primaryDevicePublicKey.toLowerCase(), it.secondaryDevicePublicKey.toLowerCase() ) listOf( it.masterHexEncodedPublicKey.toLowerCase(), it.slaveHexEncodedPublicKey.toLowerCase() )
}.toMutableSet() }.toMutableSet()
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase()) userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase())
val cursor = threadDatabase.conversationList val cursor = threadDatabase.conversationList

View File

@ -26,7 +26,7 @@ import org.whispersystems.curve25519.Curve25519
import org.whispersystems.libsignal.ecc.Curve import org.whispersystems.libsignal.ecc.Curve
import org.whispersystems.libsignal.ecc.ECKeyPair import org.whispersystems.libsignal.ecc.ECKeyPair
import org.whispersystems.libsignal.util.KeyHelper import org.whispersystems.libsignal.util.KeyHelper
import org.whispersystems.signalservice.loki.api.PairingAuthorisation import org.whispersystems.signalservice.loki.api.DeviceLink
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
@ -92,7 +92,7 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey) TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
TextSecurePreferences.setHasSeenWelcomeScreen(this, true) TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
TextSecurePreferences.setPromptedPushRegistration(this, true) TextSecurePreferences.setPromptedPushRegistration(this, true)
val authorisation = PairingAuthorisation(hexEncodedPublicKey, userHexEncodedPublicKey).sign(PairingAuthorisation.Type.REQUEST, keyPair!!.privateKey.serialize()) val authorisation = DeviceLink(hexEncodedPublicKey, userHexEncodedPublicKey).sign(DeviceLink.Type.REQUEST, keyPair!!.privateKey.serialize())
if (authorisation == null) { if (authorisation == null) {
Log.d("Loki", "Failed to sign device link request.") Log.d("Loki", "Failed to sign device link request.")
reset() reset()
@ -107,13 +107,13 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega
linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog") linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog")
AsyncTask.execute { AsyncTask.execute {
retryIfNeeded(8) { retryIfNeeded(8) {
sendPairingAuthorisationMessage(this@LandingActivity, authorisation.primaryDevicePublicKey, authorisation) sendPairingAuthorisationMessage(this@LandingActivity, authorisation.masterHexEncodedPublicKey, authorisation)
} }
} }
} }
override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) { override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
TextSecurePreferences.setMasterHexEncodedPublicKey(this, authorization.primaryDevicePublicKey) TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterHexEncodedPublicKey)
val intent = Intent(this, HomeActivity::class.java) val intent = Intent(this, HomeActivity::class.java)
show(intent) show(intent)
finish() finish()

View File

@ -20,8 +20,8 @@ import org.thoughtcrime.securesms.loki.signAndSendPairingAuthorisationMessage
import org.thoughtcrime.securesms.sms.MessageSender import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.DeviceLink
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager.LoaderCallbacks<List<Device>>, DeviceClickListener, EditDeviceNameDialogDelegate, LinkDeviceMasterModeDialogDelegate { class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager.LoaderCallbacks<List<Device>>, DeviceClickListener, EditDeviceNameDialogDelegate, LinkDeviceMasterModeDialogDelegate {
private var devices = listOf<Device>() private var devices = listOf<Device>()
@ -119,14 +119,14 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this)
val database = DatabaseFactory.getLokiAPIDatabase(this) val database = DatabaseFactory.getLokiAPIDatabase(this)
database.removePairingAuthorisation(userHexEncodedPublicKey, slaveDeviceHexEncodedPublicKey) database.removePairingAuthorisation(userHexEncodedPublicKey, slaveDeviceHexEncodedPublicKey)
LokiFileServerAPI.shared.updateUserDeviceMappings().success { LokiFileServerAPI.shared.updateUserDeviceLinks().success {
MessageSender.sendUnpairRequest(this, slaveDeviceHexEncodedPublicKey) MessageSender.sendUnpairRequest(this, slaveDeviceHexEncodedPublicKey)
} }
LoaderManager.getInstance(this).restartLoader(0, null, this) LoaderManager.getInstance(this).restartLoader(0, null, this)
Toast.makeText(this, "Your device was unlinked successfully", Toast.LENGTH_LONG).show() Toast.makeText(this, "Your device was unlinked successfully", Toast.LENGTH_LONG).show()
} }
override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) { override fun onDeviceLinkRequestAuthorized(authorization: DeviceLink) {
AsyncTask.execute { AsyncTask.execute {
signAndSendPairingAuthorisationMessage(this, authorization) signAndSendPairingAuthorisationMessage(this, authorization)
Util.runOnMain { Util.runOnMain {

View File

@ -6,7 +6,7 @@ import org.thoughtcrime.securesms.devicelist.Device
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities
import org.thoughtcrime.securesms.util.AsyncLoader import org.thoughtcrime.securesms.util.AsyncLoader
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
import java.io.File import java.io.File
@ -20,7 +20,7 @@ class LinkedDevicesLoader(context: Context) : AsyncLoader<List<Device>>(context)
override fun loadInBackground(): List<Device>? { override fun loadInBackground(): List<Device>? {
try { try {
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val slaveDeviceHexEncodedPublicKeys = LokiFileServerAPI.shared.getSecondaryDevicePublicKeys(userHexEncodedPublicKey).get() val slaveDeviceHexEncodedPublicKeys = LokiDeviceLinkUtilities.getSlaveHexEncodedPublicKeys(userHexEncodedPublicKey).get()
return slaveDeviceHexEncodedPublicKeys.map { hexEncodedPublicKey -> return slaveDeviceHexEncodedPublicKeys.map { hexEncodedPublicKey ->
val shortID = MnemonicUtilities.getFirst3Words(mnemonicCodec, hexEncodedPublicKey) val shortID = MnemonicUtilities.getFirst3Words(mnemonicCodec, hexEncodedPublicKey)
val name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey) val name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey)

View File

@ -17,15 +17,15 @@ import org.thoughtcrime.securesms.loki.redesign.utilities.QRCodeUtilities
import org.thoughtcrime.securesms.loki.toPx import org.thoughtcrime.securesms.loki.toPx
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.DeviceLink
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener { class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener {
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) } private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
private lateinit var contentView: View private lateinit var contentView: View
private var authorization: PairingAuthorisation? = null private var authorization: DeviceLink? = null
var delegate: LinkDeviceMasterModeDialogDelegate? = null var delegate: LinkDeviceMasterModeDialogDelegate? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@ -45,8 +45,8 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
return result return result
} }
override fun requestUserAuthorization(authorization: PairingAuthorisation) { override fun requestUserAuthorization(authorization: DeviceLink) {
if (authorization.type != PairingAuthorisation.Type.REQUEST || authorization.primaryDevicePublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return } if (authorization.type != DeviceLink.Type.REQUEST || authorization.masterHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return }
Util.runOnMain { Util.runOnMain {
this.authorization = authorization this.authorization = authorization
contentView.qrCodeImageView.visibility = View.GONE contentView.qrCodeImageView.visibility = View.GONE
@ -56,7 +56,7 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
contentView.titleTextView.text = "Linking Request Received" contentView.titleTextView.text = "Linking Request Received"
contentView.explanationTextView.text = "Please check that the words below match those shown on your other device" contentView.explanationTextView.text = "Please check that the words below match those shown on your other device"
contentView.mnemonicTextView.visibility = View.VISIBLE contentView.mnemonicTextView.visibility = View.VISIBLE
contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), authorization.secondaryDevicePublicKey) contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), authorization.slaveHexEncodedPublicKey)
contentView.authorizeButton.visibility = View.VISIBLE contentView.authorizeButton.visibility = View.VISIBLE
} }
} }
@ -73,7 +73,7 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
DeviceLinkingSession.shared.stopListeningForLinkingRequests() DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this) DeviceLinkingSession.shared.removeListener(this)
if (authorization != null) { if (authorization != null) {
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(authorization!!.secondaryDevicePublicKey) DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(authorization!!.slaveHexEncodedPublicKey)
} }
dismiss() dismiss()
delegate?.onDeviceLinkCanceled() delegate?.onDeviceLinkCanceled()
@ -82,6 +82,6 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene
interface LinkDeviceMasterModeDialogDelegate { interface LinkDeviceMasterModeDialogDelegate {
fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) fun onDeviceLinkRequestAuthorized(authorization: DeviceLink)
fun onDeviceLinkCanceled() fun onDeviceLinkCanceled()
} }

View File

@ -15,15 +15,15 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.DeviceLink
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener { class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener {
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) } private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
private lateinit var contentView: View private lateinit var contentView: View
private var authorization: PairingAuthorisation? = null private var authorization: DeviceLink? = null
var delegate: LinkDeviceSlaveModeDialogDelegate? = null var delegate: LinkDeviceSlaveModeDialogDelegate? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@ -40,8 +40,8 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
return result return result
} }
override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) { override fun onDeviceLinkRequestAuthorized(authorization: DeviceLink) {
if (authorization.type != PairingAuthorisation.Type.GRANT || authorization.secondaryDevicePublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return } if (authorization.type != DeviceLink.Type.AUTHORIZATION || authorization.slaveHexEncodedPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return }
Util.runOnMain { Util.runOnMain {
this.authorization = authorization this.authorization = authorization
DeviceLinkingSession.shared.stopListeningForLinkingRequests() DeviceLinkingSession.shared.stopListeningForLinkingRequests()
@ -71,6 +71,6 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener
interface LinkDeviceSlaveModeDialogDelegate { interface LinkDeviceSlaveModeDialogDelegate {
fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) fun onDeviceLinkRequestAuthorized(authorization: DeviceLink)
fun onDeviceLinkCanceled() fun onDeviceLinkCanceled()
} }

View File

@ -7,9 +7,9 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.redesign.utilities.* import org.thoughtcrime.securesms.loki.redesign.utilities.*
import org.thoughtcrime.securesms.util.Base64 import org.thoughtcrime.securesms.util.Base64
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.loki.api.DeviceLink
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol
import org.whispersystems.signalservice.loki.api.LokiAPITarget import org.whispersystems.signalservice.loki.api.LokiAPITarget
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
// TODO: Clean this up a bit // TODO: Clean this up a bit
@ -179,28 +179,28 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.delete(lastDeletionServerIDCache,"$lastDeletionServerIDCacheIndex = ?", wrap(index)) database.delete(lastDeletionServerIDCache,"$lastDeletionServerIDCacheIndex = ?", wrap(index))
} }
override fun getPairingAuthorisations(hexEncodedPublicKey: String): List<PairingAuthorisation> { override fun getDeviceLinks(hexEncodedPublicKey: String): Set<DeviceLink> {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.getAll(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor -> return database.getAll(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) { cursor ->
val primaryDevicePubKey = cursor.getString(primaryDevicePublicKey) val primaryDevicePubKey = cursor.getString(primaryDevicePublicKey)
val secondaryDevicePubKey = cursor.getString(secondaryDevicePublicKey) val secondaryDevicePubKey = cursor.getString(secondaryDevicePublicKey)
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature) val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
val grantSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(grantSignature))) null else cursor.getBase64EncodedData(grantSignature) val grantSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(grantSignature))) null else cursor.getBase64EncodedData(grantSignature)
PairingAuthorisation(primaryDevicePubKey, secondaryDevicePubKey, requestSignature, grantSignature) DeviceLink(primaryDevicePubKey, secondaryDevicePubKey, requestSignature, grantSignature)
} }.toSet()
} }
override fun insertOrUpdatePairingAuthorisation(authorisation: PairingAuthorisation) { override fun addDeviceLink(deviceLink: DeviceLink) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val values = ContentValues() val values = ContentValues()
values.put(primaryDevicePublicKey, authorisation.primaryDevicePublicKey) values.put(primaryDevicePublicKey, deviceLink.masterHexEncodedPublicKey)
values.put(secondaryDevicePublicKey, authorisation.secondaryDevicePublicKey) values.put(secondaryDevicePublicKey, deviceLink.slaveHexEncodedPublicKey)
if (authorisation.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(authorisation.requestSignature)) } if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) }
if (authorisation.grantSignature != null) { values.put(grantSignature, Base64.encodeBytes(authorisation.grantSignature)) } if (deviceLink.authorizationSignature != null) { values.put(grantSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
database.insertOrUpdate(pairingAuthorisationCache, values, "$primaryDevicePublicKey = ? AND $secondaryDevicePublicKey = ?", arrayOf( authorisation.primaryDevicePublicKey, authorisation.secondaryDevicePublicKey )) database.insertOrUpdate(pairingAuthorisationCache, values, "$primaryDevicePublicKey = ? AND $secondaryDevicePublicKey = ?", arrayOf( deviceLink.masterHexEncodedPublicKey, deviceLink.slaveHexEncodedPublicKey ))
} }
override fun removePairingAuthorisations(hexEncodedPublicKey: String) { override fun clearDeviceLinks(hexEncodedPublicKey: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
database.delete(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey )) database.delete(pairingAuthorisationCache, "$primaryDevicePublicKey = ? OR $secondaryDevicePublicKey = ?", arrayOf( hexEncodedPublicKey, hexEncodedPublicKey ))
} }

View File

@ -21,10 +21,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.api.messages.SignalServiceGroup import org.whispersystems.signalservice.api.messages.SignalServiceGroup
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage
import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.loki.api.LokiPublicChat import org.whispersystems.signalservice.loki.api.*
import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI
import org.whispersystems.signalservice.loki.api.LokiPublicChatMessage
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.successBackground import org.whispersystems.signalservice.loki.utilities.successBackground
import java.security.MessageDigest import java.security.MessageDigest
@ -156,7 +153,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
fun pollForNewMessages() { fun pollForNewMessages() {
fun processIncomingMessage(message: LokiPublicChatMessage) { fun processIncomingMessage(message: LokiPublicChatMessage) {
// If the sender of the current message is not a secondary device, we need to set the display name in the database // If the sender of the current message is not a secondary device, we need to set the display name in the database
val primaryDevice = LokiFileServerAPI.shared.getPrimaryDevicePublicKey(message.hexEncodedPublicKey).get() val primaryDevice = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(message.hexEncodedPublicKey).get()
if (primaryDevice == null) { if (primaryDevice == null) {
val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})" val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName) DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
@ -171,9 +168,9 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
} }
// Update profile avatar if needed // Update profile avatar if needed
val senderRecipient = Recipient.from(context, Address.fromSerialized(senderPublicKey), false) val senderRecipient = Recipient.from(context, Address.fromSerialized(senderPublicKey), false)
if (message.avatar != null && message.avatar!!.url.isNotEmpty()) { if (message.profilePicture != null && message.profilePicture!!.url.isNotEmpty()) {
val profileKey = message.avatar!!.profileKey val profileKey = message.profilePicture!!.profileKey
val url = message.avatar!!.url val url = message.profilePicture!!.url
if (senderRecipient.profileKey == null || !MessageDigest.isEqual(senderRecipient.profileKey, profileKey)) { if (senderRecipient.profileKey == null || !MessageDigest.isEqual(senderRecipient.profileKey, profileKey)) {
val database = DatabaseFactory.getRecipientDatabase(context) val database = DatabaseFactory.getRecipientDatabase(context)
database.setProfileKey(senderRecipient, profileKey) database.setProfileKey(senderRecipient, profileKey)
@ -204,9 +201,9 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
} }
// If we got a message from our master device then make sure our mappings stay in sync // If we got a message from our master device then make sure our mappings stay in sync
val recipient = Recipient.from(context, Address.fromSerialized(message.hexEncodedPublicKey), false) val recipient = Recipient.from(context, Address.fromSerialized(message.hexEncodedPublicKey), false)
if (recipient.isOurMasterDevice && message.avatar != null) { if (recipient.isOurMasterDevice && message.profilePicture != null) {
val profileKey = message.avatar!!.profileKey val profileKey = message.profilePicture!!.profileKey
val url = message.avatar!!.url val url = message.profilePicture!!.url
if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, profileKey)) { if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, profileKey)) {
val database = DatabaseFactory.getRecipientDatabase(context) val database = DatabaseFactory.getRecipientDatabase(context)
database.setProfileKey(recipient, profileKey) database.setProfileKey(recipient, profileKey)
@ -220,16 +217,16 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize() val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
val database = DatabaseFactory.getLokiAPIDatabase(context) val database = DatabaseFactory.getLokiAPIDatabase(context)
LokiFileServerAPI.configure(false, userHexEncodedPublicKey, userPrivateKey, database) LokiFileServerAPI.configure(false, userHexEncodedPublicKey, userPrivateKey, database)
LokiFileServerAPI.shared.getAllDevicePublicKeys(userHexEncodedPublicKey).bind { devices -> LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(userHexEncodedPublicKey).bind { devices ->
userDevices = devices userDevices = devices
api.getMessages(group.channel, group.server) api.getMessages(group.channel, group.server)
}.bind { messages -> }.bind { messages ->
if (messages.isNotEmpty()) { if (messages.isNotEmpty()) {
// We need to fetch device mappings for all the devices we don't have // We need to fetch device mappings for all the devices we don't have
uniqueDevices = messages.map { it.hexEncodedPublicKey }.toSet() uniqueDevices = messages.map { it.hexEncodedPublicKey }.toSet()
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiFileServerAPI.shared.hasCacheExpired(it) } val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && LokiFileServerAPI.shared.hasDeviceLinkCacheExpired(hexEncodedPublicKey = it) }
if (devicesToUpdate.isNotEmpty()) { if (devicesToUpdate.isNotEmpty()) {
return@bind LokiFileServerAPI.shared.getDeviceMappings(devicesToUpdate.toSet()).then { messages } return@bind LokiFileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages }
} }
} }
Promise.of(messages) Promise.of(messages)
@ -238,7 +235,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
val newDisplayNameUpdatees = uniqueDevices.mapNotNull { val newDisplayNameUpdatees = uniqueDevices.mapNotNull {
// This will return null if current device is primary // This will return null if current device is primary
// So if it's non-null then we know the device is a secondary device // So if it's non-null then we know the device is a secondary device
val primaryDevice = LokiFileServerAPI.shared.getPrimaryDevicePublicKey(it).get() val primaryDevice = LokiDeviceLinkUtilities.getMasterHexEncodedPublicKey(it).get()
primaryDevice primaryDevice
}.toSet() }.toSet()
// Fetch the display names of the primary devices // Fetch the display names of the primary devices

View File

@ -53,7 +53,7 @@ object MentionUtilities {
} }
} }
val result = SpannableString(text) val result = SpannableString(text)
val userLinkedDeviceHexEncodedPublicKeys = DatabaseFactory.getLokiAPIDatabase(context).getPairingAuthorisations(userHexEncodedPublicKey).flatMap { listOf( it.primaryDevicePublicKey, it.secondaryDevicePublicKey ) }.toMutableSet() val userLinkedDeviceHexEncodedPublicKeys = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(userHexEncodedPublicKey).flatMap { listOf( it.masterHexEncodedPublicKey, it.slaveHexEncodedPublicKey ) }.toMutableSet()
userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey) userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey)
for (mention in mentions) { for (mention in mentions) {
if (!userLinkedDeviceHexEncodedPublicKeys.contains(mention.second)) { continue } if (!userLinkedDeviceHexEncodedPublicKeys.contains(mention.second)) { continue }

View File

@ -60,7 +60,7 @@ import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.ContactTokenDetails; import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI; import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil; import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
@ -99,7 +99,7 @@ public class MessageSender {
sendBackgroundMessage(context, contactHexEncodedPublicKey); sendBackgroundMessage(context, contactHexEncodedPublicKey);
// Go through the other devices and only send background messages if we're friends or we have received friend request // Go through the other devices and only send background messages if we're friends or we have received friend request
LokiFileServerAPI.shared.getAllDevicePublicKeys(contactHexEncodedPublicKey).success(devices -> { LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(contactHexEncodedPublicKey).success(devices -> {
Util.runOnMain(() -> { Util.runOnMain(() -> {
for (String device : devices) { for (String device : devices) {
// Don't send message to the device we already have sent to // Don't send message to the device we already have sent to
@ -234,7 +234,7 @@ public class MessageSender {
final int ttl) { final int ttl) {
String ourPublicKey = TextSecurePreferences.getLocalNumber(context); String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
LokiFileServerAPI.shared.getAllDevicePublicKeys(ourPublicKey).success(devices -> { LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(ourPublicKey).success(devices -> {
Util.runOnMain(() -> { Util.runOnMain(() -> {
for (String device : devices) { for (String device : devices) {
// Don't send to ourselves // Don't send to ourselves