|
|
@ -4,22 +4,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
#import "OWSBackupImportJob.h"
|
|
|
|
#import "OWSBackupImportJob.h"
|
|
|
|
#import "Signal-Swift.h"
|
|
|
|
#import "Signal-Swift.h"
|
|
|
|
#import "zlib.h"
|
|
|
|
|
|
|
|
#import <Curve25519Kit/Randomness.h>
|
|
|
|
|
|
|
|
#import <SSZipArchive/SSZipArchive.h>
|
|
|
|
|
|
|
|
#import <SignalServiceKit/NSData+Base64.h>
|
|
|
|
#import <SignalServiceKit/NSData+Base64.h>
|
|
|
|
#import <SignalServiceKit/NSDate+OWS.h>
|
|
|
|
|
|
|
|
#import <SignalServiceKit/OWSBackgroundTask.h>
|
|
|
|
#import <SignalServiceKit/OWSBackgroundTask.h>
|
|
|
|
#import <SignalServiceKit/OWSBackupStorage.h>
|
|
|
|
#import <SignalServiceKit/OWSBackupStorage.h>
|
|
|
|
#import <SignalServiceKit/OWSError.h>
|
|
|
|
|
|
|
|
#import <SignalServiceKit/OWSFileSystem.h>
|
|
|
|
#import <SignalServiceKit/OWSFileSystem.h>
|
|
|
|
#import <SignalServiceKit/TSAttachmentStream.h>
|
|
|
|
#import <SignalServiceKit/TSAttachment.h>
|
|
|
|
#import <SignalServiceKit/TSMessage.h>
|
|
|
|
#import <SignalServiceKit/TSMessage.h>
|
|
|
|
#import <SignalServiceKit/TSThread.h>
|
|
|
|
#import <SignalServiceKit/TSThread.h>
|
|
|
|
#import <SignalServiceKit/Threading.h>
|
|
|
|
|
|
|
|
#import <SignalServiceKit/YapDatabaseConnection+OWS.h>
|
|
|
|
|
|
|
|
#import <YapDatabase/YapDatabase.h>
|
|
|
|
|
|
|
|
#import <YapDatabase/YapDatabaseCryptoUtils.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
|
|
|
|
|
|
@ -27,28 +18,19 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
|
|
|
@interface OWSBackupImportJob () <SSZipArchiveDelegate>
|
|
|
|
@interface OWSBackupImportJob ()
|
|
|
|
|
|
|
|
|
|
|
|
//@property (nonatomic, nullable) OWSBackupStorage *backupStorage;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property (nonatomic, nullable) OWSBackgroundTask *backgroundTask;
|
|
|
|
@property (nonatomic, nullable) OWSBackgroundTask *backgroundTask;
|
|
|
|
|
|
|
|
|
|
|
|
//@property (nonatomic) NSMutableArray<NSString *> *databaseFilePaths;
|
|
|
|
|
|
|
|
// A map of "record name"-to-"file name".
|
|
|
|
// A map of "record name"-to-"file name".
|
|
|
|
@property (nonatomic) NSMutableDictionary<NSString *, NSString *> *databaseRecordMap;
|
|
|
|
@property (nonatomic) NSMutableDictionary<NSString *, NSString *> *databaseRecordMap;
|
|
|
|
|
|
|
|
|
|
|
|
//// A map of "attachment id"-to-"local file path".
|
|
|
|
|
|
|
|
//@property (nonatomic) NSMutableDictionary<NSString *, NSString *> *attachmentFilePathMap;
|
|
|
|
|
|
|
|
// A map of "record name"-to-"file relative path".
|
|
|
|
// A map of "record name"-to-"file relative path".
|
|
|
|
@property (nonatomic) NSMutableDictionary<NSString *, NSString *> *attachmentRecordMap;
|
|
|
|
@property (nonatomic) NSMutableDictionary<NSString *, NSString *> *attachmentRecordMap;
|
|
|
|
|
|
|
|
|
|
|
|
// A map of "record name"-to-"downloaded file path".
|
|
|
|
// A map of "record name"-to-"downloaded file path".
|
|
|
|
@property (nonatomic) NSMutableDictionary<NSString *, NSString *> *downloadedFileMap;
|
|
|
|
@property (nonatomic) NSMutableDictionary<NSString *, NSString *> *downloadedFileMap;
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//@property (nonatomic, nullable) NSString *manifestFilePath;
|
|
|
|
|
|
|
|
//@property (nonatomic, nullable) NSString *manifestRecordName;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark -
|
|
|
@ -81,143 +63,82 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
@"Indicates that the backup import is being configured.")
|
|
|
|
@"Indicates that the backup import is being configured.")
|
|
|
|
progress:nil];
|
|
|
|
progress:nil];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (![self configureImport]) {
|
|
|
|
|
|
|
|
[self failWithErrorDescription:NSLocalizedString(@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
@"Error indicating the a backup import could not import the user's data.")];
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (self.isComplete) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_IMPORT",
|
|
|
|
|
|
|
|
@"Indicates that the backup import data is being imported.")
|
|
|
|
|
|
|
|
progress:nil];
|
|
|
|
|
|
|
|
|
|
|
|
__weak OWSBackupImportJob *weakSelf = self;
|
|
|
|
__weak OWSBackupImportJob *weakSelf = self;
|
|
|
|
[self configureImport:^(BOOL configureSuccess) {
|
|
|
|
[weakSelf downloadAndProcessManifest:^(NSError *_Nullable manifestError) {
|
|
|
|
if (!configureSuccess) {
|
|
|
|
if (manifestError) {
|
|
|
|
[weakSelf failWithErrorDescription:
|
|
|
|
[weakSelf failWithError:manifestError];
|
|
|
|
NSLocalizedString(@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
@"Error indicating the a backup import could not import the user's data.")];
|
|
|
|
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[weakSelf downloadAndProcessManifest:^(NSError *_Nullable manifestError) {
|
|
|
|
|
|
|
|
if (manifestError) {
|
|
|
|
|
|
|
|
[weakSelf failWithError:manifestError];
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NSMutableArray<NSString *> *allRecordNames = [NSMutableArray new];
|
|
|
|
|
|
|
|
[allRecordNames addObjectsFromArray:weakSelf.databaseRecordMap.allKeys];
|
|
|
|
|
|
|
|
// TODO: We could skip attachments that have already been restored
|
|
|
|
|
|
|
|
// by previous "backup import" attempts.
|
|
|
|
|
|
|
|
[allRecordNames addObjectsFromArray:weakSelf.attachmentRecordMap.allKeys];
|
|
|
|
|
|
|
|
[weakSelf
|
|
|
|
|
|
|
|
downloadFilesFromCloud:allRecordNames
|
|
|
|
|
|
|
|
completion:^(NSError *_Nullable fileDownloadError) {
|
|
|
|
|
|
|
|
if (fileDownloadError) {
|
|
|
|
|
|
|
|
[weakSelf failWithError:fileDownloadError];
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
NSMutableArray<NSString *> *allRecordNames = [NSMutableArray new];
|
|
|
|
|
|
|
|
[allRecordNames addObjectsFromArray:weakSelf.databaseRecordMap.allKeys];
|
|
|
|
|
|
|
|
// TODO: We could skip attachments that have already been restored
|
|
|
|
|
|
|
|
// by previous "backup import" attempts.
|
|
|
|
|
|
|
|
[allRecordNames addObjectsFromArray:weakSelf.attachmentRecordMap.allKeys];
|
|
|
|
|
|
|
|
[weakSelf
|
|
|
|
|
|
|
|
downloadFilesFromCloud:allRecordNames
|
|
|
|
|
|
|
|
completion:^(NSError *_Nullable fileDownloadError) {
|
|
|
|
|
|
|
|
if (fileDownloadError) {
|
|
|
|
|
|
|
|
[weakSelf failWithError:fileDownloadError];
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[weakSelf restoreAttachmentFiles];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[weakSelf restoreDatabase:^(BOOL restoreDatabaseSuccess) {
|
|
|
|
|
|
|
|
if (!restoreDatabaseSuccess) {
|
|
|
|
|
|
|
|
[weakSelf failWithErrorDescription:NSLocalizedString(
|
|
|
|
|
|
|
|
@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
@"Error indicating the a backup import "
|
|
|
|
|
|
|
|
@"could not import the user's data.")];
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[weakSelf restoreAttachmentFiles];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[weakSelf restoreDatabase:^(BOOL restoreDatabaseSuccess) {
|
|
|
|
[weakSelf succeed];
|
|
|
|
if (!restoreDatabaseSuccess) {
|
|
|
|
|
|
|
|
[weakSelf failWithErrorDescription:NSLocalizedString(
|
|
|
|
|
|
|
|
@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
@"Error indicating the a backup import "
|
|
|
|
|
|
|
|
@"could not import the user's data.")];
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (weakSelf.isComplete) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[weakSelf succeed];
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
// TODO:
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_IMPORT",
|
|
|
|
|
|
|
|
// @"Indicates that the backup import data is being imported.")
|
|
|
|
|
|
|
|
// progress:nil];
|
|
|
|
|
|
|
|
// if (![self importDatabase]) {
|
|
|
|
|
|
|
|
// [self failWithErrorDescription:
|
|
|
|
|
|
|
|
// NSLocalizedString(@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
// @"Error indicating the a backup import could not import the user's data.")];
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (self.isComplete) {
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// [self saveToCloud:^(NSError *_Nullable saveError) {
|
|
|
|
|
|
|
|
// if (saveError) {
|
|
|
|
|
|
|
|
// [weakSelf failWithError:saveError];
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// [self cleanUpCloud:^(NSError *_Nullable cleanUpError) {
|
|
|
|
|
|
|
|
// if (cleanUpError) {
|
|
|
|
|
|
|
|
// [weakSelf failWithError:cleanUpError];
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// [weakSelf succeed];
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Convert these methods to sync.
|
|
|
|
- (BOOL)configureImport
|
|
|
|
- (void)configureImport:(OWSBackupJobBoolCompletion)completion
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssert(completion);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
|
|
|
DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
|
|
|
|
|
|
|
|
|
|
|
if (![self ensureJobTempDir]) {
|
|
|
|
if (![self ensureJobTempDir]) {
|
|
|
|
OWSProdLogAndFail(@"%@ Could not create jobTempDirPath.", self.logTag);
|
|
|
|
OWSProdLogAndFail(@"%@ Could not create jobTempDirPath.", self.logTag);
|
|
|
|
return completion(NO);
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
completion(YES);
|
|
|
|
return YES;
|
|
|
|
|
|
|
|
|
|
|
|
// NSString *importDatabaseDirPath = [self.jobTempDirPath stringByAppendingPathComponent:@"Database"];
|
|
|
|
|
|
|
|
// self.tempDatabaseKeySpec = [Randomness generateRandomBytes:(int)kSQLCipherKeySpecLength];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (![OWSFileSystem ensureDirectoryExists:importDatabaseDirPath]) {
|
|
|
|
|
|
|
|
// OWSProdLogAndFail(@"%@ Could not create importDatabaseDirPath.", self.logTag);
|
|
|
|
|
|
|
|
// return completion(NO);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (!self.tempDatabaseKeySpec) {
|
|
|
|
|
|
|
|
// OWSProdLogAndFail(@"%@ Could not create tempDatabaseKeySpec.", self.logTag);
|
|
|
|
|
|
|
|
// return completion(NO);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// __weak OWSBackupImportJob *weakSelf = self;
|
|
|
|
|
|
|
|
// BackupStorageKeySpecBlock keySpecBlock = ^{
|
|
|
|
|
|
|
|
// return weakSelf.tempDatabaseKeySpec;
|
|
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
// self.backupStorage =
|
|
|
|
|
|
|
|
// [[OWSBackupStorage alloc] initBackupStorageWithDatabaseDirPath:importDatabaseDirPath
|
|
|
|
|
|
|
|
// keySpecBlock:keySpecBlock];
|
|
|
|
|
|
|
|
// if (!self.backupStorage) {
|
|
|
|
|
|
|
|
// OWSProdLogAndFail(@"%@ Could not create backupStorage.", self.logTag);
|
|
|
|
|
|
|
|
// return completion(NO);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // TODO: Do we really need to run these registrations on the main thread?
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
|
|
// [self.backupStorage runSyncRegistrations];
|
|
|
|
|
|
|
|
// [self.backupStorage runAsyncRegistrationsWithCompletion:^{
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// completion(YES);
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)downloadAndProcessManifest:(OWSBackupJobCompletion)completion
|
|
|
|
- (void)downloadAndProcessManifest:(OWSBackupJobCompletion)completion
|
|
|
@ -266,9 +187,6 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
return completion(NO);
|
|
|
|
return completion(NO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DDLogVerbose(@"%@ json: %@", self.logTag, json);
|
|
|
|
|
|
|
|
[DDLog flushLog];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NSDictionary<NSString *, NSString *> *_Nullable databaseRecordMap = json[kOWSBackup_ManifestKey_DatabaseFiles];
|
|
|
|
NSDictionary<NSString *, NSString *> *_Nullable databaseRecordMap = json[kOWSBackup_ManifestKey_DatabaseFiles];
|
|
|
|
NSDictionary<NSString *, NSString *> *_Nullable attachmentRecordMap = json[kOWSBackup_ManifestKey_AttachmentFiles];
|
|
|
|
NSDictionary<NSString *, NSString *> *_Nullable attachmentRecordMap = json[kOWSBackup_ManifestKey_AttachmentFiles];
|
|
|
|
NSString *_Nullable databaseKeySpecBase64 = json[kOWSBackup_ManifestKey_DatabaseKeySpec];
|
|
|
|
NSString *_Nullable databaseKeySpecBase64 = json[kOWSBackup_ManifestKey_DatabaseKeySpec];
|
|
|
@ -288,86 +206,12 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
return completion(NO);
|
|
|
|
return completion(NO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DDLogVerbose(@"%@ attachmentRecordMap: %@", self.logTag, attachmentRecordMap);
|
|
|
|
|
|
|
|
[DDLog flushLog];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.databaseRecordMap = [databaseRecordMap mutableCopy];
|
|
|
|
self.databaseRecordMap = [databaseRecordMap mutableCopy];
|
|
|
|
self.attachmentRecordMap = [attachmentRecordMap mutableCopy];
|
|
|
|
self.attachmentRecordMap = [attachmentRecordMap mutableCopy];
|
|
|
|
|
|
|
|
|
|
|
|
return completion(YES);
|
|
|
|
return completion(YES);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//- (void)downloadDatabaseFiles:(OWSBackupJobCompletion)completion
|
|
|
|
|
|
|
|
//{
|
|
|
|
|
|
|
|
// OWSAssert(completion);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// NSString *databaseDirPath = [self.jobTempDirPath stringByAppendingPathComponent:@"Database"];
|
|
|
|
|
|
|
|
// if (![OWSFileSystem ensureDirectoryExists:databaseDirPath]) {
|
|
|
|
|
|
|
|
// OWSProdLogAndFail(@"%@ Could not create databaseDirPath.", self.logTag);
|
|
|
|
|
|
|
|
// return completion(OWSErrorWithCodeDescription(OWSErrorCodeImportBackupFailed,
|
|
|
|
|
|
|
|
// NSLocalizedString(@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
// @"Error indicating the a backup import could
|
|
|
|
|
|
|
|
// not import the user's data.")));
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // [OWSFileSystem protectFileOrFolderAtPath:dstFilePath];
|
|
|
|
|
|
|
|
// NSMutableArray<NSString *> *recordNames = [self.databaseRecordMap.allKeys mutableCopy];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// [self downloadNextFile:recordNames
|
|
|
|
|
|
|
|
// dstDirPath:databaseDirPath
|
|
|
|
|
|
|
|
// completion:completion];
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//- (void)downloadNextFile:(NSMutableArray<NSString *> *)recordNames
|
|
|
|
|
|
|
|
// dstDirPath:(NSString *)dstDirPath
|
|
|
|
|
|
|
|
// completion:(OWSBackupJobCompletion)completion
|
|
|
|
|
|
|
|
//{
|
|
|
|
|
|
|
|
// OWSAssert(recordNames);
|
|
|
|
|
|
|
|
// OWSAssert(completion);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// NSString *databaseDirPath = [self.jobTempDirPath stringByAppendingPathComponent:@"Database"];
|
|
|
|
|
|
|
|
// if (![OWSFileSystem ensureDirectoryExists:databaseDirPath]) {
|
|
|
|
|
|
|
|
// OWSProdLogAndFail(@"%@ Could not create databaseDirPath.", self.logTag);
|
|
|
|
|
|
|
|
// return completion(OWSErrorWithCodeDescription(OWSErrorCodeImportBackupFailed,
|
|
|
|
|
|
|
|
// NSLocalizedString(@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
// @"Error indicating the a backup import could
|
|
|
|
|
|
|
|
// not import the user's data.")));
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // NSString *database
|
|
|
|
|
|
|
|
// // self.jobTempDirPath
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // [OWSFileSystem protectFileOrFolderAtPath:dstFilePath];
|
|
|
|
|
|
|
|
// NSMutableArray<NSString *> *recordNamesToDownload = [self.databaseRecordMap.allKeys mutableCopy];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// [self downloadNextFile:recordNamesToDownload
|
|
|
|
|
|
|
|
// completion:completion];
|
|
|
|
|
|
|
|
// __weak OWSBackupImportJob *weakSelf = self;
|
|
|
|
|
|
|
|
// [OWSBackupAPI downloadManifestFromCloudWithSuccess:^(NSData *data) {
|
|
|
|
|
|
|
|
// [weakSelf processManifest:data
|
|
|
|
|
|
|
|
// completion:^(BOOL success) {
|
|
|
|
|
|
|
|
// if (success) {
|
|
|
|
|
|
|
|
// completion(nil);
|
|
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// completion(OWSErrorWithCodeDescription(OWSErrorCodeImportBackupFailed,
|
|
|
|
|
|
|
|
// NSLocalizedString(@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
// @"Error indicating the a
|
|
|
|
|
|
|
|
// backup import could not import
|
|
|
|
|
|
|
|
// the user's data.")));
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// failure:^(NSError *error) {
|
|
|
|
|
|
|
|
// // The manifest file is critical so any error downloading it is
|
|
|
|
|
|
|
|
// unrecoverable. OWSProdLogAndFail(@"%@ Could not download
|
|
|
|
|
|
|
|
// manifest.", self.logTag); completion(error);
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (void)downloadFilesFromCloud:(NSMutableArray<NSString *> *)recordNames completion:(OWSBackupJobCompletion)completion
|
|
|
|
- (void)downloadFilesFromCloud:(NSMutableArray<NSString *> *)recordNames completion:(OWSBackupJobCompletion)completion
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSAssert(recordNames.count > 0);
|
|
|
|
OWSAssert(recordNames.count > 0);
|
|
|
@ -438,9 +282,6 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
|
|
|
|
|
|
|
|
NSString *attachmentsDirPath = [TSAttachmentStream attachmentsFolder];
|
|
|
|
NSString *attachmentsDirPath = [TSAttachmentStream attachmentsFolder];
|
|
|
|
|
|
|
|
|
|
|
|
DDLogVerbose(@"%@ self.attachmentRecordMap: %@", self.logTag, self.attachmentRecordMap);
|
|
|
|
|
|
|
|
[DDLog flushLog];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (NSString *recordName in self.attachmentRecordMap) {
|
|
|
|
for (NSString *recordName in self.attachmentRecordMap) {
|
|
|
|
if (self.isComplete) {
|
|
|
|
if (self.isComplete) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
@ -656,268 +497,10 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
DDLogError(@"%@ could not restore attachment file.", self.logTag);
|
|
|
|
DDLogError(@"%@ could not restore attachment file.", self.logTag);
|
|
|
|
return NO;
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: Remove
|
|
|
|
|
|
|
|
DDLogVerbose(@"%@ Restored attachment file: %@ -> %@", self.logTag, downloadedFilePath, dstFilePath);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//- (void)saveToCloud:(OWSBackupJobCompletion)completion
|
|
|
|
|
|
|
|
//{
|
|
|
|
|
|
|
|
// OWSAssert(completion);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// self.databaseRecordMap = [NSMutableDictionary new];
|
|
|
|
|
|
|
|
// self.attachmentRecordMap = [NSMutableDictionary new];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// [self saveNextFileToCloud:completion];
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//- (void)saveNextFileToCloud:(OWSBackupJobCompletion)completion
|
|
|
|
|
|
|
|
//{
|
|
|
|
|
|
|
|
// OWSAssert(completion);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (self.isComplete) {
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// CGFloat progress
|
|
|
|
|
|
|
|
// = (self.databaseRecordMap.count / (CGFloat)(self.databaseRecordMap.count + self.databaseFilePaths.count));
|
|
|
|
|
|
|
|
// [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_UPLOAD",
|
|
|
|
|
|
|
|
// @"Indicates that the backup import data is being uploaded.")
|
|
|
|
|
|
|
|
// progress:@(progress)];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// __weak OWSBackupImportJob *weakSelf = self;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (self.databaseFilePaths.count > 0) {
|
|
|
|
|
|
|
|
// NSString *filePath = self.databaseFilePaths.lastObject;
|
|
|
|
|
|
|
|
// [self.databaseFilePaths removeLastObject];
|
|
|
|
|
|
|
|
// // Database files are encrypted and can be safely stored unencrypted in the cloud.
|
|
|
|
|
|
|
|
// // TODO: Security review.
|
|
|
|
|
|
|
|
// [OWSBackupAPI saveEphemeralDatabaseFileToCloudWithFileUrl:[NSURL fileURLWithPath:filePath]
|
|
|
|
|
|
|
|
// success:^(NSString *recordName) {
|
|
|
|
|
|
|
|
// // Ensure that we continue to work off the main thread.
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// OWSBackupImportJob *strongSelf = weakSelf;
|
|
|
|
|
|
|
|
// if (!strongSelf) {
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// strongSelf.databaseRecordMap[recordName] = [filePath lastPathComponent];
|
|
|
|
|
|
|
|
// [strongSelf saveNextFileToCloud:completion];
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// failure:^(NSError *error) {
|
|
|
|
|
|
|
|
// // Database files are critical so any error uploading them is unrecoverable.
|
|
|
|
|
|
|
|
// completion(error);
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (self.attachmentFilePathMap.count > 0) {
|
|
|
|
|
|
|
|
// NSString *attachmentId = self.attachmentFilePathMap.allKeys.lastObject;
|
|
|
|
|
|
|
|
// NSString *attachmentFilePath = self.attachmentFilePathMap[attachmentId];
|
|
|
|
|
|
|
|
// [self.attachmentFilePathMap removeObjectForKey:attachmentId];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // OWSAttachmentImport is used to lazily write an encrypted copy of the
|
|
|
|
|
|
|
|
// // attachment to disk.
|
|
|
|
|
|
|
|
// OWSAttachmentImport *attachmentImport = [OWSAttachmentImport new];
|
|
|
|
|
|
|
|
// attachmentImport.delegate = self.delegate;
|
|
|
|
|
|
|
|
// attachmentImport.jobTempDirPath = self.jobTempDirPath;
|
|
|
|
|
|
|
|
// attachmentImport.attachmentId = attachmentId;
|
|
|
|
|
|
|
|
// attachmentImport.attachmentFilePath = attachmentFilePath;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// [OWSBackupAPI savePersistentFileOnceToCloudWithFileId:attachmentId
|
|
|
|
|
|
|
|
// fileUrlBlock:^{
|
|
|
|
|
|
|
|
// [attachmentImport prepareForUpload];
|
|
|
|
|
|
|
|
// if (attachmentImport.tempFilePath.length < 1) {
|
|
|
|
|
|
|
|
// DDLogError(@"%@ attachment import missing temp file path", self.logTag);
|
|
|
|
|
|
|
|
// return (NSURL *)nil;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if (attachmentImport.relativeFilePath.length < 1) {
|
|
|
|
|
|
|
|
// DDLogError(@"%@ attachment import missing relative file path", self.logTag);
|
|
|
|
|
|
|
|
// return (NSURL *)nil;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return [NSURL fileURLWithPath:attachmentImport.tempFilePath];
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// success:^(NSString *recordName) {
|
|
|
|
|
|
|
|
// // Ensure that we continue to work off the main thread.
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// OWSBackupImportJob *strongSelf = weakSelf;
|
|
|
|
|
|
|
|
// if (!strongSelf) {
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// strongSelf.attachmentRecordMap[recordName] = attachmentImport.relativeFilePath;
|
|
|
|
|
|
|
|
// [strongSelf saveNextFileToCloud:completion];
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// failure:^(NSError *error) {
|
|
|
|
|
|
|
|
// // Ensure that we continue to work off the main thread.
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// // Attachment files are non-critical so any error uploading them is recoverable.
|
|
|
|
|
|
|
|
// [weakSelf saveNextFileToCloud:completion];
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (!self.manifestFilePath) {
|
|
|
|
|
|
|
|
// if (![self writeManifestFile]) {
|
|
|
|
|
|
|
|
// completion(OWSErrorWithCodeDescription(OWSErrorCodeImportBackupFailed,
|
|
|
|
|
|
|
|
// NSLocalizedString(@"BACKUP_IMPORT_ERROR_COULD_NOT_IMPORT",
|
|
|
|
|
|
|
|
// @"Error indicating the a backup import could not import the user's data.")));
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// OWSAssert(self.manifestFilePath);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// [OWSBackupAPI upsertManifestFileToCloudWithFileUrl:[NSURL fileURLWithPath:self.manifestFilePath]
|
|
|
|
|
|
|
|
// success:^(NSString *recordName) {
|
|
|
|
|
|
|
|
// // Ensure that we continue to work off the main thread.
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// OWSBackupImportJob *strongSelf = weakSelf;
|
|
|
|
|
|
|
|
// if (!strongSelf) {
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// strongSelf.manifestRecordName = recordName;
|
|
|
|
|
|
|
|
// [strongSelf saveNextFileToCloud:completion];
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// failure:^(NSError *error) {
|
|
|
|
|
|
|
|
// // The manifest file is critical so any error uploading them is unrecoverable.
|
|
|
|
|
|
|
|
// completion(error);
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // All files have been saved to the cloud.
|
|
|
|
|
|
|
|
// completion(nil);
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//- (BOOL)writeManifestFile
|
|
|
|
|
|
|
|
//{
|
|
|
|
|
|
|
|
// OWSAssert(self.databaseRecordMap.count > 0);
|
|
|
|
|
|
|
|
// OWSAssert(self.attachmentRecordMap);
|
|
|
|
|
|
|
|
// OWSAssert(self.jobTempDirPath.length > 0);
|
|
|
|
|
|
|
|
// OWSAssert(self.tempDatabaseKeySpec.length > 0);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// NSDictionary *json = @{
|
|
|
|
|
|
|
|
// @"database_files" : self.databaseRecordMap,
|
|
|
|
|
|
|
|
// @"attachment_files" : self.attachmentRecordMap,
|
|
|
|
|
|
|
|
// // JSON doesn't support byte arrays.
|
|
|
|
|
|
|
|
// @"database_key_spec" : self.tempDatabaseKeySpec.base64EncodedString,
|
|
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
// NSError *error;
|
|
|
|
|
|
|
|
// NSData *_Nullable jsonData =
|
|
|
|
|
|
|
|
// [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:&error];
|
|
|
|
|
|
|
|
// if (!jsonData || error) {
|
|
|
|
|
|
|
|
// OWSProdLogAndFail(@"%@ error encoding manifest file: %@", self.logTag, error);
|
|
|
|
|
|
|
|
// return NO;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // TODO: Encrypt the manifest.
|
|
|
|
|
|
|
|
// self.manifestFilePath = [self.jobTempDirPath stringByAppendingPathComponent:@"manifest.json"];
|
|
|
|
|
|
|
|
// if (![jsonData writeToFile:self.manifestFilePath atomically:YES]) {
|
|
|
|
|
|
|
|
// OWSProdLogAndFail(@"%@ error writing manifest file: %@", self.logTag, error);
|
|
|
|
|
|
|
|
// return NO;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return YES;
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//- (void)cleanUpCloud:(OWSBackupJobCompletion)completion
|
|
|
|
|
|
|
|
//{
|
|
|
|
|
|
|
|
// OWSAssert(completion);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_CLEAN_UP",
|
|
|
|
|
|
|
|
// @"Indicates that the cloud is being cleaned up.")
|
|
|
|
|
|
|
|
// progress:nil];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// // Now that our backup import has successfully completed,
|
|
|
|
|
|
|
|
// // we try to clean up the cloud. We can safely delete any
|
|
|
|
|
|
|
|
// // records not involved in this backup import.
|
|
|
|
|
|
|
|
// NSMutableSet<NSString *> *activeRecordNames = [NSMutableSet new];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// OWSAssert(self.databaseRecordMap.count > 0);
|
|
|
|
|
|
|
|
// [activeRecordNames addObjectsFromArray:self.databaseRecordMap.allKeys];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// OWSAssert(self.attachmentRecordMap);
|
|
|
|
|
|
|
|
// [activeRecordNames addObjectsFromArray:self.attachmentRecordMap.allKeys];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// OWSAssert(self.manifestRecordName.length > 0);
|
|
|
|
|
|
|
|
// [activeRecordNames addObject:self.manifestRecordName];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// __weak OWSBackupImportJob *weakSelf = self;
|
|
|
|
|
|
|
|
// [OWSBackupAPI fetchAllRecordNamesWithSuccess:^(NSArray<NSString *> *recordNames) {
|
|
|
|
|
|
|
|
// // Ensure that we continue to work off the main thread.
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// NSMutableSet<NSString *> *obsoleteRecordNames = [NSMutableSet new];
|
|
|
|
|
|
|
|
// [obsoleteRecordNames addObjectsFromArray:recordNames];
|
|
|
|
|
|
|
|
// [obsoleteRecordNames minusSet:activeRecordNames];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// DDLogVerbose(@"%@ recordNames: %zd - activeRecordNames: %zd = obsoleteRecordNames: %zd",
|
|
|
|
|
|
|
|
// self.logTag,
|
|
|
|
|
|
|
|
// recordNames.count,
|
|
|
|
|
|
|
|
// activeRecordNames.count,
|
|
|
|
|
|
|
|
// obsoleteRecordNames.count);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// [weakSelf deleteRecordsFromCloud:[obsoleteRecordNames.allObjects mutableCopy]
|
|
|
|
|
|
|
|
// deletedCount:0
|
|
|
|
|
|
|
|
// completion:completion];
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// failure:^(NSError *error) {
|
|
|
|
|
|
|
|
// // Ensure that we continue to work off the main thread.
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// // Cloud cleanup is non-critical so any error is recoverable.
|
|
|
|
|
|
|
|
// completion(nil);
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//- (void)deleteRecordsFromCloud:(NSMutableArray<NSString *> *)obsoleteRecordNames
|
|
|
|
|
|
|
|
// deletedCount:(NSUInteger)deletedCount
|
|
|
|
|
|
|
|
// completion:(OWSBackupJobCompletion)completion
|
|
|
|
|
|
|
|
//{
|
|
|
|
|
|
|
|
// OWSAssert(obsoleteRecordNames);
|
|
|
|
|
|
|
|
// OWSAssert(completion);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// DDLogVerbose(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// if (obsoleteRecordNames.count < 1) {
|
|
|
|
|
|
|
|
// // No more records to delete; cleanup is complete.
|
|
|
|
|
|
|
|
// completion(nil);
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// CGFloat progress = (obsoleteRecordNames.count / (CGFloat)(obsoleteRecordNames.count + deletedCount));
|
|
|
|
|
|
|
|
// [self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_CLEAN_UP",
|
|
|
|
|
|
|
|
// @"Indicates that the cloud is being cleaned up.")
|
|
|
|
|
|
|
|
// progress:@(progress)];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// NSString *recordName = obsoleteRecordNames.lastObject;
|
|
|
|
|
|
|
|
// [obsoleteRecordNames removeLastObject];
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// __weak OWSBackupImportJob *weakSelf = self;
|
|
|
|
|
|
|
|
// [OWSBackupAPI deleteRecordFromCloudWithRecordName:recordName
|
|
|
|
|
|
|
|
// success:^{
|
|
|
|
|
|
|
|
// // Ensure that we continue to work off the main thread.
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// [weakSelf deleteRecordsFromCloud:obsoleteRecordNames
|
|
|
|
|
|
|
|
// deletedCount:deletedCount + 1
|
|
|
|
|
|
|
|
// completion:completion];
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// failure:^(NSError *error) {
|
|
|
|
|
|
|
|
// // Ensure that we continue to work off the main thread.
|
|
|
|
|
|
|
|
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
|
|
|
|
|
|
// // Cloud cleanup is non-critical so any error is recoverable.
|
|
|
|
|
|
|
|
// [weakSelf deleteRecordsFromCloud:obsoleteRecordNames
|
|
|
|
|
|
|
|
// deletedCount:deletedCount + 1
|
|
|
|
|
|
|
|
// completion:completion];
|
|
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
// }];
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|
|
|
|
NS_ASSUME_NONNULL_END
|
|
|
|