diff --git a/build.gradle b/build.gradle
index 66a778410b..0623140889 100644
--- a/build.gradle
+++ b/build.gradle
@@ -185,8 +185,8 @@ dependencies {
implementation "com.opencsv:opencsv:4.6"
}
-def canonicalVersionCode = 70
-def canonicalVersionName = "1.4.3"
+def canonicalVersionCode = 72
+def canonicalVersionName = "1.4.4"
def postFixSize = 10
def abiPostFix = ['armeabi-v7a' : 1,
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 32907f5304..a1a3dd9b3b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1853,4 +1853,6 @@
*Please note that it is not possible to use the same Session ID on multiple devices simultaneously
+ Secure session reset done
+
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
index 97a7364366..eeb1c3cc0a 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
@@ -358,7 +358,7 @@ public class ConversationFragment extends Fragment
if (messageRecord.isGroupAction() || messageRecord.isCallLog() ||
messageRecord.isJoined() || messageRecord.isExpirationTimerUpdate() ||
messageRecord.isEndSession() || messageRecord.isIdentityUpdate() ||
- messageRecord.isIdentityVerified() || messageRecord.isIdentityDefault() || messageRecord.isLokiSessionRestoreSent())
+ messageRecord.isIdentityVerified() || messageRecord.isIdentityDefault() || messageRecord.isLokiSessionRestoreSent() || messageRecord.isLokiSessionRestoreDone())
{
actionMessage = true;
}
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
index 008d4e79f7..b7e61d34b8 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
@@ -114,6 +114,7 @@ public class ConversationUpdateItem extends LinearLayout
else if (messageRecord.isIdentityVerified() ||
messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord);
else if (messageRecord.isLokiSessionRestoreSent()) setTextMessageRecord(messageRecord);
+ else if (messageRecord.isLokiSessionRestoreDone()) setTextMessageRecord(messageRecord);
else throw new AssertionError("Neither group nor log nor joined.");
if (batchSelected.contains(messageRecord)) setSelected(true);
diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
index af3e881a29..d6e365f36c 100644
--- a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
+++ b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
@@ -84,6 +84,7 @@ public interface MmsSmsColumns {
// Loki
protected static final long ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT = 0x01000000;
+ protected static final long ENCRYPTION_LOKI_SESSION_RESTORE_DONE_BIT = 0x00100000;
public static boolean isDraftMessageType(long type) {
return (type & BASE_TYPE_MASK) == BASE_DRAFT_TYPE;
@@ -237,6 +238,10 @@ public interface MmsSmsColumns {
return (type & ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT) != 0;
}
+ public static boolean isLokiSessionRestoreDoneType(long type) {
+ return (type & ENCRYPTION_LOKI_SESSION_RESTORE_DONE_BIT) != 0;
+ }
+
public static boolean isLegacyType(long type) {
return (type & ENCRYPTION_REMOTE_LEGACY_BIT) != 0 ||
(type & ENCRYPTION_REMOTE_BIT) != 0;
diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
index 308b948ed9..1727d5e86d 100644
--- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
@@ -255,6 +255,10 @@ public class SmsDatabase extends MessagingDatabase {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT);
}
+ public void markAsLokiSessionRestorationDone(long id) {
+ updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_DONE_BIT);
+ }
+
public void markAsLegacyVersion(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_LEGACY_BIT);
}
diff --git a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
index 12be5f89a5..567f03e26b 100644
--- a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
@@ -107,6 +107,8 @@ public abstract class DisplayRecord {
public boolean isLokiSessionRestoreSent() { return SmsDatabase.Types.isLokiSessionRestoreSentType(type); }
+ public boolean isLokiSessionRestoreDone() { return SmsDatabase.Types.isLokiSessionRestoreDoneType(type); }
+
public boolean isGroupUpdate() {
return SmsDatabase.Types.isGroupUpdate(type);
}
diff --git a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
index 1718d14451..2e54b9d65e 100644
--- a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
@@ -180,7 +180,7 @@ public abstract class MessageRecord extends DisplayRecord {
public boolean isUpdate() {
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
- isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() || isLokiSessionRestoreSent();
+ isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() || isLokiSessionRestoreSent() || isLokiSessionRestoreDone();
}
public boolean isMediaPending() {
diff --git a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
index 231696af07..127375cdc7 100644
--- a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
@@ -18,9 +18,10 @@
package org.thoughtcrime.securesms.database.model;
import android.content.Context;
-import androidx.annotation.NonNull;
import android.text.SpannableString;
+import androidx.annotation.NonNull;
+
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
@@ -82,7 +83,9 @@ public class SmsMessageRecord extends MessageRecord {
} else if (SmsDatabase.Types.isNoRemoteSessionType(type)) {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session));
} else if (isLokiSessionRestoreSent()) {
- return emphasisAdded(context.getString(R.string.MessageRecord_session_restore_sent, recipient.toShortString()));
+ return emphasisAdded(context.getString(R.string.SmsMessageRecord_secure_session_reset));
+ } else if (isLokiSessionRestoreDone()) {
+ return emphasisAdded(context.getString(R.string.view_reset_secure_session_done_message));
} else if (isEndSession() && isOutgoing()) {
return emphasisAdded(context.getString(R.string.SmsMessageRecord_secure_session_reset));
} else if (isEndSession()) {
diff --git a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
index 1cf2c6d328..edac627a05 100644
--- a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
@@ -19,13 +19,14 @@ package org.thoughtcrime.securesms.database.model;
import android.content.Context;
import android.net.Uri;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -83,7 +84,9 @@ public class ThreadRecord extends DisplayRecord {
} else if (SmsDatabase.Types.isNoRemoteSessionType(type)) {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session));
} else if (isLokiSessionRestoreSent()) {
- return emphasisAdded(context.getString(R.string.MessageRecord_session_restore_sent, recipient.toShortString()));
+ return emphasisAdded(context.getString(R.string.SmsMessageRecord_secure_session_reset));
+ } else if (isLokiSessionRestoreDone()) {
+ return emphasisAdded(context.getString(R.string.view_reset_secure_session_done_message));
} else if (SmsDatabase.Types.isEndSessionType(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_secure_session_reset));
} else if (MmsSmsColumns.Types.isLegacyType(type)) {
diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
index 993fb49d96..402bf50c31 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
@@ -550,7 +550,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
{
try {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
- Recipient recipient = getMessageMasterDestination(content.getSender());
+ Recipient recipient = getMessageDestination(content, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(getMessageMasterDestination(content.getSender()).getAddress(),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, true,
@@ -1073,16 +1073,17 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
@NonNull Optional smsMessageId, @NonNull Throwable e)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
+ if (SessionMetaProtocol.shouldErrorMessageShow(context, timestamp)) {
+ if (!smsMessageId.isPresent()) {
+ Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp);
- if (!smsMessageId.isPresent()) {
- Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp);
-
- if (insertResult.isPresent()) {
- smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
- messageNotifier.updateNotification(context, insertResult.get().getThreadId());
+ if (insertResult.isPresent()) {
+ smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
+ messageNotifier.updateNotification(context, insertResult.get().getThreadId());
+ }
+ } else {
+ smsDatabase.markAsDecryptFailed(smsMessageId.get());
}
- } else {
- smsDatabase.markAsDecryptFailed(smsMessageId.get());
}
// FIXME: This is a temporary patch for bad mac issues. At least with this people will be able to message again. We have to figure out the root cause of the issue though.
@@ -1100,25 +1101,26 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
}
- SessionManagementProtocol.triggerSessionRestorationUI(context, sender);
+ SessionManagementProtocol.triggerSessionRestorationUI(context, sender, timestamp);
}
private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp,
@NonNull Optional smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
+ if (SessionMetaProtocol.shouldErrorMessageShow(context, timestamp)) {
+ if (!smsMessageId.isPresent()) {
+ Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp);
- if (!smsMessageId.isPresent()) {
- Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp);
-
- if (insertResult.isPresent()) {
- smsDatabase.markAsNoSession(insertResult.get().getMessageId());
- messageNotifier.updateNotification(context, insertResult.get().getThreadId());
+ if (insertResult.isPresent()) {
+ smsDatabase.markAsNoSession(insertResult.get().getMessageId());
+ messageNotifier.updateNotification(context, insertResult.get().getThreadId());
+ }
+ } else {
+ smsDatabase.markAsNoSession(smsMessageId.get());
}
- } else {
- smsDatabase.markAsNoSession(smsMessageId.get());
}
- SessionManagementProtocol.triggerSessionRestorationUI(context, sender);
+ SessionManagementProtocol.triggerSessionRestorationUI(context, sender, timestamp);
}
private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp,
diff --git a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt
index e81f3c9fb6..d41ef58791 100644
--- a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt
+++ b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt
@@ -307,7 +307,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
override fun getSessionRequestSentTimestamp(publicKey: String): Long? {
val database = databaseHelper.readableDatabase
return database.get(sessionRequestSentTimestampTable, "${LokiAPIDatabase.publicKey} = ?", wrap(publicKey)) { cursor ->
- cursor.getInt(LokiAPIDatabase.timestamp)
+ cursor.getLong(LokiAPIDatabase.timestamp)
}?.toLong()
}
diff --git a/src/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListFragment.kt b/src/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListFragment.kt
index b3e2932848..1dfd5e577f 100644
--- a/src/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListFragment.kt
+++ b/src/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListFragment.kt
@@ -12,6 +12,7 @@ import android.view.ViewGroup
import kotlinx.android.synthetic.main.contact_selection_list_fragment.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader
+import org.thoughtcrime.securesms.logging.Log
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.recipients.Recipient
@@ -60,6 +61,11 @@ class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks) {
- if (activity?.isDestroyed == true) { return }
+ private fun update(items: List) {
+ if (activity?.isDestroyed == true) {
+ Log.e(ContactSelectionListFragment::class.java.name,
+ "Received a loader callback after the fragment was detached from the activity.",
+ IllegalStateException())
+ return
+ }
listAdapter.items = items
mainContentContainer.visibility = if (items.isEmpty()) View.GONE else View.VISIBLE
emptyStateContainer.visibility = if (items.isEmpty()) View.VISIBLE else View.GONE
@@ -110,4 +121,4 @@ class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks sentTimestamp && timestamp > processedTimestamp
+ val restorationTimestamp = TextSecurePreferences.getRestorationTime(context)
+ return timestamp > sentTimestamp && timestamp > processedTimestamp && timestamp > restorationTimestamp
}
@JvmStatic
@@ -99,10 +100,13 @@ object SessionManagementProtocol {
}
@JvmStatic
- fun triggerSessionRestorationUI(context: Context, publicKey: String) {
+ fun triggerSessionRestorationUI(context: Context, publicKey: String, errorTimestamp: Long) {
val masterDevicePublicKey = MultiDeviceProtocol.shared.getMasterDevice(publicKey) ?: publicKey
val masterDeviceAsRecipient = recipient(context, masterDevicePublicKey)
if (masterDeviceAsRecipient.isGroupRecipient) { return }
+ if (TextSecurePreferences.getRestorationTime(context) > errorTimestamp) {
+ return ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(publicKey)
+ }
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(masterDeviceAsRecipient)
DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey)
}
diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt
index 2327cbe3bb..b4a16ed505 100644
--- a/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt
+++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt
@@ -30,6 +30,12 @@ object SessionMetaProtocol {
return shouldIgnoreMessage
}
+ @JvmStatic
+ fun shouldErrorMessageShow(context: Context, timestamp: Long): Boolean {
+ val restorationTimestamp = TextSecurePreferences.getRestorationTime(context)
+ return timestamp > restorationTimestamp
+ }
+
@JvmStatic
fun handleProfileUpdateIfNeeded(context: Context, content: SignalServiceContent) {
val rawDisplayName = content.senderDisplayName.orNull() ?: return
diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt
index 1c8a283125..b8e0fa8e87 100644
--- a/src/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt
+++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionResetImplementation.kt
@@ -2,7 +2,10 @@ package org.thoughtcrime.securesms.loki.protocol
import android.content.Context
import org.thoughtcrime.securesms.ApplicationContext
+import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
+import org.thoughtcrime.securesms.recipients.Recipient
+import org.thoughtcrime.securesms.sms.OutgoingTextMessage
import org.whispersystems.libsignal.loki.SessionResetProtocol
import org.whispersystems.libsignal.loki.SessionResetStatus
import org.whispersystems.libsignal.protocol.PreKeySignalMessage
@@ -22,7 +25,14 @@ class SessionResetImplementation(private val context: Context) : SessionResetPro
val job = NullMessageSendJob(publicKey)
ApplicationContext.getInstance(context).jobManager.add(job)
}
- // TODO: Show session reset succeed message
+ val smsDB = DatabaseFactory.getSmsDatabase(context)
+ val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
+ val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient)
+ val infoMessage = OutgoingTextMessage(recipient, "", 0, 0)
+ val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null)
+ if (infoMessageID > -1) {
+ smsDB.markAsLokiSessionRestorationDone(infoMessageID)
+ }
}
override fun validatePreKeySignalMessage(publicKey: String, message: PreKeySignalMessage) {