3 #import <CloudKit/CloudKit.h>
4 #import <CloudKit/CloudKit_Private.h>
6 #import "keychain/ckks/CKKSCreateCKZoneOperation.h"
7 #import "keychain/ckks/CKKSZoneStateEntry.h"
8 #import "keychain/categories/NSError+UsefulConstructors.h"
9 #import "keychain/ot/ObjCImprovements.h"
10 #import "keychain/ot/OTDefines.h"
12 @implementation CKKSCreateCKZoneOperation
13 @synthesize nextState = _nextState;
14 @synthesize intendedState = _intendedState;
16 - (instancetype)initWithDependencies:(CKKSOperationDependencies*)dependencies
17 intendedState:(OctagonState*)intendedState
18 errorState:(OctagonState*)errorState
20 if(self = [super init]) {
23 _intendedState = intendedState;
24 _nextState = errorState;
31 __block CKKSZoneStateEntry* ckseOriginal = nil;
32 [self.deps.databaseProvider dispatchSyncWithReadOnlySQLTransaction:^{
33 ckseOriginal = [CKKSZoneStateEntry state:self.deps.zoneID.zoneName];
36 if(ckseOriginal.ckzonecreated && ckseOriginal.ckzonesubscribed) {
37 ckksinfo("ckkskey", self.deps.zoneID, "Zone is already created and subscribed");
38 self.nextState = self.intendedState;
42 ckksnotice("ckkszone", self.deps.zoneID, "Asking to create and subscribe to CloudKit zone '%@'", self.deps.zoneID.zoneName);
43 CKRecordZone* zone = [[CKRecordZone alloc] initWithZoneID:self.deps.zoneID];
44 CKKSZoneModifyOperations* zoneOps = [self.deps.zoneModifier createZone:zone];
48 CKKSResultOperation* handleModificationsOperation = [CKKSResultOperation named:@"handle-modification" withBlock:^{
50 BOOL zoneCreated = NO;
51 BOOL zoneSubscribed = NO;
52 if([zoneOps.savedRecordZones containsObject:zone]) {
53 ckksinfo("ckkszone", self.deps.zoneID, "Successfully created '%@'", self.deps.zoneID);
56 ckkserror("ckkszone", self.deps.zoneID, "Failed to create '%@'", self.deps.zoneID);
59 bool createdSubscription = false;
60 for(CKSubscription* subscription in zoneOps.savedSubscriptions) {
61 if([subscription.subscriptionID isEqual:[@"zone:" stringByAppendingString: self.deps.zoneID.zoneName]]) {
62 zoneSubscribed = true;
67 if(createdSubscription) {
68 ckksinfo("ckkszone", self.deps.zoneID, "Successfully subscribed '%@'", self.deps.zoneID);
71 ckkserror("ckkszone", self.deps.zoneID, "Failed to subscribe to '%@'", self.deps.zoneID);
74 [self.deps.databaseProvider dispatchSyncWithSQLTransaction:^CKKSDatabaseTransactionResult{
75 ckksnotice("ckkszone", self.deps.zoneID, "Zone setup progress: created:%d %@ subscribed:%d %@",
77 zoneOps.zoneModificationOperation.error,
79 zoneOps.zoneSubscriptionOperation.error);
82 CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state:self.deps.zoneID.zoneName];
83 ckse.ckzonecreated = zoneCreated;
84 ckse.ckzonesubscribed = zoneSubscribed;
86 // Although, if the zone subscribed error says there's no zone, mark down that there's no zone
87 if(zoneOps.zoneSubscriptionOperation.error &&
88 [zoneOps.zoneSubscriptionOperation.error.domain isEqualToString:CKErrorDomain] && zoneOps.zoneSubscriptionOperation.error.code == CKErrorPartialFailure) {
89 NSError* subscriptionError = zoneOps.zoneSubscriptionOperation.error.userInfo[CKPartialErrorsByItemIDKey][self.deps.zoneID];
90 if(subscriptionError && [subscriptionError.domain isEqualToString:CKErrorDomain] && subscriptionError.code == CKErrorZoneNotFound) {
92 ckkserror("ckks", self.deps.zoneID, "zone subscription error appears to say the zone doesn't exist, fixing status: %@", zoneOps.zoneSubscriptionOperation.error);
93 ckse.ckzonecreated = false;
97 [ckse saveToDatabase:&error];
99 ckkserror("ckks", self.deps.zoneID, "couldn't save zone creation status for %@: %@", self.deps.zoneID, error);
102 if(!zoneCreated || !zoneSubscribed) {
103 // Go into 'zonecreationfailed'
104 self.nextState = SecCKKSZoneKeyStateZoneCreationFailed;
105 self.error = zoneOps.zoneModificationOperation.error ?: zoneOps.zoneSubscriptionOperation.error;
107 self.nextState = self.intendedState;
110 return CKKSDatabaseTransactionCommit;
114 [handleModificationsOperation addNullableDependency:zoneOps.zoneModificationOperation];
115 [handleModificationsOperation addNullableDependency:zoneOps.zoneSubscriptionOperation];
116 [self runBeforeGroupFinished:handleModificationsOperation];