Handle unpair request flag.

This commit is contained in:
Mikunj 2019-11-21 12:43:33 +11:00
parent c66786e0f1
commit 3a79e1f215
5 changed files with 108 additions and 48 deletions

View File

@ -331,27 +331,38 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SignalServiceDataMessage message = content.getDataMessage().get();
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId);
else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId, Optional.absent());
else if (message.getBody().isPresent()) handleTextMessage(content, message, smsMessageId, Optional.absent());
if (message.isUnpairingRequest()) {
// Make sure we got the request from our primary device
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(content.getSender())) {
MultiDeviceUtilities.checkForRevocation(context);
}
} else {
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
else if (message.isExpirationUpdate())
handleExpirationUpdate(content, message, smsMessageId);
else if (isMediaMessage)
handleMediaMessage(content, message, smsMessageId, Optional.absent());
else if (message.getBody().isPresent())
handleTextMessage(content, message, smsMessageId, Optional.absent());
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(content, message.getGroupInfo().get());
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(content, message.getGroupInfo().get());
}
if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
handleProfileKey(content, message);
}
// Loki - This doesn't get invoked for group chats
if (content.isNeedsReceipt()) {
handleNeedsDeliveryReceipt(content, message);
}
// Loki - Handle friend request logic if needed
updateFriendRequestStatusIfNeeded(envelope, content, message);
}
if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
handleProfileKey(content, message);
}
// Loki - This doesn't get invoked for group chats
if (content.isNeedsReceipt()) {
handleNeedsDeliveryReceipt(content, message);
}
// Loki - Handle friend request logic if needed
updateFriendRequestStatusIfNeeded(envelope, content, message);
} else if (content.getSyncMessage().isPresent()) {
TextSecurePreferences.setMultiDevice(context, true);

View File

@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.*
import org.thoughtcrime.securesms.util.DynamicTheme
import org.thoughtcrime.securesms.util.DynamicLanguage
import network.loki.messenger.R
import nl.komponents.kovenant.then
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.TextSecurePreferences
@ -44,9 +45,10 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity(), DeviceLinki
val database = DatabaseFactory.getLokiAPIDatabase(this)
database.removePairingAuthorisation(ourPublicKey, devicePublicKey)
// Update mapping on the file server
LokiStorageAPI.shared.updateUserDeviceMappings()
// Send a background message to let the device know that it has been revoked
MessageSender.sendBackgroundMessage(this, devicePublicKey)
LokiStorageAPI.shared.updateUserDeviceMappings().success {
// Send an unpair request to let the device know that it has been revoked
MessageSender.sendUnpairRequest(this, devicePublicKey)
}
// Refresh the list
this.deviceListFragment.refresh()
Toast.makeText(this, R.string.DeviceListActivity_unlinked_device, Toast.LENGTH_LONG).show()

View File

@ -6,6 +6,7 @@ import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.all
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map
import nl.komponents.kovenant.then
import nl.komponents.kovenant.toFailVoid
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
@ -27,6 +28,23 @@ import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
import java.util.*
import kotlin.concurrent.schedule
fun checkForRevocation(context: Context) {
val primaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return
val ourDevice = TextSecurePreferences.getLocalNumber(context)
LokiStorageAPI.shared.fetchDeviceMappings(primaryDevice).bind { mappings ->
val ourMapping = mappings.find { it.secondaryDevicePublicKey == ourDevice }
if (ourMapping != null) throw Error("Device has not been revoked")
// remove pairing auths for our device
DatabaseFactory.getLokiAPIDatabase(context).removePairingAuthorisations(ourDevice)
LokiStorageAPI.shared.updateUserDeviceMappings()
}.success {
// TODO: Revoke here
}.fail { error ->
Log.d("Loki", "Revocation check failed: $error")
}
}
fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
return LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys ->

View File

@ -11,39 +11,58 @@ import org.whispersystems.libsignal.util.guava.Optional
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair
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.util.concurrent.TimeUnit
data class BackgroundMessage private constructor(val recipient: String, val body: String?, val friendRequest: Boolean, val unpairingRequest: Boolean) {
companion object {
@JvmStatic
fun create(recipient: String) = BackgroundMessage(recipient, null, false, false)
@JvmStatic
fun createFriendRequest(recipient: String, messageBody: String) = BackgroundMessage(recipient, messageBody, true, false)
@JvmStatic
fun createUnpairingRequest(recipient: String) = BackgroundMessage(recipient, null, false, true)
internal fun parse(serialized: String): BackgroundMessage {
val node = JsonUtil.fromJson(serialized)
val recipient = node.get("recipient").asText()
val body = if (node.hasNonNull("body")) node.get("body").asText() else null
val friendRequest = node.get("friendRequest").asBoolean(false)
val unpairingRequest = node.get("unpairingRequest").asBoolean(false)
return BackgroundMessage(recipient, body, friendRequest, unpairingRequest)
}
}
fun serialize(): String {
val map = mapOf("recipient" to recipient, "body" to body, "friendRequest" to friendRequest, "unpairingRequest" to unpairingRequest)
return JsonUtil.toJson(map)
}
}
class PushBackgroundMessageSendJob private constructor(
parameters: Parameters,
private val recipient: String,
private val messageBody: String?,
private val friendRequest: Boolean
private val message: BackgroundMessage
) : BaseJob(parameters) {
companion object {
const val KEY = "PushBackgroundMessageSendJob"
private val TAG = PushBackgroundMessageSendJob::class.java.simpleName
private val KEY_RECIPIENT = "recipient"
private val KEY_MESSAGE_BODY = "message_body"
private val KEY_FRIEND_REQUEST = "asFriendRequest"
private val KEY_MESSAGE = "message"
}
constructor(recipient: String): this(recipient, null, false)
constructor(recipient: String, messageBody: String?, friendRequest: Boolean) : this(Parameters.Builder()
constructor(message: BackgroundMessage) : this(Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setQueue(KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(1)
.build(),
recipient, messageBody, friendRequest)
message)
override fun serialize(): Data {
return Data.Builder()
.putString(KEY_RECIPIENT, recipient)
.putString(KEY_MESSAGE_BODY, messageBody)
.putBoolean(KEY_FRIEND_REQUEST, friendRequest)
.putString(KEY_MESSAGE, message.serialize())
.build()
}
@ -52,22 +71,24 @@ class PushBackgroundMessageSendJob private constructor(
}
public override fun onRun() {
val message = SignalServiceDataMessage.newBuilder()
val dataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(System.currentTimeMillis())
.withBody(messageBody)
.withBody(message.body)
if (friendRequest) {
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient)
message.withPreKeyBundle(bundle)
if (message.friendRequest) {
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(message.recipient)
dataMessage.withPreKeyBundle(bundle)
.asFriendRequest(true)
} else if (message.unpairingRequest) {
dataMessage.asUnpairingRequest(true)
}
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
val address = SignalServiceAddress(recipient)
val address = SignalServiceAddress(message.recipient)
try {
messageSender.sendMessage(-1, address, Optional.absent<UnidentifiedAccessPair>(), message.build()) // The message ID doesn't matter
messageSender.sendMessage(-1, address, Optional.absent<UnidentifiedAccessPair>(), dataMessage.build()) // The message ID doesn't matter
} catch (e: Exception) {
Log.d("Loki", "Failed to send background message to: $recipient.")
Log.d("Loki", "Failed to send background message to: ${message.recipient}.")
throw e
}
}
@ -82,10 +103,8 @@ class PushBackgroundMessageSendJob private constructor(
class Factory : Job.Factory<PushBackgroundMessageSendJob> {
override fun create(parameters: Parameters, data: Data): PushBackgroundMessageSendJob {
try {
val recipient = data.getString(KEY_RECIPIENT)
val messageBody = if (data.hasString(KEY_MESSAGE_BODY)) data.getString(KEY_MESSAGE_BODY) else null
val friendRequest = data.getBooleanOrDefault(KEY_FRIEND_REQUEST, false)
return PushBackgroundMessageSendJob(parameters, recipient, messageBody, friendRequest)
val messageJSON = data.getString(KEY_MESSAGE)
return PushBackgroundMessageSendJob(parameters, BackgroundMessage.parse(messageJSON))
} catch (e: IOException) {
throw AssertionError(e)
}

View File

@ -17,6 +17,7 @@
package org.thoughtcrime.securesms.sms;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext;
@ -44,6 +45,7 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.BackgroundMessage;
import org.thoughtcrime.securesms.loki.FriendRequestHandler;
import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
@ -58,7 +60,11 @@ 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;
@ -123,11 +129,15 @@ public class MessageSender {
// We don't call the message sender here directly and instead we just opt to create a specific job for the send
// This is because calling message sender directly would cause the application to freeze in some cases as it was blocking the thread when waiting for a response from the send
public static void sendBackgroundMessage(Context context, String contactHexEncodedPublicKey) {
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(contactHexEncodedPublicKey));
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.create(contactHexEncodedPublicKey)));
}
public static void sendBackgroundFriendRequest(Context context, String contactHexEncodedPublicKey, String messageBody) {
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(contactHexEncodedPublicKey, messageBody, true));
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createFriendRequest(contactHexEncodedPublicKey, messageBody)));
}
public static void sendUnpairRequest(Context context, String contactHexEncodedPublicKey) {
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createUnpairingRequest(contactHexEncodedPublicKey)));
}
// endregion