diff --git a/Signal/ConversationView/ConversationViewController+MessageActionsDelegate.swift b/Signal/ConversationView/ConversationViewController+MessageActionsDelegate.swift index fea6b3308e..6dc69e129e 100644 --- a/Signal/ConversationView/ConversationViewController+MessageActionsDelegate.swift +++ b/Signal/ConversationView/ConversationViewController+MessageActionsDelegate.swift @@ -318,7 +318,10 @@ extension ConversationViewController: MessageActionsDelegate { let sentTimestamp = Date.ows_millisecondTimestamp() if let _pinMessage = pinMessage as? OutgoingPinMessage { - let expiresAtMs: UInt64? = _pinMessage.pinDurationSeconds > 0 ? Date.ows_millisecondTimestamp() + UInt64(_pinMessage.pinDurationSeconds * 1000) : nil + var expiresAtMs: UInt64? + if _pinMessage.pinDurationSeconds > 0 { + expiresAtMs = Date.ows_millisecondTimestamp() + UInt64(_pinMessage.pinDurationSeconds) * 1000 + } pinnedMessageManager.applyPinMessageChangeToLocalState( targetTimestamp: _pinMessage.targetMessageTimestamp, diff --git a/SignalServiceKit/Backups/Archiving/Archivers/BackupArchive+Errors.swift b/SignalServiceKit/Backups/Archiving/Archivers/BackupArchive+Errors.swift index c153e4f8b0..52f9da6003 100644 --- a/SignalServiceKit/Backups/Archiving/Archivers/BackupArchive+Errors.swift +++ b/SignalServiceKit/Backups/Archiving/Archivers/BackupArchive+Errors.swift @@ -254,6 +254,9 @@ extension BackupArchive { /// A poll vote recipient id was not found case pollVoteAuthorSignalRecipientIdMissing + /// A poll vote count was not a valid Int32 value + case pollVoteCountInvalid + /// Author Aci for end poll message was invalid case endPollUpdateInvalidAuthorAci @@ -381,6 +384,7 @@ extension BackupArchive { .invalidPollVoteRecordDatabaseRow, .pollMessageMissingQuestionBody, .pollVoteAuthorSignalRecipientIdMissing, + .pollVoteCountInvalid, .endPollUpdateInvalidAuthorAci, .pollEndMissingQuestion, .pollEndMissingPersistableData, @@ -455,6 +459,7 @@ extension BackupArchive { .invalidPollVoteRecordDatabaseRow, .pollMessageMissingQuestionBody, .pollVoteAuthorSignalRecipientIdMissing, + .pollVoteCountInvalid, .endPollUpdateInvalidAuthorAci, .pollEndMissingQuestion, .pollEndMissingPersistableData, @@ -838,6 +843,9 @@ extension BackupArchive { /// We expect all authors to have an associated latest vote count, but there wasn't case noPollVoteCountForAuthor + /// Vote count was not a valid Int32 value + case invalidPollVoteCount + /// The pin message author had an invalid non-contact Address case pinMessageAuthorNotContact @@ -1009,6 +1017,7 @@ extension BackupArchive { .pollVoteAuthorNotContact, .pollVoteCountRepeated, .noPollVoteCountForAuthor, + .invalidPollVoteCount, .pinMessageAuthorNotContact, .invalidNumberOfPinnedMessages, .sentTimestampOverflowedLocalType, @@ -1124,6 +1133,7 @@ extension BackupArchive { .pollVoteAuthorNotContact, .pollVoteCountRepeated, .noPollVoteCountForAuthor, + .invalidPollVoteCount, .pinMessageAuthorNotContact, .invalidNumberOfPinnedMessages, .sentTimestampOverflowedLocalType, diff --git a/SignalServiceKit/Backups/Archiving/Archivers/ChatItem/BackupArchivePollArchiver.swift b/SignalServiceKit/Backups/Archiving/Archivers/ChatItem/BackupArchivePollArchiver.swift index dcaa3c2050..bb1594c544 100644 --- a/SignalServiceKit/Backups/Archiving/Archivers/ChatItem/BackupArchivePollArchiver.swift +++ b/SignalServiceKit/Backups/Archiving/Archivers/ChatItem/BackupArchivePollArchiver.swift @@ -59,8 +59,12 @@ class BackupArchivePollArchiver: BackupArchiveProtoStreamWriter { return .messageFailure([.archiveFrameError(.pollVoteAuthorSignalRecipientIdMissing, BackupArchive.InteractionUniqueId(interaction: message))]) } + guard vote.voteCount >= 0 else { + return .messageFailure([.archiveFrameError(.pollVoteCountInvalid, BackupArchive.InteractionUniqueId(interaction: message))]) + } + pollVoteProto.voterID = voterId.value - pollVoteProto.voteCount = vote.voteCount + pollVoteProto.voteCount = UInt32(vote.voteCount) pollOptionProto.votes.append(pollVoteProto) } pollProto.options.append(pollOptionProto) diff --git a/SignalServiceKit/Backups/Archiving/Archivers/ChatItem/BackupArchiveTSMessageContentsArchiver.swift b/SignalServiceKit/Backups/Archiving/Archivers/ChatItem/BackupArchiveTSMessageContentsArchiver.swift index 23b39f29ef..b2ea36d99a 100644 --- a/SignalServiceKit/Backups/Archiving/Archivers/ChatItem/BackupArchiveTSMessageContentsArchiver.swift +++ b/SignalServiceKit/Backups/Archiving/Archivers/ChatItem/BackupArchiveTSMessageContentsArchiver.swift @@ -2160,7 +2160,16 @@ class BackupArchiveTSMessageContentsArchiver: BackupArchiveProtoStreamWriter { )] continue } - votes.append(BackupsPollVote(voteAuthorId: voteAuthorId, voteCount: voteProto.voteCount)) + + guard voteProto.voteCount <= Int32.max else { + partialErrors += [.restoreFrameError( + .invalidProtoData(.invalidPollVoteCount), + chatItemId, + )] + continue + } + + votes.append(BackupsPollVote(voteAuthorId: voteAuthorId, voteCount: Int32(voteProto.voteCount))) } options.append(BackupsPollOption(text: optionProto.option, votes: votes)) } diff --git a/SignalServiceKit/Messages/Interactions/PinnedMessages/OutgoingPinMessage.swift b/SignalServiceKit/Messages/Interactions/PinnedMessages/OutgoingPinMessage.swift index 19ecd8afae..1fb43ef3fa 100644 --- a/SignalServiceKit/Messages/Interactions/PinnedMessages/OutgoingPinMessage.swift +++ b/SignalServiceKit/Messages/Interactions/PinnedMessages/OutgoingPinMessage.swift @@ -17,7 +17,7 @@ public class OutgoingPinMessage: TransientOutgoingMessage { guard let pinDurationSeconds = coder.decodeObject(of: NSNumber.self, forKey: "pinDurationSeconds") else { return nil } - self.pinDurationSeconds = pinDurationSeconds.uint32Value + self.pinDurationSeconds = pinDurationSeconds.int32Value guard let targetMessageAuthorAciBinary = coder.decodeObject(of: NSData.self, forKey: "targetMessageAuthorAciBinary") as Data?, let targetMessageAuthorAci = try? Aci.parseFrom(serviceIdBinary: targetMessageAuthorAciBinary) @@ -62,14 +62,14 @@ public class OutgoingPinMessage: TransientOutgoingMessage { public let targetMessageTimestamp: UInt64 public let targetMessageAuthorAci: Aci - public let pinDurationSeconds: UInt32 + public let pinDurationSeconds: Int32 private let pinDurationForever: Bool public init( thread: TSThread, targetMessageTimestamp: UInt64, targetMessageAuthorAciBinary: Aci, - pinDurationSeconds: UInt32, + pinDurationSeconds: Int32, pinDurationForever: Bool, messageExpiresInSeconds: UInt32, tx: DBReadTransaction, @@ -123,7 +123,7 @@ public class OutgoingPinMessage: TransientOutgoingMessage { pinMessageBuilder.setTargetAuthorAciBinary(targetMessageAuthorAci.serviceIdBinary) if pinDurationSeconds > 0 { - pinMessageBuilder.setPinDurationSeconds(pinDurationSeconds) + pinMessageBuilder.setPinDurationSeconds(UInt32(pinDurationSeconds)) } else if pinDurationForever { pinMessageBuilder.setPinDurationForever(pinDurationForever) } @@ -138,7 +138,11 @@ public class OutgoingPinMessage: TransientOutgoingMessage { override public func updateWithSendSuccess(tx: DBWriteTransaction) { let pinnedMessageManager = DependenciesBridge.shared.pinnedMessageManager - let expiresAtMs: UInt64? = pinDurationSeconds > 0 ? Date.ows_millisecondTimestamp() + UInt64(pinDurationSeconds * 1000) : nil + var expiresAtMs: UInt64? + if pinDurationSeconds > 0 { + let pinDurationMilliseconds = UInt64(pinDurationSeconds) * 1000 + expiresAtMs = Date.ows_millisecondTimestamp() + pinDurationMilliseconds + } pinnedMessageManager.applyPinMessageChangeToLocalState( targetTimestamp: targetMessageTimestamp, diff --git a/SignalServiceKit/Messages/Interactions/PinnedMessages/PinnedMessageManager.swift b/SignalServiceKit/Messages/Interactions/PinnedMessages/PinnedMessageManager.swift index b5016aeb8e..596a7321b0 100644 --- a/SignalServiceKit/Messages/Interactions/PinnedMessages/PinnedMessageManager.swift +++ b/SignalServiceKit/Messages/Interactions/PinnedMessages/PinnedMessageManager.swift @@ -71,7 +71,7 @@ public class PinnedMessageManager { throw OWSAssertionError("Invalid timestamp.") } - guard pinMessageProto.pinDurationSeconds < Int32.max else { + guard pinMessageProto.pinDurationSeconds <= Int32.max else { throw OWSAssertionError("Invalid timestamp.") } } @@ -416,9 +416,9 @@ public class PinnedMessageManager { return nil } - var pinDurationSeconds: UInt32? + var pinDurationSeconds: Int32? if let expiresAt { - pinDurationSeconds = UInt32(expiresAt) + pinDurationSeconds = Int32(expiresAt) } return OutgoingPinMessage( diff --git a/SignalServiceKit/Messages/Interactions/Polls/OutgoingPollVote.swift b/SignalServiceKit/Messages/Interactions/Polls/OutgoingPollVote.swift index 2d3e162a7d..d324a698e0 100644 --- a/SignalServiceKit/Messages/Interactions/Polls/OutgoingPollVote.swift +++ b/SignalServiceKit/Messages/Interactions/Polls/OutgoingPollVote.swift @@ -24,7 +24,7 @@ public class OutgoingPollVoteMessage: TransientOutgoingMessage { guard let voteCount = coder.decodeObject(of: NSNumber.self, forKey: "voteCount") else { return nil } - self.voteCount = voteCount.uint32Value + self.voteCount = voteCount.int32Value guard let voteOptionIndexes = coder.decodeArrayOfObjects(ofClass: NSNumber.self, forKey: "voteOptionIndexes") else { return nil } @@ -63,14 +63,14 @@ public class OutgoingPollVoteMessage: TransientOutgoingMessage { let targetPollTimestamp: UInt64 let targetPollAuthorAci: Aci let voteOptionIndexes: [UInt32] - let voteCount: UInt32 + let voteCount: Int32 public init( thread: TSThread, targetPollTimestamp: UInt64, targetPollAuthorAci: Aci, voteOptionIndexes: [UInt32], - voteCount: UInt32, + voteCount: Int32, tx: DBReadTransaction, ) { self.targetPollTimestamp = targetPollTimestamp @@ -110,7 +110,7 @@ public class OutgoingPollVoteMessage: TransientOutgoingMessage { pollVoteBuilder.setOptionIndexes(voteOptionIndexes) - pollVoteBuilder.setVoteCount(voteCount) + pollVoteBuilder.setVoteCount(UInt32(clamping: voteCount)) dataMessageBuilder.setPollVote( pollVoteBuilder.buildInfallibly(), @@ -179,7 +179,7 @@ public class OutgoingPollVoteMessage: TransientOutgoingMessage { } try PollStore().revertVoteCount( - voteCount: Int32(voteCount), + voteCount: voteCount, interactionId: interactionId, voteAuthorId: localRecipientId, transaction: tx, diff --git a/SignalServiceKit/Messages/Interactions/Polls/PollMessageManager.swift b/SignalServiceKit/Messages/Interactions/Polls/PollMessageManager.swift index 66ffc3cbe7..dded1d9ea8 100644 --- a/SignalServiceKit/Messages/Interactions/Polls/PollMessageManager.swift +++ b/SignalServiceKit/Messages/Interactions/Polls/PollMessageManager.swift @@ -318,7 +318,7 @@ public class PollMessageManager { targetPollTimestamp: UInt64, targetPollAuthorAci: Aci, optionIndexes: [OWSPoll.OptionIndex], - voteCount: UInt32, + voteCount: Int32, threadUniqueId: String, tx: DBWriteTransaction, ) throws { @@ -353,7 +353,7 @@ public class PollMessageManager { interactionId: interactionId, optionsVoted: optionIndexes, voteAuthorId: localAuthorRecipientId, - voteCount: voteCount, + voteCount: UInt32(clamping: voteCount), transaction: tx, ) @@ -442,7 +442,7 @@ public class PollMessageManager { targetPollTimestamp: pollInteraction.timestamp, targetPollAuthorAci: authorAci, voteOptionIndexes: optionIndexVotes, - voteCount: UInt32(newHighestVoteCount), + voteCount: newHighestVoteCount, tx: tx, ) } @@ -454,7 +454,7 @@ public struct BackupsPollData { public struct BackupsPollOption { public struct BackupsPollVote { let voteAuthorId: SignalRecipient.RowId - let voteCount: UInt32 + let voteCount: Int32 } let text: String @@ -527,7 +527,7 @@ extension PollMessageManager { var partialErrors = [BackupArchive.RestoreFrameError]() var votesByAuthorId: [Int64: [OWSPoll.OptionIndex]] = [:] - var voteCountByAuthorId: [Int64: UInt32] = [:] + var voteCountByAuthorId: [Int64: Int32] = [:] for (index, optionData) in pollBackupData.options.enumerated() { for vote in optionData.votes { @@ -560,7 +560,7 @@ extension PollMessageManager { interactionId: interactionId, optionsVoted: optionIndices, voteAuthorId: voteAuthorId, - voteCount: voteCount, + voteCount: UInt32(clamping: voteCount), transaction: tx, ) } catch { diff --git a/SignalServiceKit/Messages/Interactions/Polls/PollStore.swift b/SignalServiceKit/Messages/Interactions/Polls/PollStore.swift index 2434005736..c65303ecec 100644 --- a/SignalServiceKit/Messages/Interactions/Polls/PollStore.swift +++ b/SignalServiceKit/Messages/Interactions/Polls/PollStore.swift @@ -384,13 +384,20 @@ public class PollStore { } // Include pending here so we don't reuse an already-sent but not-yet-delivered vote count. - let newHighestVoteCount = try highestVoteCount( + + let highestVoteCount = try highestVoteCount( pollId: pollId, voteAuthorId: localRecipientId, includePending: true, transaction: transaction, - ) + 1 + ) + guard highestVoteCount < Int32.max else { + Logger.error("Failed to apply pending vote, highestVoteCount is too large") + return nil + } + + let newHighestVoteCount = highestVoteCount + 1 guard let optionId = try voteOptionIds( from: [optionIndex], @@ -569,7 +576,7 @@ extension PollStore { var votes: [BackupsPollData.BackupsPollOption.BackupsPollVote] = [] for voteRow in optionIdToVotes[optionId] ?? [] { if voteRow.voteState == .vote { - votes.append(BackupsPollData.BackupsPollOption.BackupsPollVote(voteAuthorId: voteRow.voteAuthorId, voteCount: UInt32(voteRow.voteCount))) + votes.append(BackupsPollData.BackupsPollOption.BackupsPollVote(voteAuthorId: voteRow.voteAuthorId, voteCount: voteRow.voteCount)) } } optionData.append(BackupsPollData.BackupsPollOption(text: optionRow.option, votes: votes))