diff --git a/SignalServiceKit/MessageBackup/Archivers/AccountData/MessageBackupAccountDataArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/AccountData/MessageBackupAccountDataArchiver.swift index 544f972bce..fb3736a766 100644 --- a/SignalServiceKit/MessageBackup/Archivers/AccountData/MessageBackupAccountDataArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/AccountData/MessageBackupAccountDataArchiver.swift @@ -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): diff --git a/SignalServiceKit/MessageBackup/Archivers/AdHocCall/MessageBackupAdHocCallArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/AdHocCall/MessageBackupAdHocCallArchiver.swift index d5b2d3d9c4..3f6153c7e2 100644 --- a/SignalServiceKit/MessageBackup/Archivers/AdHocCall/MessageBackupAdHocCallArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/AdHocCall/MessageBackupAdHocCallArchiver.swift @@ -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 } diff --git a/SignalServiceKit/MessageBackup/Archivers/Chat/MessageBackupChatArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/Chat/MessageBackupChatArchiver.swift index bd2d2b5880..e07a4d6f40 100644 --- a/SignalServiceKit/MessageBackup/Archivers/Chat/MessageBackupChatArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/Chat/MessageBackupChatArchiver.swift @@ -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): diff --git a/SignalServiceKit/MessageBackup/Archivers/Chat/MessageBackupChatStyleArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/Chat/MessageBackupChatStyleArchiver.swift index d1ea749634..f034a2a7c7 100644 --- a/SignalServiceKit/MessageBackup/Archivers/Chat/MessageBackupChatStyleArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/Chat/MessageBackupChatStyleArchiver.swift @@ -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 } } } diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/Calls/MessageBackupGroupCallArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/Calls/MessageBackupGroupCallArchiver.swift index ff7bf9ad85..6f36c4ccd1 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/Calls/MessageBackupGroupCallArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/Calls/MessageBackupGroupCallArchiver.swift @@ -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 diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/Calls/MessageBackupIndividualCallArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/Calls/MessageBackupIndividualCallArchiver.swift index af9cb4f185..db0cf8f490 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/Calls/MessageBackupIndividualCallArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/Calls/MessageBackupIndividualCallArchiver.swift @@ -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? diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/GroupUpdates/MessageBackupGroupUpdateMessageArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/GroupUpdates/MessageBackupGroupUpdateMessageArchiver.swift index 5605a5ec27..b18b405ff9 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/GroupUpdates/MessageBackupGroupUpdateMessageArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/GroupUpdates/MessageBackupGroupUpdateMessageArchiver.swift @@ -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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/GroupUpdates/MessageBackupGroupUpdateProtoToSwiftConverter.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/GroupUpdates/MessageBackupGroupUpdateProtoToSwiftConverter.swift index 5cdab797eb..d303e9d613 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/GroupUpdates/MessageBackupGroupUpdateProtoToSwiftConverter.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/GroupUpdates/MessageBackupGroupUpdateProtoToSwiftConverter.swift @@ -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: diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupChatUpdateMessageArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupChatUpdateMessageArchiver.swift index 9146c1e209..d7e0e112fe 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupChatUpdateMessageArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupChatUpdateMessageArchiver.swift @@ -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, diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupExpirationTimerChatUpdateArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupExpirationTimerChatUpdateArchiver.swift index 2856347b8f..36b4358ccc 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupExpirationTimerChatUpdateArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupExpirationTimerChatUpdateArchiver.swift @@ -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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupLearnedProfileChatUpdateArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupLearnedProfileChatUpdateArchiver.swift index d56663c36f..651be11587 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupLearnedProfileChatUpdateArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupLearnedProfileChatUpdateArchiver.swift @@ -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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupProfileChangeChatUpdateArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupProfileChangeChatUpdateArchiver.swift index e9ae67659e..2e93ba4b01 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupProfileChangeChatUpdateArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupProfileChangeChatUpdateArchiver.swift @@ -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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupSessionSwitchoverChatUpdateArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupSessionSwitchoverChatUpdateArchiver.swift index 35194314ae..6f0020e889 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupSessionSwitchoverChatUpdateArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupSessionSwitchoverChatUpdateArchiver.swift @@ -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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupSimpleChatUpdateArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupSimpleChatUpdateArchiver.swift index 6eeea4487e..0919f85600 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupSimpleChatUpdateArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupSimpleChatUpdateArchiver.swift @@ -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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupThreadMergeChatUpdateArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupThreadMergeChatUpdateArchiver.swift index 4efa5e8125..d0e920f8bb 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupThreadMergeChatUpdateArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupThreadMergeChatUpdateArchiver.swift @@ -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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackup+InteractionTypes.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackup+InteractionTypes.swift index effe71babf..8e8f25058a 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackup+InteractionTypes.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackup+InteractionTypes.swift @@ -310,6 +310,9 @@ extension MessageBackup { enum RestoreInteractionResult { 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]) @@ -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 { + case `continue`(ComponentType) + case bubbleUpError(MessageBackup.RestoreInteractionResult) + } + + /// 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.Type = Component.self, partialErrors: inout [MessageBackup.RestoreFrameError] - ) -> Component? { + ) -> BubbleUp { 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] - ) -> 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 - } - } -} diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupChatItemArchiverImpl.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupChatItemArchiverImpl.swift index cd85fa8cfa..34f4c93df4 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupChatItemArchiverImpl.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupChatItemArchiverImpl.swift @@ -357,7 +357,9 @@ public class MessageBackupChatItemArchiverImpl: MessageBackupChatItemArchiver { let restoreInteractionResult: MessageBackup.RestoreInteractionResult 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): diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupContactAttachmentArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupContactAttachmentArchiver.swift index 98426a6d36..b0e114a604 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupContactAttachmentArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupContactAttachmentArchiver.swift @@ -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( diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSIncomingMessageArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSIncomingMessageArchiver.swift index da1d53a276..c56c0cc6f0 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSIncomingMessageArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSIncomingMessageArchiver.swift @@ -67,15 +67,19 @@ class MessageBackupTSIncomingMessageArchiver { ) -> MessageBackup.RestoreInteractionResult { 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 { 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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageContentsArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageContentsArchiver.swift index 28a3ab7798..9672ec24a2 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageContentsArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageContentsArchiver.swift @@ -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 { 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( diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageEditHistoryArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageEditHistoryArchiver.swift index fee001c82c..a655e5784a 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageEditHistoryArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageEditHistoryArchiver.swift @@ -279,16 +279,21 @@ final class MessageBackupTSMessageEditHistoryArchiver ) -> MessageBackup.RestoreInteractionResult { 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 /// 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( diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSOutgoingMessageArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSOutgoingMessageArchiver.swift index c034a828c7..71630b02ab 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSOutgoingMessageArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSOutgoingMessageArchiver.swift @@ -67,15 +67,19 @@ class MessageBackupTSOutgoingMessageArchiver { ) -> MessageBackup.RestoreInteractionResult { 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 { 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): diff --git a/SignalServiceKit/MessageBackup/Archivers/MessageBackupErrors.swift b/SignalServiceKit/MessageBackup/Archivers/MessageBackupErrors.swift index 284e5fe7fb..b4e669fa2b 100644 --- a/SignalServiceKit/MessageBackup/Archivers/MessageBackupErrors.swift +++ b/SignalServiceKit/MessageBackup/Archivers/MessageBackupErrors.swift @@ -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: diff --git a/SignalServiceKit/MessageBackup/Archivers/MessageBackupProtoArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/MessageBackupProtoArchiver.swift index a15bcf4e52..f4daf17ad9 100644 --- a/SignalServiceKit/MessageBackup/Archivers/MessageBackupProtoArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/MessageBackupProtoArchiver.swift @@ -45,6 +45,9 @@ extension MessageBackup { /// Frames are always restored individually. public enum RestoreFrameResult { 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]) } + + 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 { diff --git a/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupCallLinkRecipientArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupCallLinkRecipientArchiver.swift index 53377753ab..0ca1feaeab 100644 --- a/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupCallLinkRecipientArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupCallLinkRecipientArchiver.swift @@ -149,19 +149,14 @@ public class MessageBackupCallLinkRecipientArchiver: MessageBackupProtoArchiver adminKey = nil } - var partialErrors = [MessageBackup.RestoreFrameError]() - 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 } } diff --git a/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupContactRecipientArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupContactRecipientArchiver.swift index aa1b6c5fd5..f812854057 100644 --- a/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupContactRecipientArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupContactRecipientArchiver.swift @@ -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 diff --git a/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupDistributionListRecipientArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupDistributionListRecipientArchiver.swift index 1aa1af7d3b..9e70dc7b70 100644 --- a/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupDistributionListRecipientArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupDistributionListRecipientArchiver.swift @@ -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. diff --git a/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupGroupRecipientArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupGroupRecipientArchiver.swift index 222b8b2a5d..5da3961985 100644 --- a/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupGroupRecipientArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/Recipient/MessageBackupGroupRecipientArchiver.swift @@ -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 } diff --git a/SignalServiceKit/MessageBackup/MessageBackupManagerImpl.swift b/SignalServiceKit/MessageBackup/MessageBackupManagerImpl.swift index 40f2241e00..9c7ee4389e 100644 --- a/SignalServiceKit/MessageBackup/MessageBackupManagerImpl.swift +++ b/SignalServiceKit/MessageBackup/MessageBackupManagerImpl.swift @@ -874,10 +874,9 @@ public class MessageBackupManagerImpl: MessageBackupManager { let recipientResult: MessageBackup.RestoreFrameResult 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 ))