diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 78cdf0a3c7..d89994f701 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -146,6 +146,7 @@ import org.thoughtcrime.securesms.giph.ui.GiphyActivity; import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel; +import org.thoughtcrime.securesms.loki.LokiFriendRequestStatus; import org.thoughtcrime.securesms.mediasend.MediaSendActivity; import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; @@ -2151,12 +2152,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity ApplicationContext.getInstance(context).getTypingStatusSender().onTypingStopped(threadId); } else { message = new OutgoingTextMessage(recipient, messageBody, expiresIn, subscriptionId); - - // Loki - Always send friend requests if we're not friends with the user - int friendRequestStatus = DatabaseFactory.getThreadDatabase(context).getFriendRequestStatus(threadId); - message.isFriendRequest = (friendRequestStatus != ThreadDatabase.LokiFriendRequestStatus.FRIENDS); } + // Loki - Always send friend requests if we're not friends with the user + int friendRequestStatus = DatabaseFactory.getLokiThreadFriendRequestDatabase(context).getFriendRequestStatus(threadId); + message.isFriendRequest = (friendRequestStatus != LokiFriendRequestStatus.FRIENDS); + Permissions.with(this) .request(Manifest.permission.SEND_SMS) .ifNecessary(forceSms || !isSecureText) diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java index 617bfce8e3..0dcd665172 100644 --- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java +++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java @@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.loki.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.LokiContactPreKeyDatabase; import org.thoughtcrime.securesms.loki.LokiPreKeyBundleDatabase; import org.thoughtcrime.securesms.loki.LokiSmsFriendRequestDatabase; +import org.thoughtcrime.securesms.loki.LokiThreadFriendRequestDatabase; import org.thoughtcrime.securesms.util.TextSecurePreferences; public class DatabaseFactory { @@ -68,6 +69,7 @@ public class DatabaseFactory { private final LokiContactPreKeyDatabase lokiContactPreKeyDatabase; private final LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase; private final LokiSmsFriendRequestDatabase lokiSmsFriendRequestDatabase; + private final LokiThreadFriendRequestDatabase lokiThreadFriendRequestDatabase; public static DatabaseFactory getInstance(Context context) { synchronized (lock) { @@ -170,6 +172,10 @@ public class DatabaseFactory { public static LokiSmsFriendRequestDatabase getLokiSmsFriendRequestDatabase(Context context) { return getInstance(context).lokiSmsFriendRequestDatabase; } + + public static LokiThreadFriendRequestDatabase getLokiThreadFriendRequestDatabase(Context context) { + return getInstance(context).lokiThreadFriendRequestDatabase; + } // endregion public static void upgradeRestored(Context context, SQLiteDatabase database){ @@ -207,6 +213,7 @@ public class DatabaseFactory { this.lokiContactPreKeyDatabase = new LokiContactPreKeyDatabase(context, databaseHelper); this.lokiPreKeyBundleDatabase = new LokiPreKeyBundleDatabase(context, databaseHelper); this.lokiSmsFriendRequestDatabase = new LokiSmsFriendRequestDatabase(context, databaseHelper); + this.lokiThreadFriendRequestDatabase = new LokiThreadFriendRequestDatabase(context, databaseHelper); } public void onApplicationLevelUpgrade(@NonNull Context context, @NonNull MasterSecret masterSecret, diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java index 834f0159d6..ca1f47eb05 100644 --- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -78,7 +78,6 @@ public class ThreadDatabase extends Database { public static final String EXPIRES_IN = "expires_in"; public static final String LAST_SEEN = "last_seen"; private static final String HAS_SENT = "has_sent"; - private static final String FRIEND_REQUEST_STATUS = "friend_request_status"; // Loki public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + @@ -89,8 +88,7 @@ public class ThreadDatabase extends Database { ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " + DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " + - READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0," + - FRIEND_REQUEST_STATUS + "INTEGER DEFAULT 0);"; + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);"; public static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");", @@ -430,32 +428,6 @@ public class ThreadDatabase extends Database { notifyConversationListListeners(); } - // region Loki - public int getFriendRequestStatus(long threadId) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, new String[]{FRIEND_REQUEST_STATUS}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); - - try { - if (cursor != null && cursor.moveToNext()) { - return cursor.getInt(cursor.getColumnIndexOrThrow(FRIEND_REQUEST_STATUS)); - } - } finally { - if (cursor != null) cursor.close(); - } - - return LokiFriendRequestStatus.NONE; - } - - public void setFriendRequestStatus(long threadId, int status) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(1); - contentValues.put(FRIEND_REQUEST_STATUS, status); - - db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(threadId)}); - notifyConversationListListeners(); - } - //endregion - public Pair getLastSeenAndHasSent(long threadId) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); Cursor cursor = db.query(TABLE_NAME, new String[]{LAST_SEEN, HAS_SENT}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); @@ -666,22 +638,6 @@ public class ThreadDatabase extends Database { public static final int INBOX_ZERO = 4; } - // Loki - public static class LokiFriendRequestStatus { - // New conversation; no messages sent or received. - public static final int NONE = 0; - // This state is used to lock the input early while sending. - public static final int REQUEST_SENDING = 1; - // Friend request sent; awaiting response. - public static final int REQUEST_SENT = 2; - // Friend request received; awaiting user input. - public static final int REQUEST_RECEIVED = 3; - // We are friends with the user in this thread. - public static final int FRIENDS = 4; - // A friend request was sent, but it timed out (i.e other user didn't accept within the allocated time) - public static final int REQUEST_EXPIRED = 5; - } - public class Reader implements Closeable { private final Cursor cursor; diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 84751aaa78..b69c9caa2d 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.loki.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.LokiContactPreKeyDatabase; import org.thoughtcrime.securesms.loki.LokiPreKeyBundleDatabase; import org.thoughtcrime.securesms.loki.LokiSmsFriendRequestDatabase; +import org.thoughtcrime.securesms.loki.LokiThreadFriendRequestDatabase; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -122,6 +123,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand()); db.execSQL(LokiContactPreKeyDatabase.getCreateTableCommand()); db.execSQL(LokiSmsFriendRequestDatabase.getCreateTableCommand()); + db.execSQL(LokiThreadFriendRequestDatabase.getCreateTableCommand()); executeStatements(db, SmsDatabase.CREATE_INDEXS); executeStatements(db, MmsDatabase.CREATE_INDEXS); diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 8434e00524..89b830adee 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -62,7 +62,9 @@ import org.thoughtcrime.securesms.linkpreview.Link; import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.LokiFriendRequestStatus; import org.thoughtcrime.securesms.loki.LokiPreKeyBundleDatabase; +import org.thoughtcrime.securesms.loki.LokiThreadFriendRequestDatabase; import org.thoughtcrime.securesms.mms.IncomingMediaMessage; import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; @@ -825,11 +827,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType { private void handleFriendRequestIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { Recipient recipient = getMessageDestination(content, message); - ThreadDatabase database = DatabaseFactory.getThreadDatabase(context); - long threadId = database.getThreadIdIfExistsFor(recipient); + long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient); + + LokiThreadFriendRequestDatabase database = DatabaseFactory.getLokiThreadFriendRequestDatabase(context); int friendRequestStatus = database.getFriendRequestStatus(threadId); if (envelope.isFriendRequest()) { - if (friendRequestStatus == ThreadDatabase.LokiFriendRequestStatus.REQUEST_SENT) { + if (friendRequestStatus == LokiFriendRequestStatus.REQUEST_SENT) { // This can happen if Alice sent Bob a friend request, Bob declined, but then Bob changed his // mind and sent a friend request to Alice. In this case we want Alice to auto-accept the request // and send a friend request accepted message back to Bob. We don't check that sending the @@ -840,21 +843,21 @@ public class PushDecryptJob extends BaseJob implements InjectableType { // before updating Alice's thread's friend request status to `FRIENDS`, // we can end up in a deadlock where both users' threads' friend request statuses are // `REQUEST_SENT`. - database.setFriendRequestStatus(threadId, ThreadDatabase.LokiFriendRequestStatus.FRIENDS); + database.setFriendRequestStatus(threadId, LokiFriendRequestStatus.FRIENDS); // Accept the friend request sendEmptyMessageTo(envelope.getSource()); - } else if (friendRequestStatus != ThreadDatabase.LokiFriendRequestStatus.FRIENDS) { + } else if (friendRequestStatus != LokiFriendRequestStatus.FRIENDS) { // Checking that the sender of the message isn't already a friend is necessary because otherwise // the following situation can occur: Alice and Bob are friends. Bob loses his database and his // friend request status is reset to `NONE`. Bob now sends Alice a friend // request. Alice's thread's friend request status is reset to // `REQUEST_RECEIVED`. - database.setFriendRequestStatus(threadId, ThreadDatabase.LokiFriendRequestStatus.REQUEST_RECEIVED); + database.setFriendRequestStatus(threadId, LokiFriendRequestStatus.REQUEST_RECEIVED); } - } else if (friendRequestStatus != ThreadDatabase.LokiFriendRequestStatus.FRIENDS) { + } else if (friendRequestStatus != LokiFriendRequestStatus.FRIENDS) { // If the thread's friend request status is not `FRIENDS`, but we're receiving a message, // it must be a friend request accepted message. Declining a friend request doesn't send a message. - database.setFriendRequestStatus(threadId, ThreadDatabase.LokiFriendRequestStatus.FRIENDS); + database.setFriendRequestStatus(threadId, LokiFriendRequestStatus.FRIENDS); // TODO: Send p2p details here } } diff --git a/src/org/thoughtcrime/securesms/loki/DatabaseUtilities.kt b/src/org/thoughtcrime/securesms/loki/DatabaseUtilities.kt index 04615296b7..a62f380e66 100644 --- a/src/org/thoughtcrime/securesms/loki/DatabaseUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/DatabaseUtilities.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.loki +import android.content.ContentValues import net.sqlcipher.Cursor import net.sqlcipher.database.SQLiteDatabase import org.thoughtcrime.securesms.util.Base64 @@ -17,6 +18,13 @@ fun SQLiteDatabase.get(table: String, query: String, arguments: Array) { + val id = this.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE).toInt() + if (id == -1) { + this.update(table, values, whereClause, whereArgs) + } +} + fun Cursor.getInt(columnName: String): Int { return this.getInt(this.getColumnIndexOrThrow(columnName)) } diff --git a/src/org/thoughtcrime/securesms/loki/LokiFriendRequestStatus.java b/src/org/thoughtcrime/securesms/loki/LokiFriendRequestStatus.java new file mode 100644 index 0000000000..626ca534de --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/LokiFriendRequestStatus.java @@ -0,0 +1,17 @@ +package org.thoughtcrime.securesms.loki; + +public class LokiFriendRequestStatus { + // New conversation; no messages sent or received. + public static final int NONE = 0; + // This state is used to lock the input early while sending. + public static final int REQUEST_SENDING = 1; + // Friend request sent; awaiting response. + public static final int REQUEST_SENT = 2; + // Friend request received; awaiting user input. + public static final int REQUEST_RECEIVED = 3; + // We are friends with the user in this thread. + public static final int FRIENDS = 4; + // A friend request was sent, but it timed out (i.e other user didn't accept within the allocated time) + public static final int REQUEST_EXPIRED = 5; +} + diff --git a/src/org/thoughtcrime/securesms/loki/LokiSmsFriendRequestDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiSmsFriendRequestDatabase.kt index 13907a0d64..0b2714c62f 100644 --- a/src/org/thoughtcrime/securesms/loki/LokiSmsFriendRequestDatabase.kt +++ b/src/org/thoughtcrime/securesms/loki/LokiSmsFriendRequestDatabase.kt @@ -2,14 +2,11 @@ package org.thoughtcrime.securesms.loki import android.content.ContentValues import android.content.Context -import net.sqlcipher.database.SQLiteDatabase -import org.thoughtcrime.securesms.crypto.PreKeyUtil import org.thoughtcrime.securesms.database.Database import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.whispersystems.libsignal.state.PreKeyRecord /** - * A database for associating friend request data to Sms objects + * A database for associating friend request data to Sms */ class LokiSmsFriendRequestDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) { @@ -22,20 +19,6 @@ class LokiSmsFriendRequestDatabase(context: Context, helper: SQLCipherOpenHelper val createTableCommand = "CREATE TABLE $tableName ($smsId INTEGER PRIMARY KEY, $isFriendRequest INTEGER DEFAULT 0);" } - fun setIsFriendRequest(messageId: Long, isFriendRequest: Boolean) { - val database = databaseHelper.writableDatabase - - val rawIsFriendRequest = if (isFriendRequest) 1 else 0 - - val values = ContentValues() - values.put(smsId, messageId) - values.put(Companion.isFriendRequest, rawIsFriendRequest) - - // Note: If we add any other fields, then `SQLiteDatabase.CONFLICT_REPLACE` will most likely overwrite them - // we probably want to switch to `database.update` later, for now since we only have 1 field, it is fine - database.insertWithOnConflict(tableName, null, values, SQLiteDatabase.CONFLICT_REPLACE) - } - fun getIsFriendRequest(messageId: Long): Boolean { val database = databaseHelper.readableDatabase return database.get(tableName, ID_WHERE, arrayOf(messageId.toString())) { cursor -> @@ -43,4 +26,16 @@ class LokiSmsFriendRequestDatabase(context: Context, helper: SQLCipherOpenHelper rawIsFriendRequest == 1 } ?: false } + + fun setIsFriendRequest(messageId: Long, isFriendRequest: Boolean) { + val database = databaseHelper.writableDatabase + + val rawIsFriendRequest = if (isFriendRequest) 1 else 0 + + val contentValues = ContentValues() + contentValues.put(smsId, messageId) + contentValues.put(Companion.isFriendRequest, rawIsFriendRequest) + + database.insertOrUpdate(tableName, contentValues, ID_WHERE, arrayOf(messageId.toString())) + } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/LokiThreadFriendRequestDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiThreadFriendRequestDatabase.kt new file mode 100644 index 0000000000..147d3ccdab --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/LokiThreadFriendRequestDatabase.kt @@ -0,0 +1,38 @@ +package org.thoughtcrime.securesms.loki + +import android.content.ContentValues +import android.content.Context +import org.thoughtcrime.securesms.database.Database +import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper + +/** + * A database for associating friend request data to Threads + */ +class LokiThreadFriendRequestDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) { + + companion object { + private val tableName = "loki_thread_friend_request_database" + private val threadId = "_id" + private val friendRequestStatus = "friend_request_status" + + @JvmStatic + val createTableCommand = "CREATE TABLE $tableName ($threadId INTEGER PRIMARY KEY, $friendRequestStatus INTEGER DEFAULT 0);" + } + + fun getFriendRequestStatus(threadId: Long): Int { + val db = databaseHelper.readableDatabase + return db.get(tableName, ID_WHERE, arrayOf(threadId.toString())) { cursor -> + cursor.getInt(friendRequestStatus) + } ?: LokiFriendRequestStatus.NONE + } + + fun setFriendRequestStatus(threadId: Long, status: Int) { + val database = databaseHelper.writableDatabase + val contentValues = ContentValues(1) + contentValues.put(Companion.threadId, threadId) + contentValues.put(friendRequestStatus, status) + + database.insertOrUpdate(tableName, contentValues, ID_WHERE, arrayOf(threadId.toString())) + notifyConversationListListeners() + } +} \ No newline at end of file