From 4d5ded7557152472cc402bfe211c9a6b70d42dba Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Fri, 29 Jul 2022 15:26:24 +1000 Subject: [PATCH 1/5] Fixed a few bugs with media attachment handling, added webp support Updated the OpenGroupManager to create a BlindedIdLookup for messages within the `inbox` (validating that the sessionId does actually match the blindedId) Added support for static and animated WebP images Added basic support for HEIC and HEIF images Fixed an issue where the file size limit was set to 10,000,000 bytes instead of 10,485,760 bytes (which is actually 10Mb) Fixed an issue where attachments uploaded by the current user on other devices would always show a loading indicator Fixed an issue where media attachments that don't contain width/height information in their protos weren't updating the values once the download was completed Fixed an issue where the media view could download an invalid file and endlessly appear to be downloading --- Podfile | 3 + Podfile.lock | 17 +- .../Content Views/MediaView.swift | 34 ++- .../MediaDetailViewController.swift | 20 +- .../Database/Models/Attachment.swift | 29 ++- .../Database/Models/BlindedIdLookup.swift | 17 ++ .../File Server/FileServerAPI.swift | 2 +- .../Jobs/Types/DisappearingMessagesJob.swift | 34 +++ .../Open Groups/OpenGroupManager.swift | 53 ++-- SessionUtilitiesKit/Media/MIMETypeUtil.h | 7 + SessionUtilitiesKit/Media/MIMETypeUtil.m | 45 +++- SessionUtilitiesKit/Media/NSData+Image.m | 230 ++++++++++++++++-- 12 files changed, 424 insertions(+), 67 deletions(-) diff --git a/Podfile b/Podfile index 705f23a53..a8b57ea29 100644 --- a/Podfile +++ b/Podfile @@ -24,6 +24,7 @@ abstract_target 'GlobalDependencies' do pod 'PureLayout', '~> 3.1.8' pod 'NVActivityIndicatorView' pod 'YYImage', git: 'https://github.com/signalapp/YYImage' + pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage' pod 'ZXingObjC' pod 'DifferenceKit' end @@ -52,6 +53,7 @@ abstract_target 'GlobalDependencies' do pod 'SAMKeychain' pod 'SwiftProtobuf', '~> 1.5.0' pod 'YYImage', git: 'https://github.com/signalapp/YYImage' + pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage' pod 'DifferenceKit' end @@ -71,6 +73,7 @@ abstract_target 'GlobalDependencies' do target 'SessionUtilitiesKit' do pod 'SAMKeychain' + pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage' target 'SessionUtilitiesKitTests' do inherit! :complete diff --git a/Podfile.lock b/Podfile.lock index 70045a1da..58c3a3735 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -29,6 +29,15 @@ PODS: - DifferenceKit/Core - GRDB.swift/SQLCipher (5.24.1): - SQLCipher (>= 3.4.0) + - libwebp (1.2.1): + - libwebp/demux (= 1.2.1) + - libwebp/mux (= 1.2.1) + - libwebp/webp (= 1.2.1) + - libwebp/demux (1.2.1): + - libwebp/webp + - libwebp/mux (1.2.1): + - libwebp/demux + - libwebp/webp (1.2.1) - Nimble (10.0.0) - NVActivityIndicatorView (5.1.1): - NVActivityIndicatorView/Base (= 5.1.1) @@ -124,6 +133,9 @@ PODS: - YYImage (1.0.4): - YYImage/Core (= 1.0.4) - YYImage/Core (1.0.4) + - YYImage/libwebp (1.0.4): + - libwebp + - YYImage/Core - ZXingObjC (3.6.5): - ZXingObjC/All (= 3.6.5) - ZXingObjC/All (3.6.5) @@ -149,6 +161,7 @@ DEPENDENCIES: - WebRTC-lib - YapDatabase/SQLCipher (from `https://github.com/oxen-io/session-ios-yap-database.git`, branch `signal-release`) - YYImage (from `https://github.com/signalapp/YYImage`) + - YYImage/libwebp (from `https://github.com/signalapp/YYImage`) - ZXingObjC SPEC REPOS: @@ -158,6 +171,7 @@ SPEC REPOS: - CryptoSwift - DifferenceKit - GRDB.swift + - libwebp - Nimble - NVActivityIndicatorView - OpenSSL-Universal @@ -212,6 +226,7 @@ SPEC CHECKSUMS: Curve25519Kit: e63f9859ede02438ae3defc5e1a87e09d1ec7ee6 DifferenceKit: 5659c430bb7fe45876fa32ce5cba5d6167f0c805 GRDB.swift: b3180ce2135fc06a453297889b746b1478c4d8c7 + libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84 NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667 OpenSSL-Universal: e7311447fd2419f57420c79524b641537387eff2 @@ -230,6 +245,6 @@ SPEC CHECKSUMS: YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 6ab902a81a379cc2c0a9a92c334c78d413190338 +PODFILE CHECKSUM: 456facc7043447a9c67733cf8846ec62afff8ea8 COCOAPODS: 1.11.3 diff --git a/Session/Conversations/Message Cells/Content Views/MediaView.swift b/Session/Conversations/Message Cells/Content Views/MediaView.swift index a194e6588..77f2183c1 100644 --- a/Session/Conversations/Message Cells/Content Views/MediaView.swift +++ b/Session/Conversations/Message Cells/Content Views/MediaView.swift @@ -129,7 +129,10 @@ public class MediaView: UIView { configure(forError: .failed) return false } - guard attachment.state != .uploaded else { return false } + + // If this message was uploaded on a different device it'll now be seen as 'downloaded' (but + // will still be outgoing - we don't want to show a loading indicator in this case) + guard attachment.state != .uploaded && attachment.state != .downloaded else { return false } let loader = MediaLoaderView() addSubview(loader) @@ -164,9 +167,13 @@ public class MediaView: UIView { } strongSelf.tryToLoadMedia( loadMediaBlock: { applyMediaBlock in - guard attachment.isValid else { return } + guard attachment.isValid else { + self?.configure(forError: .invalid) + return + } guard let filePath: String = attachment.originalFilePath else { owsFailDebug("Attachment stream missing original file path.") + self?.configure(forError: .invalid) return } @@ -177,6 +184,7 @@ public class MediaView: UIView { guard let image: YYImage = media as? YYImage else { owsFailDebug("Media has unexpected type: \(type(of: media))") + self?.configure(forError: .invalid) return } // FIXME: Animated images flicker when reloading the cells (even though they are in the cache) @@ -216,12 +224,18 @@ public class MediaView: UIView { } self?.tryToLoadMedia( loadMediaBlock: { applyMediaBlock in - guard attachment.isValid else { return } + guard attachment.isValid else { + self?.configure(forError: .invalid) + return + } attachment.thumbnail( size: .large, success: { image, _ in applyMediaBlock(image) }, - failure: { Logger.error("Could not load thumbnail") } + failure: { + Logger.error("Could not load thumbnail") + self?.configure(forError: .invalid) + } ) }, applyMediaBlock: { media in @@ -229,6 +243,7 @@ public class MediaView: UIView { guard let image: UIImage = media as? UIImage else { owsFailDebug("Media has unexpected type: \(type(of: media))") + self?.configure(forError: .invalid) return } @@ -277,12 +292,18 @@ public class MediaView: UIView { } self?.tryToLoadMedia( loadMediaBlock: { applyMediaBlock in - guard attachment.isValid else { return } + guard attachment.isValid else { + self?.configure(forError: .invalid) + return + } attachment.thumbnail( size: .medium, success: { image, _ in applyMediaBlock(image) }, - failure: { Logger.error("Could not load thumbnail") } + failure: { + Logger.error("Could not load thumbnail") + self?.configure(forError: .invalid) + } ) }, applyMediaBlock: { media in @@ -290,6 +311,7 @@ public class MediaView: UIView { guard let image: UIImage = media as? UIImage else { owsFailDebug("Media has unexpected type: \(type(of: media))") + self?.configure(forError: .invalid) return } diff --git a/Session/Media Viewing & Editing/MediaDetailViewController.swift b/Session/Media Viewing & Editing/MediaDetailViewController.swift index 80538eb4a..2d89e625d 100644 --- a/Session/Media Viewing & Editing/MediaDetailViewController.swift +++ b/Session/Media Viewing & Editing/MediaDetailViewController.swift @@ -129,7 +129,15 @@ class MediaDetailViewController: OWSViewController, UIScrollViewDelegate, OWSVid // MARK: - Functions private func updateMinZoomScale() { - guard let image: UIImage = image else { + let maybeImageSize: CGSize? = { + switch self.mediaView { + case let imageView as UIImageView: return (imageView.image?.size ?? .zero) + case let imageView as YYAnimatedImageView: return (imageView.image?.size ?? .zero) + default: return nil + } + }() + + guard let imageSize: CGSize = maybeImageSize else { self.scrollView.minimumZoomScale = 1 self.scrollView.maximumZoomScale = 1 self.scrollView.zoomScale = 1 @@ -138,13 +146,13 @@ class MediaDetailViewController: OWSViewController, UIScrollViewDelegate, OWSVid let viewSize: CGSize = self.scrollView.bounds.size - guard image.size.width > 0 && image.size.height > 0 else { - SNLog("Invalid image dimensions (\(image.size.width), \(image.size.height))") - return; + guard imageSize.width > 0 && imageSize.height > 0 else { + SNLog("Invalid image dimensions (\(imageSize.width), \(imageSize.height))") + return } - let scaleWidth: CGFloat = (viewSize.width / image.size.width) - let scaleHeight: CGFloat = (viewSize.height / image.size.height) + let scaleWidth: CGFloat = (viewSize.width / imageSize.width) + let scaleHeight: CGFloat = (viewSize.height / imageSize.height) let minScale: CGFloat = min(scaleWidth, scaleHeight) if minScale != self.scrollView.minimumZoomScale { diff --git a/SessionMessagingKit/Database/Models/Attachment.swift b/SessionMessagingKit/Database/Models/Attachment.swift index a0d18d392..e0ceef673 100644 --- a/SessionMessagingKit/Database/Models/Attachment.swift +++ b/SessionMessagingKit/Database/Models/Attachment.swift @@ -339,6 +339,23 @@ extension Attachment { default: return (self.isValid, self.duration) } }() + // Regenerate this just in case we added support since the attachment was inserted into + // the database (eg. manually downloaded in a later update) + let isVisualMedia: Bool = ( + MIMETypeUtil.isImage(contentType) || + MIMETypeUtil.isVideo(contentType) || + MIMETypeUtil.isAnimated(contentType) + ) + let attachmentResolution: CGSize? = { + if let width: UInt = self.width, let height: UInt = self.height, width > 0, height > 0 { + return CGSize(width: Int(width), height: Int(height)) + } + guard isVisualMedia else { return nil } + guard state == .downloaded else { return nil } + guard let originalFilePath: String = originalFilePath else { return nil } + + return Attachment.imageSize(contentType: contentType, originalFilePath: originalFilePath) + }() return Attachment( id: self.id, @@ -351,10 +368,16 @@ extension Attachment { sourceFilename: sourceFilename, downloadUrl: (downloadUrl ?? self.downloadUrl), localRelativeFilePath: (localRelativeFilePath ?? self.localRelativeFilePath), - width: width, - height: height, + width: attachmentResolution.map { UInt($0.width) }, + height: attachmentResolution.map { UInt($0.height) }, duration: duration, - isVisualMedia: isVisualMedia, + isVisualMedia: ( + // Regenerate this just in case we added support since the attachment was inserted into + // the database (eg. manually downloaded in a later update) + MIMETypeUtil.isImage(contentType) || + MIMETypeUtil.isVideo(contentType) || + MIMETypeUtil.isAnimated(contentType) + ), isValid: isValid, encryptionKey: (encryptionKey ?? self.encryptionKey), digest: (digest ?? self.digest), diff --git a/SessionMessagingKit/Database/Models/BlindedIdLookup.swift b/SessionMessagingKit/Database/Models/BlindedIdLookup.swift index 6d63624ae..d5e8704c6 100644 --- a/SessionMessagingKit/Database/Models/BlindedIdLookup.swift +++ b/SessionMessagingKit/Database/Models/BlindedIdLookup.swift @@ -72,6 +72,7 @@ public extension BlindedIdLookup { static func fetchOrCreate( _ db: Database, blindedId: String, + sessionId: String? = nil, openGroupServer: String, openGroupPublicKey: String, isCheckingForOutbox: Bool, @@ -90,6 +91,22 @@ public extension BlindedIdLookup { // If the lookup already has a resolved sessionId then just return it immediately guard lookup.sessionId == nil else { return lookup } + // If we we given a sessionId then validate it is correct and if so save it + if + let sessionId: String = sessionId, + dependencies.sodium.sessionId( + sessionId, + matchesBlindedId: blindedId, + serverPublicKey: openGroupPublicKey, + genericHash: dependencies.genericHash + ) + { + lookup = try lookup + .with(sessionId: sessionId) + .saved(db) + return lookup + } + // We now need to try to match the blinded id to an existing contact, this can only be done by looping // through all approved contacts and generating a blinded id for the provided open group for each to // see if it matches the provided blindedId diff --git a/SessionMessagingKit/File Server/FileServerAPI.swift b/SessionMessagingKit/File Server/FileServerAPI.swift index c92c9499e..694bf53be 100644 --- a/SessionMessagingKit/File Server/FileServerAPI.swift +++ b/SessionMessagingKit/File Server/FileServerAPI.swift @@ -14,7 +14,7 @@ public final class FileServerAPI: NSObject { public static let oldServerPublicKey = "7cb31905b55cd5580c686911debf672577b3fb0bff81df4ce2d5c4cb3a7aaa69" @objc public static let server = "http://filev2.getsession.org" public static let serverPublicKey = "da21e1d886c6fbaea313f75298bd64aab03a97ce985b46bb2dad9f2089c8ee59" - public static let maxFileSize = 10_000_000 // 10 MB + public static let maxFileSize = (10 * 1024 * 1024) // 10 MB /// The file server has a file size limit of `maxFileSize`, which the Service Nodes try to enforce as well. However, the limit applied by the Service Nodes /// is on the **HTTP request** and not the actual file size. Because the file server expects the file data to be base 64 encoded, the size of the HTTP /// request for a given file will be at least `ceil(n / 3) * 4` bytes, where n is the file size in bytes. This is the minimum size because there might also diff --git a/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift b/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift index 9ed31c7e7..4bad27849 100644 --- a/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift +++ b/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift @@ -41,6 +41,40 @@ public enum DisappearingMessagesJob: JobExecutor { // The 'if' is only there to prevent the "variable never read" warning from showing if backgroundTask != nil { backgroundTask = nil } + + // TODO: Remove this for the final build + Storage.shared.writeAsync { db in + // Re-process all WebP images, and images with no width/height values to update their validity state + let supportedVisualMediaMimeTypes: Set = MIMETypeUtil.supportedImageMIMETypes() + .appending(contentsOf: MIMETypeUtil.supportedAnimatedImageMIMETypes()) + .appending(contentsOf: MIMETypeUtil.supportedVideoMIMETypes()) + .asSet() + let attachments: [Attachment] = try Attachment + .filter(Attachment.Columns.state == Attachment.State.downloaded) + .filter( + Attachment.Columns.contentType == "image/webp" || ( + ( + Attachment.Columns.width == nil || + Attachment.Columns.height == nil + ) && + supportedVisualMediaMimeTypes.contains(Attachment.Columns.contentType) + ) + ) + .filter( + !Attachment.Columns.isValid || + !Attachment.Columns.isVisualMedia || + Attachment.Columns.width == nil || + Attachment.Columns.height == nil + ) + .fetchAll(db) + + if !attachments.isEmpty { + attachments.forEach { attachment in + _ = try? attachment.with(state: attachment.state).saved(db) + } + } + } + // TODO: Remove this for the final build } } diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift index 5a2fb5c8a..c72dea525 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManager.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -617,28 +617,37 @@ public final class OpenGroupManager: NSObject { dependencies: dependencies ) - // If the message was an outgoing message then attempt to unblind the recipient (this will help put - // messages in the correct thread in case of message request approval race conditions as well as - // during device sync'ing and restoration) + // We want to update the BlindedIdLookup cache with the message info so we can avoid using the + // "expensive" lookup when possible + let lookup: BlindedIdLookup = try { + // Minor optimisation to avoid processing the same sender multiple times in the same + // 'handleMessages' call (since the 'mapping' call is done within a transaction we + // will never have a mapping come through part-way through processing these messages) + if let result: BlindedIdLookup = lookupCache[message.recipient] { + return result + } + + return try BlindedIdLookup.fetchOrCreate( + db, + blindedId: (fromOutbox ? + message.recipient : + message.sender + ), + sessionId: (fromOutbox ? + nil : + processedMessage?.threadId + ), + openGroupServer: server.lowercased(), + openGroupPublicKey: openGroup.publicKey, + isCheckingForOutbox: fromOutbox, + dependencies: dependencies + ) + }() + lookupCache[message.recipient] = lookup + + // We also need to set the 'syncTarget' for outgoing messages to be consistent with + // standard messages if fromOutbox { - // Attempt to un-blind the 'message.recipient' - let lookup: BlindedIdLookup = try { - // Minor optimisation to avoid processing the same sender multiple times in the same - // 'handleMessages' call (since the 'mapping' call is done within a transaction we - // will never have a mapping come through part-way through processing these messages) - if let result: BlindedIdLookup = lookupCache[message.recipient] { - return result - } - - return try BlindedIdLookup.fetchOrCreate( - db, - blindedId: message.recipient, - openGroupServer: server.lowercased(), - openGroupPublicKey: openGroup.publicKey, - isCheckingForOutbox: true, - dependencies: dependencies - ) - }() let syncTarget: String = (lookup.sessionId ?? message.recipient) switch processedMessage?.messageInfo.variant { @@ -650,8 +659,6 @@ public final class OpenGroupManager: NSObject { default: break } - - lookupCache[message.recipient] = lookup } if let messageInfo: MessageReceiveJob.Details.MessageInfo = processedMessage?.messageInfo { diff --git a/SessionUtilitiesKit/Media/MIMETypeUtil.h b/SessionUtilitiesKit/Media/MIMETypeUtil.h index 811fea270..697e15e3f 100644 --- a/SessionUtilitiesKit/Media/MIMETypeUtil.h +++ b/SessionUtilitiesKit/Media/MIMETypeUtil.h @@ -12,6 +12,9 @@ extern NSString *const OWSMimeTypeImageTiff1; extern NSString *const OWSMimeTypeImageTiff2; extern NSString *const OWSMimeTypeImageBmp1; extern NSString *const OWSMimeTypeImageBmp2; +extern NSString *const OWSMimeTypeImageWebp; +extern NSString *const OWSMimeTypeImageHeic; +extern NSString *const OWSMimeTypeImageHeif; extern NSString *const OWSMimeTypeUnknownForTests; extern NSString *const kOversizeTextAttachmentUTI; @@ -36,6 +39,10 @@ extern NSString *const kSyncMessageFileExtension; + (nullable NSString *)getSupportedExtensionFromImageMIMEType:(NSString *)supportedMIMEType; + (nullable NSString *)getSupportedExtensionFromAnimatedMIMEType:(NSString *)supportedMIMEType; ++ (NSArray *)supportedImageMIMETypes; ++ (NSArray *)supportedAnimatedImageMIMETypes; ++ (NSArray *)supportedVideoMIMETypes; + + (BOOL)isAnimated:(NSString *)contentType; + (BOOL)isImage:(NSString *)contentType; + (BOOL)isVideo:(NSString *)contentType; diff --git a/SessionUtilitiesKit/Media/MIMETypeUtil.m b/SessionUtilitiesKit/Media/MIMETypeUtil.m index e93ed6bf6..469898125 100644 --- a/SessionUtilitiesKit/Media/MIMETypeUtil.m +++ b/SessionUtilitiesKit/Media/MIMETypeUtil.m @@ -19,6 +19,9 @@ NSString *const OWSMimeTypeImageTiff1 = @"image/tiff"; NSString *const OWSMimeTypeImageTiff2 = @"image/x-tiff"; NSString *const OWSMimeTypeImageBmp1 = @"image/bmp"; NSString *const OWSMimeTypeImageBmp2 = @"image/x-windows-bmp"; +NSString *const OWSMimeTypeImageWebp = @"image/webp"; +NSString *const OWSMimeTypeImageHeic = @"image/heic"; +NSString *const OWSMimeTypeImageHeif = @"image/heif"; NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype"; NSString *const OWSMimeTypeApplicationZip = @"application/zip"; NSString *const OWSMimeTypeApplicationPdf = @"application/pdf"; @@ -85,7 +88,8 @@ NSString *const kSyncMessageFileExtension = @"bin"; @"image/bmp" : @"bmp", @"image/x-windows-bmp" : @"bmp", @"image/gif" : @"gif", - @"image/x-icon": @"ico" + @"image/x-icon": @"ico", + OWSMimeTypeImageWebp : @"webp" }; }); return result; @@ -97,6 +101,7 @@ NSString *const kSyncMessageFileExtension = @"bin"; dispatch_once(&onceToken, ^{ result = @{ OWSMimeTypeImageGif : @"gif", + OWSMimeTypeImageWebp : @"image/webp", }; }); return result; @@ -175,7 +180,8 @@ NSString *const kSyncMessageFileExtension = @"bin"; @"jpeg" : @"image/jpeg", @"jpg" : @"image/jpeg", @"tif" : @"image/tiff", - @"tiff" : @"image/tiff" + @"tiff" : @"image/tiff", + @"webp" : OWSMimeTypeImageWebp }; }); return result; @@ -187,6 +193,7 @@ NSString *const kSyncMessageFileExtension = @"bin"; dispatch_once(&onceToken, ^{ result = @{ @"gif" : OWSMimeTypeImageGif, + @"image/webp" : OWSMimeTypeImageWebp }; }); return result; @@ -556,6 +563,36 @@ NSString *const kSyncMessageFileExtension = @"bin"; return result; } ++ (NSArray *)supportedImageMIMETypes +{ + static NSArray *result = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + result = [self supportedImageMIMETypesToExtensionTypes].allKeys; + }); + return result; +} + ++ (NSArray *)supportedAnimatedImageMIMETypes +{ + static NSArray *result = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + result = [self supportedAnimatedMIMETypesToExtensionTypes].allKeys; + }); + return result; +} + ++ (NSArray *)supportedVideoMIMETypes +{ + static NSArray *result = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + result = [self supportedVideoMIMETypesToExtensionTypes].allKeys; + }); + return result; +} + + (NSDictionary *)genericMIMETypesToExtensionTypes { static NSDictionary *result = nil; @@ -1386,6 +1423,8 @@ NSString *const kSyncMessageFileExtension = @"bin"; @"image/fif" : @"fif", @"image/g3fax" : @"g3", @"image/gif" : @"gif", + @"image/heic" : @"heic", + @"image/heif" : @"heif", @"image/ief" : @"ief", @"image/jpeg" : @"jpg", @"image/jutvision" : @"jut", @@ -1935,6 +1974,8 @@ NSString *const kSyncMessageFileExtension = @"bin"; @"hal" : @"application/vnd.hal+xml", @"hbci" : @"application/vnd.hbci", @"hdf" : @"application/x-hdf", + @"heic" : @"image/heic", + @"heif" : @"image/heif", @"hh" : @"text/x-c", @"hlp" : @"application/winhlp", @"hpgl" : @"application/vnd.hp-hpgl", diff --git a/SessionUtilitiesKit/Media/NSData+Image.m b/SessionUtilitiesKit/Media/NSData+Image.m index 9d2747809..5af4610cd 100644 --- a/SessionUtilitiesKit/Media/NSData+Image.m +++ b/SessionUtilitiesKit/Media/NSData+Image.m @@ -2,6 +2,8 @@ #import "MIMETypeUtil.h" #import "OWSFileSystem.h" #import +#import +#import #import NS_ASSUME_NONNULL_BEGIN @@ -13,8 +15,18 @@ typedef NS_ENUM(NSInteger, ImageFormat) { ImageFormat_Tiff, ImageFormat_Jpeg, ImageFormat_Bmp, + ImageFormat_Webp, + ImageFormat_Heic, + ImageFormat_Heif, }; +#pragma mark - + +typedef struct { + CGSize pixelSize; + CGFloat depthBytes; +} ImageDimensionInfo; + // FIXME: Refactor all of these to be in Swift against 'Data' @implementation NSData (Image) @@ -47,40 +59,47 @@ typedef NS_ENUM(NSInteger, ImageFormat) { return YES; } -+ (BOOL)ows_isValidImageAtPath:(NSString *)filePath mimeType:(nullable NSString *)mimeType ++ (nullable NSData *)ows_validImageDataAtPath:(NSString *)filePath mimeType:(nullable NSString *)mimeType { if (mimeType.length < 1) { NSString *fileExtension = [filePath pathExtension].lowercaseString; mimeType = [MIMETypeUtil mimeTypeForFileExtension:fileExtension]; } if (mimeType.length < 1) { - return NO; + return nil; } NSNumber *_Nullable fileSize = [OWSFileSystem fileSizeOfPath:filePath]; if (!fileSize) { - return NO; + return nil; } BOOL isAnimated = [MIMETypeUtil isSupportedAnimatedMIMEType:mimeType]; if (isAnimated) { if (fileSize.unsignedIntegerValue > OWSMediaUtils.kMaxFileSizeAnimatedImage) { - return NO; + return nil; } } else if ([MIMETypeUtil isSupportedImageMIMEType:mimeType]) { if (fileSize.unsignedIntegerValue > OWSMediaUtils.kMaxFileSizeImage) { - return NO; + return nil; } } else { - return NO; + return nil; } NSError *error = nil; - NSData *_Nullable data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error]; - if (!data || error) { + return [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error]; +} + ++ (BOOL)ows_isValidImageAtPath:(NSString *)filePath mimeType:(nullable NSString *)mimeType +{ + NSData *_Nullable data = [NSData ows_validImageDataAtPath:filePath mimeType:mimeType]; + if (!data) { return NO; } - if (![self ows_hasValidImageDimensionsAtPath:filePath isAnimated:isAnimated]) { + BOOL isAnimated = [MIMETypeUtil isSupportedAnimatedMIMEType:mimeType]; + + if (![self ows_hasValidImageDimensionsAtPath:filePath withData:data mimeType:mimeType isAnimated:isAnimated]) { return NO; } @@ -93,45 +112,98 @@ typedef NS_ENUM(NSInteger, ImageFormat) { if (imageSource == NULL) { return NO; } - BOOL result = [NSData ows_hasValidImageDimensionWithImageSource:imageSource isAnimated:isAnimated]; + + ImageDimensionInfo dimensionInfo = [NSData ows_imageDimensionWithImageSource:imageSource isAnimated:isAnimated]; CFRelease(imageSource); - return result; + + return [NSData ows_isValidImageDimension:dimensionInfo.pixelSize depthBytes:dimensionInfo.depthBytes isAnimated:isAnimated]; +} + ++ (BOOL)ows_hasValidImageDimensionsAtPath:(NSString *)path withData:(NSData *)data mimeType:(nullable NSString *)mimeType isAnimated:(BOOL)isAnimated +{ + CGSize imageDimensions = [self ows_imageDimensionsAtPath:path withData:data mimeType:mimeType isAnimated:isAnimated]; + + if (imageDimensions.width < 1 || imageDimensions.height < 1) { + return NO; + } + + return YES; } -+ (BOOL)ows_hasValidImageDimensionsAtPath:(NSString *)path isAnimated:(BOOL)isAnimated ++ (CGSize)ows_imageDimensionsAtPath:(NSString *)path withData:(nullable NSData *)data mimeType:(nullable NSString *)mimeType isAnimated:(BOOL)isAnimated { NSURL *url = [NSURL fileURLWithPath:path]; if (!url) { - return NO; + return CGSizeZero; + } + + if ([mimeType isEqualToString:OWSMimeTypeImageWebp]) { + NSData *targetData = data; + + if (targetData == nil) { + NSError *error = nil; + NSData *_Nullable loadedData = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedIfSafe error:&error]; + + if (!data || error) { + return CGSizeZero; + } + + targetData = loadedData; + } + + CGSize imageSize = [data sizeForWebpData]; + + if (imageSize.width < 1 || imageSize.height < 1) { + return CGSizeZero; + } + + const CGFloat kExpectedBytePerPixel = 4; + CGFloat kMaxValidImageDimension = OWSMediaUtils.kMaxAnimatedImageDimensions; + CGFloat kMaxBytes = kMaxValidImageDimension * kMaxValidImageDimension * kExpectedBytePerPixel; + + if (data.length > kMaxBytes) { + return CGSizeZero; + } + + return imageSize; } CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL); if (imageSource == NULL) { - return NO; + return CGSizeZero; } - BOOL result = [self ows_hasValidImageDimensionWithImageSource:imageSource isAnimated:isAnimated]; + + ImageDimensionInfo dimensionInfo = [self ows_imageDimensionWithImageSource:imageSource isAnimated:isAnimated]; CFRelease(imageSource); - return result; + + if (![self ows_isValidImageDimension:dimensionInfo.pixelSize depthBytes:dimensionInfo.depthBytes isAnimated:isAnimated]) { + return CGSizeZero; + } + + return dimensionInfo.pixelSize; } -+ (BOOL)ows_hasValidImageDimensionWithImageSource:(CGImageSourceRef)imageSource isAnimated:(BOOL)isAnimated ++ (ImageDimensionInfo)ows_imageDimensionWithImageSource:(CGImageSourceRef)imageSource isAnimated:(BOOL)isAnimated { NSDictionary *imageProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); + ImageDimensionInfo info; + info.pixelSize = CGSizeZero; + info.depthBytes = 0; if (!imageProperties) { - return NO; + return info; } NSNumber *widthNumber = imageProperties[(__bridge NSString *)kCGImagePropertyPixelWidth]; if (!widthNumber) { - return NO; + return info; } CGFloat width = widthNumber.floatValue; NSNumber *heightNumber = imageProperties[(__bridge NSString *)kCGImagePropertyPixelHeight]; if (!heightNumber) { - return NO; + return info; } CGFloat height = heightNumber.floatValue; @@ -139,7 +211,7 @@ typedef NS_ENUM(NSInteger, ImageFormat) { * key is a CFNumberRef. */ NSNumber *depthNumber = imageProperties[(__bridge NSString *)kCGImagePropertyDepth]; if (!depthNumber) { - return NO; + return info; } NSUInteger depthBits = depthNumber.unsignedIntegerValue; // This should usually be 1. @@ -149,13 +221,27 @@ typedef NS_ENUM(NSInteger, ImageFormat) { * The value of this key is CFStringRef. */ NSString *colorModel = imageProperties[(__bridge NSString *)kCGImagePropertyColorModel]; if (!colorModel) { - return NO; + return info; } if (![colorModel isEqualToString:(__bridge NSString *)kCGImagePropertyColorModelRGB] && ![colorModel isEqualToString:(__bridge NSString *)kCGImagePropertyColorModelGray]) { + return info; + } + + // Update the struct to return + info.pixelSize = CGSizeMake(width, height); + info.depthBytes = depthBytes; + + return info; +} + ++ (BOOL)ows_isValidImageDimension:(CGSize)imageSize depthBytes:(CGFloat)depthBytes isAnimated:(BOOL)isAnimated +{ + if (imageSize.width < 1 || imageSize.height < 1 || depthBytes < 1) { + // Invalid metadata. return NO; } - + // We only support (A)RGB and (A)Grayscale, so worst case is 4. const CGFloat kWorseCastComponentsPerPixel = 4; CGFloat bytesPerPixel = kWorseCastComponentsPerPixel * depthBytes; @@ -164,7 +250,7 @@ typedef NS_ENUM(NSInteger, ImageFormat) { CGFloat kMaxValidImageDimension = (isAnimated ? OWSMediaUtils.kMaxAnimatedImageDimensions : OWSMediaUtils.kMaxStillImageDimensions); CGFloat kMaxBytes = kMaxValidImageDimension * kMaxValidImageDimension * kExpectedBytePerPixel; - CGFloat actualBytes = width * height * bytesPerPixel; + CGFloat actualBytes = imageSize.width * imageSize.height * bytesPerPixel; if (actualBytes > kMaxBytes) { return NO; } @@ -205,6 +291,12 @@ typedef NS_ENUM(NSInteger, ImageFormat) { case ImageFormat_Bmp: return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageBmp1] || [mimeType isEqualToString:OWSMimeTypeImageBmp2]); + case ImageFormat_Webp: + return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageWebp]); + case ImageFormat_Heic: + return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageHeic]); + case ImageFormat_Heif: + return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageHeif]); } } @@ -235,9 +327,52 @@ typedef NS_ENUM(NSInteger, ImageFormat) { } else if (byte0 == 0x49 && byte1 == 0x49) { // Intel byte order TIFF return ImageFormat_Tiff; + } else if (byte0 == 0x52 && byte1 == 0x49) { + // First two letters of RIFF tag. + return ImageFormat_Webp; + } + + return [self ows_guessHighEfficiencyImageFormat]; +} + +- (ImageFormat)ows_guessHighEfficiencyImageFormat +{ + // A HEIF image file has the first 16 bytes like + // 0000 0018 6674 7970 6865 6963 0000 0000 + // so in this case the 5th to 12th bytes shall make a string of "ftypheic" + const NSUInteger kHeifHeaderStartsAt = 4; + const NSUInteger kHeifBrandStartsAt = 8; + // We support "heic", "mif1" or "msf1". Other brands are invalid for us for now. + // The length is 4 + 1 because the brand must be terminated with a null. + // Include the null in the comparison to prevent a bogus brand like "heicfake" + // from being considered valid. + const NSUInteger kHeifSupportedBrandLength = 5; + const NSUInteger kTotalHeaderLength = kHeifBrandStartsAt - kHeifHeaderStartsAt + kHeifSupportedBrandLength; + if (self.length < kHeifBrandStartsAt + kHeifSupportedBrandLength) { + return ImageFormat_Unknown; } return ImageFormat_Unknown; + // These are the brands of HEIF formatted files that are renderable by CoreGraphics + const NSString *kHeifBrandHeaderHeic = @"ftypheic\0"; + const NSString *kHeifBrandHeaderHeif = @"ftypmif1\0"; + const NSString *kHeifBrandHeaderHeifStream = @"ftypmsf1\0"; + + // Pull the string from the header and compare it with the supported formats + unsigned char bytes[kTotalHeaderLength]; + [self getBytes:&bytes range:NSMakeRange(kHeifHeaderStartsAt, kTotalHeaderLength)]; + NSData *data = [[NSData alloc] initWithBytes:bytes length:kTotalHeaderLength]; + NSString *marker = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + if ([kHeifBrandHeaderHeic isEqualToString:marker]) { + return ImageFormat_Heic; + } else if ([kHeifBrandHeaderHeif isEqualToString:marker]) { + return ImageFormat_Heif; + } else if ([kHeifBrandHeaderHeifStream isEqualToString:marker]) { + return ImageFormat_Heif; + } else { + return ImageFormat_Unknown; + } } - (NSString *_Nullable)ows_guessMimeType @@ -304,9 +439,18 @@ typedef NS_ENUM(NSInteger, ImageFormat) { + (CGSize)imageSizeForFilePath:(NSString *)filePath mimeType:(NSString *)mimeType { - if (![NSData ows_isValidImageAtPath:filePath mimeType:mimeType]) { + NSData *_Nullable data = [NSData ows_validImageDataAtPath:filePath mimeType:mimeType]; + if (!data) { return CGSizeZero; } + + BOOL isAnimated = [MIMETypeUtil isSupportedAnimatedMIMEType:mimeType]; + CGSize pixelSize = [NSData ows_imageDimensionsAtPath:filePath withData:data mimeType:mimeType isAnimated:isAnimated]; + + if (pixelSize.width > 0 && pixelSize.height > 0 && [mimeType isEqualToString:OWSMimeTypeImageWebp]) { + return pixelSize; + } + NSURL *url = [NSURL fileURLWithPath:filePath]; // With CGImageSource we avoid loading the whole image into memory. @@ -386,6 +530,42 @@ typedef NS_ENUM(NSInteger, ImageFormat) { return result; } +// MARK: - Webp + ++ (CGSize)sizeForWebpFilePath:(NSString *)filePath +{ + NSError *error = nil; + NSData *_Nullable data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error]; + if (!data || error) { + return CGSizeZero; + } + return [data sizeForWebpData]; +} + +- (CGSize)sizeForWebpData +{ + WebPData webPData = { 0 }; + webPData.bytes = self.bytes; + webPData.size = self.length; + WebPDemuxer *demuxer = WebPDemux(&webPData); + + if (!demuxer) { + return CGSizeZero; + } + + CGFloat canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); + CGFloat canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); + CGFloat frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT); + + WebPDemuxDelete(demuxer); + + if (canvasWidth > 0 && canvasHeight > 0 && frameCount > 0) { + return CGSizeMake(canvasWidth, canvasHeight); + } + + return CGSizeZero; +} + @end NS_ASSUME_NONNULL_END From 775cc4f156cac2af483bf51650eb2d00ba92a6d0 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Fri, 29 Jul 2022 15:27:53 +1000 Subject: [PATCH 2/5] Increased build number --- Session.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 985a383ac..ad26299c1 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -6814,7 +6814,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 360; + CURRENT_PROJECT_VERSION = 361; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -6886,7 +6886,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 360; + CURRENT_PROJECT_VERSION = 361; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", From fde19efc1386a5baccd990b659049c366b6aee84 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Fri, 29 Jul 2022 15:29:20 +1000 Subject: [PATCH 3/5] Fixed a duplicate dependency issue --- Podfile | 2 -- Podfile.lock | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Podfile b/Podfile index a8b57ea29..ae568f035 100644 --- a/Podfile +++ b/Podfile @@ -23,7 +23,6 @@ abstract_target 'GlobalDependencies' do pod 'Reachability' pod 'PureLayout', '~> 3.1.8' pod 'NVActivityIndicatorView' - pod 'YYImage', git: 'https://github.com/signalapp/YYImage' pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage' pod 'ZXingObjC' pod 'DifferenceKit' @@ -52,7 +51,6 @@ abstract_target 'GlobalDependencies' do pod 'Reachability' pod 'SAMKeychain' pod 'SwiftProtobuf', '~> 1.5.0' - pod 'YYImage', git: 'https://github.com/signalapp/YYImage' pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage' pod 'DifferenceKit' end diff --git a/Podfile.lock b/Podfile.lock index 58c3a3735..89c8650fc 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -130,8 +130,6 @@ PODS: - YapDatabase/SQLCipher/Core - YapDatabase/SQLCipher/Extensions/View (3.1.1): - YapDatabase/SQLCipher/Core - - YYImage (1.0.4): - - YYImage/Core (= 1.0.4) - YYImage/Core (1.0.4) - YYImage/libwebp (1.0.4): - libwebp @@ -160,7 +158,6 @@ DEPENDENCIES: - SwiftProtobuf (~> 1.5.0) - WebRTC-lib - YapDatabase/SQLCipher (from `https://github.com/oxen-io/session-ios-yap-database.git`, branch `signal-release`) - - YYImage (from `https://github.com/signalapp/YYImage`) - YYImage/libwebp (from `https://github.com/signalapp/YYImage`) - ZXingObjC @@ -245,6 +242,6 @@ SPEC CHECKSUMS: YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331 ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 456facc7043447a9c67733cf8846ec62afff8ea8 +PODFILE CHECKSUM: f0857369c4831b2e5c1946345e76e493f3286805 COCOAPODS: 1.11.3 From b468efc33bd075414269ff32858f0ee720791a1b Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Mon, 1 Aug 2022 10:05:30 +1000 Subject: [PATCH 4/5] Updated the GarbageCollectionJob to log the number of files it removes --- SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift b/SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift index 21abafe58..0777de7a2 100644 --- a/SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift +++ b/SessionMessagingKit/Jobs/Types/GarbageCollectionJob.swift @@ -372,6 +372,8 @@ public enum GarbageCollectionJob: JobExecutor { } catch { deletionErrors.append(error) } } + + SNLog("[GarbageCollectionJob] Removed \(orphanedAttachmentFiles.count) orphaned attachment\(orphanedAttachmentFiles.count == 1 ? "" : "s")") } // Orphaned profile avatar files (actual deletion) @@ -393,6 +395,8 @@ public enum GarbageCollectionJob: JobExecutor { } catch { deletionErrors.append(error) } } + + SNLog("[GarbageCollectionJob] Removed \(orphanedAvatarFiles.count) orphaned avatar image\(orphanedAvatarFiles.count == 1 ? "" : "s")") } // Report a single file deletion as a job failure (even if other content was successfully removed) From 8f3e7fc36ae420477289a4066ee3616ac33544c0 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Mon, 1 Aug 2022 16:53:05 +1000 Subject: [PATCH 5/5] Removed the debug code and added a migration to remove the old YDB Fixed a typo --- Session.xcodeproj/project.pbxproj | 4 ++ Session/Meta/AppDelegate.swift | 72 ++++++++----------- .../Translations/de.lproj/Localizable.strings | 2 +- .../Translations/en.lproj/Localizable.strings | 2 +- .../Translations/es.lproj/Localizable.strings | 2 +- .../Translations/fa.lproj/Localizable.strings | 2 +- .../Translations/fi.lproj/Localizable.strings | 2 +- .../Translations/fr.lproj/Localizable.strings | 2 +- .../Translations/hi.lproj/Localizable.strings | 2 +- .../Translations/hr.lproj/Localizable.strings | 2 +- .../id-ID.lproj/Localizable.strings | 2 +- .../Translations/it.lproj/Localizable.strings | 2 +- .../Translations/ja.lproj/Localizable.strings | 2 +- .../Translations/nl.lproj/Localizable.strings | 2 +- .../Translations/pl.lproj/Localizable.strings | 2 +- .../pt_BR.lproj/Localizable.strings | 2 +- .../Translations/ru.lproj/Localizable.strings | 2 +- .../Translations/si.lproj/Localizable.strings | 2 +- .../Translations/sk.lproj/Localizable.strings | 2 +- .../Translations/sv.lproj/Localizable.strings | 2 +- .../Translations/th.lproj/Localizable.strings | 2 +- .../vi-VN.lproj/Localizable.strings | 2 +- .../zh-Hant.lproj/Localizable.strings | 2 +- .../zh_CN.lproj/Localizable.strings | 2 +- Session/Path/PathVC.swift | 1 + Session/Settings/SettingsVC.swift | 20 ------ SessionMessagingKit/Configuration.swift | 3 + .../Migrations/_004_RemoveLegacyYDB.swift | 20 ++++++ .../Jobs/Types/DisappearingMessagesJob.swift | 34 --------- .../Notifications/PushNotificationAPI.swift | 4 +- SessionUtilitiesKit/Database/Storage.swift | 30 +------- 31 files changed, 84 insertions(+), 148 deletions(-) create mode 100644 SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index ad26299c1..f6719afe2 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -800,6 +800,7 @@ FDF2220B2818F38D000A4995 /* SessionApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF2220A2818F38D000A4995 /* SessionApp.swift */; }; FDF2220F281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF2220E281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift */; }; FDF22211281B5E0B000A4995 /* TableRecord+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF22210281B5E0B000A4995 /* TableRecord+Utilities.swift */; }; + FDF40CDE2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF40CDD2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift */; }; FDFD645927F26C6800808CA1 /* Array+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Utilities.swift */; }; FDFD645B27F26D4600808CA1 /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */; }; FDFD645D27F273F300808CA1 /* MockGeneralCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */; }; @@ -1837,6 +1838,7 @@ FDF2220A2818F38D000A4995 /* SessionApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionApp.swift; sourceTree = ""; }; FDF2220E281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueryInterfaceRequest+Utilities.swift"; sourceTree = ""; }; FDF22210281B5E0B000A4995 /* TableRecord+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TableRecord+Utilities.swift"; sourceTree = ""; }; + FDF40CDD2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _004_RemoveLegacyYDB.swift; sourceTree = ""; }; FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = ""; }; FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGeneralCache.swift; sourceTree = ""; }; FDFDE123282D04F20098B17F /* MediaDismissAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaDismissAnimationController.swift; sourceTree = ""; }; @@ -3451,6 +3453,7 @@ FD17D79527F3E04600122BE0 /* _001_InitialSetupMigration.swift */, FDF0B7412804EA4F004C14C5 /* _002_SetupStandardJobs.swift */, FD17D79827F40AB800122BE0 /* _003_YDBToGRDBMigration.swift */, + FDF40CDD2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift */, ); path = Migrations; sourceTree = ""; @@ -5089,6 +5092,7 @@ FD09799927FFC1A300936362 /* Attachment.swift in Sources */, FD245C5F2850662200B966DD /* OWSWindowManager.m in Sources */, C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */, + FDF40CDE2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift in Sources */, FDF0B74928060D13004C14C5 /* QuotedReplyModel.swift in Sources */, FD77289A284AF1BD0018502F /* Sodium+Utilities.swift in Sources */, FD5C7309285007920029977D /* BlindedIdLookup.swift in Sources */, diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 7067ea3db..41b106584 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -240,54 +240,40 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD private func showFailedMigrationAlert(error: Error?) { let alert = UIAlertController( title: "Session", - message: ((error as? StorageError) == StorageError.devRemigrationRequired ? - "The database has changed since the last version and you need to re-migrate (this will close the app and migrate on the next launch)" : - "DATABASE_MIGRATION_FAILED".localized() - ), + message: "DATABASE_MIGRATION_FAILED".localized(), preferredStyle: .alert ) - - switch (error as? StorageError) { - case .devRemigrationRequired: - alert.addAction(UIAlertAction(title: "Re-Migrate Database", style: .default) { _ in - Storage.deleteDatabaseFiles() - try? Storage.deleteDbKeys() - exit(1) - }) - - default: - alert.addAction(UIAlertAction(title: "modal_share_logs_title".localized(), style: .default) { _ in - ShareLogsModal.shareLogs(from: alert) { [weak self] in + alert.addAction(UIAlertAction(title: "modal_share_logs_title".localized(), style: .default) { _ in + ShareLogsModal.shareLogs(from: alert) { [weak self] in + self?.showFailedMigrationAlert(error: error) + } + }) + alert.addAction(UIAlertAction(title: "vc_restore_title".localized(), style: .destructive) { _ in + // Remove the legacy database and any message hashes that have been migrated to the new DB + try? SUKLegacy.deleteLegacyDatabaseFilesAndKey() + + Storage.shared.write { db in + try SnodeReceivedMessageInfo.deleteAll(db) + } + + // The re-run the migration (should succeed since there is no data) + AppSetup.runPostSetupMigrations( + migrationProgressChanged: { [weak self] progress, minEstimatedTotalTime in + self?.loadingViewController?.updateProgress( + progress: progress, + minEstimatedTotalTime: minEstimatedTotalTime + ) + }, + migrationsCompletion: { [weak self] error, needsConfigSync in + guard error == nil else { self?.showFailedMigrationAlert(error: error) + return } - }) - alert.addAction(UIAlertAction(title: "vc_restore_title".localized(), style: .destructive) { _ in - // Remove the legacy database and any message hashes that have been migrated to the new DB - try? SUKLegacy.deleteLegacyDatabaseFilesAndKey() - Storage.shared.write { db in - try SnodeReceivedMessageInfo.deleteAll(db) - } - - // The re-run the migration (should succeed since there is no data) - AppSetup.runPostSetupMigrations( - migrationProgressChanged: { [weak self] progress, minEstimatedTotalTime in - self?.loadingViewController?.updateProgress( - progress: progress, - minEstimatedTotalTime: minEstimatedTotalTime - ) - }, - migrationsCompletion: { [weak self] error, needsConfigSync in - guard error == nil else { - self?.showFailedMigrationAlert(error: error) - return - } - - self?.completePostMigrationSetup(needsConfigSync: needsConfigSync) - } - ) - }) - } + self?.completePostMigrationSetup(needsConfigSync: needsConfigSync) + } + ) + }) alert.addAction(UIAlertAction(title: "Close", style: .default) { _ in DDLog.flushLog() diff --git a/Session/Meta/Translations/de.lproj/Localizable.strings b/Session/Meta/Translations/de.lproj/Localizable.strings index 60196d5df..d12868e54 100644 --- a/Session/Meta/Translations/de.lproj/Localizable.strings +++ b/Session/Meta/Translations/de.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index 70292f244..6dd37a8bb 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/es.lproj/Localizable.strings b/Session/Meta/Translations/es.lproj/Localizable.strings index eda8d36e6..e5d4f9d44 100644 --- a/Session/Meta/Translations/es.lproj/Localizable.strings +++ b/Session/Meta/Translations/es.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/fa.lproj/Localizable.strings b/Session/Meta/Translations/fa.lproj/Localizable.strings index 1177dd54e..f333f1b70 100644 --- a/Session/Meta/Translations/fa.lproj/Localizable.strings +++ b/Session/Meta/Translations/fa.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/fi.lproj/Localizable.strings b/Session/Meta/Translations/fi.lproj/Localizable.strings index 0ccbf8630..b9e9b8267 100644 --- a/Session/Meta/Translations/fi.lproj/Localizable.strings +++ b/Session/Meta/Translations/fi.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/fr.lproj/Localizable.strings b/Session/Meta/Translations/fr.lproj/Localizable.strings index 71bba7d52..f99011932 100644 --- a/Session/Meta/Translations/fr.lproj/Localizable.strings +++ b/Session/Meta/Translations/fr.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/hi.lproj/Localizable.strings b/Session/Meta/Translations/hi.lproj/Localizable.strings index e74664705..eca955776 100644 --- a/Session/Meta/Translations/hi.lproj/Localizable.strings +++ b/Session/Meta/Translations/hi.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/hr.lproj/Localizable.strings b/Session/Meta/Translations/hr.lproj/Localizable.strings index f8a01cf6a..bb4556500 100644 --- a/Session/Meta/Translations/hr.lproj/Localizable.strings +++ b/Session/Meta/Translations/hr.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/id-ID.lproj/Localizable.strings b/Session/Meta/Translations/id-ID.lproj/Localizable.strings index 65e9f6828..6322bf9f2 100644 --- a/Session/Meta/Translations/id-ID.lproj/Localizable.strings +++ b/Session/Meta/Translations/id-ID.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/it.lproj/Localizable.strings b/Session/Meta/Translations/it.lproj/Localizable.strings index 6ca09139f..eedf2137b 100644 --- a/Session/Meta/Translations/it.lproj/Localizable.strings +++ b/Session/Meta/Translations/it.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/ja.lproj/Localizable.strings b/Session/Meta/Translations/ja.lproj/Localizable.strings index 7467457c3..253f72de8 100644 --- a/Session/Meta/Translations/ja.lproj/Localizable.strings +++ b/Session/Meta/Translations/ja.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/nl.lproj/Localizable.strings b/Session/Meta/Translations/nl.lproj/Localizable.strings index 41354fa3c..d58ebff00 100644 --- a/Session/Meta/Translations/nl.lproj/Localizable.strings +++ b/Session/Meta/Translations/nl.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/pl.lproj/Localizable.strings b/Session/Meta/Translations/pl.lproj/Localizable.strings index b480bbe12..5a08f7e35 100644 --- a/Session/Meta/Translations/pl.lproj/Localizable.strings +++ b/Session/Meta/Translations/pl.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings index d785a31be..a848b7587 100644 --- a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings +++ b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/ru.lproj/Localizable.strings b/Session/Meta/Translations/ru.lproj/Localizable.strings index 07a7951f2..a3984387e 100644 --- a/Session/Meta/Translations/ru.lproj/Localizable.strings +++ b/Session/Meta/Translations/ru.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/si.lproj/Localizable.strings b/Session/Meta/Translations/si.lproj/Localizable.strings index 6c6d14467..cacf3f5d1 100644 --- a/Session/Meta/Translations/si.lproj/Localizable.strings +++ b/Session/Meta/Translations/si.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/sk.lproj/Localizable.strings b/Session/Meta/Translations/sk.lproj/Localizable.strings index 76e709953..102514ede 100644 --- a/Session/Meta/Translations/sk.lproj/Localizable.strings +++ b/Session/Meta/Translations/sk.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/sv.lproj/Localizable.strings b/Session/Meta/Translations/sv.lproj/Localizable.strings index eb846d574..da8933ad7 100644 --- a/Session/Meta/Translations/sv.lproj/Localizable.strings +++ b/Session/Meta/Translations/sv.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/th.lproj/Localizable.strings b/Session/Meta/Translations/th.lproj/Localizable.strings index bbc550448..b8f6e41ac 100644 --- a/Session/Meta/Translations/th.lproj/Localizable.strings +++ b/Session/Meta/Translations/th.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings index f7be858a2..fd93b1f63 100644 --- a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings +++ b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings index ecf86aca8..f936369d4 100644 --- a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings index 44a6ea8d4..9145c3fe8 100644 --- a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings @@ -652,7 +652,7 @@ "DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred"; "DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later"; "LOADING_CONVERSATIONS" = "Loading Conversations..."; -"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; +"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks"; "CHATS_TITLE" = "Chats"; "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; diff --git a/Session/Path/PathVC.swift b/Session/Path/PathVC.swift index 14d09dfa2..b3fdb9b37 100644 --- a/Session/Path/PathVC.swift +++ b/Session/Path/PathVC.swift @@ -1,5 +1,6 @@ import NVActivityIndicatorView import UIKit +import SessionMessagingKit final class PathVC : BaseVC { diff --git a/Session/Settings/SettingsVC.swift b/Session/Settings/SettingsVC.swift index a58081392..27d4ecec0 100644 --- a/Session/Settings/SettingsVC.swift +++ b/Session/Settings/SettingsVC.swift @@ -298,8 +298,6 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate { return button } - let debugReMigrateButton = getSettingButton(withTitle: "DEBUG - Re-Migrate Database", color: Colors.destructive, action: #selector(remigrateDatabase)) - let pathButton = getSettingButton(withTitle: NSLocalizedString("vc_path_title", comment: ""), color: Colors.text, action: #selector(showPath)) let pathStatusView = PathStatusView() pathStatusView.set(.width, to: PathStatusView.size) @@ -310,8 +308,6 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate { pathStatusView.autoVCenterInSuperview() return [ - getSeparator(), - debugReMigrateButton, getSeparator(), pathButton, getSeparator(), @@ -603,22 +599,6 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate { navigationController!.present(shareVC, animated: true, completion: nil) } - @objc private func remigrateDatabase() { - let alert = UIAlertController( - title: "Session", - message: "Are you sure you want to re-migrate from your old database state?\n\nWarning: If you had a migration error and picked the \"Restore your account\" option this will result in a complete loss of data and the need to manually restore from the seed", - preferredStyle: .alert - ) - alert.addAction(UIAlertAction(title: "Re-migrate", style: .destructive) { _ in - Storage.deleteDatabaseFiles() - try? Storage.deleteDbKeys() - exit(1) - }) - alert.addAction(UIAlertAction(title: "Cancel", style: .default)) - - navigationController?.present(alert, animated: true) - } - @objc private func showPath() { let pathVC = PathVC() navigationController!.pushViewController(pathVC, animated: true) diff --git a/SessionMessagingKit/Configuration.swift b/SessionMessagingKit/Configuration.swift index bdc23dc0d..22a726c2e 100644 --- a/SessionMessagingKit/Configuration.swift +++ b/SessionMessagingKit/Configuration.swift @@ -12,6 +12,9 @@ public enum SNMessagingKit { // Just to make the external API nice ], [ _003_YDBToGRDBMigration.self + ], + [ + _004_RemoveLegacyYDB.self ] ] ) diff --git a/SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift b/SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift new file mode 100644 index 000000000..97aa7462e --- /dev/null +++ b/SessionMessagingKit/Database/Migrations/_004_RemoveLegacyYDB.swift @@ -0,0 +1,20 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Foundation +import GRDB +import Curve25519Kit +import SessionUtilitiesKit +import SessionSnodeKit + +/// This migration removes the legacy YapDatabase files +enum _004_RemoveLegacyYDB: Migration { + static let target: TargetMigrations.Identifier = .messagingKit + static let identifier: String = "RemoveLegacyYDB" + static let needsConfigSync: Bool = false + static let minExpectedRunDuration: TimeInterval = 0.1 + + static func migrate(_ db: Database) throws { + try? SUKLegacy.deleteLegacyDatabaseFilesAndKey() + Storage.update(progress: 1, for: self, in: target) // In case this is the last migration + } +} diff --git a/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift b/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift index 4bad27849..9ed31c7e7 100644 --- a/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift +++ b/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift @@ -41,40 +41,6 @@ public enum DisappearingMessagesJob: JobExecutor { // The 'if' is only there to prevent the "variable never read" warning from showing if backgroundTask != nil { backgroundTask = nil } - - // TODO: Remove this for the final build - Storage.shared.writeAsync { db in - // Re-process all WebP images, and images with no width/height values to update their validity state - let supportedVisualMediaMimeTypes: Set = MIMETypeUtil.supportedImageMIMETypes() - .appending(contentsOf: MIMETypeUtil.supportedAnimatedImageMIMETypes()) - .appending(contentsOf: MIMETypeUtil.supportedVideoMIMETypes()) - .asSet() - let attachments: [Attachment] = try Attachment - .filter(Attachment.Columns.state == Attachment.State.downloaded) - .filter( - Attachment.Columns.contentType == "image/webp" || ( - ( - Attachment.Columns.width == nil || - Attachment.Columns.height == nil - ) && - supportedVisualMediaMimeTypes.contains(Attachment.Columns.contentType) - ) - ) - .filter( - !Attachment.Columns.isValid || - !Attachment.Columns.isVisualMedia || - Attachment.Columns.width == nil || - Attachment.Columns.height == nil - ) - .fetchAll(db) - - if !attachments.isEmpty { - attachments.forEach { attachment in - _ = try? attachment.with(state: attachment.state).saved(db) - } - } - } - // TODO: Remove this for the final build } } diff --git a/SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift b/SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift index 3ad5dbcc9..a63af0b54 100644 --- a/SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift +++ b/SessionMessagingKit/Sending & Receiving/Notifications/PushNotificationAPI.swift @@ -128,8 +128,8 @@ public final class PushNotificationAPI : NSObject { promises.append( attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) { OnionRequestAPI.sendOnionRequest(request, to: server, using: .v2, with: serverPublicKey) - .map2 { _, response -> Void in - guard let response: UpdateRegistrationResponse = try? response?.decoded(as: UpdateRegistrationResponse.self) else { + .map2 { _, data -> Void in + guard let response: UpdateRegistrationResponse = try? data?.decoded(as: UpdateRegistrationResponse.self) else { return SNLog("Couldn't register device token.") } guard response.body.code != 0 else { diff --git a/SessionUtilitiesKit/Database/Storage.swift b/SessionUtilitiesKit/Database/Storage.swift index 92ba77ec0..87e283918 100644 --- a/SessionUtilitiesKit/Database/Storage.swift +++ b/SessionUtilitiesKit/Database/Storage.swift @@ -186,30 +186,7 @@ public final class Storage { SNLog("[Migration Error] Migration failed with error: \(error)") } - // TODO: Remove this once everyone has updated - var finalError: Error? = error - let jobTableInfo: [Row] = (try? Row.fetchAll(db, sql: "PRAGMA table_info(\(Job.databaseTableName))")) - .defaulting(to: []) - if !jobTableInfo.contains(where: { $0["name"] == "shouldSkipLaunchBecomeActive" }) { - finalError = StorageError.devRemigrationRequired - } - // Forcibly change any 'infoUpdates' on open groups from '-1' to '0' (-1 is invalid) - try? db.execute(literal: """ - UPDATE openGroup - SET infoUpdates = 0 - WHERE openGroup.infoUpdates = -1 - """) - // TODO: Remove this once everyone has updated - let openGroupTableInfo: [Row] = (try? Row.fetchAll(db, sql: "PRAGMA table_info(openGroup)")) - .defaulting(to: []) - if !openGroupTableInfo.contains(where: { $0["name"] == "pollFailureCount" }) { - try? db.execute(literal: """ - ALTER TABLE openGroup - ADD pollFailureCount INTEGER NOT NULL DEFAULT 0 - """) - } - - onComplete(finalError, needsConfigSync) + onComplete(error, needsConfigSync) } // Note: The non-async migration should only be used for unit tests @@ -314,14 +291,13 @@ public final class Storage { try? self.deleteDbKeys() } - // TODO: Change these back to private - public/*private*/ static func deleteDatabaseFiles() { + private static func deleteDatabaseFiles() { OWSFileSystem.deleteFile(databasePath) OWSFileSystem.deleteFile(databasePathShm) OWSFileSystem.deleteFile(databasePathWal) } - public/*private*/ static func deleteDbKeys() throws { + private static func deleteDbKeys() throws { try SSKDefaultKeychainStorage.shared.remove(service: keychainService, key: dbCipherKeySpecKey) }