|
|
@ -141,14 +141,17 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
// These items should be downloaded immediately.
|
|
|
|
// These items should be downloaded immediately.
|
|
|
|
NSMutableArray<OWSBackupFragment *> *allItems = [NSMutableArray new];
|
|
|
|
NSMutableArray<OWSBackupFragment *> *allItems = [NSMutableArray new];
|
|
|
|
[allItems addObjectsFromArray:self.databaseItems];
|
|
|
|
[allItems addObjectsFromArray:self.databaseItems];
|
|
|
|
if (self.manifest.localProfileAvatarItem) {
|
|
|
|
|
|
|
|
[allItems addObject:self.manifest.localProfileAvatarItem];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Make a copy of the blockingItems before we add
|
|
|
|
// Make a copy of the blockingItems before we add
|
|
|
|
// the attachment items.
|
|
|
|
// the optional items.
|
|
|
|
NSArray<OWSBackupFragment *> *blockingItems = [allItems copy];
|
|
|
|
NSArray<OWSBackupFragment *> *blockingItems = [allItems copy];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Local profile avatars are optional in the sense that if their
|
|
|
|
|
|
|
|
// download fails, we want to proceed with the import.
|
|
|
|
|
|
|
|
if (self.manifest.localProfileAvatarItem) {
|
|
|
|
|
|
|
|
[allItems addObject:self.manifest.localProfileAvatarItem];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Attachment items can be downloaded later;
|
|
|
|
// Attachment items can be downloaded later;
|
|
|
|
// they will can be lazy-restored.
|
|
|
|
// they will can be lazy-restored.
|
|
|
|
[allItems addObjectsFromArray:self.attachmentsItems];
|
|
|
|
[allItems addObjectsFromArray:self.attachmentsItems];
|
|
|
@ -224,13 +227,35 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
|
|
|
|
|
|
|
|
AnyPromise *promise = [AnyPromise promiseWithValue:@(1)];
|
|
|
|
AnyPromise *promise = [AnyPromise promiseWithValue:@(1)];
|
|
|
|
for (OWSBackupFragment *item in items) {
|
|
|
|
for (OWSBackupFragment *item in items) {
|
|
|
|
promise = promise.thenInBackground(^{
|
|
|
|
promise = promise
|
|
|
|
CGFloat progress = (recordCount > 0 ? ((recordCount - items.count) / (CGFloat)recordCount) : 0.f);
|
|
|
|
.thenInBackground(^{
|
|
|
|
[self updateProgressWithDescription:NSLocalizedString(@"BACKUP_IMPORT_PHASE_DOWNLOAD",
|
|
|
|
CGFloat progress
|
|
|
|
|
|
|
|
= (recordCount > 0 ? ((recordCount - items.count) / (CGFloat)recordCount) : 0.f);
|
|
|
|
|
|
|
|
[self updateProgressWithDescription:
|
|
|
|
|
|
|
|
NSLocalizedString(@"BACKUP_IMPORT_PHASE_DOWNLOAD",
|
|
|
|
@"Indicates that the backup import data is being downloaded.")
|
|
|
|
@"Indicates that the backup import data is being downloaded.")
|
|
|
|
progress:@(progress)];
|
|
|
|
progress:@(progress)];
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.thenInBackground(^{
|
|
|
|
|
|
|
|
return [self downloadFileFromCloud:item ignoreErrors:NO];
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (AnyPromise *)downloadFileFromCloud:(OWSBackupFragment *)item ignoreErrors:(BOOL)ignoreErrors
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
OWSAssertDebug(item);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OWSLogVerbose(@"");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (self.isComplete) {
|
|
|
|
|
|
|
|
// Job was aborted.
|
|
|
|
|
|
|
|
return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
|
|
|
// TODO: Use a predictable file path so that multiple "import backup" attempts
|
|
|
|
// TODO: Use a predictable file path so that multiple "import backup" attempts
|
|
|
|
// will leverage successful file downloads from previous attempts.
|
|
|
|
// will leverage successful file downloads from previous attempts.
|
|
|
|
//
|
|
|
|
//
|
|
|
@ -243,23 +268,51 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
|
|
|
|
|
|
|
|
item.downloadFilePath = tempFilePath;
|
|
|
|
item.downloadFilePath = tempFilePath;
|
|
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
return resolve(@(1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
promise = promise.thenInBackground(^{
|
|
|
|
[OWSBackupAPI downloadFileFromCloudObjcWithRecordName:item.recordName
|
|
|
|
return [OWSBackupAPI downloadFileFromCloudObjcWithRecordName:item.recordName
|
|
|
|
|
|
|
|
toFileUrl:[NSURL fileURLWithPath:tempFilePath]]
|
|
|
|
toFileUrl:[NSURL fileURLWithPath:tempFilePath]]
|
|
|
|
.thenInBackground(^{
|
|
|
|
.thenInBackground(^{
|
|
|
|
[OWSFileSystem protectFileOrFolderAtPath:tempFilePath];
|
|
|
|
[OWSFileSystem protectFileOrFolderAtPath:tempFilePath];
|
|
|
|
item.downloadFilePath = tempFilePath;
|
|
|
|
item.downloadFilePath = tempFilePath;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.catchInBackground(^(NSError *error) {
|
|
|
|
|
|
|
|
if (ignoreErrors) {
|
|
|
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
resolve(error);
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (AnyPromise *)restoreLocalProfile
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
OWSLogVerbose(@"");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (self.isComplete) {
|
|
|
|
|
|
|
|
// Job was aborted.
|
|
|
|
|
|
|
|
return [AnyPromise promiseWithValue:OWSBackupErrorWithDescription(@"Backup import no longer active.")];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AnyPromise *promise = [AnyPromise promiseWithValue:@(1)];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (self.manifest.localProfileAvatarItem) {
|
|
|
|
|
|
|
|
promise = promise.thenInBackground(^{
|
|
|
|
|
|
|
|
return [self downloadFileFromCloud:self.manifest.localProfileAvatarItem ignoreErrors:YES];
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
promise = promise.thenInBackground(^{
|
|
|
|
|
|
|
|
return [self applyLocalProfile];
|
|
|
|
|
|
|
|
});
|
|
|
|
return promise;
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (AnyPromise *)restoreLocalProfile
|
|
|
|
- (AnyPromise *)applyLocalProfile
|
|
|
|
{
|
|
|
|
{
|
|
|
|
OWSLogVerbose(@"");
|
|
|
|
OWSLogVerbose(@"");
|
|
|
|
|
|
|
|
|
|
|
@ -269,14 +322,42 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSString *_Nullable localProfileName = self.manifest.localProfileName;
|
|
|
|
NSString *_Nullable localProfileName = self.manifest.localProfileName;
|
|
|
|
UIImage *_Nullable localProfileAvatar = nil;
|
|
|
|
UIImage *_Nullable localProfileAvatar = [self tryToLoadLocalProfileAvatar];
|
|
|
|
|
|
|
|
|
|
|
|
if (self.manifest.localProfileAvatarItem) {
|
|
|
|
OWSLogVerbose(@"local profile name: %@, avatar: %d", localProfileName, localProfileAvatar != nil);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (localProfileName.length < 1 && !localProfileAvatar) {
|
|
|
|
|
|
|
|
return [AnyPromise promiseWithValue:@(1)];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
|
|
|
|
|
|
|
[self.profileManager updateLocalProfileName:localProfileName
|
|
|
|
|
|
|
|
avatarImage:localProfileAvatar
|
|
|
|
|
|
|
|
success:^{
|
|
|
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
failure:^{
|
|
|
|
|
|
|
|
// Ignore errors related to local profile.
|
|
|
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (nullable UIImage *)tryToLoadLocalProfileAvatar
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!self.manifest.localProfileAvatarItem) {
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!self.manifest.localProfileAvatarItem.downloadFilePath) {
|
|
|
|
|
|
|
|
// Download of the avatar failed.
|
|
|
|
|
|
|
|
// We can safely ignore errors related to local profile.
|
|
|
|
|
|
|
|
OWSLogError(@"local profile avatar was not downloaded.");
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
}
|
|
|
|
OWSBackupFragment *item = self.manifest.localProfileAvatarItem;
|
|
|
|
OWSBackupFragment *item = self.manifest.localProfileAvatarItem;
|
|
|
|
if (item.recordName.length < 1) {
|
|
|
|
if (item.recordName.length < 1) {
|
|
|
|
OWSLogError(@"item was not downloaded.");
|
|
|
|
OWSFailDebug(@"item missing record name.");
|
|
|
|
// Ignore errors related to local profile.
|
|
|
|
return nil;
|
|
|
|
return [AnyPromise promiseWithValue:@(1)];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@autoreleasepool {
|
|
|
|
@autoreleasepool {
|
|
|
@ -285,36 +366,16 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|
|
|
if (!data) {
|
|
|
|
if (!data) {
|
|
|
|
OWSLogError(@"could not decrypt local profile avatar.");
|
|
|
|
OWSLogError(@"could not decrypt local profile avatar.");
|
|
|
|
// Ignore errors related to local profile.
|
|
|
|
// Ignore errors related to local profile.
|
|
|
|
return [AnyPromise promiseWithValue:@(1)];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: Verify that we're not compressing the profile avatar data.
|
|
|
|
// TODO: Verify that we're not compressing the profile avatar data.
|
|
|
|
UIImage *_Nullable image = [UIImage imageWithData:data];
|
|
|
|
UIImage *_Nullable image = [UIImage imageWithData:data];
|
|
|
|
if (!image) {
|
|
|
|
if (!image) {
|
|
|
|
OWSLogError(@"could not decrypt local profile avatar.");
|
|
|
|
OWSLogError(@"could not decrypt local profile avatar.");
|
|
|
|
// Ignore errors related to local profile.
|
|
|
|
// Ignore errors related to local profile.
|
|
|
|
return [AnyPromise promiseWithValue:@(1)];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
localProfileAvatar = image;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return image;
|
|
|
|
OWSLogVerbose(@"local profile name: %@, avatar: %d", localProfileName, localProfileAvatar != nil);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (localProfileName.length > 0 || localProfileAvatar) {
|
|
|
|
|
|
|
|
AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
|
|
|
|
|
|
|
[self.profileManager updateLocalProfileName:localProfileName
|
|
|
|
|
|
|
|
avatarImage:localProfileAvatar
|
|
|
|
|
|
|
|
success:^{
|
|
|
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
failure:^{
|
|
|
|
|
|
|
|
// Ignore errors related to local profile.
|
|
|
|
|
|
|
|
resolve(@(1));
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return [AnyPromise promiseWithValue:@(1)];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|