Ignore or use fallback for unrecognized backup enums/oneOfs for forwards compatibility

This commit is contained in:
Harry 2025-01-28 15:22:04 -08:00 committed by GitHub
parent 9e4ec6ac1f
commit 9fd8d3e8be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 470 additions and 494 deletions

View File

@ -216,10 +216,9 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
let storiesDisabled = storyManager.areStoriesEnabled(tx: context.tx).negated
let hasSeenGroupStoryEducationSheet = systemStoryManager.hasSeenGroupStoryEducationSheet(tx: context.tx)
let hasCompletedUsernameOnboarding = usernameEducationManager.shouldShowUsernameEducation(tx: context.tx).negated
let phoneNumberSharingMode: BackupProto_AccountData.PhoneNumberSharingMode = switch udManager.phoneNumberSharingMode(tx: context.tx) {
let phoneNumberSharingMode: BackupProto_AccountData.PhoneNumberSharingMode = switch udManager.phoneNumberSharingMode(tx: context.tx).orDefault {
case .everybody: .everybody
case .nobody: .nobody
case .none: .unknown
}
// Populate the proto with the settings
@ -377,6 +376,8 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
switch customChatColorsResult {
case .success:
break
case .unrecognizedEnum:
return customChatColorsResult
case .partialRestore(let errors):
partialErrors.append(contentsOf: errors)
case .failure(let errors):
@ -399,6 +400,8 @@ public class MessageBackupAccountDataArchiverImpl: MessageBackupAccountDataArchi
switch defaultChatStyleResult {
case .success:
break
case .unrecognizedEnum:
return defaultChatStyleResult
case .partialRestore(let errors):
partialErrors.append(contentsOf: errors)
case .failure(let errors):

View File

@ -170,17 +170,7 @@ public class MessageBackupAdHocCallArchiverImpl: MessageBackupAdHocCallArchiver
switch adHocCall.state {
case .generic:
state = .generic
case .unknownState:
partialErrors.append(
.restoreFrameError(.invalidProtoData(.adHocCallUnknownState),
callId
))
state = .generic
case .UNRECOGNIZED:
partialErrors.append(
.restoreFrameError(.invalidProtoData(.adHocCallUnrecognizedState),
callId
))
case .unknownState, .UNRECOGNIZED:
state = .generic
}

View File

@ -502,6 +502,8 @@ public class MessageBackupChatArchiverImpl: MessageBackupChatArchiver {
switch chatStyleResult {
case .success:
break
case .unrecognizedEnum:
return chatStyleResult
case .partialRestore(let errors):
partialErrors.append(contentsOf: errors)
case .failure(let errors):

View File

@ -119,10 +119,7 @@ public class MessageBackupChatStyleArchiver: MessageBackupProtoArchiver {
let colorOrGradientSetting: ColorOrGradientSetting
switch chatColorProto.color {
case .none:
partialErrors.append(.restoreFrameError(
.invalidProtoData(.unrecognizedCustomChatStyleColor),
.forCustomChatColorError(chatColorId: customChatColorId)
))
// Fallback to default (skip this chat color)
continue
case .solid(let colorARGBHex):
colorOrGradientSetting = .solidColor(
@ -222,9 +219,10 @@ public class MessageBackupChatStyleArchiver: MessageBackupProtoArchiver {
if let wallpaper = wallpaperStore.fetchWallpaper(for: thread?.tsThread.uniqueId, tx: context.tx) {
var protoWallpaper: BackupProto_ChatStyle.OneOf_Wallpaper?
if let preset = wallpaper.asBackupProto() {
switch wallpaper.asBackupProto() {
case .wallpaperPreset(let preset):
protoWallpaper = .wallpaperPreset(preset)
} else if wallpaper == .photo {
case .photo:
switch self.archiveWallpaperAttachment(
thread: thread,
errorId: errorId,
@ -238,11 +236,6 @@ public class MessageBackupChatStyleArchiver: MessageBackupProtoArchiver {
case .failure(let error):
return .failure(error)
}
} else {
return .failure(.archiveFrameError(
.unknownWallpaper,
errorId
))
}
if let protoWallpaper {
@ -347,10 +340,8 @@ public class MessageBackupChatStyleArchiver: MessageBackupProtoArchiver {
break
case .bubbleColorPreset(let bubbleColorPreset):
guard let palette = bubbleColorPreset.asPaletteChatColor() else {
return .failure([.restoreFrameError(
.invalidProtoData(.unrecognizedChatStyleBubbleColorPreset),
errorId
)])
// If we can't recognize the preset, use auto (skip)
break
}
chatColorSettingStore.setChatColorSetting(
ChatColorSetting.builtIn(palette),
@ -404,10 +395,9 @@ public class MessageBackupChatStyleArchiver: MessageBackupProtoArchiver {
break
case .wallpaperPreset(let wallpaperPreset):
guard let wallpaper = wallpaperPreset.asWallpaper() else {
return .failure([.restoreFrameError(
.invalidProtoData(.unrecognizedChatStyleWallpaperPreset),
errorId
)])
// If we can't recognize the preset enum,
// leave the wallpaper unset.
break
}
wallpaperStore.setWallpaperType(
wallpaper,
@ -429,6 +419,8 @@ public class MessageBackupChatStyleArchiver: MessageBackupProtoArchiver {
switch attachmentResult {
case .success:
break
case .unrecognizedEnum:
return attachmentResult
case .partialRestore(let errors):
partialErrors.append(contentsOf: errors)
case .failure(let errors):
@ -564,33 +556,38 @@ public class MessageBackupChatStyleArchiver: MessageBackupProtoArchiver {
fileprivate extension Wallpaper {
func asBackupProto() -> BackupProto_ChatStyle.WallpaperPreset? {
enum BackupRepresentation {
case wallpaperPreset(BackupProto_ChatStyle.WallpaperPreset)
case photo
}
func asBackupProto() -> BackupRepresentation {
// These don't match names exactly because...well nobody knows why
// the iOS enum names were defined this way. They're persisted to the
// db now, so we just gotta keep the mapping.
return switch self {
case .blush: .solidBlush
case .copper: .solidCopper
case .zorba: .solidDust
case .envy: .solidCeladon
case .sky: .solidPacific
case .wildBlueYonder: .solidFrost
case .lavender: .solidLilac
case .shocking: .solidPink
case .gray: .solidSilver
case .eden: .solidRainforest
case .violet: .solidNavy
case .eggplant: .solidEggplant
case .starshipGradient: .gradientSunset
case .woodsmokeGradient: .gradientNoir
case .coralGradient: .gradientHeatmap
case .ceruleanGradient: .gradientAqua
case .roseGradient: .gradientIridescent
case .aquamarineGradient: .gradientMonstera
case .tropicalGradient: .gradientBliss
case .blueGradient: .gradientSky
case .bisqueGradient: .gradientPeach
case .photo: nil
case .blush: .wallpaperPreset(.solidBlush)
case .copper: .wallpaperPreset(.solidCopper)
case .zorba: .wallpaperPreset(.solidDust)
case .envy: .wallpaperPreset(.solidCeladon)
case .sky: .wallpaperPreset(.solidPacific)
case .wildBlueYonder: .wallpaperPreset(.solidFrost)
case .lavender: .wallpaperPreset(.solidLilac)
case .shocking: .wallpaperPreset(.solidPink)
case .gray: .wallpaperPreset(.solidSilver)
case .eden: .wallpaperPreset(.solidRainforest)
case .violet: .wallpaperPreset(.solidNavy)
case .eggplant: .wallpaperPreset(.solidEggplant)
case .starshipGradient: .wallpaperPreset(.gradientSunset)
case .woodsmokeGradient: .wallpaperPreset(.gradientNoir)
case .coralGradient: .wallpaperPreset(.gradientHeatmap)
case .ceruleanGradient: .wallpaperPreset(.gradientAqua)
case .roseGradient: .wallpaperPreset(.gradientIridescent)
case .aquamarineGradient: .wallpaperPreset(.gradientMonstera)
case .tropicalGradient: .wallpaperPreset(.gradientBliss)
case .blueGradient: .wallpaperPreset(.gradientSky)
case .bisqueGradient: .wallpaperPreset(.gradientPeach)
case .photo: .photo
}
}
}

View File

@ -200,7 +200,9 @@ final class MessageBackupGroupCallArchiver {
let callStatus: CallRecord.CallStatus.GroupCallStatus
switch groupCall.state {
case .unknownState, .UNRECOGNIZED:
return .messageFailure([.restoreFrameError(.invalidProtoData(.groupCallUnrecognizedState), chatItem.id)])
// Fallback to generic
callDirection = .incoming
callStatus = .generic
case .generic:
callDirection = .incoming
callStatus = .generic

View File

@ -163,41 +163,40 @@ final class MessageBackupIndividualCallArchiver {
)])
}
let callInteractionType: RPRecentCallType
let callRecordDirection: CallRecord.CallDirection
let callRecordStatus: CallRecord.CallStatus.IndividualCallStatus
switch (individualCall.direction, individualCall.state) {
case (.unknownDirection, _), (.UNRECOGNIZED, _):
return .messageFailure([.restoreFrameError(.invalidProtoData(.individualCallUnrecognizedDirection), chatItem.id)])
case (_, .unknownState), (_, .UNRECOGNIZED):
return .messageFailure([.restoreFrameError(.invalidProtoData(.individualCallUnrecognizedState), chatItem.id)])
case (.incoming, .accepted):
callInteractionType = .incoming
switch individualCall.direction {
case .unknownDirection, .UNRECOGNIZED:
// Fallback to incoming
callRecordDirection = .incoming
case .incoming:
callRecordDirection = .incoming
case .outgoing:
callRecordDirection = .outgoing
}
let callInteractionType: RPRecentCallType
let callRecordStatus: CallRecord.CallStatus.IndividualCallStatus
switch (callRecordDirection, individualCall.state) {
case (.incoming, .accepted), (.incoming, .unknownState), (.incoming, .UNRECOGNIZED):
callInteractionType = .incoming
callRecordStatus = .accepted
case (.incoming, .notAccepted):
callInteractionType = .incomingDeclined
callRecordDirection = .incoming
callRecordStatus = .notAccepted
case (.incoming, .missed):
callInteractionType = .incomingMissed
callRecordDirection = .incoming
callRecordStatus = .incomingMissed
case (.incoming, .missedNotificationProfile):
callInteractionType = .incomingMissedBecauseOfDoNotDisturb
callRecordDirection = .incoming
callRecordStatus = .incomingMissed
case (.outgoing, .accepted):
case (.outgoing, .accepted), (.outgoing, .unknownState), (.outgoing, .UNRECOGNIZED):
callInteractionType = .outgoing
callRecordDirection = .outgoing
callRecordStatus = .accepted
case (.outgoing, .notAccepted):
callInteractionType = .outgoingIncomplete
callRecordDirection = .outgoing
callRecordStatus = .notAccepted
case (.outgoing, .missed), (.outgoing, .missedNotificationProfile):
callInteractionType = .outgoingMissed
callRecordDirection = .outgoing
callRecordStatus = .notAccepted
}
@ -211,7 +210,9 @@ final class MessageBackupIndividualCallArchiver {
callInteractionOfferType = .video
callRecordType = .videoCall
case .unknownType, .UNRECOGNIZED:
return .messageFailure([.restoreFrameError(.invalidProtoData(.individualCallUnrecognizedType), chatItem.id)])
// Fallback to audio
callInteractionOfferType = .audio
callRecordType = .audioCall
}
let callerAci: Aci?

View File

@ -208,10 +208,12 @@ final class MessageBackupGroupUpdateMessageArchiver {
partialErrors: &partialErrors,
chatItemId: chatItem.id
)
guard var persistableUpdates =
result.unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
var persistableUpdates: [PersistableGroupUpdateItem]
switch result.bubbleUp(Void.self, partialErrors: &partialErrors) {
case .continue(let component):
persistableUpdates = component
case .bubbleUpError(let error):
return error
}
guard persistableUpdates.isEmpty.negated else {
@ -250,10 +252,9 @@ final class MessageBackupGroupUpdateMessageArchiver {
)
guard let directionalDetails = chatItem.directionalDetails else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.chatItemMissingDirectionalDetails),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
do {

View File

@ -27,10 +27,11 @@ final class MessageBackupGroupUpdateProtoToSwiftConverter {
localUserAci: localUserAci,
chatItemId: chatItemId
)
if let persistableItems = result.unwrap(partialErrors: &partialErrors) {
persistableUpdates.append(contentsOf: persistableItems)
} else {
return .messageFailure(partialErrors)
switch result.bubbleUp([PersistableGroupUpdateItem].self, partialErrors: &partialErrors) {
case .continue(let component):
persistableUpdates.append(contentsOf: component)
case .bubbleUpError(let error):
return error
}
}
return .success(persistableUpdates)
@ -98,7 +99,8 @@ final class MessageBackupGroupUpdateProtoToSwiftConverter {
switch groupUpdate.update {
case nil:
return .messageFailure([.restoreFrameError(.invalidProtoData(.unrecognizedGroupUpdate), chatItemId)])
// Fallback to a generic update.
return .success([.genericUpdateByUnknownUser])
case .genericGroupUpdate(let proto):
switch unwrapOptionalAci(proto, \.updaterAci) {
case .unknown:

View File

@ -220,10 +220,9 @@ final class MessageBackupChatUpdateMessageArchiver: MessageBackupProtoArchiver {
switch chatUpdateMessage.update {
case nil:
return .messageFailure([.restoreFrameError(
.invalidProtoData(.emptyChatUpdateMessage),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatUpdateMessage.OneOf_Update.self
))
case .groupChange(let groupChangeChatUpdateProto):
return groupUpdateMessageArchiver.restoreGroupUpdate(
groupChangeChatUpdateProto,

View File

@ -192,7 +192,9 @@ final class MessageBackupExpirationTimerChatUpdateArchiver {
)
guard let directionalDetails = chatItem.directionalDetails else {
return invalidProtoData(.chatItemMissingDirectionalDetails)
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
do {

View File

@ -88,16 +88,12 @@ final class MessageBackupLearnedProfileChatUpdateArchiver {
)])
}
guard let previousName = learnedProfileUpdateProto.previousName else {
return invalidProtoData(.learnedProfileUpdateMissingPreviousName)
}
guard case .contact(let contactThread) = chatThread.threadType else {
return invalidProtoData(.learnedProfileUpdateNotFromContact)
}
let displayNameBefore: TSInfoMessage.DisplayNameBeforeLearningProfileName
switch previousName {
switch learnedProfileUpdateProto.previousName {
case .e164(let uintValue):
guard let e164 = E164(uintValue) else {
return invalidProtoData(.invalidE164(protoClass: BackupProto_LearnedProfileChatUpdate.self))
@ -106,6 +102,9 @@ final class MessageBackupLearnedProfileChatUpdateArchiver {
displayNameBefore = .phoneNumber(e164.stringValue)
case .username(let username):
displayNameBefore = .username(username)
case nil:
// This isn't great, but we just use an empty username.
displayNameBefore = .username("")
}
let learnedProfileKeyInfoMessage: TSInfoMessage = .makeForLearnedProfileName(
@ -115,10 +114,9 @@ final class MessageBackupLearnedProfileChatUpdateArchiver {
)
guard let directionalDetails = chatItem.directionalDetails else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.chatItemMissingDirectionalDetails),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
do {

View File

@ -112,10 +112,9 @@ final class MessageBackupProfileChangeChatUpdateArchiver {
)
guard let directionalDetails = chatItem.directionalDetails else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.chatItemMissingDirectionalDetails),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
do {

View File

@ -106,10 +106,9 @@ final class MessageBackupSessionSwitchoverChatUpdateArchiver {
)
guard let directionalDetails = chatItem.directionalDetails else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.chatItemMissingDirectionalDetails),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
do {

View File

@ -336,7 +336,9 @@ final class MessageBackupSimpleChatUpdateArchiver {
switch simpleChatUpdate.type {
case .unknown, .UNRECOGNIZED:
return invalidProtoData(.unrecognizedSimpleChatUpdate)
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_SimpleChatUpdate.TypeEnum.self
))
case .joinedSignal:
simpleChatUpdateInteraction = .simpleInfoMessage(.userJoinedSignal)
case .identityUpdate:
@ -527,10 +529,9 @@ final class MessageBackupSimpleChatUpdateArchiver {
}
guard let directionalDetails = chatItem.directionalDetails else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.chatItemMissingDirectionalDetails),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
switch simpleChatUpdateInteraction {

View File

@ -106,10 +106,9 @@ final class MessageBackupThreadMergeChatUpdateArchiver {
)
guard let directionalDetails = chatItem.directionalDetails else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.chatItemMissingDirectionalDetails),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
do {

View File

@ -310,6 +310,9 @@ extension MessageBackup {
enum RestoreInteractionResult<Component> {
case success(Component)
/// There was an unrecognized enum (or oneOf) for which we skip restoring this frame
/// but we should proceed restoring other frames.
case unrecognizedEnum(UnrecognizedEnumError)
/// Some portion of the interaction failed to restore, but we can still restore the rest of it.
/// e.g. a reaction failed to parse, so we just drop that reaction.
case partialRestore(Component, [RestoreFrameError<ChatItemId>])
@ -386,9 +389,14 @@ extension MessageBackup.ArchiveInteractionResult {
extension MessageBackup.RestoreInteractionResult {
/// Returns nil for ``RestoreInteractionResult.messageFailure``, otherwise
/// returns the restored component. Regardless, accumulates any errors so that the caller
/// can return the passed in ``partialErrors`` array in the final result.
enum BubbleUp<ComponentType, ErrorComponentType> {
case `continue`(ComponentType)
case bubbleUpError(MessageBackup.RestoreInteractionResult<ErrorComponentType>)
}
/// Make it easier to "bubble up" an error case of ``RestoreInteractionResult`` thrown deeper in the call stack.
/// Basically, collapses all the cases that should just be bubbled up to the caller (error cases) into an easily returnable case,
/// ditto for the success or partial success cases, and handles updating partialErrors along the way.
///
/// Concretely, turns this:
///
@ -398,28 +406,38 @@ extension MessageBackup.RestoreInteractionResult {
/// case .partialRestore(let value, let errors):
/// myVar = value
/// partialErrors.append(contentsOf: errors)
/// case messageFailure(let errors)
/// partialErrors.append(contentsOf: errors)
/// return .messageFailure(partialErrors)
/// case someFailureCase(let someErrorOrErrors)
/// let coalescedErrorOrErrors = partialErrors.coalesceSomehow(with: someErrorOrErrors)
/// // Just bubble up the error after coalescing
/// return .someFailureCase(coalescedErrorOrErrors)
/// // ...
/// // The same for every other error case that should be bubbled up
/// // ...
/// }
///
/// Into this:
///
/// guard let myVar = someResult.unwrap(&partialErrors) else {
/// return .messageFailure(partialErrors)
/// switch someResult.bubbleUp(&partialErrors) {
/// case .success(let value):
/// myVar = value
/// case .bubbleUpError(let error):
/// return error
/// }
func unwrap(
func bubbleUp<ErrorComponentType>(
_ errorComponentType: ErrorComponentType.Type = Component.self,
partialErrors: inout [MessageBackup.RestoreFrameError<MessageBackup.ChatItemId>]
) -> Component? {
) -> BubbleUp<Component, ErrorComponentType> {
switch self {
case .success(let component):
return component
return .continue(component)
case .unrecognizedEnum(let error):
return .bubbleUpError(.unrecognizedEnum(error))
case .partialRestore(let component, let errors):
partialErrors.append(contentsOf: errors)
return component
return .continue(component)
case .messageFailure(let errors):
partialErrors.append(contentsOf: errors)
return nil
return .bubbleUpError(.messageFailure(partialErrors))
}
}
}
@ -434,6 +452,8 @@ extension MessageBackup.RestoreInteractionResult where Component == Void {
switch (self, other) {
case (.success, .success):
return .success(())
case (.unrecognizedEnum(let error), _), (_, .unrecognizedEnum(let error)):
return .unrecognizedEnum(error)
case let (.messageFailure(lhs), .messageFailure(rhs)):
return .messageFailure(lhs + rhs)
case let (.partialRestore(_, lhs), .partialRestore(_, rhs)):
@ -453,24 +473,3 @@ extension MessageBackup.RestoreInteractionResult where Component == Void {
}
}
}
extension MessageBackup.RestoreInteractionResult where Component == Void {
/// Returns false for ``RestoreInteractionResult.messageFailure``, otherwise
/// returns true. Regardless, accumulates any errors so that the caller
/// can return the passed in ``partialErrors`` array in the final result.
func unwrap(
partialErrors: inout [MessageBackup.RestoreFrameError<MessageBackup.ChatItemId>]
) -> Bool {
switch self {
case .success:
return true
case .partialRestore(_, let errors):
partialErrors.append(contentsOf: errors)
return true
case .messageFailure(let errors):
partialErrors.append(contentsOf: errors)
return false
}
}
}

View File

@ -357,7 +357,9 @@ public class MessageBackupChatItemArchiverImpl: MessageBackupChatItemArchiver {
let restoreInteractionResult: MessageBackup.RestoreInteractionResult<Void>
switch chatItem.directionalDetails {
case nil:
return restoreFrameError(.invalidProtoData(.chatItemMissingDirectionalDetails))
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
case .incoming:
restoreInteractionResult = incomingMessageArchiver.restoreIncomingChatItem(
chatItem,
@ -373,7 +375,9 @@ public class MessageBackupChatItemArchiverImpl: MessageBackupChatItemArchiver {
case .directionless:
switch chatItem.item {
case nil:
return restoreFrameError(.invalidProtoData(.chatItemMissingItem))
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_Item.self
))
case
.standardMessage,
.contactMessage,
@ -396,6 +400,8 @@ public class MessageBackupChatItemArchiverImpl: MessageBackupChatItemArchiver {
switch restoreInteractionResult {
case .success:
return .success
case .unrecognizedEnum(let error):
return .unrecognizedEnum(error)
case .partialRestore(_, let errors):
return .partialRestore(errors)
case .messageFailure(let errors):

View File

@ -251,35 +251,53 @@ internal class MessageBackupContactAttachmentArchiver: MessageBackupProtoArchive
let contact = OWSContact(name: contactName)
contact.phoneNumbers = contactProto.number.compactMap { phoneNumberProto in
return self
for phoneNumberProto in contactProto.number {
switch self
.restoreContactPhoneNumber(
proto: phoneNumberProto,
chatItemId: chatItemId
)
.unwrap(partialErrors: &partialErrors)
// Double unwrap; we will just drops nulls.
?? nil
.bubbleUp(OWSContact.self, partialErrors: &partialErrors)
{
case .continue(let phoneNumber):
if let phoneNumber {
contact.phoneNumbers.append(phoneNumber)
}
case .bubbleUpError(let error):
return error
}
}
contact.emails = contactProto.email.compactMap { emailProto in
return self
for emailProto in contactProto.email {
switch self
.restoreContactEmail(
proto: emailProto,
chatItemId: chatItemId
)
.unwrap(partialErrors: &partialErrors)
// Double unwrap; we will just drops nulls.
?? nil
.bubbleUp(OWSContact.self, partialErrors: &partialErrors)
{
case .continue(let email):
if let email {
contact.emails.append(email)
}
case .bubbleUpError(let error):
return error
}
}
contact.addresses = contactProto.address.compactMap { addressProto in
return self
for addressProto in contactProto.address {
switch self
.restoreContactAddress(
proto: addressProto,
chatItemId: chatItemId
)
.unwrap(partialErrors: &partialErrors)
// Double unwrap; we will just drops nulls.
?? nil
.bubbleUp(OWSContact.self, partialErrors: &partialErrors)
{
case .continue(let address):
if let address {
contact.addresses.append(address)
}
case .bubbleUpError(let error):
return error
}
}
// Note: the contact attachment's avatar is restored later (if any is set).
@ -313,10 +331,7 @@ internal class MessageBackupContactAttachmentArchiver: MessageBackupProtoArchive
case .custom:
type = .custom
case .unknown, .UNRECOGNIZED(_):
return .partialRestore(nil, [.restoreFrameError(
.invalidProtoData(.contactAttachmentPhoneNumberUnknownType),
chatItemId
)])
type = .home
}
return .success(OWSContactPhoneNumber(
@ -348,10 +363,7 @@ internal class MessageBackupContactAttachmentArchiver: MessageBackupProtoArchive
case .custom:
type = .custom
case .unknown, .UNRECOGNIZED(_):
return .partialRestore(nil, [.restoreFrameError(
.invalidProtoData(.contactAttachmentEmailUnknownType),
chatItemId
)])
type = .home
}
return .success(OWSContactEmail(
@ -374,10 +386,7 @@ internal class MessageBackupContactAttachmentArchiver: MessageBackupProtoArchive
case .custom:
type = .custom
case .unknown, .UNRECOGNIZED(_):
return .partialRestore(nil, [.restoreFrameError(
.invalidProtoData(.contactAttachmentAddressUnknownType),
chatItemId
)])
type = .home
}
let address = OWSContactAddress(

View File

@ -67,15 +67,19 @@ class MessageBackupTSIncomingMessageArchiver {
) -> MessageBackup.RestoreInteractionResult<Void> {
var partialErrors = [RestoreFrameError]()
guard
editHistoryArchiver.restoreMessageAndEditHistory(
switch editHistoryArchiver
.restoreMessageAndEditHistory(
topLevelChatItem,
chatThread: chatThread,
context: context,
builder: self
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(Void.self, partialErrors: &partialErrors)
{
case .continue:
break
case .bubbleUpError(let error):
return error
}
if partialErrors.isEmpty {
@ -222,21 +226,24 @@ extension MessageBackupTSIncomingMessageArchiver: MessageBackupTSMessageEditHist
context: MessageBackup.ChatItemRestoringContext
) -> MessageBackup.RestoreInteractionResult<EditHistoryMessageType> {
guard let chatItemItem = chatItem.item else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.chatItemMissingItem),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_Item.self
))
}
let incomingDetails: BackupProto_ChatItem.IncomingMessageDetails
switch chatItem.directionalDetails {
case .incoming(let _incomingDetails):
incomingDetails = _incomingDetails
case nil, .outgoing, .directionless:
case .outgoing, .directionless:
return .messageFailure([.restoreFrameError(
.invalidProtoData(.revisionOfIncomingMessageMissingIncomingDetails),
chatItem.id
)])
case nil:
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
let authorAci: Aci?
@ -303,15 +310,20 @@ extension MessageBackupTSIncomingMessageArchiver: MessageBackupTSMessageEditHist
var partialErrors = [RestoreFrameError]()
guard
let contents = contentsArchiver.restoreContents(
let contents: MessageBackup.RestoredMessageContents
switch contentsArchiver
.restoreContents(
chatItemItem,
chatItemId: chatItem.id,
chatThread: chatThread,
context: context
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(EditHistoryMessageType.self, partialErrors: &partialErrors)
{
case .continue(let component):
contents = component
case .bubbleUpError(let error):
return error
}
let message: TSIncomingMessage = {
@ -408,16 +420,20 @@ extension MessageBackupTSIncomingMessageArchiver: MessageBackupTSMessageEditHist
return .messageFailure(partialErrors + [.restoreFrameError(.databaseInsertionFailed(error), chatItem.id)])
}
guard
contentsArchiver.restoreDownstreamObjects(
switch contentsArchiver
.restoreDownstreamObjects(
message: message,
thread: chatThread,
chatItemId: chatItem.id,
restoredContents: contents,
context: context
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(TSIncomingMessage.self, partialErrors: &partialErrors)
{
case .continue:
break
case .bubbleUpError(let error):
return error
}
if partialErrors.isEmpty {

View File

@ -1236,6 +1236,12 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
return nil
}
}()
guard let senderOrRecipientAci else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.paymentNotificationInGroup),
chatItemId
)])
}
let direction: ArchivedPayment.Direction
switch message {
@ -1249,19 +1255,12 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
chatItemId
)])
}
guard
let senderOrRecipientAci,
let archivedPayment = ArchivedPayment.fromBackup(
transaction,
senderOrRecipientAci: senderOrRecipientAci,
direction: direction,
interactionUniqueId: message.uniqueId
) else {
return .messageFailure([.restoreFrameError(
.invalidProtoData(.unrecognizedPaymentTransaction),
chatItemId
)])
}
let archivedPayment = ArchivedPayment.fromBackup(
transaction,
senderOrRecipientAci: senderOrRecipientAci,
direction: direction,
interactionUniqueId: message.uniqueId
)
do {
try archivedPaymentStore.insert(archivedPayment, tx: context.tx)
} catch {
@ -1331,17 +1330,24 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
let quotedMessage: TSQuotedMessage?
let quotedMessageThumbnail: BackupProto_MessageAttachment?
if standardMessage.hasQuote {
guard
let quoteResult = restoreQuote(
switch self
.restoreQuote(
standardMessage.quote,
chatItemId: chatItemId,
thread: chatThread,
context: context
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(
MessageBackup.RestoredMessageContents.self,
partialErrors: &partialErrors
)
{
case .continue(let component):
quotedMessage = component.0
quotedMessageThumbnail = component.1
case .bubbleUpError(let error):
return error
}
(quotedMessage, quotedMessageThumbnail) = quoteResult
} else {
quotedMessage = nil
quotedMessageThumbnail = nil
@ -1350,21 +1356,28 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
let linkPreview: OWSLinkPreview?
let linkPreviewAttachment: BackupProto_FilePointer?
if let linkPreviewProto = standardMessage.linkPreview.first {
guard
let linkPreviewResult = restoreLinkPreview(
switch self
.restoreLinkPreview(
linkPreviewProto,
standardMessage: standardMessage,
chatItemId: chatItemId,
context: context
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
}
if let linkPreviewResult {
(linkPreview, linkPreviewAttachment) = linkPreviewResult
} else {
linkPreview = nil
linkPreviewAttachment = nil
)
.bubbleUp(
MessageBackup.RestoredMessageContents.self,
partialErrors: &partialErrors
)
{
case .continue(let component):
if let component {
linkPreview = component.0
linkPreviewAttachment = component.1
} else {
linkPreview = nil
linkPreviewAttachment = nil
}
case .bubbleUpError(let error):
return error
}
} else {
linkPreview = nil
@ -1437,6 +1450,8 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
)),
partialErrors + messageBodyErrors
)
case .unrecognizedEnum(let error):
return .unrecognizedEnum(error)
case .messageFailure(let messageBodyErrors):
return .messageFailure(partialErrors + messageBodyErrors)
}
@ -1483,10 +1498,6 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
let swiftStyle: MessageBodyRanges.SingleStyle
switch protoBodyRangeStyle {
case .none, .UNRECOGNIZED:
partialErrors.append(.restoreFrameError(
.invalidProtoData(.unrecognizedBodyRangeStyle),
chatItemId
))
continue
case .bold:
swiftStyle = .bold
@ -1573,17 +1584,22 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
let quoteBody: MessageBody?
if quote.hasText {
guard
let bodyResult = restoreMessageBody(
switch self
.restoreMessageBody(
text: quote.text.body,
bodyRangeProtos: quote.text.bodyRanges,
chatItemId: chatItemId
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(
(TSQuotedMessage, BackupProto_MessageAttachment?).self,
partialErrors: &partialErrors
)
{
case .continue(let component):
quoteBody = component
case .bubbleUpError(let error):
return error
}
quoteBody = bodyResult
} else {
quoteBody = nil
}
@ -1724,8 +1740,17 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
contactAttachment,
chatItemId: chatItemId
)
guard let contact = contactResult.unwrap(partialErrors: &partialErrors) else {
return .messageFailure(partialErrors)
let contact: OWSContact
switch contactResult
.bubbleUp(
MessageBackup.RestoredMessageContents.self,
partialErrors: &partialErrors
)
{
case .continue(let component):
contact = component
case .bubbleUpError(let error):
return error
}
let avatar: BackupProto_FilePointer?
@ -1781,7 +1806,7 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
) -> RestoreInteractionResult<MessageBackup.RestoredMessageContents> {
let giftBadge: OWSGiftBadge
switch giftBadgeProto.state {
case .unopened:
case .unopened, .UNRECOGNIZED:
giftBadge = .restoreFromBackup(
receiptCredentialPresentation: giftBadgeProto.receiptCredentialPresentation,
redemptionState: .pending
@ -1805,11 +1830,6 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
receiptCredentialPresentation: nil,
redemptionState: .pending
)
case .UNRECOGNIZED(_):
return .messageFailure([.restoreFrameError(
.invalidProtoData(.unrecognizedGiftBadgeState),
chatItemId
)])
}
return .success(.giftBadge(MessageBackup.RestoredMessageContents.GiftBadge(
@ -1860,13 +1880,21 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
nil
}
guard
let messageBody = restoreMessageBody(
let messageBody: MessageBody?
switch self
.restoreMessageBody(
textReply.text,
chatItemId: chatItemId
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(
MessageBackup.RestoredMessageContents.self,
partialErrors: &partialErrors
)
{
case .continue(let component):
messageBody = component
case .bubbleUpError(let error):
return error
}
if let messageBody {
@ -1890,10 +1918,9 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver {
case .emoji(let string):
replyType = .emoji(string)
case .none:
return .messageFailure([.restoreFrameError(
.invalidProtoData(.directStoryReplyMessageUnknownType),
chatItemId
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_DirectStoryReplyMessage.OneOf_Reply.self
))
}
return .success(.storyReply(.init(
@ -1911,8 +1938,8 @@ private extension ArchivedPayment {
senderOrRecipientAci: Aci,
direction: Direction,
interactionUniqueId: String?
) -> ArchivedPayment? {
var archivedPayment: ArchivedPayment?
) -> ArchivedPayment {
var archivedPayment: ArchivedPayment
switch backup.status {
case .failure(let reason):
archivedPayment = ArchivedPayment(

View File

@ -279,16 +279,21 @@ final class MessageBackupTSMessageEditHistoryArchiver<MessageType: TSMessage>
) -> MessageBackup.RestoreInteractionResult<Void> {
var partialErrors = [RestoreFrameError]()
guard
let latestRevisionMessage = builder.restoreMessage(
let latestRevisionMessage: MessageType
switch builder
.restoreMessage(
topLevelChatItem,
isPastRevision: false,
hasPastRevisions: topLevelChatItem.revisions.count > 0,
chatThread: chatThread,
context: context
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(Void.self, partialErrors: &partialErrors)
{
case .continue(let component):
latestRevisionMessage = component
case .bubbleUpError(let error):
return error
}
var earlierRevisionMessages = [MessageType]()
@ -297,30 +302,39 @@ final class MessageBackupTSMessageEditHistoryArchiver<MessageType: TSMessage>
/// how we want to insert them. Older revisions should be inserted
/// before newer ones.
for revisionChatItem in topLevelChatItem.revisions {
guard
let earlierRevisionMessage = builder.restoreMessage(
let earlierRevisionMessage: MessageType
switch builder
.restoreMessage(
revisionChatItem,
isPastRevision: true,
hasPastRevisions: false, // Past revisions can't have their own past revisions!
chatThread: chatThread,
context: context
).unwrap(partialErrors: &partialErrors)
else {
)
.bubbleUp(Void.self, partialErrors: &partialErrors)
{
case .continue(let component):
earlierRevisionMessage = component
case .bubbleUpError(let error):
/// This means we won't attempt to restore any later revisions,
/// but we can't be confident they would have restored
/// successfully anyway.
return .messageFailure(partialErrors)
return error
}
earlierRevisionMessages.append(earlierRevisionMessage)
}
for earlierRevisionMessage in earlierRevisionMessages {
guard
let wasRead = earlierRevisionMessage.wasRead()
.unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
let wasRead: Bool
switch earlierRevisionMessage
.wasRead()
.bubbleUp(Void.self, partialErrors: &partialErrors)
{
case .continue(let component):
wasRead = component
case .bubbleUpError(let error):
return error
}
let editRecord = EditRecord(

View File

@ -67,15 +67,19 @@ class MessageBackupTSOutgoingMessageArchiver {
) -> MessageBackup.RestoreInteractionResult<Void> {
var partialErrors = [RestoreFrameError]()
guard
editHistoryArchiver.restoreMessageAndEditHistory(
switch editHistoryArchiver
.restoreMessageAndEditHistory(
topLevelChatItem,
chatThread: chatThread,
context: context,
builder: self
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(Void.self, partialErrors: &partialErrors)
{
case .continue:
break
case .bubbleUpError(let error):
return error
}
if partialErrors.isEmpty {
@ -317,35 +321,45 @@ extension MessageBackupTSOutgoingMessageArchiver: MessageBackupTSMessageEditHist
context: MessageBackup.ChatItemRestoringContext
) -> MessageBackup.RestoreInteractionResult<EditHistoryMessageType> {
guard let chatItemType = chatItem.item else {
// Unrecognized item type!
return .messageFailure([.restoreFrameError(
.invalidProtoData(.chatItemMissingItem),
chatItem.id
)])
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_Item.self
))
}
let outgoingDetails: BackupProto_ChatItem.OutgoingMessageDetails
switch chatItem.directionalDetails {
case .outgoing(let _outgoingDetails):
outgoingDetails = _outgoingDetails
case nil, .incoming, .directionless:
case .incoming, .directionless:
return .messageFailure([.restoreFrameError(
.invalidProtoData(.revisionOfOutgoingMessageMissingOutgoingDetails),
chatItem.id
)])
case nil:
return .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_ChatItem.OneOf_DirectionalDetails.self
))
}
var partialErrors = [RestoreFrameError]()
guard
let contents = contentsArchiver.restoreContents(
let contents: MessageBackup.RestoredMessageContents
switch contentsArchiver
.restoreContents(
chatItemType,
chatItemId: chatItem.id,
chatThread: chatThread,
context: context
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(
EditHistoryMessageType.self,
partialErrors: &partialErrors
)
{
case .continue(let component):
contents = component
case .bubbleUpError(let error):
return error
}
let editState: TSEditState = {
@ -359,29 +373,44 @@ extension MessageBackupTSOutgoingMessageArchiver: MessageBackupTSMessageEditHist
}
}()
guard
let outgoingMessage = restoreAndInsertOutgoingMessage(
let outgoingMessage: TSOutgoingMessage
switch self
.restoreAndInsertOutgoingMessage(
chatItem: chatItem,
contents: contents,
outgoingDetails: outgoingDetails,
editState: editState,
context: context,
chatThread: chatThread
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(
EditHistoryMessageType.self,
partialErrors: &partialErrors
)
{
case .continue(let component):
outgoingMessage = component
case .bubbleUpError(let error):
return error
}
guard
contentsArchiver.restoreDownstreamObjects(
switch contentsArchiver
.restoreDownstreamObjects(
message: outgoingMessage,
thread: chatThread,
chatItemId: chatItem.id,
restoredContents: contents,
context: context
).unwrap(partialErrors: &partialErrors)
else {
return .messageFailure(partialErrors)
)
.bubbleUp(
EditHistoryMessageType.self,
partialErrors: &partialErrors
)
{
case .continue:
break
case .bubbleUpError(let error):
return error
}
if partialErrors.isEmpty {
@ -578,8 +607,12 @@ extension MessageBackupTSOutgoingMessageArchiver: MessageBackupTSMessageEditHist
))
}()
guard let outgoingMessage: TSOutgoingMessage = outgoingMessageResult.unwrap(partialErrors: &partialErrors) else {
return .messageFailure(partialErrors)
let outgoingMessage: TSOutgoingMessage
switch outgoingMessageResult.bubbleUp(TSOutgoingMessage.self, partialErrors: &partialErrors) {
case .continue(let component):
outgoingMessage = component
case .bubbleUpError(let error):
return error
}
do {
@ -614,18 +647,13 @@ extension MessageBackupTSOutgoingMessageArchiver: MessageBackupTSMessageEditHist
partialErrors: inout [RestoreFrameError],
chatItemId: MessageBackup.ChatItemId
) -> TSOutgoingMessageRecipientState? {
guard let deliveryStatus = sendStatus.deliveryStatus else {
partialErrors.append(.restoreFrameError(
.invalidProtoData(.unrecognizedMessageSendStatus),
chatItemId
))
return nil
}
let recipientStatus: OWSOutgoingMessageRecipientStatus
var wasSentByUD: Bool = false
var errorCode: Int?
switch deliveryStatus {
switch sendStatus.deliveryStatus {
case nil:
// Fallback to pending
recipientStatus = .pending
case .pending(_):
recipientStatus = .pending
case .sent(let sent):

View File

@ -67,9 +67,6 @@ extension MessageBackup {
/// Custom chat colors should never have light/dark theme. The UI
/// disallows it and the proto cannot represent it.
case themedCustomChatColor
/// An unknown type of wallpaper was found that we couldn't translate to proto,
/// causing the wallpaper to be skipped.
case unknownWallpaper
/// An incoming message has an invalid or missing author address information,
/// causing the message to be skipped.
@ -218,9 +215,6 @@ extension MessageBackup {
/// Associated value provides the number of attachments.
case unviewedViewOnceMessageTooManyAttachments(Int)
/// Restrictions for a call link are unknown.
case callLinkRestrictionsUnknown
/// An ad hoc call's ``CallRecord/conversationId`` is not a
/// call link, which is illegal.
case adHocCallDoesNotHaveCallLinkAsConversationId
@ -291,7 +285,6 @@ extension MessageBackup {
.fileIOError,
.groupMasterKeyError,
.themedCustomChatColor,
.unknownWallpaper,
.unableToFetchRecipientIdentity,
.distributionListMissingDistributionId,
.distributionListHasDefaultViewMode,
@ -342,7 +335,6 @@ extension MessageBackup {
.unableToReadStoryContextAssociatedData,
.unviewedViewOnceMessageMissingAttachment,
.unviewedViewOnceMessageTooManyAttachments,
.callLinkRestrictionsUnknown,
.adHocCallDoesNotHaveCallLinkAsConversationId,
.invalidAdHocCallTimestamp,
.unexpectedRevisionsOnMessage:
@ -362,7 +354,6 @@ extension MessageBackup {
.fileIOError,
.groupMasterKeyError,
.themedCustomChatColor,
.unknownWallpaper,
.distributionListMissingDistributionId,
.distributionListHasDefaultViewMode,
.customDistributionListBlocklistViewMode,
@ -408,7 +399,6 @@ extension MessageBackup {
.unableToReadStoryContextAssociatedData,
.unviewedViewOnceMessageMissingAttachment,
.unviewedViewOnceMessageTooManyAttachments,
.callLinkRestrictionsUnknown,
.adHocCallDoesNotHaveCallLinkAsConversationId,
.invalidAdHocCallTimestamp,
.unexpectedRevisionsOnMessage:
@ -570,35 +560,16 @@ extension MessageBackup {
/// An invalid member (group, distribution list, etc) was specified as a distribution list member. Includes the offending proto
case invalidDistributionListMember(protoClass: Any.Type)
/// A ``BackupProto/Recipient`` with a missing destination.
case recipientMissingDestination
/// A ``BackupProto_Contact`` with unknown identityState.
case unknownContactIdentityState
/// A ``BackupProto/Contact`` with no aci, pni, or e164.
case contactWithoutIdentifiers
/// A ``BackupProto/Contact`` for the local user. This shouldn't exist.
case otherContactWithLocalIdentifiers
/// A ``BackupProto/Contact`` missing info as to whether or not
/// it is registered.
case contactWithoutRegistrationInfo
/// A ``BackupProto_ChatStyle/BubbleColorPreset`` had an unrecognized case.
case unrecognizedChatStyleBubbleColorPreset
/// Some custom chat color identifier being referenced was not present earlier in the backup file.
case customChatColorNotFound(CustomChatColorId)
/// A ``BackupProto_ChatStyle/CustomChatColor`` had an unrecognized color oneof.
case unrecognizedCustomChatStyleColor
/// A ``BackupProto_ChatStyle/Gradient`` had less than two colors.
case chatStyleGradientSingleOrNoColors
/// A ``BackupProto_ChatStyle/WallpaperPreset`` had an unrecognized case.
case unrecognizedChatStyleWallpaperPreset
/// A ``BackupProto/ChatItem`` was missing directional details.
case chatItemMissingDirectionalDetails
/// A ``BackupProto/ChatItem`` was missing its actual item.
case chatItemMissingItem
/// A directionless chat item was not an update message.
case directionlessChatItemNotUpdateMessage
/// A ``BackupProto/ChatItem`` has a missing or invalid dateSent.
@ -610,8 +581,6 @@ extension MessageBackup {
/// Outgoing message's `BackupProto_SendStatus` can only be for `BackupProto_Contacts`.
/// One in the backup was to a group, self recipient, or something else.
case outgoingNonContactMessageRecipient
/// A `BackupProto_SendStatus` had an unregonized `BackupProto_SendStatusStatus`.
case unrecognizedMessageSendStatus
/// `BackupProto_Reaction` must come from either an Aci or an E164.
/// One in the backup did not.
@ -624,8 +593,6 @@ extension MessageBackup {
case directStoryReplyMessageEmpty
/// A ``BackupProto_DirectStoryReplyMessage`` had an empty text body, but a long-text attachment was present.
case directStoryReplyMessageEmptyWithLongText
/// A ``BackupProto_DirectStoryReplyMessage/OneOf_Reply`` has an unknown case.
case directStoryReplyMessageUnknownType
/// A ``BackupProto_DirectStoryReplyMessage`` author didn't have an aci.
case directStoryReplyFromNonAci
/// A ``BackupProto_DirectStoryReplyMessage`` was in a group thread.
@ -635,9 +602,6 @@ extension MessageBackup {
/// message body (the body text must always be a prefix of the long text)
case longTextStandardMessageMissingBody
/// A `BackupProto_BodyRange` with a missing or unrecognized style.
case unrecognizedBodyRangeStyle
/// A quoted message had no body, attachment, gift badge, or other
/// content in its representation of the original being quoted.
case quotedMessageEmptyContent
@ -652,25 +616,16 @@ extension MessageBackup {
case contactMessageMissingContactAttachment
/// A ``BackupProto_ContactAttachment/Phone/value`` was missing or empty.
case contactAttachmentPhoneNumberMissingValue
/// A ``BackupProto_ContactAttachment/Phone/type`` was unknown.
case contactAttachmentPhoneNumberUnknownType
/// A ``BackupProto_ContactAttachment/Email/value`` was missing or empty.
case contactAttachmentEmailMissingValue
/// A ``BackupProto_ContactAttachment/Email/type`` was unknown.
case contactAttachmentEmailUnknownType
/// A ``BackupProto_ContactAttachment/PostalAddress`` with all empty fields;
/// at least some field has to be nonempty to be a valid address.
case contactAttachmentEmptyAddress
/// A ``BackupProto_ContactAttachment/PostalAddress/type`` was unknown.
case contactAttachmentAddressUnknownType
/// A `BackupProto_Group's` gv2 master key could not be parsed by libsignal.
case invalidGV2MasterKey
/// A `BackupProto_Group` was missing its group snapshot.
case missingGV2GroupSnapshot
/// A ``BackupProtoGroup/BackupProtoFullGroupMember/role`` was
/// unrecognized. Includes the class of the offending proto.
case unrecognizedGV2MemberRole(protoClass: Any.Type)
/// A ``BackupProtoGroup/BackupProtoMemberPendingProfileKey`` was
/// missing its member details.
case invitedGV2MemberMissingMemberDetails
@ -684,11 +639,6 @@ extension MessageBackup {
/// A `BackupProto_GroupSequenceOfRequestsAndCancelsUpdate` where
/// the requester is the local user, which isn't allowed.
case sequenceOfRequestsAndCancelsWithLocalAci
/// An unrecognized `BackupProto_GroupChangeChatUpdate`.
case unrecognizedGroupUpdate
/// A frame was entirely missing its enclosed item.
case frameMissingItem
/// A profile key for the local user that could not be parsed into a valid aes256 key
case invalidLocalProfileKey
@ -698,28 +648,16 @@ extension MessageBackup {
/// A `BackupProto_IndividualCall` chat item update was associated
/// with a thread that was not a contact thread.
case individualCallNotInContactThread
/// A `BackupProto_IndividualCall` had an unrecognized type.
case individualCallUnrecognizedType
/// A `BackupProto_IndividualCall` had an unrecognized direction.
case individualCallUnrecognizedDirection
/// A `BackupProto_IndividualCall` had an unrecognized state.
case individualCallUnrecognizedState
/// A `BackupProto_GroupCall` chat item update was associated with
/// a thread that was not a group thread.
case groupCallNotInGroupThread
/// A `BackupProto_GroupCall` had an unrecognized state.
case groupCallUnrecognizedState
/// A `BackupProto_GroupCall` referenced a recipient that was not
/// a contact or otherwise did not contain an ACI.
case groupCallRecipientIdNotAnAci(RecipientId)
/// `BackupProto_DistributionListItem` was missing its item
case distributionListItemMissingItem
/// `BackupProto_DistributionList.distributionId` was not a valid UUID
case invalidDistributionListId
/// `BackupProto_DistributionList.privacyMode` was missing, or contained an unknown privacy mode
case invalidDistributionListPrivacyMode
/// A custom (non-MyStory) distribution list had ``BackupProto_DistributionList/PrivacyMode/all``
/// or ``BackupProto_DistributionList/PrivacyMode/allExcept``, which are only allowed
/// for My Story.
@ -734,10 +672,6 @@ extension MessageBackup {
/// other than a ``BackupProto_AdHocCall``; this isn't allowed.
case callLinkUsedAsChatRecipient
/// A ``BackupProto/ChatUpdateMessage/update`` was empty.
case emptyChatUpdateMessage
/// A ``BackupProto/SimpleChatUpdate/type`` was unrecognized.
case unrecognizedSimpleChatUpdate
/// A "verification state change" simple chat update was
/// associated with a non-contact recipient.
case verificationStateChangeNotFromContact
@ -760,9 +694,9 @@ extension MessageBackup {
/// associated with a non-contact recipient.
case unsupportedProtocolVersionNotFromContact
/// An ArchivedPayment was unable to be crated from the
/// restored payment information.
case unrecognizedPaymentTransaction
/// An ``BackupProto_PaymentNotification`` was sent
/// in a group chat (not a 1:1 chat).
case paymentNotificationInGroup
/// An "expiration timer update" was in a non-contact thread.
/// - Note
@ -786,8 +720,6 @@ extension MessageBackup {
/// A "session switchover update" was not authored by a contact.
case sessionSwitchoverUpdateNotFromContact
/// A "learned profile update" was missing its previous name.
case learnedProfileUpdateMissingPreviousName
/// A "learned profile update" was not authored by a contact.
case learnedProfileUpdateNotFromContact
@ -814,18 +746,9 @@ extension MessageBackup {
/// A ``BackupProto_MessageAttachment/clientUuid`` contained an invalid UUID.
case invalidAttachmentClientUUID
/// A ``BackupProto_GiftBadge/state`` was unrecognized.
case unrecognizedGiftBadgeState
/// A ``BackupProto_CallLink/rootKey`` was invalid.
case callLinkInvalidRootKey
/// A ``BackupProto_CallLink/restrictions`` was unrecognized.
case callLinkRestrictionsUnrecognizedType
/// A ``BackupProto_AdHocCall/state`` was unknown.
case adHocCallUnknownState
/// A ``BackupProto_AdHocCall/state`` was unrecognized.
case adHocCallUnrecognizedState
/// The recipient on an ad hoc call was not a call link. No other
/// recipient types are valid for an ad hoc call.
case recipientOfAdHocCallWasNotCallLink
@ -919,75 +842,50 @@ extension MessageBackup {
.invalidProfileKey,
.invalidContactIdentityKey,
.invalidDistributionListMember,
.recipientMissingDestination,
.unknownContactIdentityState,
.contactWithoutIdentifiers,
.otherContactWithLocalIdentifiers,
.contactWithoutRegistrationInfo,
.chatItemMissingDirectionalDetails,
.chatItemMissingItem,
.chatItemInvalidDateSent,
.unrecognizedChatStyleBubbleColorPreset,
.unrecognizedCustomChatStyleColor,
.chatStyleGradientSingleOrNoColors,
.unrecognizedChatStyleWallpaperPreset,
.directionlessChatItemNotUpdateMessage,
.incomingMessageNotFromAciOrE164,
.outgoingNonContactMessageRecipient,
.unrecognizedMessageSendStatus,
.reactionNotFromAciOrE164,
.emptyStandardMessage,
.directStoryReplyMessageEmpty,
.directStoryReplyMessageEmptyWithLongText,
.directStoryReplyMessageUnknownType,
.directStoryReplyFromNonAci,
.directStoryReplyInGroupThread,
.longTextStandardMessageMissingBody,
.unrecognizedBodyRangeStyle,
.quotedMessageEmptyContent,
.linkPreviewEmptyUrl,
.linkPreviewUrlNotInBody,
.contactMessageMissingContactAttachment,
.contactAttachmentPhoneNumberMissingValue,
.contactAttachmentPhoneNumberUnknownType,
.contactAttachmentEmailMissingValue,
.contactAttachmentEmailUnknownType,
.contactAttachmentEmptyAddress,
.contactAttachmentAddressUnknownType,
.invalidGV2MasterKey,
.missingGV2GroupSnapshot,
.unrecognizedGV2MemberRole,
.invitedGV2MemberMissingMemberDetails,
.failedToBuildGV2GroupModel,
.groupUpdateMessageInNonGroupChat,
.emptyGroupUpdates,
.sequenceOfRequestsAndCancelsWithLocalAci,
.unrecognizedGroupUpdate,
.frameMissingItem,
.invalidLocalProfileKey,
.invalidLocalUsernameLink,
.individualCallNotInContactThread,
.individualCallUnrecognizedType,
.individualCallUnrecognizedDirection,
.individualCallUnrecognizedState,
.groupCallNotInGroupThread,
.groupCallUnrecognizedState,
.groupCallRecipientIdNotAnAci,
.distributionListItemMissingItem,
.invalidDistributionListId,
.invalidDistributionListPrivacyMode,
.customDistributionListPrivacyModeAllOrAllExcept,
.invalidDistributionListDeletionTimestamp,
.distributionListUsedAsChatRecipient,
.emptyChatUpdateMessage,
.unrecognizedSimpleChatUpdate,
.verificationStateChangeNotFromContact,
.phoneNumberChangeNotFromContact,
.endSessionNotFromContact,
.decryptionErrorNotFromContact,
.paymentsActivationRequestNotFromAci,
.paymentsActivatedNotFromAci,
.unrecognizedPaymentTransaction,
.paymentNotificationInGroup,
.unsupportedProtocolVersionNotFromContact,
.expirationTimerUpdateNotInContactThread,
.expirationTimerOverflowedLocalType,
@ -995,7 +893,6 @@ extension MessageBackup {
.profileChangeUpdateNotFromContact,
.threadMergeUpdateNotFromContact,
.sessionSwitchoverUpdateNotFromContact,
.learnedProfileUpdateMissingPreviousName,
.learnedProfileUpdateNotFromContact,
.revisionOfIncomingMessageMissingIncomingDetails,
.revisionOfOutgoingMessageMissingOutgoingDetails,
@ -1004,12 +901,8 @@ extension MessageBackup {
.filePointerMissingEncryptionKey,
.filePointerMissingDigest,
.invalidAttachmentClientUUID,
.unrecognizedGiftBadgeState,
.callLinkInvalidRootKey,
.callLinkRestrictionsUnrecognizedType,
.callLinkUsedAsChatRecipient,
.adHocCallUnknownState,
.adHocCallUnrecognizedState,
.recipientOfAdHocCallWasNotCallLink:
// Collapse all others by the id of the containing frame.
return idLogString
@ -1054,74 +947,49 @@ extension MessageBackup {
.invalidProfileKey,
.invalidContactIdentityKey,
.invalidDistributionListMember,
.recipientMissingDestination,
.unknownContactIdentityState,
.contactWithoutIdentifiers,
.otherContactWithLocalIdentifiers,
.contactWithoutRegistrationInfo,
.chatItemMissingDirectionalDetails,
.chatItemMissingItem,
.chatItemInvalidDateSent,
.unrecognizedChatStyleBubbleColorPreset,
.unrecognizedCustomChatStyleColor,
.chatStyleGradientSingleOrNoColors,
.customChatColorNotFound,
.unrecognizedChatStyleWallpaperPreset,
.directionlessChatItemNotUpdateMessage,
.incomingMessageNotFromAciOrE164,
.outgoingNonContactMessageRecipient,
.unrecognizedMessageSendStatus,
.reactionNotFromAciOrE164,
.emptyStandardMessage,
.directStoryReplyMessageEmpty,
.directStoryReplyMessageEmptyWithLongText,
.directStoryReplyMessageUnknownType,
.directStoryReplyFromNonAci,
.directStoryReplyInGroupThread,
.longTextStandardMessageMissingBody,
.unrecognizedBodyRangeStyle,
.linkPreviewEmptyUrl,
.contactMessageMissingContactAttachment,
.contactAttachmentPhoneNumberMissingValue,
.contactAttachmentPhoneNumberUnknownType,
.contactAttachmentEmailMissingValue,
.contactAttachmentEmailUnknownType,
.contactAttachmentEmptyAddress,
.contactAttachmentAddressUnknownType,
.invalidGV2MasterKey,
.missingGV2GroupSnapshot,
.unrecognizedGV2MemberRole,
.invitedGV2MemberMissingMemberDetails,
.failedToBuildGV2GroupModel,
.groupUpdateMessageInNonGroupChat,
.emptyGroupUpdates,
.sequenceOfRequestsAndCancelsWithLocalAci,
.unrecognizedGroupUpdate,
.frameMissingItem,
.invalidLocalProfileKey,
.invalidLocalUsernameLink,
.individualCallNotInContactThread,
.individualCallUnrecognizedType,
.individualCallUnrecognizedDirection,
.individualCallUnrecognizedState,
.groupCallNotInGroupThread,
.groupCallUnrecognizedState,
.groupCallRecipientIdNotAnAci,
.distributionListItemMissingItem,
.invalidDistributionListId,
.invalidDistributionListPrivacyMode,
.customDistributionListPrivacyModeAllOrAllExcept,
.invalidDistributionListDeletionTimestamp,
.distributionListUsedAsChatRecipient,
.emptyChatUpdateMessage,
.unrecognizedSimpleChatUpdate,
.verificationStateChangeNotFromContact,
.phoneNumberChangeNotFromContact,
.endSessionNotFromContact,
.decryptionErrorNotFromContact,
.paymentsActivationRequestNotFromAci,
.paymentsActivatedNotFromAci,
.unrecognizedPaymentTransaction,
.paymentNotificationInGroup,
.unsupportedProtocolVersionNotFromContact,
.expirationTimerUpdateNotInContactThread,
.expirationTimerOverflowedLocalType,
@ -1129,7 +997,6 @@ extension MessageBackup {
.profileChangeUpdateNotFromContact,
.threadMergeUpdateNotFromContact,
.sessionSwitchoverUpdateNotFromContact,
.learnedProfileUpdateMissingPreviousName,
.learnedProfileUpdateNotFromContact,
.revisionOfIncomingMessageMissingIncomingDetails,
.revisionOfOutgoingMessageMissingOutgoingDetails,
@ -1138,12 +1005,8 @@ extension MessageBackup {
.filePointerMissingEncryptionKey,
.filePointerMissingDigest,
.invalidAttachmentClientUUID,
.unrecognizedGiftBadgeState,
.callLinkInvalidRootKey,
.callLinkRestrictionsUnrecognizedType,
.callLinkUsedAsChatRecipient,
.adHocCallUnknownState,
.adHocCallUnrecognizedState,
.recipientOfAdHocCallWasNotCallLink:
return .error
case .quotedMessageEmptyContent:

View File

@ -45,6 +45,9 @@ extension MessageBackup {
/// Frames are always restored individually.
public enum RestoreFrameResult<ProtoIdType: MessageBackupLoggableId> {
case success
/// There was an unrecognized enum (or oneOf) for which we skip restoring this frame
/// but we should proceed restoring other frames.
case unrecognizedEnum(UnrecognizedEnumError)
/// We managed to restore some part of the frame, meaning it is represented in our database.
/// For example, we restored a message but dropped some invalid recipients.
/// Generally restoration of other frames can proceed, but the caller can determine
@ -55,6 +58,21 @@ extension MessageBackup {
/// whether to stop or not based on the specific error(s).
case failure([RestoreFrameError<ProtoIdType>])
}
public class UnrecognizedEnumError: MessageBackupLoggableError {
private let enumType: Any.Type
init(enumType: Any.Type) {
self.enumType = enumType
}
var typeLogString: String { String(describing: enumType) }
var idLogString: String { "Unrecognized Enum" }
var callsiteLogString: String { "" }
var collapseKey: String? { typeLogString }
var logLevel: MessageBackup.LogLevel { .warning }
}
}
public protocol MessageBackupProtoArchiver {

View File

@ -149,19 +149,14 @@ public class MessageBackupCallLinkRecipientArchiver: MessageBackupProtoArchiver
adminKey = nil
}
var partialErrors = [MessageBackup.RestoreFrameError<RecipientId>]()
let restrictions: CallLinkRecord.Restrictions
switch callLinkProto.restrictions {
case .adminApproval:
restrictions = .adminApproval
case .none:
restrictions = .none
case .unknown:
case .unknown, .UNRECOGNIZED:
restrictions = .unknown
case .UNRECOGNIZED:
partialErrors.append(.restoreFrameError(.invalidProtoData(.callLinkRestrictionsUnrecognizedType), recipient.recipientId))
restrictions = .adminApproval
}
do {
@ -181,11 +176,7 @@ public class MessageBackupCallLinkRecipientArchiver: MessageBackupProtoArchiver
return .failure([.restoreFrameError(.databaseInsertionFailed(error), recipient.recipientId)])
}
if partialErrors.isEmpty {
return .success
} else {
return .partialRestore(partialErrors)
}
return .success
}
}

View File

@ -537,10 +537,9 @@ public class MessageBackupContactRecipientArchiver: MessageBackupProtoArchiver {
let unregisteredTimestamp: UInt64?
switch contactProto.registration {
case nil:
return .failure([.restoreFrameError(
.invalidProtoData(.contactWithoutRegistrationInfo),
recipient.recipientId
)])
// We treat unknown values as registered.
isRegistered = true
unregisteredTimestamp = nil
case .notRegistered(let notRegisteredProto):
isRegistered = false
unregisteredTimestamp = notRegisteredProto.unregisteredTimestamp
@ -661,10 +660,7 @@ public class MessageBackupContactRecipientArchiver: MessageBackupProtoArchiver {
case .unverified:
verificationState = .noLongerVerified
case .UNRECOGNIZED:
return .failure([.restoreFrameError(
.invalidProtoData(.unknownContactIdentityState),
recipientProto.recipientId
)])
verificationState = .default
}
// Write directly to the OWSRecipientIdentity table, bypassing

View File

@ -274,7 +274,9 @@ public class MessageBackupDistributionListRecipientArchiver: MessageBackupProtoA
context: context
)
case nil:
result = restoreFrameError(.invalidProtoData(.distributionListItemMissingItem))
result = .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_DistributionListItem.OneOf_Item.self
))
}
context[recipient.recipientId] = .distributionList(distributionId)
@ -334,10 +336,9 @@ public class MessageBackupDistributionListRecipientArchiver: MessageBackupProtoA
partialErrors: &partialErrors
)
case .unknown, .UNRECOGNIZED:
return .failure([.restoreFrameError(
.invalidProtoData(.invalidDistributionListPrivacyMode),
recipientId
)])
// Fallback to an empty explicit list.
viewMode = .explicit
addresses = []
}
// MyStory is created during warmCaches(), so it should be present at this point.

View File

@ -272,9 +272,7 @@ public class MessageBackupGroupRecipientArchiver: MessageBackupProtoArchiver {
guard let aci = try? Aci.parseFrom(serviceIdBinary: fullMember.userID) else {
return restoreFrameError(.invalidProtoData(.invalidAci(protoClass: BackupProto_Group.Member.self)))
}
guard let role = TSGroupMemberRole(backupProtoRole: fullMember.role) else {
return restoreFrameError(.invalidProtoData(.unrecognizedGV2MemberRole(protoClass: BackupProto_Group.Member.self)))
}
let role = TSGroupMemberRole(backupProtoRole: fullMember.role)
groupMembershipBuilder.addFullMember(aci, role: role)
fullGroupMemberAcis.insert(aci)
@ -287,9 +285,7 @@ public class MessageBackupGroupRecipientArchiver: MessageBackupProtoArchiver {
guard let serviceId = try? ServiceId.parseFrom(serviceIdBinary: memberDetails.userID) else {
return restoreFrameError(.invalidProtoData(.invalidServiceId(protoClass: BackupProto_Group.MemberPendingProfileKey.self)))
}
guard let role = TSGroupMemberRole(backupProtoRole: memberDetails.role) else {
return restoreFrameError(.invalidProtoData(.unrecognizedGV2MemberRole(protoClass: BackupProto_Group.MemberPendingProfileKey.self)))
}
let role = TSGroupMemberRole(backupProtoRole: memberDetails.role)
guard let addedByAci = try? Aci.parseFrom(serviceIdBinary: invitedMember.addedByUserID) else {
return restoreFrameError(.invalidProtoData(.invalidAci(protoClass: BackupProto_Group.MemberPendingProfileKey.self)))
}
@ -502,9 +498,11 @@ private extension BackupProto_Group.Member {
// MARK: -
private extension TSGroupMemberRole {
init?(backupProtoRole: BackupProto_Group.Member.Role) {
init(backupProtoRole: BackupProto_Group.Member.Role) {
switch backupProtoRole {
case .unknown, .UNRECOGNIZED: return nil
case .unknown, .UNRECOGNIZED:
// Fallback to normal (default)
self = .normal
case .default: self = .normal
case .administrator: self = .administrator
}

View File

@ -874,10 +874,9 @@ public class MessageBackupManagerImpl: MessageBackupManager {
let recipientResult: MessageBackup.RestoreFrameResult<MessageBackup.RecipientId>
switch recipient.destination {
case nil:
recipientResult = .failure([.restoreFrameError(
.invalidProtoData(.recipientMissingDestination),
recipient.recipientId
)])
recipientResult = .unrecognizedEnum(MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_Recipient.OneOf_Destination.self
))
case .self_p(let selfRecipientProto):
recipientResult = localRecipientArchiver.restoreSelfRecipient(
selfRecipientProto,
@ -919,6 +918,9 @@ public class MessageBackupManagerImpl: MessageBackupManager {
switch recipientResult {
case .success:
return
case .unrecognizedEnum(let error):
frameErrors.append(LoggableErrorAndProto(error: error, wasFatal: false, protoFrame: recipient))
return
case .partialRestore(let errors):
frameErrors.append(contentsOf: errors.map { LoggableErrorAndProto(error: $0, wasFatal: false, protoFrame: recipient) })
case .failure(let errors):
@ -933,6 +935,9 @@ public class MessageBackupManagerImpl: MessageBackupManager {
switch chatResult {
case .success:
return
case .unrecognizedEnum(let error):
frameErrors.append(LoggableErrorAndProto(error: error, wasFatal: false, protoFrame: chat))
return
case .partialRestore(let errors):
frameErrors.append(contentsOf: errors.map { LoggableErrorAndProto(error: $0, wasFatal: false, protoFrame: chat) })
case .failure(let errors):
@ -947,6 +952,9 @@ public class MessageBackupManagerImpl: MessageBackupManager {
switch chatItemResult {
case .success:
return
case .unrecognizedEnum(let error):
frameErrors.append(LoggableErrorAndProto(error: error, wasFatal: false, protoFrame: chatItem))
return
case .partialRestore(let errors):
frameErrors.append(contentsOf: errors.map { LoggableErrorAndProto(error: $0, wasFatal: false, protoFrame: chatItem) })
case .failure(let errors):
@ -962,6 +970,9 @@ public class MessageBackupManagerImpl: MessageBackupManager {
switch accountDataResult {
case .success:
return
case .unrecognizedEnum(let error):
frameErrors.append(LoggableErrorAndProto(error: error, wasFatal: false, protoFrame: backupProtoAccountData))
return
case .partialRestore(let errors):
frameErrors.append(contentsOf: errors.map { LoggableErrorAndProto(error: $0, wasFatal: false, protoFrame: backupProtoAccountData) })
case .failure(let errors):
@ -976,6 +987,9 @@ public class MessageBackupManagerImpl: MessageBackupManager {
switch stickerPackResult {
case .success:
return
case .unrecognizedEnum(let error):
frameErrors.append(LoggableErrorAndProto(error: error, wasFatal: false, protoFrame: backupProtoStickerPack))
return
case .partialRestore(let errors):
frameErrors.append(contentsOf: errors.map { LoggableErrorAndProto(error: $0, wasFatal: false, protoFrame: backupProtoStickerPack) })
case .failure(let errors):
@ -990,6 +1004,9 @@ public class MessageBackupManagerImpl: MessageBackupManager {
switch adHocCallResult {
case .success:
return
case .unrecognizedEnum(let error):
frameErrors.append(LoggableErrorAndProto(error: error, wasFatal: false, protoFrame: backupProtoAdHocCall))
return
case .partialRestore(let errors):
frameErrors.append(contentsOf: errors.map { LoggableErrorAndProto(error: $0, wasFatal: false, protoFrame: backupProtoAdHocCall) })
case .failure(let errors):
@ -1006,11 +1023,9 @@ public class MessageBackupManagerImpl: MessageBackupManager {
break
case nil:
if hasMoreFrames {
owsFailDebug("Frame missing item!")
frameErrors.append(LoggableErrorAndProto(
error: MessageBackup.RestoreFrameError.restoreFrameError(
.invalidProtoData(.frameMissingItem),
MessageBackup.EmptyFrameId.shared
error: MessageBackup.UnrecognizedEnumError(
enumType: BackupProto_Frame.OneOf_Item.self
),
wasFatal: false
))