mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-30 13:35:18 +00:00
Implement the new protocol
This commit is contained in:
parent
455471b20e
commit
06f547dc88
@ -61,14 +61,13 @@ import org.thoughtcrime.securesms.logging.PersistentLogger;
|
||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||
import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
|
||||
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
||||
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||
import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushSessionRequestMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
|
||||
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
@ -601,8 +600,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
if (hasSentOrProcessedSessionRequest) { return; }
|
||||
// Send the session request
|
||||
DatabaseFactory.getLokiAPIDatabase(this).setSessionRequestTimestamp(publicKey, new Date().getTime());
|
||||
EphemeralMessage sessionRequest = EphemeralMessage.createSessionRequest(publicKey);
|
||||
jobManager.add(new PushEphemeralMessageSendJob(sessionRequest));
|
||||
PushSessionRequestMessageSendJob job = new PushSessionRequestMessageSendJob(publicKey);
|
||||
jobManager.add(job);
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -2359,6 +2359,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
||||
|
||||
if (!recipient.isGroupRecipient()) {
|
||||
ApplicationContext.getInstance(this).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
|
||||
}
|
||||
|
||||
new AsyncTask<Void, Void, Long>() {
|
||||
@Override
|
||||
protected Long doInBackground(Void... param) {
|
||||
@ -2402,6 +2406,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
silentlySetComposeText("");
|
||||
final long id = fragment.stageOutgoingMessage(message);
|
||||
|
||||
if (!recipient.isGroupRecipient()) {
|
||||
ApplicationContext.getInstance(this).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
|
||||
}
|
||||
|
||||
new AsyncTask<OutgoingTextMessage, Void, Long>() {
|
||||
@Override
|
||||
protected Long doInBackground(OutgoingTextMessage... messages) {
|
||||
|
@ -14,8 +14,8 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushNullMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushSessionRequestMessageSendJob;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -72,7 +72,7 @@ public final class JobManagerFactories {
|
||||
put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
|
||||
put(TypingSendJob.KEY, new TypingSendJob.Factory());
|
||||
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
|
||||
put(PushEphemeralMessageSendJob.KEY, new PushEphemeralMessageSendJob.Factory());
|
||||
put(PushSessionRequestMessageSendJob.KEY, new PushSessionRequestMessageSendJob.Factory());
|
||||
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
|
||||
}};
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushNullMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||
@ -266,13 +267,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loki - Handle pre key bundle message if needed
|
||||
SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content);
|
||||
|
||||
// Loki - Handle session request if needed
|
||||
if (SessionManagementProtocol.handleSessionRequestIfNeeded(context, content)) { return; } // Don't process the message any further
|
||||
|
||||
// Loki - Handle profile update if needed
|
||||
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
||||
|
||||
if (content.getDeviceLink().isPresent()) {
|
||||
@ -281,7 +277,6 @@ 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();
|
||||
|
||||
// Loki - Handle unlinking request if needed
|
||||
if (message.isDeviceUnlinkingRequest()) {
|
||||
MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
|
||||
} else {
|
||||
@ -341,14 +336,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
} else if (content.getTypingMessage().isPresent()) {
|
||||
handleTypingMessage(content, content.getTypingMessage().get());
|
||||
} else if (content.getNullMessage().isPresent()) {
|
||||
// Loki - This is needed for compatibility with refactored desktop clients
|
||||
// ========
|
||||
// if (content.isFriendRequest()) {
|
||||
// ApplicationContext.getInstance(context).getJobManager().add(new PushNullMessageSendJob(content.getSender()));
|
||||
// } else {
|
||||
// Log.w(TAG, "Got unrecognized message...");
|
||||
// }
|
||||
// ========
|
||||
if (content.preKeyBundleMessage.isPresent()) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushNullMessageSendJob(content.getSender()));
|
||||
} else {
|
||||
Log.w(TAG, "Got unrecognized message...");
|
||||
}
|
||||
}
|
||||
|
||||
resetRecipientToPush(Recipient.from(context, Address.fromSerialized(content.getSender()), false));
|
||||
@ -645,11 +637,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
|
||||
}
|
||||
|
||||
// Loki - Handle profile key update if needed
|
||||
handleProfileKey(content, message.getMessage());
|
||||
}
|
||||
|
||||
// Loki - Update profile if needed
|
||||
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
||||
|
||||
if (threadId != null) {
|
||||
@ -754,7 +744,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
database.beginTransaction();
|
||||
|
||||
// Loki - Ignore message if it has no body and no attachments
|
||||
// Ignore message if it has no body and no attachments
|
||||
if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||
import org.thoughtcrime.securesms.transport.RetryLaterException;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
|
||||
@ -196,11 +197,17 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
||||
|
||||
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
|
||||
|
||||
PreKeyBundle preKeyBundle = null;
|
||||
if (message.isEndSession()) {
|
||||
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize());
|
||||
}
|
||||
|
||||
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
||||
.withTimestamp(message.getDateSent())
|
||||
.withBody(message.getBody())
|
||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||
.withProfileKey(profileKey.orNull())
|
||||
.withPreKeyBundle(preKeyBundle)
|
||||
.asEndSessionMessage(message.isEndSession())
|
||||
.build();
|
||||
|
||||
|
@ -47,11 +47,11 @@ object LokiPushNotificationManager {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun register(token: String, hexEncodedPublicKey: String, context: Context?, force: Boolean) {
|
||||
fun register(token: String, publicKey: String, context: Context?, force: Boolean) {
|
||||
val oldToken = TextSecurePreferences.getFCMToken(context)
|
||||
val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context)
|
||||
if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return }
|
||||
val parameters = mapOf( "token" to token, "pubKey" to hexEncodedPublicKey )
|
||||
val parameters = mapOf( "token" to token, "pubKey" to publicKey )
|
||||
val url = "$server/register"
|
||||
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
|
||||
val request = Request.Builder().url(url).post(body).build()
|
||||
|
@ -165,7 +165,7 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
|
||||
}
|
||||
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.publicKey
|
||||
val serviceDataMessage = getDataMessage(message)
|
||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false, false)
|
||||
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false)
|
||||
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
|
||||
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
|
||||
} else {
|
||||
|
@ -1,31 +0,0 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||
|
||||
class EphemeralMessage private constructor(val data: Map<*, *>) {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun create(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey ))
|
||||
|
||||
@JvmStatic
|
||||
fun createDeviceUnlinkingRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "unpairingRequest" to true ))
|
||||
|
||||
@JvmStatic
|
||||
fun createSessionRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "friendRequest" to true, "sessionRequest" to true ))
|
||||
|
||||
internal fun parse(serialized: String): EphemeralMessage {
|
||||
val data = JsonUtil.fromJson(serialized, Map::class.java) ?: throw IllegalArgumentException("Couldn't parse string to JSON")
|
||||
return EphemeralMessage(data)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> get(key: String, defaultValue: T): T {
|
||||
return data[key] as? T ?: defaultValue
|
||||
}
|
||||
|
||||
fun serialize(): String {
|
||||
return JsonUtil.toJson(data)
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters)
|
||||
|
||||
constructor() : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue("MultiDeviceOpenGroupUpdateJob")
|
||||
.setQueue(KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build())
|
||||
|
@ -30,16 +30,16 @@ object MultiDeviceProtocol {
|
||||
enum class MessageType { Text, Media }
|
||||
|
||||
@JvmStatic
|
||||
fun sendTextPush(context: Context, recipient: Recipient, messageID: Long, isEndSession: Boolean) {
|
||||
sendMessagePush(context, recipient, messageID, MessageType.Text, isEndSession)
|
||||
fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) {
|
||||
sendMessagePush(context, recipient, messageID, MessageType.Text)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) {
|
||||
sendMessagePush(context, recipient, messageID, MessageType.Media, false)
|
||||
sendMessagePush(context, recipient, messageID, MessageType.Media)
|
||||
}
|
||||
|
||||
private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType, isEndSession: Boolean) {
|
||||
private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType) {
|
||||
val jobManager = ApplicationContext.getInstance(context).jobManager
|
||||
val isMultiDeviceRequired = !recipient.address.isOpenGroup
|
||||
if (!isMultiDeviceRequired) {
|
||||
@ -162,6 +162,8 @@ object MultiDeviceProtocol {
|
||||
}
|
||||
val isValid = isValidDeviceLinkMessage(context, deviceLink)
|
||||
if (!isValid) { return }
|
||||
// The line below isn't actually necessary because this is called after PushDecryptJob
|
||||
// calls handlePreKeyBundleMessageIfNeeded, but it also doesn't hurt.
|
||||
SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content)
|
||||
linkingSession.processLinkingAuthorization(deviceLink)
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||
|
@ -1,84 +0,0 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||
import org.thoughtcrime.securesms.logging.Log
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class PushEphemeralMessageSendJob private constructor(parameters: Parameters, private val message: EphemeralMessage) : BaseJob(parameters) {
|
||||
|
||||
companion object {
|
||||
private const val KEY_MESSAGE = "message"
|
||||
const val KEY = "PushBackgroundMessageSendJob"
|
||||
}
|
||||
|
||||
constructor(message: EphemeralMessage) : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(1)
|
||||
.build(),
|
||||
message)
|
||||
|
||||
override fun serialize(): Data {
|
||||
return Data.Builder()
|
||||
.putString(KEY_MESSAGE, message.serialize())
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun getFactoryKey(): String { return KEY }
|
||||
|
||||
public override fun onRun() {
|
||||
val recipient = message.get<String?>("recipient", null) ?: throw IllegalStateException()
|
||||
val dataMessage = SignalServiceDataMessage.newBuilder().withTimestamp(System.currentTimeMillis())
|
||||
// Attach a pre key bundle if needed
|
||||
if (message.get("friendRequest", false)) {
|
||||
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient)
|
||||
dataMessage.withPreKeyBundle(bundle)
|
||||
}
|
||||
// Set flags if needed (these are mutually exclusive)
|
||||
when {
|
||||
message.get("unpairingRequest", false) -> dataMessage.asDeviceUnlinkingRequest(true)
|
||||
message.get("sessionRequest", false) -> dataMessage.asSessionRequest(true)
|
||||
}
|
||||
// Send the message
|
||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||
val address = SignalServiceAddress(recipient)
|
||||
try {
|
||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(recipient), false))
|
||||
messageSender.sendMessage(0, address, udAccess, dataMessage.build()) // The message ID doesn't matter
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to send background message to: $recipient due to error: $e.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onShouldRetry(e: Exception): Boolean {
|
||||
// Disable since we have our own retrying
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCanceled() { }
|
||||
|
||||
class Factory : Job.Factory<PushEphemeralMessageSendJob> {
|
||||
|
||||
override fun create(parameters: Parameters, data: Data): PushEphemeralMessageSendJob {
|
||||
try {
|
||||
val messageJSON = data.getString(KEY_MESSAGE)
|
||||
return PushEphemeralMessageSendJob(parameters, EphemeralMessage.parse(messageJSON))
|
||||
} catch (e: IOException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||
import org.thoughtcrime.securesms.logging.Log
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||
import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities
|
||||
import java.io.IOException
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class PushSessionRequestMessageSendJob private constructor(parameters: Parameters, private val publicKey: String) : BaseJob(parameters) {
|
||||
|
||||
companion object {
|
||||
const val KEY = "PushSessionRequestMessageSendJob"
|
||||
}
|
||||
|
||||
constructor(publicKey: String) : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(1)
|
||||
.build(),
|
||||
publicKey)
|
||||
|
||||
override fun serialize(): Data {
|
||||
return Data.Builder().putString("publicKey", publicKey).build()
|
||||
}
|
||||
|
||||
override fun getFactoryKey(): String { return KEY }
|
||||
|
||||
public override fun onRun() {
|
||||
// Prepare
|
||||
val contentMessage = SignalServiceProtos.Content.newBuilder()
|
||||
// Attach the pre key bundle message
|
||||
val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder()
|
||||
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return
|
||||
preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize())
|
||||
preKeyBundleMessage.deviceId = preKeyBundle.deviceId
|
||||
preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId
|
||||
preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId
|
||||
preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize())
|
||||
preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize())
|
||||
preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature)
|
||||
contentMessage.preKeyBundleMessage = preKeyBundleMessage.build()
|
||||
// Attach the null message
|
||||
val nullMessage = SignalServiceProtos.NullMessage.newBuilder()
|
||||
val sr = SecureRandom()
|
||||
val paddingSize = sr.nextInt(512)
|
||||
val padding = ByteArray(paddingSize)
|
||||
sr.nextBytes(padding)
|
||||
nullMessage.padding = ByteString.copyFrom(padding)
|
||||
contentMessage.nullMessage = nullMessage.build()
|
||||
// Send the result
|
||||
val serializedContentMessage = contentMessage.build().toByteArray()
|
||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||
val address = SignalServiceAddress(publicKey)
|
||||
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
|
||||
val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest)
|
||||
try {
|
||||
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
|
||||
Date().time, serializedContentMessage, false, ttl, false,
|
||||
true, false, false)
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to send session request to: $publicKey due to error: $e.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onShouldRetry(e: Exception): Boolean {
|
||||
// Disable since we have our own retrying
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCanceled() { }
|
||||
|
||||
class Factory : Job.Factory<PushSessionRequestMessageSendJob> {
|
||||
|
||||
override fun create(parameters: Parameters, data: Data): PushSessionRequestMessageSendJob {
|
||||
try {
|
||||
val publicKey = data.getString("publicKey")
|
||||
return PushSessionRequestMessageSendJob(parameters, publicKey)
|
||||
} catch (e: IOException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob
|
||||
import org.thoughtcrime.securesms.loki.utilities.recipient
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.libsignal.loki.SessionResetStatus
|
||||
@ -27,8 +29,8 @@ object SessionManagementProtocol {
|
||||
val smsDB = DatabaseFactory.getSmsDatabase(context)
|
||||
val devices = lokiThreadDB.getSessionRestoreDevices(threadID)
|
||||
for (device in devices) {
|
||||
val sessionRequest = EphemeralMessage.createSessionRequest(recipient.address.serialize())
|
||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRequest))
|
||||
val endSessionMessage = OutgoingEndSessionMessage(OutgoingTextMessage(recipient, "TERMINATE", 0, -1))
|
||||
MessageSender.send(context, endSessionMessage, threadID, false, null)
|
||||
}
|
||||
val infoMessage = OutgoingTextMessage(recipient, "", 0, 0)
|
||||
val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null)
|
||||
@ -53,37 +55,24 @@ object SessionManagementProtocol {
|
||||
|
||||
@JvmStatic
|
||||
fun handlePreKeyBundleMessageIfNeeded(context: Context, content: SignalServiceContent) {
|
||||
val recipient = recipient(context, content.sender)
|
||||
if (recipient.isGroupRecipient) { return }
|
||||
val preKeyBundleMessage = content.lokiServiceMessage.orNull()?.preKeyBundleMessage ?: return
|
||||
val publicKey = content.sender
|
||||
val recipient = recipient(context, publicKey)
|
||||
if (recipient.isGroupRecipient) { return } // Should never occur
|
||||
val preKeyBundleMessage = content.preKeyBundleMessage.orNull() ?: return
|
||||
val registrationID = TextSecurePreferences.getLocalRegistrationId(context) // TODO: It seems wrong to use the local registration ID for this?
|
||||
val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context)
|
||||
Log.d("Loki", "Received a pre key bundle from: " + content.sender.toString() + ".")
|
||||
if (content.dataMessage.isPresent && content.dataMessage.get().isSessionRequest) {
|
||||
val sessionRequestTimestamp = DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestTimestamp(content.sender)
|
||||
if (sessionRequestTimestamp != null && content.timestamp < sessionRequestTimestamp) {
|
||||
// We sent or processed a session request after this one was sent
|
||||
Log.d("Loki", "Ignoring session request from: ${content.sender}.")
|
||||
return
|
||||
}
|
||||
}
|
||||
val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID)
|
||||
lokiPreKeyBundleDatabase.setPreKeyBundle(content.sender, preKeyBundle)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun handleSessionRequestIfNeeded(context: Context, content: SignalServiceContent): Boolean {
|
||||
if (!content.dataMessage.isPresent || !content.dataMessage.get().isSessionRequest) { return false }
|
||||
Log.d("Loki", "Received a pre key bundle from: $publicKey.")
|
||||
val sessionRequestTimestamp = DatabaseFactory.getLokiAPIDatabase(context).getSessionRequestTimestamp(content.sender)
|
||||
if (sessionRequestTimestamp != null && content.timestamp < sessionRequestTimestamp) {
|
||||
// We sent or processed a session request after this one was sent
|
||||
Log.d("Loki", "Ignoring session request from: ${content.sender}.")
|
||||
return false
|
||||
return
|
||||
}
|
||||
val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID)
|
||||
lokiPreKeyBundleDatabase.setPreKeyBundle(content.sender, preKeyBundle)
|
||||
DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestTimestamp(content.sender, Date().time)
|
||||
val ephemeralMessage = EphemeralMessage.create(content.sender)
|
||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
|
||||
return true
|
||||
val job = PushNullMessageSendJob(content.sender)
|
||||
ApplicationContext.getInstance(context).jobManager.add(job)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@ -95,8 +84,8 @@ object SessionManagementProtocol {
|
||||
sessionStore.archiveAllSessions(content.sender)
|
||||
lokiThreadDB.setSessionResetStatus(content.sender, SessionResetStatus.REQUEST_RECEIVED)
|
||||
Log.d("Loki", "Sending an ephemeral message back to: ${content.sender}.")
|
||||
val ephemeralMessage = EphemeralMessage.create(content.sender)
|
||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
|
||||
val job = PushNullMessageSendJob(content.sender)
|
||||
ApplicationContext.getInstance(context).jobManager.add(job)
|
||||
SecurityEvent.broadcastSecurityUpdateEvent(context)
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,8 @@ class SessionResetImplementation(private val context: Context) : SessionResetPro
|
||||
|
||||
override fun onNewSessionAdopted(publicKey: String, oldSessionResetStatus: SessionResetStatus) {
|
||||
if (oldSessionResetStatus == SessionResetStatus.IN_PROGRESS) {
|
||||
val ephemeralMessage = EphemeralMessage.create(publicKey)
|
||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
|
||||
val job = PushNullMessageSendJob(publicKey)
|
||||
ApplicationContext.getInstance(context).jobManager.add(job)
|
||||
}
|
||||
// TODO: Show session reset succeed message
|
||||
}
|
||||
|
@ -94,10 +94,8 @@ object SyncMessagesProtocol {
|
||||
val contactPublicKeys = contactsInputStream.readAll().map { it.number }
|
||||
for (contactPublicKey in contactPublicKeys) {
|
||||
if (contactPublicKey == userPublicKey || !PublicKeyValidation.isValid(contactPublicKey)) { return }
|
||||
val recipient = recipient(context, contactPublicKey)
|
||||
val applicationContext = context.applicationContext as ApplicationContext
|
||||
applicationContext.sendSessionRequestIfNeeded(contactPublicKey)
|
||||
// TODO: Make the thread visible
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSF
|
||||
val id = feed.id.toByteArray()
|
||||
val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, SignalServiceGroup.GroupType.RSS_FEED, null, null, null, null)
|
||||
val x2 = SignalServiceDataMessage(timestamp, x1, null, body)
|
||||
val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false, false, false)
|
||||
val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false, false)
|
||||
PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent())
|
||||
}
|
||||
}.fail { exception ->
|
||||
|
@ -28,22 +28,22 @@ object MentionUtilities {
|
||||
val mentions = mutableListOf<Tuple2<Range<Int>, String>>()
|
||||
var startIndex = 0
|
||||
val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||
if (matcher.find(startIndex)) {
|
||||
while (true) {
|
||||
val hexEncodedPublicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
|
||||
val userDisplayName: String? = if (hexEncodedPublicKey.toLowerCase() == userHexEncodedPublicKey.toLowerCase()) {
|
||||
val publicKey = text.subSequence(matcher.start() + 1, matcher.end()).toString() // +1 to get rid of the @
|
||||
val userDisplayName: String? = if (publicKey.toLowerCase() == userPublicKey.toLowerCase()) {
|
||||
TextSecurePreferences.getProfileName(context)
|
||||
} else if (publicChat != null) {
|
||||
DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, hexEncodedPublicKey)
|
||||
DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(publicChat.id, publicKey)
|
||||
} else {
|
||||
DatabaseFactory.getLokiUserDatabase(context).getDisplayName(hexEncodedPublicKey)
|
||||
DatabaseFactory.getLokiUserDatabase(context).getDisplayName(publicKey)
|
||||
}
|
||||
if (userDisplayName != null) {
|
||||
text = text.subSequence(0, matcher.start()).toString() + "@" + userDisplayName + text.subSequence(matcher.end(), text.length)
|
||||
val endIndex = matcher.start() + 1 + userDisplayName.length
|
||||
startIndex = endIndex
|
||||
mentions.add(Tuple2(Range.create(matcher.start(), endIndex), hexEncodedPublicKey))
|
||||
mentions.add(Tuple2(Range.create(matcher.start(), endIndex), publicKey))
|
||||
} else {
|
||||
startIndex = matcher.end()
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public class MessageSender {
|
||||
|
||||
long messageId = database.insertMessageOutbox(allocatedThreadId, message, forceSms, System.currentTimeMillis(), insertListener);
|
||||
|
||||
sendTextMessage(context, recipient, forceSms, keyExchange, messageId, message.isEndSession());
|
||||
sendTextMessage(context, recipient, forceSms, keyExchange, messageId);
|
||||
|
||||
return allocatedThreadId;
|
||||
}
|
||||
@ -124,7 +124,7 @@ public class MessageSender {
|
||||
if (messageRecord.isMms()) {
|
||||
sendMediaMessage(context, recipient, forceSms, messageId, expiresIn);
|
||||
} else {
|
||||
sendTextMessage(context, recipient, forceSms, keyExchange, messageId, messageRecord.isEndSession());
|
||||
sendTextMessage(context, recipient, forceSms, keyExchange, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,17 +141,17 @@ public class MessageSender {
|
||||
|
||||
private static void sendTextMessage(Context context, Recipient recipient,
|
||||
boolean forceSms, boolean keyExchange,
|
||||
long messageId, boolean isEndSession)
|
||||
long messageId)
|
||||
{
|
||||
if (isLocalSelfSend(context, recipient, forceSms)) {
|
||||
sendLocalTextSelf(context, messageId);
|
||||
} else {
|
||||
sendTextPush(context, recipient, messageId, isEndSession);
|
||||
sendTextPush(context, recipient, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendTextPush(Context context, Recipient recipient, long messageId, boolean isEndSession) {
|
||||
MultiDeviceProtocol.sendTextPush(context, recipient, messageId, isEndSession);
|
||||
private static void sendTextPush(Context context, Recipient recipient, long messageId) {
|
||||
MultiDeviceProtocol.sendTextPush(context, recipient, messageId);
|
||||
}
|
||||
|
||||
private static void sendMediaPush(Context context, Recipient recipient, long messageId) {
|
||||
|
Loading…
Reference in New Issue
Block a user