Forward messages from secondary devices to primary device.

This commit is contained in:
Mikunj 2019-10-08 13:23:03 +11:00
parent 3c5ef19ca7
commit 21e529c6c9
4 changed files with 106 additions and 22 deletions

View File

@ -128,6 +128,7 @@ import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.Database;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
@ -158,6 +159,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.loki.LokiAPIUtilities;
import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.LokiThreadDatabaseDelegate;
import org.thoughtcrime.securesms.loki.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.MentionCandidateSelectionView;
@ -2988,7 +2990,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// region Loki
@Override
public void acceptFriendRequest(@NotNull MessageRecord friendRequest) {
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(this.threadId).getAddress().toString();
// Send the accept to the original friend request thread id
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
long threadId = originalThreadID < 0 ? this.threadId : originalThreadID;
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadId).getAddress().toString();
SignalServiceMessageSender messageSender = ApplicationContext.getInstance(this).communicationModule.provideSignalMessageSender();
SignalServiceAddress address = new SignalServiceAddress(contactID);
SignalServiceDataMessage message = new SignalServiceDataMessage(System.currentTimeMillis(), "");
@ -2996,8 +3003,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
AsyncTask.execute(() -> {
try {
messageSender.sendMessage(0, address, Optional.absent(), message); // The message ID doesn't matter
DatabaseFactory.getLokiThreadDatabase(context).setFriendRequestStatus(this.threadId, LokiThreadFriendRequestStatus.FRIENDS);
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(friendRequest.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
DatabaseFactory.getLokiThreadDatabase(context).setFriendRequestStatus(threadId, LokiThreadFriendRequestStatus.FRIENDS);
lokiMessageDatabase.setFriendRequestStatus(friendRequest.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
} catch (Exception e) {
Log.d("Loki", "Failed to send background message to: " + contactID + ".");
}
@ -3006,8 +3013,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(this.threadId, LokiThreadFriendRequestStatus.NONE);
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(this.threadId).getAddress().toString();
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
long threadId = originalThreadID < 0 ? this.threadId : originalThreadID;
DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadId, LokiThreadFriendRequestStatus.NONE);
String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadId).getAddress().toString();
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(contactID);
}
// endregion

View File

@ -70,6 +70,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int lokiV1 = 22;
private static final int lokiV2 = 23;
private static final int lokiV3 = 24;
private static final int lokiV4 = 25;
private static final int DATABASE_VERSION = lokiV3; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
private static final String DATABASE_NAME = "signal.db";
@ -128,7 +129,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand());
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
db.execSQL(LokiMessageDatabase.getCreateTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageFriendRequestTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand());
db.execSQL(LokiThreadDatabase.getCreateFriendRequestTableCommand());
db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand());
db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand());
@ -504,6 +506,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE part ADD COLUMN url TEXT");
}
if (oldVersion < lokiV4) {
db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand());
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@ -97,6 +97,7 @@ import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libsignal.state.PreKeyBundle;
import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.util.guava.Optional;
@ -749,13 +750,16 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
@NonNull Optional<Long> messageServerIDOrNull)
throws StorageFailedException
{
notifyTypingStoppedFromIncomingMessage(getMessageDestination(content, message), content.getSender(), content.getSenderDevice());
Recipient originalRecipient = getMessageDestination(content, message);
Recipient primaryDeviceRecipient = getMessagePrimaryDestination(content, message);
notifyTypingStoppedFromIncomingMessage(primaryDeviceRecipient, content.getSender(), content.getSenderDevice());
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()), message.getTimestamp(), -1,
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(primaryDeviceRecipient.getAddress(), message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(),
quote, sharedContacts, linkPreviews, sticker);
@ -798,8 +802,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// Loki - Store message server ID
updateGroupChatMessageServerID(messageServerIDOrNull, insertResult);
// Loki - Update mapping of message to original thread id
if (insertResult.isPresent()) {
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
lokiMessageDatabase.setOriginalThreadID(insertResult.get().getMessageId(), originalThreadId);
}
}
@ -912,20 +922,21 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
{
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : "";
Recipient recipient = getMessageDestination(content, message);
Recipient originalRecipient = getMessageDestination(content, message);
Recipient primaryDeviceRecipient = message.isGroupUpdate() ? originalRecipient : getMessagePrimaryDestination(content, message);
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
if (message.getExpiresInSeconds() != originalRecipient.getExpireMessages()) {
handleExpirationUpdate(content, message, Optional.absent());
}
Long threadId;
Long threadId = null;
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second;
} else {
notifyTypingStoppedFromIncomingMessage(recipient, content.getSender(), content.getSenderDevice());
notifyTypingStoppedFromIncomingMessage(primaryDeviceRecipient, content.getSender(), content.getSenderDevice());
IncomingTextMessage _textMessage = new IncomingTextMessage(Address.fromSerialized(content.getSender()),
IncomingTextMessage _textMessage = new IncomingTextMessage(primaryDeviceRecipient.getAddress(),
content.getSenderDevice(),
message.getTimestamp(), body,
message.getGroupInfo(),
@ -940,8 +951,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// Insert the message into the database
Optional<InsertResult> insertResult = database.insertMessageInbox(textMessage);
if (insertResult.isPresent()) threadId = insertResult.get().getThreadId();
else threadId = null;
Long messageId = null;
if (insertResult.isPresent()) {
threadId = insertResult.get().getThreadId();
messageId = insertResult.get().getMessageId();
}
if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get());
@ -954,6 +968,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// Loki - Store message server ID
updateGroupChatMessageServerID(messageServerIDOrNull, insertResult);
// Loki - Update mapping of message to original thread id
if (messageId != null) {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
lokiMessageDatabase.setOriginalThreadID(messageId, originalThreadId);
}
boolean isGroupMessage = message.getGroupInfo().isPresent();
if (threadId != null && !isGroupMessage) {
MessageNotifier.updateNotification(context, threadId);
@ -1496,6 +1518,33 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
}
private Recipient getMessagePrimaryDestination(SignalServiceContent content, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) {
return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false);
} else {
SettableFuture<String> device = new SettableFuture<>();
String contentSender = content.getSender();
// Get the primary device
LokiStorageAPI.shared.getPrimaryDevicePublicKey(contentSender).success(primaryDevice -> {
String publicKey = primaryDevice == null ? contentSender : primaryDevice;
device.set(publicKey);
return Unit.INSTANCE;
}).fail(exception -> {
device.set(contentSender);
return Unit.INSTANCE;
});
try {
String primarySender = device.get();
return Recipient.from(context, Address.fromSerialized(primarySender), false);
} catch (Exception e) {
Log.d("Loki", "Failed to get primary device public key for message. " + e.getMessage());
return Recipient.from(context, Address.fromSerialized(content.getSender()), false);
}
}
}
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) {
return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false);

View File

@ -12,11 +12,14 @@ import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestS
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
companion object {
private val tableName = "loki_message_friend_request_database"
private val messageFriendRequestTableName = "loki_message_friend_request_database"
private val messageThreadMappingTableName = "loki_message_thread_mapping_database"
private val messageID = "message_id"
private val serverID = "server_id"
private val friendRequestStatus = "friend_request_status"
@JvmStatic val createTableCommand = "CREATE TABLE $tableName ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);"
private val threadID = "thread_id"
@JvmStatic val createMessageFriendRequestTableCommand = "CREATE TABLE $messageFriendRequestTableName ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);"
@JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE $messageThreadMappingTableName ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);"
}
override fun getQuoteServerID(quoteID: Long, quoteeHexEncodedPublicKey: String): Long? {
@ -26,14 +29,14 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
fun getServerID(messageID: Long): Long? {
val database = databaseHelper.readableDatabase
return database.get(tableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
return database.get(messageFriendRequestTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getInt(Companion.serverID)
}?.toLong()
}
fun getMessageID(serverID: Long): Long? {
val database = databaseHelper.readableDatabase
return database.get(tableName, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor ->
return database.get(messageFriendRequestTableName, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor ->
cursor.getInt(messageID)
}?.toLong()
}
@ -43,12 +46,27 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.serverID, serverID)
database.insertOrUpdate(tableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
database.insertOrUpdate(messageFriendRequestTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
}
fun getOriginalThreadID(messageID: Long): Long {
val database = databaseHelper.readableDatabase
return database.get(messageThreadMappingTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getInt(Companion.threadID)
}?.toLong() ?: -1L
}
fun setOriginalThreadID(messageID: Long, threadID: Long) {
val database = databaseHelper.writableDatabase
val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.threadID, threadID)
database.insertOrUpdate(messageThreadMappingTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
}
fun getFriendRequestStatus(messageID: Long): LokiMessageFriendRequestStatus {
val database = databaseHelper.readableDatabase
val result = database.get(tableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
val result = database.get(messageFriendRequestTableName, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) { cursor ->
cursor.getInt(friendRequestStatus)
}
return if (result != null) {
@ -63,7 +81,7 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
val contentValues = ContentValues(2)
contentValues.put(Companion.messageID, messageID)
contentValues.put(Companion.friendRequestStatus, friendRequestStatus.rawValue)
database.insertOrUpdate(tableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
database.insertOrUpdate(messageFriendRequestTableName, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() ))
val threadID = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageID)
notifyConversationListeners(threadID)
}