diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index bdd2ae5c47..614019ced9 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -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 } diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 72e29bb344..b7e74b8d1b 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -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() { @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() { @Override protected Long doInBackground(OutgoingTextMessage... messages) { diff --git a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index c1c869e920..be5aef3962 100644 --- a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -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()); }}; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 31c0ab7710..8d75a599b5 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -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; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index 3854760a24..6e28d4163c 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -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(); diff --git a/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt b/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt index a51ed929bb..39a9c46078 100644 --- a/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt +++ b/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt @@ -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() diff --git a/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt b/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt index f3473ff413..c89fad387c 100644 --- a/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/api/PublicChatPoller.kt @@ -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 { diff --git a/src/org/thoughtcrime/securesms/loki/protocol/EphemeralMessage.kt b/src/org/thoughtcrime/securesms/loki/protocol/EphemeralMessage.kt deleted file mode 100644 index 1b04e9d037..0000000000 --- a/src/org/thoughtcrime/securesms/loki/protocol/EphemeralMessage.kt +++ /dev/null @@ -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 get(key: String, defaultValue: T): T { - return data[key] as? T ?: defaultValue - } - - fun serialize(): String { - return JsonUtil.toJson(data) - } -} \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceOpenGroupUpdateJob.kt b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceOpenGroupUpdateJob.kt index ff8a8eb0a6..70de694044 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceOpenGroupUpdateJob.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceOpenGroupUpdateJob.kt @@ -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()) diff --git a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt index d62331f6c0..e1b1ada7a2 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt @@ -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) diff --git a/src/org/thoughtcrime/securesms/loki/protocol/PushEphemeralMessageSendJob.kt b/src/org/thoughtcrime/securesms/loki/protocol/PushEphemeralMessageSendJob.kt deleted file mode 100644 index 7c28f9fa90..0000000000 --- a/src/org/thoughtcrime/securesms/loki/protocol/PushEphemeralMessageSendJob.kt +++ /dev/null @@ -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("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 { - - 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) - } - } - } -} diff --git a/src/org/thoughtcrime/securesms/loki/protocol/PushSessionRequestMessageSendJob.kt b/src/org/thoughtcrime/securesms/loki/protocol/PushSessionRequestMessageSendJob.kt new file mode 100644 index 0000000000..b55e30aa65 --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/protocol/PushSessionRequestMessageSendJob.kt @@ -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 { + + override fun create(parameters: Parameters, data: Data): PushSessionRequestMessageSendJob { + try { + val publicKey = data.getString("publicKey") + return PushSessionRequestMessageSendJob(parameters, publicKey) + } catch (e: IOException) { + throw AssertionError(e) + } + } + } +} diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt index 94533af303..bf24cc5d10 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt @@ -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) } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt index bb12235aa5..4c34d64b64 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt @@ -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 } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt index fbd4b8ee83..16ec6aa8de 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt @@ -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 } } diff --git a/src/org/thoughtcrime/securesms/loki/shelved/LokiRSSFeedPoller.kt b/src/org/thoughtcrime/securesms/loki/shelved/LokiRSSFeedPoller.kt index 4846412ae7..2e93f96039 100644 --- a/src/org/thoughtcrime/securesms/loki/shelved/LokiRSSFeedPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/shelved/LokiRSSFeedPoller.kt @@ -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 -> diff --git a/src/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt index af5b80d868..203e74a478 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/MentionUtilities.kt @@ -28,22 +28,22 @@ object MentionUtilities { val mentions = mutableListOf, 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() } diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index 0ef6d29eeb..c34721c213 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -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) {