5 #import <CloudKit/CloudKit.h>
6 #import <CloudKit/CloudKit_Private.h>
8 #import "keychain/ckks/CKKSDeleteCKZoneOperation.h"
9 #import "keychain/ckks/CKKSZoneStateEntry.h"
10 #import "keychain/categories/NSError+UsefulConstructors.h"
11 #import "keychain/ot/ObjCImprovements.h"
12 #import "keychain/ot/OTDefines.h"
14 @implementation CKKSDeleteCKZoneOperation
15 @synthesize nextState = _nextState;
16 @synthesize intendedState = _intendedState;
18 - (instancetype)initWithDependencies:(CKKSOperationDependencies*)dependencies
19 intendedState:(OctagonState*)intendedState
20 errorState:(OctagonState*)errorState
22 if(self = [super init]) {
25 _intendedState = intendedState;
26 _nextState = errorState;
34 ckksnotice("ckkszone", self.deps.zoneID, "Deleting CloudKit zone '%@'", self.deps.zoneID);
35 CKKSZoneModifyOperations* zoneOps = [self.deps.zoneModifier deleteZone:self.deps.zoneID];
40 CKKSResultOperation* handleModificationsOperation = [CKKSResultOperation named:@"handle-modification" withBlock:^{
43 bool fatalError = false;
45 NSError* operationError = zoneOps.zoneModificationOperation.error;
46 bool removed = [zoneOps.deletedRecordZoneIDs containsObject:self.deps.zoneID];
48 if(!removed && operationError) {
49 // Okay, but if this error is either 'ZoneNotFound' or 'UserDeletedZone', that's fine by us: the zone is deleted.
50 NSDictionary* partialErrors = operationError.userInfo[CKPartialErrorsByItemIDKey];
51 if([operationError.domain isEqualToString:CKErrorDomain] && operationError.code == CKErrorPartialFailure && partialErrors) {
52 for(CKRecordZoneID* errorZoneID in partialErrors.allKeys) {
53 NSError* errorZone = partialErrors[errorZoneID];
55 if(errorZone && [errorZone.domain isEqualToString:CKErrorDomain] &&
56 (errorZone.code == CKErrorZoneNotFound || errorZone.code == CKErrorUserDeletedZone)) {
57 ckksnotice("ckkszone", self.deps.zoneID, "Attempted to delete zone %@, but it's already missing. This is okay: %@", errorZoneID, errorZone);
67 ckksnotice("ckkszone", self.deps.zoneID, "deletion of record zone %@ completed with error: %@", self.deps.zoneID, operationError);
71 self.error = operationError;
76 ckksnotice("ckkszone", self.deps.zoneID, "deletion of record zone %@ completed successfully", self.deps.zoneID);
78 [self.deps.databaseProvider dispatchSyncWithSQLTransaction:^CKKSDatabaseTransactionResult{
80 CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state:self.deps.zoneID.zoneName];
81 ckse.ckzonecreated = NO;
82 ckse.ckzonesubscribed = NO;
84 [ckse saveToDatabase:&error];
86 ckkserror("ckks", self.deps.zoneID, "couldn't save zone creation status for %@: %@", self.deps.zoneID, error);
89 self.nextState = self.intendedState;
90 return CKKSDatabaseTransactionCommit;
94 [handleModificationsOperation addNullableDependency:zoneOps.zoneModificationOperation];
95 [handleModificationsOperation addNullableDependency:zoneOps.zoneSubscriptionOperation];
96 [self runBeforeGroupFinished:handleModificationsOperation];