diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java index 030c638c9e..0069511471 100644 --- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -79,6 +79,9 @@ public class ThreadDatabase extends Database { public static final String LAST_SEEN = "last_seen"; private static final String HAS_SENT = "has_sent"; + // Loki + private static final String FRIEND_REQUEST_STATUS = "friend_request_status"; + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " + @@ -88,7 +91,8 @@ 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);"; + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0," + + FRIEND_REQUEST_STATUS + "INTEGER DEFAULT 0);"; public static final String[] CREATE_INDEXS = { "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");", @@ -428,6 +432,32 @@ 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); @@ -638,6 +668,22 @@ 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/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 83ddabbc08..53f505f994 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -86,7 +86,6 @@ import org.whispersystems.libsignal.state.PreKeyBundle; import org.whispersystems.libsignal.state.SessionStore; import org.whispersystems.libsignal.state.SignalProtocolStore; import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview; @@ -267,7 +266,11 @@ public class PushDecryptJob extends BaseJob { else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId); else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId); else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId); - else if (message.getBody().isPresent()) handleTextMessage(content, message, smsMessageId); + else if (message.getBody().isPresent()) { + // Loki - Handle friend request logic + handleFriendRequestIfNeeded(envelope, content, message); + handleTextMessage(content, message, smsMessageId); + } if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) { handleUnknownGroupMessage(content, message.getGroupInfo().get()); @@ -812,6 +815,46 @@ public class PushDecryptJob extends BaseJob { } } + 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); + int friendRequestStatus = database.getFriendRequestStatus(threadId); + + if (envelope.isFriendRequest()) { + if (friendRequestStatus == ThreadDatabase.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 + // friend request accepted message succeeded. Even if it doesn't, the thread's current friend + // request status will be set to `FRIENDS` for Alice making it possible + // for Alice to send messages to Bob. When Bob receives a message, his thread's friend request status + // will then be set to `FRIENDS`. If we do check for a successful send + // 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); + // TODO: Send empty message here + } else if (friendRequestStatus != ThreadDatabase.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); + } + } else if (friendRequestStatus != ThreadDatabase.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); + + // TODO: Send p2p details here + } + } + private long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message) throws MmsException {