diff --git a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageContentsArchiver.swift b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageContentsArchiver.swift index 356f5a97b5..53b0c09da3 100644 --- a/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageContentsArchiver.swift +++ b/SignalServiceKit/MessageBackup/Archivers/ChatItem/MessageBackupTSMessageContentsArchiver.swift @@ -373,6 +373,7 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver { if let linkPreview = message.linkPreview { let linkPreviewResult = self.archiveLinkPreview( linkPreview, + messageBody: standardMessage.text.body, interactionUniqueId: message.uniqueInteractionId, context: context, messageRowId: messageRowId @@ -574,6 +575,7 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver { private func archiveLinkPreview( _ linkPreview: OWSLinkPreview, + messageBody: String, interactionUniqueId: MessageBackup.InteractionUniqueId, context: MessageBackup.RecipientArchivingContext, messageRowId: Int64 @@ -591,6 +593,14 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver { return .partialFailure(nil, partialErrors) } + guard messageBody.contains(url) else { + partialErrors.append(.archiveFrameError( + .linkPreviewUrlNotInBody, + interactionUniqueId + )) + return .partialFailure(nil, partialErrors) + } + var proto = BackupProto_LinkPreview() proto.url = url linkPreview.title.map { proto.title = $0 } @@ -1158,7 +1168,12 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver { else { return .messageFailure(partialErrors) } - (linkPreview, linkPreviewAttachment) = linkPreviewResult + if let linkPreviewResult { + (linkPreview, linkPreviewAttachment) = linkPreviewResult + } else { + linkPreview = nil + linkPreviewAttachment = nil + } } else { linkPreview = nil linkPreviewAttachment = nil @@ -1458,15 +1473,15 @@ class MessageBackupTSMessageContentsArchiver: MessageBackupProtoArchiver { standardMessage: BackupProto_StandardMessage, chatItemId: MessageBackup.ChatItemId, context: MessageBackup.RestoringContext - ) -> RestoreInteractionResult<(OWSLinkPreview, BackupProto_FilePointer?)> { + ) -> RestoreInteractionResult<(OWSLinkPreview, BackupProto_FilePointer?)?> { guard let url = linkPreviewProto.url.nilIfEmpty else { - return .messageFailure([.restoreFrameError( + return .partialRestore(nil, [.restoreFrameError( .invalidProtoData(.linkPreviewEmptyUrl), chatItemId )]) } guard standardMessage.text.body.contains(url) else { - return .messageFailure([.restoreFrameError( + return .partialRestore(nil, [.restoreFrameError( .invalidProtoData(.linkPreviewUrlNotInBody), chatItemId )]) diff --git a/SignalServiceKit/MessageBackup/Archivers/MessageBackupErrors.swift b/SignalServiceKit/MessageBackup/Archivers/MessageBackupErrors.swift index 6402a3ae38..3b9de62f07 100644 --- a/SignalServiceKit/MessageBackup/Archivers/MessageBackupErrors.swift +++ b/SignalServiceKit/MessageBackup/Archivers/MessageBackupErrors.swift @@ -69,6 +69,8 @@ extension MessageBackup { /// A link preview is missing its url case linkPreviewMissingUrl + /// A link preview's URL isn't in the message body + case linkPreviewUrlNotInBody /// A sticker message had no associated attachment for the sticker's image contents. case stickerMessageMissingStickerAttachment @@ -258,6 +260,7 @@ extension MessageBackup { .invalidOutgoingMessageRecipient, .invalidQuoteAuthor, .linkPreviewMissingUrl, + .linkPreviewUrlNotInBody, .stickerMessageMissingStickerAttachment, .failedToEnqueueAttachmentForUpload, .invalidReactionAddress, @@ -367,6 +370,10 @@ extension MessageBackup { // (see `hasRenderableChanges`), so drop them from the backup // with a warning but not an error. return .warning + case .linkPreviewUrlNotInBody: + // We've seen real world databases with invalid link previews; we + // just drop these on export and just issue a warning. + return .warning } } }