mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Lazy attachment restores.
This commit is contained in:
parent
2a31223b1b
commit
d0c691bb7f
17 changed files with 422 additions and 30 deletions
|
@ -205,6 +205,7 @@
|
|||
34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D1F0BF1F8EC1760066283D /* MessageRecipientStatusUtils.swift */; };
|
||||
34D2CCD4206294B900CB1A14 /* OWSScreenLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D2CCD3206294B900CB1A14 /* OWSScreenLock.swift */; };
|
||||
34D2CCDA2062E7D000CB1A14 /* OWSScreenLockUI.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D2CCD92062E7D000CB1A14 /* OWSScreenLockUI.m */; };
|
||||
34D2CCD220618B3000CB1A14 /* OWSBackupLazyRestoreJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D2CCD120618B2F00CB1A14 /* OWSBackupLazyRestoreJob.swift */; };
|
||||
34D5CCA91EAE3D30005515DB /* AvatarViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */; };
|
||||
34D8C0271ED3673300188D7C /* DebugUIMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0241ED3673300188D7C /* DebugUIMessages.m */; };
|
||||
34D8C0281ED3673300188D7C /* DebugUITableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0261ED3673300188D7C /* DebugUITableViewController.m */; };
|
||||
|
@ -812,6 +813,7 @@
|
|||
34D2CCD3206294B900CB1A14 /* OWSScreenLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSScreenLock.swift; sourceTree = "<group>"; };
|
||||
34D2CCD82062E7D000CB1A14 /* OWSScreenLockUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSScreenLockUI.h; sourceTree = "<group>"; };
|
||||
34D2CCD92062E7D000CB1A14 /* OWSScreenLockUI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSScreenLockUI.m; sourceTree = "<group>"; };
|
||||
34D2CCD120618B2F00CB1A14 /* OWSBackupLazyRestoreJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSBackupLazyRestoreJob.swift; sourceTree = "<group>"; };
|
||||
34D5CCA71EAE3D30005515DB /* AvatarViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvatarViewHelper.h; sourceTree = "<group>"; };
|
||||
34D5CCA81EAE3D30005515DB /* AvatarViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AvatarViewHelper.m; sourceTree = "<group>"; };
|
||||
34D8C0231ED3673300188D7C /* DebugUIMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIMessages.h; sourceTree = "<group>"; };
|
||||
|
@ -1974,6 +1976,7 @@
|
|||
34D2CCD3206294B900CB1A14 /* OWSScreenLock.swift */,
|
||||
34D2CCD82062E7D000CB1A14 /* OWSScreenLockUI.h */,
|
||||
34D2CCD92062E7D000CB1A14 /* OWSScreenLockUI.m */,
|
||||
34D2CCD120618B2F00CB1A14 /* OWSBackupLazyRestoreJob.swift */,
|
||||
4579431C1E7C8CE9008ED0C0 /* Pastelog.h */,
|
||||
4579431D1E7C8CE9008ED0C0 /* Pastelog.m */,
|
||||
450DF2041E0D74AC003D14BE /* Platform.swift */,
|
||||
|
@ -3143,6 +3146,7 @@
|
|||
458DE9D91DEE7B360071BB03 /* OWSWebRTCDataProtos.pb.m in Sources */,
|
||||
76EB063C18170B33006006FC /* NumberUtil.m in Sources */,
|
||||
451166C01FD86B98000739BA /* AccountManager.swift in Sources */,
|
||||
34D2CCD220618B3000CB1A14 /* OWSBackupLazyRestoreJob.swift in Sources */,
|
||||
3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */,
|
||||
34A55F3720485465002CC6DE /* OWS2FARegistrationViewController.m in Sources */,
|
||||
340FC8AD204DAC8D007AEB0F /* OWSLinkedDevicesTableViewController.m in Sources */,
|
||||
|
|
|
@ -418,8 +418,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
|
|||
// Every time we change or add a database view in such a way that
|
||||
// might cause a delay on launch, we need to bump this constant.
|
||||
//
|
||||
// We upgraded YapDatabase in v2.20.0 and need to regenerate all database views.
|
||||
NSString *kLastVersionWithDatabaseViewChange = @"2.20.0";
|
||||
// We added a database view in v2.23.0.
|
||||
NSString *kLastVersionWithDatabaseViewChange = @"2.23.0";
|
||||
BOOL mayNeedUpgrade = ([TSAccountManager isRegistered] && lastLaunchedAppVersion
|
||||
&& (!lastCompletedLaunchAppVersion ||
|
||||
[VersionMigrations isVersion:lastCompletedLaunchAppVersion
|
||||
|
@ -1134,6 +1134,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
|
|||
[self ensureRootViewController];
|
||||
|
||||
[OWSBackup.sharedManager setup];
|
||||
|
||||
[OWSBackupLazyRestoreJob run];
|
||||
}
|
||||
|
||||
- (void)registrationStateDidChange
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#import "NotificationsManager.h"
|
||||
#import "OWSAnyTouchGestureRecognizer.h"
|
||||
#import "OWSAudioPlayer.h"
|
||||
#import "OWSBackup.h"
|
||||
#import "OWSBackupIO.h"
|
||||
#import "OWSBezierPathView.h"
|
||||
#import "OWSCallNotificationsAdaptee.h"
|
||||
#import "OWSDatabaseMigration.h"
|
||||
|
|
|
@ -20,13 +20,15 @@ typedef NS_ENUM(NSUInteger, OWSBackupState) {
|
|||
OWSBackupState_Succeeded,
|
||||
};
|
||||
|
||||
@class OWSBackupIO;
|
||||
@class TSAttachmentStream;
|
||||
@class TSThread;
|
||||
|
||||
@interface OWSBackup : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
+ (instancetype)sharedManager;
|
||||
+ (instancetype)sharedManager NS_SWIFT_NAME(shared());
|
||||
|
||||
- (void)setup;
|
||||
|
||||
|
@ -71,6 +73,16 @@ typedef NS_ENUM(NSUInteger, OWSBackupState) {
|
|||
- (void)logBackupRecords;
|
||||
- (void)clearAllCloudKitRecords;
|
||||
|
||||
#pragma mark - Lazy Restore
|
||||
|
||||
- (NSArray<TSAttachmentStream *> *)attachmentsForLazyRestore;
|
||||
|
||||
- (NSArray<NSString *> *)attachmentIdsForLazyRestore;
|
||||
|
||||
- (void)lazyRestoreAttachment:(TSAttachmentStream *)attachment
|
||||
backupIO:(OWSBackupIO *)backupIO
|
||||
completion:(OWSBackupBoolBlock)completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import "OWSBackup.h"
|
||||
#import "OWSBackupExportJob.h"
|
||||
#import "OWSBackupIO.h"
|
||||
#import "OWSBackupImportJob.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import <Curve25519Kit/Randomness.h>
|
||||
|
@ -483,6 +484,149 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Lazy Restore
|
||||
|
||||
- (NSArray<TSAttachmentStream *> *)attachmentsForLazyRestore
|
||||
{
|
||||
NSMutableArray<TSAttachmentStream *> *attachments = [NSMutableArray new];
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
id ext = [transaction ext:TSMessageDatabaseViewExtensionName];
|
||||
if (!ext) {
|
||||
OWSProdLogAndFail(@"%@ Could not load database view.", self.logTag);
|
||||
return;
|
||||
}
|
||||
|
||||
[ext enumerateKeysAndObjectsInGroup:TSLazyRestoreAttachmentsGroup
|
||||
usingBlock:^(
|
||||
NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) {
|
||||
if (![object isKindOfClass:[TSAttachmentStream class]]) {
|
||||
OWSProdLogAndFail(@"%@ Unexpected object: %@ in collection:%@",
|
||||
self.logTag,
|
||||
[object class],
|
||||
collection);
|
||||
return;
|
||||
}
|
||||
[attachments addObject:object];
|
||||
}];
|
||||
}];
|
||||
return attachments;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)attachmentIdsForLazyRestore
|
||||
{
|
||||
NSMutableArray<NSString *> *attachmentIds = [NSMutableArray new];
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
id ext = [transaction ext:TSMessageDatabaseViewExtensionName];
|
||||
if (!ext) {
|
||||
OWSProdLogAndFail(@"%@ Could not load database view.", self.logTag);
|
||||
return;
|
||||
}
|
||||
|
||||
[ext enumerateKeysInGroup:TSLazyRestoreAttachmentsGroup
|
||||
usingBlock:^(NSString *collection, NSString *key, NSUInteger index, BOOL *stop) {
|
||||
[attachmentIds addObject:key];
|
||||
}];
|
||||
}];
|
||||
return attachmentIds;
|
||||
}
|
||||
|
||||
- (void)lazyRestoreAttachment:(TSAttachmentStream *)attachment
|
||||
backupIO:(OWSBackupIO *)backupIO
|
||||
completion:(OWSBackupBoolBlock)completion
|
||||
{
|
||||
OWSAssert(attachment);
|
||||
OWSAssert(backupIO);
|
||||
OWSAssert(completion);
|
||||
|
||||
NSString *_Nullable attachmentFilePath = [attachment filePath];
|
||||
if (attachmentFilePath.length < 1) {
|
||||
DDLogError(@"%@ Attachment has invalid file path.", self.logTag);
|
||||
return completion(NO);
|
||||
}
|
||||
if ([NSFileManager.defaultManager fileExistsAtPath:attachmentFilePath]) {
|
||||
DDLogError(@"%@ Attachment already has file.", self.logTag);
|
||||
return completion(NO);
|
||||
}
|
||||
|
||||
NSString *_Nullable recordName = attachment.backupRestoreRecordName;
|
||||
NSData *_Nullable encryptionKey = attachment.backupRestoreEncryptionKey;
|
||||
if (recordName.length < 1 || encryptionKey.length < 1) {
|
||||
return completion(NO);
|
||||
}
|
||||
|
||||
// Use a predictable file path so that multiple "import backup" attempts
|
||||
// will leverage successful file downloads from previous attempts.
|
||||
//
|
||||
// TODO: This will also require imports using a predictable jobTempDirPath.
|
||||
NSString *_Nullable tempFilePath = [backupIO createTempFile];
|
||||
if (!tempFilePath) {
|
||||
return completion(NO);
|
||||
}
|
||||
|
||||
[OWSBackupAPI downloadFileFromCloudWithRecordName:recordName
|
||||
toFileUrl:[NSURL fileURLWithPath:tempFilePath]
|
||||
success:^{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self lazyRestoreAttachment:attachment
|
||||
backupIO:backupIO
|
||||
encryptedFilePath:tempFilePath
|
||||
encryptionKey:encryptionKey
|
||||
completion:completion];
|
||||
});
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
completion(NO);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)lazyRestoreAttachment:(TSAttachmentStream *)attachment
|
||||
backupIO:(OWSBackupIO *)backupIO
|
||||
encryptedFilePath:(NSString *)encryptedFilePath
|
||||
encryptionKey:(NSData *)encryptionKey
|
||||
completion:(OWSBackupBoolBlock)completion
|
||||
{
|
||||
OWSAssert(attachment);
|
||||
OWSAssert(backupIO);
|
||||
OWSAssert(encryptedFilePath.length > 0);
|
||||
OWSAssert(encryptionKey.length > 0);
|
||||
OWSAssert(completion);
|
||||
|
||||
NSData *_Nullable data = [NSData dataWithContentsOfFile:encryptedFilePath];
|
||||
if (!data) {
|
||||
DDLogError(@"%@ Could not load encrypted file.", self.logTag);
|
||||
return completion(NO);
|
||||
}
|
||||
|
||||
NSString *_Nullable decryptedFilePath = [backupIO createTempFile];
|
||||
if (!decryptedFilePath) {
|
||||
return completion(NO);
|
||||
}
|
||||
|
||||
if (![backupIO decryptFileAsFile:encryptedFilePath dstFilePath:decryptedFilePath encryptionKey:encryptionKey]) {
|
||||
DDLogError(@"%@ Could not load decrypt file.", self.logTag);
|
||||
return completion(NO);
|
||||
}
|
||||
|
||||
NSString *_Nullable attachmentFilePath = [attachment filePath];
|
||||
if (attachmentFilePath.length < 1) {
|
||||
DDLogError(@"%@ Attachment has invalid file path.", self.logTag);
|
||||
return completion(NO);
|
||||
}
|
||||
NSError *error;
|
||||
BOOL success =
|
||||
[NSFileManager.defaultManager moveItemAtPath:decryptedFilePath toPath:attachmentFilePath error:&error];
|
||||
if (!success || error) {
|
||||
DDLogError(@"%@ Attachment file could not be restored: %@.", self.logTag, error);
|
||||
return completion(NO);
|
||||
}
|
||||
|
||||
[attachment updateWithBackupRestoreComplete];
|
||||
|
||||
completion(YES);
|
||||
}
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
- (void)postDidChangeNotification
|
||||
|
|
|
@ -6,6 +6,15 @@ import Foundation
|
|||
import SignalServiceKit
|
||||
import CloudKit
|
||||
|
||||
// We don't worry about atomic writes. Each backup export
|
||||
// will diff against last successful backup.
|
||||
//
|
||||
// Note that all of our CloudKit records are immutable.
|
||||
// "Persistent" records are only uploaded once.
|
||||
// "Ephemeral" records are always uploaded to a new record name.
|
||||
//
|
||||
// TODO: We could store known encryption ids locally to
|
||||
// facilitate "resume" of failed backup exports.
|
||||
@objc public class OWSBackupAPI: NSObject {
|
||||
|
||||
// If we change the record types, we need to ensure indices
|
||||
|
|
|
@ -20,6 +20,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (instancetype)initWithJobTempDirPath:(NSString *)jobTempDirPath;
|
||||
|
||||
- (nullable NSString *)createTempFile;
|
||||
|
||||
#pragma mark - Encrypt
|
||||
|
||||
- (nullable OWSBackupEncryptedItem *)encryptFileAsTempFile:(NSString *)srcFilePath;
|
||||
|
|
|
@ -44,6 +44,16 @@ static const compression_algorithm SignalCompressionAlgorithm = COMPRESSION_LZMA
|
|||
return self;
|
||||
}
|
||||
|
||||
- (nullable NSString *)createTempFile
|
||||
{
|
||||
NSString *filePath = [self.jobTempDirPath stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
|
||||
if (![OWSFileSystem ensureFileExists:filePath]) {
|
||||
OWSProdLogAndFail(@"%@ could not create temp file.", self.logTag);
|
||||
return nil;
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
#pragma mark - Encrypt
|
||||
|
||||
- (nullable OWSBackupEncryptedItem *)encryptFileAsTempFile:(NSString *)srcFilePath
|
||||
|
@ -92,7 +102,10 @@ static const compression_algorithm SignalCompressionAlgorithm = COMPRESSION_LZMA
|
|||
// TODO: Encrypt the data using key;
|
||||
NSData *encryptedData = unencryptedData;
|
||||
|
||||
NSString *dstFilePath = [self.jobTempDirPath stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
|
||||
NSString *_Nullable dstFilePath = [self createTempFile];
|
||||
if (!dstFilePath) {
|
||||
return nil;
|
||||
}
|
||||
NSError *error;
|
||||
BOOL success = [encryptedData writeToFile:dstFilePath options:NSDataWritingAtomic error:&error];
|
||||
if (!success || error) {
|
||||
|
|
|
@ -86,16 +86,12 @@ NSString *const kOWSBackup_KeychainService = @"kOWSBackup_KeychainService";
|
|||
// might want to use a predictable directory so that repeated
|
||||
// import attempts can reuse downloads from previous attempts.
|
||||
NSString *temporaryDirectory = NSTemporaryDirectory();
|
||||
self.jobTempDirPath = [temporaryDirectory stringByAppendingString:[NSUUID UUID].UUIDString];
|
||||
self.jobTempDirPath = [temporaryDirectory stringByAppendingPathComponent:[NSUUID UUID].UUIDString];
|
||||
|
||||
if (![OWSFileSystem ensureDirectoryExists:self.jobTempDirPath]) {
|
||||
OWSProdLogAndFail(@"%@ Could not create jobTempDirPath.", self.logTag);
|
||||
return NO;
|
||||
}
|
||||
if (![OWSFileSystem protectFileOrFolderAtPath:self.jobTempDirPath]) {
|
||||
OWSProdLogAndFail(@"%@ Could not protect jobTempDirPath.", self.logTag);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
|
90
Signal/src/util/OWSBackupLazyRestoreJob.swift
Normal file
90
Signal/src/util/OWSBackupLazyRestoreJob.swift
Normal file
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import SignalServiceKit
|
||||
|
||||
@objc
|
||||
public class OWSBackupLazyRestoreJob: NSObject {
|
||||
|
||||
let TAG = "[OWSBackupLazyRestoreJob]"
|
||||
|
||||
let primaryStorage: OWSPrimaryStorage
|
||||
|
||||
private var jobTempDirPath: String?
|
||||
|
||||
deinit {
|
||||
if let jobTempDirPath = self.jobTempDirPath {
|
||||
DispatchQueue.global().async {
|
||||
OWSFileSystem.deleteFile(jobTempDirPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
public class func run() {
|
||||
OWSBackupLazyRestoreJob().run()
|
||||
}
|
||||
|
||||
public override init() {
|
||||
self.primaryStorage = OWSPrimaryStorage.shared()
|
||||
}
|
||||
|
||||
private func run() {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
DispatchQueue.global().async {
|
||||
self.restoreAttachments()
|
||||
}
|
||||
}
|
||||
|
||||
private func restoreAttachments() {
|
||||
let temporaryDirectory = NSTemporaryDirectory()
|
||||
let jobTempDirPath = (temporaryDirectory as NSString).appendingPathComponent(NSUUID().uuidString)
|
||||
// let jobTempDirPath = temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
|
||||
guard OWSFileSystem.ensureDirectoryExists(jobTempDirPath) else {
|
||||
Logger.error("\(TAG) could not create temp directory.")
|
||||
return
|
||||
}
|
||||
|
||||
self.jobTempDirPath = jobTempDirPath
|
||||
|
||||
let backupIO = OWSBackupIO(jobTempDirPath: jobTempDirPath)
|
||||
|
||||
let attachmentIds = OWSBackup.shared().attachmentIdsForLazyRestore()
|
||||
self.tryToRestoreNextAttachment(attachmentIds: attachmentIds, backupIO: backupIO)
|
||||
}
|
||||
|
||||
private func tryToRestoreNextAttachment(attachmentIds: [String], backupIO: OWSBackupIO) {
|
||||
var attachmentIdsCopy = attachmentIds
|
||||
guard let attachmentId = attachmentIdsCopy.last else {
|
||||
// This job is done.
|
||||
Logger.verbose("\(TAG) job is done.")
|
||||
return
|
||||
}
|
||||
attachmentIdsCopy.removeLast()
|
||||
guard let attachment = TSAttachmentStream.fetch(uniqueId: attachmentId) else {
|
||||
Logger.warn("\(TAG) could not load attachment.")
|
||||
// Not necessarily an error.
|
||||
// The attachment might have been deleted since the job began.
|
||||
// Continue trying to restore the other attachments.
|
||||
tryToRestoreNextAttachment(attachmentIds: attachmentIds, backupIO: backupIO)
|
||||
return
|
||||
}
|
||||
OWSBackup.shared().lazyRestoreAttachment(attachment,
|
||||
backupIO: backupIO,
|
||||
completion: { (success) in
|
||||
if success {
|
||||
Logger.info("\(self.TAG) restored attachment.")
|
||||
} else {
|
||||
Logger.warn("\(self.TAG) could not restore attachment.")
|
||||
}
|
||||
// Continue trying to restore the other attachments.
|
||||
self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, backupIO: backupIO)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
|
@ -34,6 +34,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@property (nonatomic, readonly) NSDate *creationTimestamp;
|
||||
|
||||
// Optional properties. Set only for attachments which
|
||||
// need "lazy backup restore."
|
||||
@property (nonatomic, readonly, nullable) NSString *backupRestoreRecordName;
|
||||
@property (nonatomic, readonly, nullable) NSData *backupRestoreEncryptionKey;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
- (nullable UIImage *)image;
|
||||
- (nullable UIImage *)thumbnailImage;
|
||||
|
@ -62,6 +67,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (nullable NSError *)migrateToSharedData;
|
||||
|
||||
#pragma mark - Update With... Methods
|
||||
|
||||
// Marks attachment as needing "lazy backup restore."
|
||||
- (void)updateWithBackupRestoreRecordName:(NSString *)recordName encryptionKey:(NSData *)encryptionKey;
|
||||
// Marks attachment as having completed "lazy backup restore."
|
||||
- (void)updateWithBackupRestoreComplete;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -26,6 +26,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// This property should only be accessed on the main thread.
|
||||
@property (nullable, nonatomic) NSNumber *cachedAudioDurationSeconds;
|
||||
|
||||
@property (nonatomic, nullable) NSString *backupRestoreRecordName;
|
||||
@property (nonatomic, nullable) NSData *backupRestoreEncryptionKey;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
@ -610,6 +613,33 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return audioDurationSeconds;
|
||||
}
|
||||
|
||||
#pragma mark - Update With... Methods
|
||||
|
||||
- (void)updateWithBackupRestoreRecordName:(NSString *)recordName encryptionKey:(NSData *)encryptionKey
|
||||
{
|
||||
OWSAssert(recordName.length > 0);
|
||||
OWSAssert(encryptionKey.length > 0);
|
||||
|
||||
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[self applyChangeToSelfAndLatestCopy:transaction
|
||||
changeBlock:^(TSAttachmentStream *attachment) {
|
||||
[attachment setBackupRestoreRecordName:recordName];
|
||||
[attachment setBackupRestoreEncryptionKey:encryptionKey];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateWithBackupRestoreComplete
|
||||
{
|
||||
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[self applyChangeToSelfAndLatestCopy:transaction
|
||||
changeBlock:^(TSAttachmentStream *attachment) {
|
||||
[attachment setBackupRestoreRecordName:nil];
|
||||
[attachment setBackupRestoreEncryptionKey:nil];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -57,6 +57,7 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage)
|
|||
[OWSFailedMessagesJob asyncRegisterDatabaseExtensionsWithPrimaryStorage:storage];
|
||||
[OWSFailedAttachmentDownloadsJob asyncRegisterDatabaseExtensionsWithPrimaryStorage:storage];
|
||||
[OWSMediaGalleryFinder asyncRegisterDatabaseExtensionsWithPrimaryStorage:storage];
|
||||
[TSDatabaseView asyncRegisterLazyRestoreAttachmentsDatabaseView:storage];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
|
|
@ -17,6 +17,9 @@ extern NSString *const TSUnreadDatabaseViewExtensionName;
|
|||
|
||||
extern NSString *const TSSecondaryDevicesDatabaseViewExtensionName;
|
||||
|
||||
extern NSString *const TSLazyRestoreAttachmentsGroup;
|
||||
extern NSString *const TSLazyRestoreAttachmentsDatabaseViewExtensionName;
|
||||
|
||||
@interface TSDatabaseView : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
@ -55,4 +58,6 @@ extern NSString *const TSSecondaryDevicesDatabaseViewExtensionName;
|
|||
|
||||
+ (void)asyncRegisterSecondaryDevicesDatabaseView:(OWSStorage *)storage;
|
||||
|
||||
+ (void)asyncRegisterLazyRestoreAttachmentsDatabaseView:(OWSStorage *)storage;
|
||||
|
||||
@end
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#import "TSDatabaseView.h"
|
||||
#import "OWSDevice.h"
|
||||
#import "OWSReadTracking.h"
|
||||
#import "TSAttachment.h"
|
||||
#import "TSAttachmentStream.h"
|
||||
#import "TSIncomingMessage.h"
|
||||
#import "TSInvalidIdentityKeyErrorMessage.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
|
@ -28,6 +30,9 @@ NSString *const TSUnreadDatabaseViewExtensionName = @"TSUnreadDatabaseViewExtens
|
|||
NSString *const TSUnseenDatabaseViewExtensionName = @"TSUnseenDatabaseViewExtensionName";
|
||||
NSString *const TSThreadSpecialMessagesDatabaseViewExtensionName = @"TSThreadSpecialMessagesDatabaseViewExtensionName";
|
||||
NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevicesDatabaseViewExtensionName";
|
||||
NSString *const TSLazyRestoreAttachmentsDatabaseViewExtensionName
|
||||
= @"TSLazyRestoreAttachmentsDatabaseViewExtensionName";
|
||||
NSString *const TSLazyRestoreAttachmentsGroup = @"TSLazyRestoreAttachmentsGroup";
|
||||
|
||||
@interface OWSStorage (TSDatabaseView)
|
||||
|
||||
|
@ -295,30 +300,26 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
|
|||
|
||||
+ (void)asyncRegisterSecondaryDevicesDatabaseView:(OWSStorage *)storage
|
||||
{
|
||||
YapDatabaseViewGrouping *viewGrouping =
|
||||
[YapDatabaseViewGrouping withObjectBlock:^NSString *_Nullable(YapDatabaseReadTransaction *_Nonnull transaction,
|
||||
NSString *_Nonnull collection,
|
||||
NSString *_Nonnull key,
|
||||
id _Nonnull object) {
|
||||
if ([object isKindOfClass:[OWSDevice class]]) {
|
||||
OWSDevice *device = (OWSDevice *)object;
|
||||
if (![device isPrimaryDevice]) {
|
||||
return TSSecondaryDevicesGroup;
|
||||
}
|
||||
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *_Nullable(
|
||||
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
|
||||
if ([object isKindOfClass:[OWSDevice class]]) {
|
||||
OWSDevice *device = (OWSDevice *)object;
|
||||
if (![device isPrimaryDevice]) {
|
||||
return TSSecondaryDevicesGroup;
|
||||
}
|
||||
return nil;
|
||||
}];
|
||||
}
|
||||
return nil;
|
||||
}];
|
||||
|
||||
YapDatabaseViewSorting *viewSorting =
|
||||
[YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *_Nonnull transaction,
|
||||
NSString *_Nonnull group,
|
||||
NSString *_Nonnull collection1,
|
||||
NSString *_Nonnull key1,
|
||||
id _Nonnull object1,
|
||||
NSString *_Nonnull collection2,
|
||||
NSString *_Nonnull key2,
|
||||
id _Nonnull object2) {
|
||||
|
||||
[YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction,
|
||||
NSString *group,
|
||||
NSString *collection1,
|
||||
NSString *key1,
|
||||
id object1,
|
||||
NSString *collection2,
|
||||
NSString *key2,
|
||||
id object2) {
|
||||
if ([object1 isKindOfClass:[OWSDevice class]] && [object2 isKindOfClass:[OWSDevice class]]) {
|
||||
OWSDevice *device1 = (OWSDevice *)object1;
|
||||
OWSDevice *device2 = (OWSDevice *)object2;
|
||||
|
@ -341,6 +342,58 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic
|
|||
[storage asyncRegisterExtension:view withName:TSSecondaryDevicesDatabaseViewExtensionName];
|
||||
}
|
||||
|
||||
+ (void)asyncRegisterLazyRestoreAttachmentsDatabaseView:(OWSStorage *)storage
|
||||
{
|
||||
YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *_Nullable(
|
||||
YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
|
||||
if (![object isKindOfClass:[TSAttachment class]]) {
|
||||
OWSProdLogAndFail(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object class], collection);
|
||||
return nil;
|
||||
}
|
||||
if (![object isKindOfClass:[TSAttachmentStream class]]) {
|
||||
return nil;
|
||||
}
|
||||
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)object;
|
||||
if (attachmentStream.backupRestoreRecordName.length > 0
|
||||
&& attachmentStream.backupRestoreEncryptionKey.length > 0) {
|
||||
return TSLazyRestoreAttachmentsGroup;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}];
|
||||
|
||||
YapDatabaseViewSorting *viewSorting = [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(
|
||||
YapDatabaseReadTransaction *transaction,
|
||||
NSString *group,
|
||||
NSString *collection1,
|
||||
NSString *key1,
|
||||
id object1,
|
||||
NSString *collection2,
|
||||
NSString *key2,
|
||||
id object2) {
|
||||
if (![object1 isKindOfClass:[TSAttachment class]]) {
|
||||
OWSProdLogAndFail(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object1 class], collection1);
|
||||
return NSOrderedSame;
|
||||
}
|
||||
if (![object2 isKindOfClass:[TSAttachment class]]) {
|
||||
OWSProdLogAndFail(@"%@ Unexpected entity %@ in collection: %@", self.logTag, [object2 class], collection2);
|
||||
return NSOrderedSame;
|
||||
}
|
||||
|
||||
TSAttachmentStream *attachmentStream1 = (TSAttachmentStream *)object1;
|
||||
TSAttachmentStream *attachmentStream2 = (TSAttachmentStream *)object2;
|
||||
return [attachmentStream2.creationTimestamp compare:attachmentStream1.creationTimestamp];
|
||||
}];
|
||||
|
||||
YapDatabaseViewOptions *options = [YapDatabaseViewOptions new];
|
||||
options.isPersistent = YES;
|
||||
options.allowedCollections =
|
||||
[[YapWhitelistBlacklist alloc] initWithWhitelist:[NSSet setWithObject:[TSAttachment collection]]];
|
||||
YapDatabaseView *view =
|
||||
[[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"1" options:options];
|
||||
[storage asyncRegisterExtension:view withName:TSLazyRestoreAttachmentsDatabaseViewExtensionName];
|
||||
}
|
||||
|
||||
+ (id)unseenDatabaseViewExtension:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(transaction);
|
||||
|
|
|
@ -28,6 +28,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// Returns NO IFF the directory does not exist and could not be created.
|
||||
+ (BOOL)ensureDirectoryExists:(NSString *)dirPath;
|
||||
|
||||
+ (BOOL)ensureFileExists:(NSString *)filePath;
|
||||
|
||||
+ (BOOL)deleteFile:(NSString *)filePath;
|
||||
|
||||
+ (BOOL)deleteFileIfExists:(NSString *)filePath;
|
||||
|
|
|
@ -227,6 +227,21 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
+ (BOOL)ensureFileExists:(NSString *)filePath
|
||||
{
|
||||
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
|
||||
if (exists) {
|
||||
return [self protectFileOrFolderAtPath:filePath];
|
||||
} else {
|
||||
BOOL success = [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
|
||||
if (!success) {
|
||||
OWSFail(@"%@ Failed to create file.", self.logTag);
|
||||
return NO;
|
||||
}
|
||||
return [self protectFileOrFolderAtPath:filePath];
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)deleteFile:(NSString *)filePath
|
||||
{
|
||||
NSError *error;
|
||||
|
|
Loading…
Reference in a new issue