Implement the new protocol

This commit is contained in:
nielsandriesse 2020-07-15 15:14:37 +10:00
parent 455471b20e
commit 06f547dc88
18 changed files with 168 additions and 191 deletions

View File

@ -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
}

View File

@ -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) {

View File

@ -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());
}};
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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()

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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())

View File

@ -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)

View File

@ -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)
}
}
}
}

View File

@ -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)
}
}
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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 ->

View File

@ -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()
}

View File

@ -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) {