diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_00.binproto new file mode 100644 index 0000000000..9825d75ed0 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_00.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_01.binproto new file mode 100644 index 0000000000..eb72e75163 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_01.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_02.binproto new file mode 100644 index 0000000000..23b8166323 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_02.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_03.binproto new file mode 100644 index 0000000000..e77ee98df6 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_03.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_04.binproto new file mode 100644 index 0000000000..3ee22f024e Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_04.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_05.binproto new file mode 100644 index 0000000000..7bbbf42c85 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_05.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_06.binproto new file mode 100644 index 0000000000..b6833761c5 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_06.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_07.binproto new file mode 100644 index 0000000000..3da2215a8f Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_07.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_08.binproto new file mode 100644 index 0000000000..dc5bdf418d Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_08.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_09.binproto new file mode 100644 index 0000000000..06ab5272a3 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_09.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_10.binproto new file mode 100644 index 0000000000..2ca494475f Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_10.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_11.binproto new file mode 100644 index 0000000000..891686ecc1 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_11.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_12.binproto new file mode 100644 index 0000000000..dbbcf80f57 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_12.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_13.binproto new file mode 100644 index 0000000000..c7cfe15c10 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_13.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_14.binproto new file mode 100644 index 0000000000..8d908de626 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_14.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_terminate_update_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_terminate_update_00.binproto new file mode 100644 index 0000000000..ee782215d7 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_terminate_update_00.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_poll_terminate_update_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_poll_terminate_update_01.binproto new file mode 100644 index 0000000000..5d41c923e4 Binary files /dev/null and b/app/src/androidTest/assets/backupTests/chat_item_poll_terminate_update_01.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto index 4702150126..c472adb1cb 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_00.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto index 6d7f20d88d..5135da028c 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_01.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_02.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_02.binproto index be204779f4..1580875dd7 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_02.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_02.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_03.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_03.binproto index b3e1d07efd..27bdbf5c51 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_03.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_03.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_04.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_04.binproto index cf78d03f88..cabd52fbaa 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_04.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_04.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto index b112543d35..e74e26865b 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_05.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_06.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_06.binproto index 3b3cec5a36..e377d45ef8 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_06.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_06.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto index 02d46a7394..5462b58094 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_07.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_08.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_08.binproto index b65d4b2f69..1ea4c98957 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_08.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_08.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto index 3e3b2680b5..7fb6b178e2 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_09.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_10.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_10.binproto index 8e143d6f75..afd2a7185f 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_10.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_10.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_11.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_11.binproto index c38e6ba059..18c7bfec2b 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_11.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_11.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_12.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_12.binproto index 4a9bd55a9e..93aa137021 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_12.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_12.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto index ed25e6213b..067f763b72 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_13.binproto differ diff --git a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_14.binproto b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_14.binproto index b88dbd6718..2c8d86c6e5 100644 Binary files a/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_14.binproto and b/app/src/androidTest/assets/backupTests/chat_item_standard_message_with_quote_14.binproto differ diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt index b9001228dd..6a4008aacb 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ArchiveImportExportTests.kt @@ -211,6 +211,11 @@ class ArchiveImportExportTests { runTests { it.startsWith("chat_item_view_once_") } } +// @Test + fun chatItemPoll() { + runTests { it.startsWith("chat_item_poll_") } + } + // @Test fun notificationProfiles() { runTests { it.startsWith("notification_profile_") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveErrorCases.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveErrorCases.kt index c7446693c8..0c7d321da2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveErrorCases.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveErrorCases.kt @@ -123,6 +123,14 @@ object ExportSkips { return log(sentTimestamp, "Poll terminate update was empty.") } + fun invalidPollQuestion(sentTimestamp: Long): String { + return log(sentTimestamp, "Poll question was invalid.") + } + + fun invalidPollOption(sentTimestamp: Long): String { + return log(sentTimestamp, "Poll option was invalid.") + } + fun individualChatUpdateInWrongTypeOfChat(sentTimestamp: Long): String { return log(sentTimestamp, "A chat update that only makes sense for individual chats was found in a different kind of chat.") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt index 74f2b7d903..d12caf267f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/exporters/ChatItemArchiveExporter.kt @@ -117,6 +117,8 @@ private val TAG = Log.tag(ChatItemArchiveExporter::class.java) private val MAX_INLINED_BODY_SIZE = 128.kibiBytes.bytes.toInt() private val MAX_INLINED_BODY_SIZE_WITH_LONG_ATTACHMENT_POINTER = 2.kibiBytes.bytes.toInt() private val MAX_INLINED_QUOTE_BODY_SIZE = 2.kibiBytes.bytes.toInt() +private const val MAX_POLL_CHARACTER_LENGTH = 100 +private const val MAX_POLL_OPTIONS = 10 /** * An iterator for chat items with a clever performance twist: rather than do the extra queries one at a time (for reactions, @@ -386,7 +388,15 @@ class ChatItemArchiveExporter( extraData.pollsById[record.id] != null -> { val poll = extraData.pollsById[record.id]!! - builder.poll = poll.toRemotePollMessage() + if (poll.question.isEmpty() || poll.question.length > MAX_POLL_CHARACTER_LENGTH) { + Log.w(TAG, ExportSkips.invalidPollQuestion(record.dateSent)) + continue + } + if (poll.pollOptions.isEmpty() || poll.pollOptions.size > MAX_POLL_OPTIONS || poll.pollOptions.any { it.text.isEmpty() || it.text.length > MAX_POLL_CHARACTER_LENGTH }) { + Log.w(TAG, ExportSkips.invalidPollOption(record.dateSent)) + continue + } + builder.poll = poll.toRemotePollMessage(reactionRecords = extraData.reactionsById[record.id]) transformTimer.emit("poll") } @@ -492,7 +502,7 @@ class ChatItemArchiveExporter( val pollsFuture = executor.submitTyped { extraDataTimer.timeEvent("polls") { - db.pollTable.getPollsForMessages(messageIds) + db.pollTable.getPollsForMessages(messageIds = messageIds, includePending = false) } } @@ -1167,7 +1177,7 @@ private fun BackupMessageRecord.toRemoteGiftBadgeUpdate(): BackupGiftBadge? { ) } -private fun PollRecord.toRemotePollMessage(): Poll { +private fun PollRecord.toRemotePollMessage(reactionRecords: List?): Poll { return Poll( question = this.question, allowMultiple = this.allowMultipleVotes, @@ -1182,7 +1192,8 @@ private fun PollRecord.toRemotePollMessage(): Poll { ) } ) - } + }, + reactions = reactionRecords?.toRemote() ?: emptyList() ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt index 6abfb6deca..179db21afa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/importer/ChatItemArchiveImporter.kt @@ -693,6 +693,7 @@ class ChatItemArchiveImporter( this.stickerMessage != null -> this.stickerMessage.reactions this.viewOnceMessage != null -> this.viewOnceMessage.reactions this.directStoryReplyMessage != null -> this.directStoryReplyMessage.reactions + this.poll != null -> this.poll.reactions else -> emptyList() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/PollTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/PollTables.kt index b75e16f71f..e3e82703a7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/PollTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/PollTables.kt @@ -607,7 +607,7 @@ class PollTables(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT /** * Maps message ids to its associated poll (if it exists) */ - fun getPollsForMessages(messageIds: Collection): Map { + fun getPollsForMessages(messageIds: Collection, includePending: Boolean = true): Map { if (messageIds.isEmpty() || !Recipient.isSelfSet) { return emptyMap() } @@ -625,9 +625,9 @@ class PollTables(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT val (pendingAdds, pendingRemoves) = getPendingVotes(pollId) val pollOptions = getPollOptions(pollId).map { option -> val voters = pollVotes[option.key] ?: emptyList() - val voteState = if (pendingAdds.contains(option.key)) { + val voteState = if (includePending && pendingAdds.contains(option.key)) { VoteState.PENDING_ADD - } else if (pendingRemoves.contains(option.key)) { + } else if (includePending && pendingRemoves.contains(option.key)) { VoteState.PENDING_REMOVE } else if (voters.any { it.id == self }) { VoteState.ADDED diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt index cac927e098..707a999da6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt @@ -120,6 +120,8 @@ import kotlin.time.Duration.Companion.seconds object DataMessageProcessor { private const val BODY_RANGE_PROCESSING_LIMIT = 250 + private const val POLL_CHARACTER_LIMIT = 100 + private const val POLL_OPTIONS_LIMIT = 10 fun process( context: Context, @@ -1079,6 +1081,16 @@ object DataMessageProcessor { return null } + if (poll.question == null || poll.question!!.isEmpty() || poll.question!!.length > POLL_CHARACTER_LIMIT) { + warn(envelope.timestamp!!, "[handlePollCreate] Poll question is invalid.") + return null + } + + if (poll.options.isEmpty() || poll.options.size > POLL_OPTIONS_LIMIT || poll.options.any { it.isEmpty() || it.length > POLL_CHARACTER_LIMIT }) { + warn(envelope.timestamp!!, "[handlePollCreate] Poll option is invalid.") + return null + } + val pollMessage = IncomingMessage( type = MessageType.NORMAL, from = senderRecipient.id, diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index 306898922f..1d14e01826 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -824,6 +824,7 @@ message Poll { bool allowMultiple = 2; repeated PollOption options = 3; // At least two bool hasEnded = 4; + repeated Reaction reactions = 5; } message ChatUpdateMessage {