Clean up vote count & pin expiry parsing

This commit is contained in:
kate-signal 2026-05-12 14:38:43 -04:00 committed by GitHub
parent c68f334581
commit ffae2627d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 62 additions and 25 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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))
}

View File

@ -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,

View File

@ -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(

View File

@ -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,

View File

@ -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<BackupArchive.ChatItemId>]()
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 {

View File

@ -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))