]> git.saurik.com Git - apple/security.git/blob - keychain/otctl/OTControlCLI.m
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / otctl / OTControlCLI.m
1
2
3 #import <Foundation/Foundation.h>
4 #import <Foundation/NSXPCConnection_Private.h>
5 #import <Security/SecItemPriv.h>
6 #import <Security/Security.h>
7 #import <xpc/xpc.h>
8
9 #include "utilities/SecCFWrappers.h"
10 #include "utilities/SecInternalReleasePriv.h"
11 #import "utilities/debugging.h"
12
13 #import "keychain/ot/OTClique.h"
14 #import "keychain/ot/OT.h"
15 #import "keychain/ot/OTConstants.h"
16 #import "keychain/ot/OTControl.h"
17
18 #import "keychain/otctl/OTControlCLI.h"
19
20 #import "keychain/OctagonTrust/OctagonTrust.h"
21
22 #import <AuthKit/AKAppleIDAuthenticationController.h>
23 #import <AuthKit/AKAppleIDAuthenticationContext.h>
24 #import <AuthKit/AKAppleIDAuthenticationContext_Private.h>
25
26 static NSString * fetch_pet(NSString * appleID, NSString * dsid)
27 {
28 if(!appleID && !dsid) {
29 NSLog(@"Must provide either an AppleID or a DSID to fetch a PET");
30 exit(1);
31 }
32
33 AKAppleIDAuthenticationContext* authContext = [[AKAppleIDAuthenticationContext alloc] init];
34 authContext.username = appleID;
35
36 authContext.authenticationType = AKAppleIDAuthenticationTypeSilent;
37 authContext.isUsernameEditable = NO;
38
39 __block NSString * pet = nil;
40
41 dispatch_semaphore_t s = dispatch_semaphore_create(0);
42
43 AKAppleIDAuthenticationController *authenticationController = [[AKAppleIDAuthenticationController alloc] init];
44 [authenticationController authenticateWithContext:authContext
45 completion:^(AKAuthenticationResults authenticationResults, NSError *error) {
46 if(error) {
47 NSLog(@"error fetching PET: %@", error);
48 exit(1);
49 }
50
51 pet = authenticationResults[AKAuthenticationPasswordKey];
52 dispatch_semaphore_signal(s);
53 }];
54 dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER);
55
56 return pet;
57 }
58
59 // Mutual recursion to set up an object for jsonification
60 static NSDictionary* cleanDictionaryForJSON(NSDictionary* dict);
61
62 static id cleanObjectForJSON(id obj) {
63 if(!obj) {
64 return nil;
65 }
66 if([obj isKindOfClass:[NSError class]]) {
67 NSError* obje = (NSError*) obj;
68 NSMutableDictionary* newErrorDict = [@{@"code": @(obje.code), @"domain": obje.domain} mutableCopy];
69 newErrorDict[@"userInfo"] = cleanDictionaryForJSON(obje.userInfo);
70 return newErrorDict;
71 } else if([NSJSONSerialization isValidJSONObject:obj]) {
72 return obj;
73
74 } else if ([obj isKindOfClass: [NSNumber class]]) {
75 return obj;
76
77 } else if([obj isKindOfClass: [NSData class]]) {
78 NSData* dataObj = (NSData*)obj;
79 return [dataObj base64EncodedStringWithOptions:0];
80
81 } else if ([obj isKindOfClass: [NSDictionary class]]) {
82 return cleanDictionaryForJSON((NSDictionary*) obj);
83
84 } else if ([obj isKindOfClass: [NSArray class]]) {
85 NSArray* arrayObj = (NSArray*)obj;
86 NSMutableArray* cleanArray = [NSMutableArray arrayWithCapacity:arrayObj.count];
87
88 for(id x in arrayObj) {
89 [cleanArray addObject: cleanObjectForJSON(x)];
90 }
91 return cleanArray;
92
93 } else {
94 return [obj description];
95 }
96 }
97
98 static NSDictionary* cleanDictionaryForJSON(NSDictionary* dict) {
99 if(!dict) {
100 return nil;
101 }
102 NSMutableDictionary* mutDict = [dict mutableCopy];
103 for(id key in mutDict.allKeys) {
104 id obj = mutDict[key];
105 mutDict[key] = cleanObjectForJSON(obj);
106 }
107 return mutDict;
108 }
109
110 static void print_json(NSDictionary* dict)
111 {
112 NSError* err;
113
114 NSData* json = [NSJSONSerialization dataWithJSONObject:cleanDictionaryForJSON(dict)
115 options:(NSJSONWritingPrettyPrinted | NSJSONWritingSortedKeys)
116 error:&err];
117 if(!json) {
118 NSLog(@"error during JSONification: %@", err.localizedDescription);
119 } else {
120 printf("%s\n", [[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding] UTF8String]);
121 }
122 }
123
124
125 @implementation OTControlCLI
126
127 - (instancetype)initWithOTControl:(OTControl*)control {
128 if((self = [super init])) {
129 _control = control;
130 }
131
132 return self;
133 }
134
135 - (long)startOctagonStateMachine:(NSString *)container context:(NSString *)contextID {
136 #if OCTAGON
137 __block long ret = -1;
138
139 [self.control startOctagonStateMachine:container
140 context:contextID
141 reply:^(NSError* _Nullable error) {
142 if(error) {
143 printf("Error starting state machine: %s\n", [[error description] UTF8String]);
144 ret = -1;
145 } else {
146 printf("state machine started.\n");
147 }
148 }];
149
150 return ret;
151 #else
152 printf("Unimplemented.\n");
153 return -1;
154 #endif
155 }
156
157 - (long)signIn:(NSString *)altDSID container:(NSString * _Nullable)container context:(NSString *)contextID {
158 #if OCTAGON
159 __block long ret = -1;
160
161 [self.control signIn:altDSID
162 container:container
163 context:contextID
164 reply:^(NSError* _Nullable error) {
165 if(error) {
166 printf("Error signing in: %s\n", [[error description] UTF8String]);
167 } else {
168 printf("Sign in complete.\n");
169 ret = 0;
170 }
171 }];
172
173 return ret;
174 #else
175 printf("Unimplemented.\n");
176 return -1;
177 #endif
178 }
179
180 - (long)signOut:(NSString * _Nullable)container context:(NSString *)contextID {
181 #if OCTAGON
182 __block long ret = -1;
183 [self.control signOut:container
184 context:contextID
185 reply:^(NSError* _Nullable error) {
186 if(error) {
187 printf("Error signing out: %s\n", [[error description] UTF8String]);
188 } else {
189 printf("Sign out complete.\n");
190 ret = 0;
191 }
192 }];
193 return ret;
194 #else
195 printf("Unimplemented.\n");
196 return -1;
197 #endif
198 }
199
200 - (long)depart:(NSString * _Nullable)container context:(NSString *)contextID {
201 #if OCTAGON
202 __block long ret = -1;
203
204 [self.control leaveClique:container
205 context:contextID
206 reply:^(NSError* _Nullable error) {
207 if(error) {
208 printf("Error departing clique: %s\n", [[error description] UTF8String]);
209 } else {
210 printf("Departing clique completed.\n");
211 ret = 0;
212 }
213 }];
214 return ret;
215 #else
216 printf("Unimplemented.\n");
217 return -1;
218 #endif
219 }
220
221 - (long)resetOctagon:(NSString *)container context:(NSString *)contextID altDSID:(NSString *)altDSID {
222 #if OCTAGON
223 __block long ret = -1;
224
225 [self.control resetAndEstablish:container
226 context:contextID
227 altDSID:altDSID
228 resetReason:CuttlefishResetReasonUserInitiatedReset
229 reply:^(NSError* _Nullable error) {
230 if(error) {
231 printf("Error resetting: %s\n", [[error description] UTF8String]);
232 } else {
233 printf("reset and establish:\n");
234 ret = 0;
235 }
236 }];
237
238 return ret;
239 #else
240 printf("Unimplemented.\n");
241 return -1;
242 #endif
243 }
244
245
246 - (long)resetProtectedData:(NSString *)container context:(NSString *)contextID altDSID:(NSString *)altDSID appleID:(NSString *)appleID dsid:(NSString *)dsid
247 {
248 #if OCTAGON
249 __block long ret = -1;
250
251 NSError* error = nil;
252 OTConfigurationContext *data = [[OTConfigurationContext alloc] init];
253 data.passwordEquivalentToken = fetch_pet(appleID, dsid);
254 data.authenticationAppleID = appleID;
255 data.altDSID = altDSID;
256 data.context = contextID;
257 data.containerName = container;
258
259 OTClique* clique = [OTClique resetProtectedData:data error:&error];
260 if(clique != nil && error == nil) {
261 ret = 0;
262 }
263 return ret;
264 #else
265 printf("Unimplemented.\n");
266 return -1;
267 #endif
268 }
269
270 - (void)printPeer:(NSDictionary*)peerInformation prefix:(NSString * _Nullable)prefix {
271 NSString * peerID = peerInformation[@"peerID"];
272 NSString * model = peerInformation[@"permanentInfo"][@"model_id"];
273 NSNumber* epoch = peerInformation[@"permanentInfo"][@"epoch"];
274 NSString * deviceName = peerInformation[@"stableInfo"][@"device_name"];
275 NSString * serialNumber = peerInformation[@"stableInfo"][@"serial_number"];
276 NSString * os = peerInformation[@"stableInfo"][@"os_version"];
277
278 printf("%s%s hw:'%s' name:'%s' serial: '%s' os:'%s' epoch:%d\n",
279 (prefix ? [prefix UTF8String] : ""),
280 [peerID UTF8String],
281 [model UTF8String],
282 [deviceName UTF8String],
283 [serialNumber UTF8String],
284 [os UTF8String],
285 [epoch intValue]);
286 }
287
288 - (void)printPeers:(NSArray<NSString *>*)peerIDs
289 egoPeerID:(NSString * _Nullable)egoPeerID
290 informationOnPeers:(NSDictionary<NSString *, NSDictionary*>*)informationOnPeers {
291 for(NSString * peerID in peerIDs) {
292 NSDictionary* peerInformation = informationOnPeers[peerID];
293
294 if(!peerInformation) {
295 printf(" Peer: %s; further information missing\n", [peerID UTF8String]);
296 continue;
297 }
298
299 if([peerID isEqualToString:egoPeerID]) {
300 [self printPeer:peerInformation prefix:@" Self: "];
301 } else {
302 [self printPeer:peerInformation prefix:@" Peer: "];
303 }
304 }
305 }
306
307 - (long)fetchEscrowRecords:(NSString * _Nullable)container context:(NSString *)contextID {
308 #if OCTAGON
309 __block long ret = -1;
310
311 NSError* error = nil;
312 OTConfigurationContext *data = [[OTConfigurationContext alloc] init];
313 data.context = contextID;
314 data.containerName = container;
315
316 NSArray<OTEscrowRecord*>* records = [OTClique fetchEscrowRecords:data error:&error];
317 if(records != nil && error == nil) {
318 printf("Successfully fetched %lu records.\n", (unsigned long)records.count);
319 ret = 0;
320 for(OTEscrowRecord* record in records){
321 CFErrorRef* localError = NULL;
322 SOSPeerInfoRef peer = SOSPeerInfoCreateFromData(kCFAllocatorDefault, localError, (__bridge CFDataRef)record.escrowInformationMetadata.peerInfo);
323 CFStringRef peerID = SOSPeerInfoGetPeerID(peer);
324 printf("fetched record id: %s\n", [(__bridge NSString *)peerID UTF8String]);
325 }
326 }
327 return ret;
328 #else
329 printf("Unimplemented.\n");
330 return -1;
331 #endif
332 }
333
334 - (long)fetchAllEscrowRecords:(NSString* _Nullable)container context:(NSString*)contextID {
335 #if OCTAGON
336 __block long ret = -1;
337
338 NSError* error = nil;
339 OTConfigurationContext *data = [[OTConfigurationContext alloc] init];
340 data.context = contextID;
341 data.containerName = container;
342
343 NSArray<OTEscrowRecord*>* records = [OTClique fetchAllEscrowRecords:data error:&error];
344 if(records != nil && error == nil) {
345 printf("Successfully fetched %lu records.\n", (unsigned long)records.count);
346 ret = 0;
347 for(OTEscrowRecord* record in records){
348 CFErrorRef* localError = NULL;
349 SOSPeerInfoRef peer = SOSPeerInfoCreateFromData(kCFAllocatorDefault, localError, (__bridge CFDataRef)record.escrowInformationMetadata.peerInfo);
350 CFStringRef peerID = SOSPeerInfoGetPeerID(peer);
351 printf("fetched record id: %s\n", [(__bridge NSString*)peerID UTF8String]);
352 }
353 }
354 return ret;
355 #else
356 printf("Unimplemented.\n");
357 return -1;
358 #endif
359 }
360
361 - (long)performEscrowRecovery:(NSString * _Nullable)container context:(NSString *)contextID recordID:(NSString *)recordID appleID:(NSString *)appleID secret:(NSString *)secret
362 {
363 #if OCTAGON
364 __block long ret = -1;
365
366 NSError* error = nil;
367 OTConfigurationContext *data = [[OTConfigurationContext alloc] init];
368 data.context = contextID;
369
370 OTICDPRecordContext* cdpContext = [[OTICDPRecordContext alloc] init];
371 cdpContext.cdpInfo = [[OTCDPRecoveryInformation alloc] init];
372 cdpContext.cdpInfo.recoverySecret = secret;
373 cdpContext.cdpInfo.containsIcdpData = true;
374 cdpContext.cdpInfo.usesMultipleIcsc = true;
375 cdpContext.authInfo = [[OTEscrowAuthenticationInformation alloc] init];
376 cdpContext.authInfo.authenticationAppleid = appleID;
377 cdpContext.authInfo.authenticationPassword = fetch_pet(appleID, nil);
378
379 NSArray<OTEscrowRecord*>* escrowRecords = [OTClique fetchEscrowRecords:data error:&error];
380 if (escrowRecords == nil || error != nil) {
381 printf("Failed to fetch escrow records.\n");
382 ret = -1;
383 }
384 OTEscrowRecord* record = nil;
385
386 for (OTEscrowRecord* r in escrowRecords) {
387 CFErrorRef* localError = NULL;
388 SOSPeerInfoRef peer = SOSPeerInfoCreateFromData(kCFAllocatorDefault, localError, (__bridge CFDataRef)r.escrowInformationMetadata.peerInfo);
389 CFStringRef peerID = SOSPeerInfoGetPeerID(peer);
390
391 if ([(__bridge NSString *)peerID isEqualToString:recordID]) {
392 record = r;
393 break;
394 }
395 }
396 if (record == nil){
397 printf("Failed to find escrow record to restore. \n");
398 return -1;
399 }
400
401 OTClique* clique = [OTClique performEscrowRecovery:data cdpContext:cdpContext escrowRecord:record error:&error];
402 if (clique != nil && error == nil) {
403 printf("Successfully performed escrow recovery.\n");
404 ret = 0;
405 } else {
406 fprintf(stderr, "Escrow recovery failed: %s\n", error.description.UTF8String);
407 }
408 return ret;
409 #else
410 printf("Unimplemented.\n");
411 return -1;
412 #endif
413 }
414
415 - (long)performSilentEscrowRecovery:(NSString * _Nullable)container context:(NSString *)contextID appleID:(NSString *)appleID secret:(NSString *)secret {
416 #if OCTAGON
417 __block long ret = -1;
418
419 NSError* error = nil;
420 OTConfigurationContext *data = [[OTConfigurationContext alloc] init];
421 data.context = contextID;
422
423 OTICDPRecordContext* cdpContext = [[OTICDPRecordContext alloc] init];
424 cdpContext.cdpInfo = [[OTCDPRecoveryInformation alloc] init];
425
426 cdpContext.cdpInfo.recoverySecret = secret;
427 cdpContext.cdpInfo.containsIcdpData = true;
428 cdpContext.cdpInfo.silentRecoveryAttempt = true;
429 cdpContext.cdpInfo.usesMultipleIcsc = true;
430
431 cdpContext.authInfo = [[OTEscrowAuthenticationInformation alloc] init];
432 cdpContext.authInfo.authenticationAppleid = appleID;
433 cdpContext.authInfo.authenticationPassword = fetch_pet(appleID, nil);
434
435
436 NSArray<OTEscrowRecord*>* records = [OTClique fetchEscrowRecords:data error:&error];
437 if (records == nil || error != nil) {
438 printf("Failed to fetch escrow records.\n");
439 ret = -1;
440 }
441 OTClique* clique = [OTClique performSilentEscrowRecovery:data cdpContext:cdpContext allRecords:records error:&error];
442 if (clique != nil && error == nil) {
443 printf("Successfully performed escrow recovery.\n");
444 ret = 0;
445 } else {
446 fprintf(stderr, "Escrow recovery failed: %s\n", error.description.UTF8String);
447 }
448 return ret;
449 #else
450 printf("Unimplemented.\n");
451 return -1;
452 #endif
453 }
454
455
456 - (long)status:(NSString * _Nullable)container context:(NSString *)contextID json:(bool)json {
457 #if OCTAGON
458 __block long ret = 0;
459
460 [self.control status:container
461 context:contextID
462 reply:^(NSDictionary* result, NSError* _Nullable error) {
463 if(error) {
464 if(json) {
465 print_json(@{@"error" : [error description]});
466 } else {
467 printf("Error fetching status: %s\n", [[error description] UTF8String]);
468 }
469 ret = -1;
470 } else {
471 if(json) {
472 print_json(result);
473 } else {
474 printf("Status for %s,%s:\n", [result[@"containerName"] UTF8String], [result[@"contextID"] UTF8String]);
475
476 printf("\n");
477 printf("State: %s\n", [[result[@"state"] description] UTF8String]);
478 printf("Flags: %s; Flags Pending: %s\n\n",
479 ([result[@"stateFlags"] count] == 0u) ? "none" : [[result[@"stateFlags"] description] UTF8String],
480 ([result[@"statePendingFlags"] count] == 0u) ? "none" : [[result[@"statePendingFlags"] description] UTF8String]);
481
482 NSDictionary* contextDump = result[@"contextDump"];
483
484 // Make it easy to find peer information
485 NSMutableDictionary<NSString *, NSDictionary*>* peers = [NSMutableDictionary dictionary];
486 NSMutableArray<NSString *>* allPeerIDs = [NSMutableArray array];
487 for(NSDictionary* peerInformation in contextDump[@"peers"]) {
488 NSString * peerID = peerInformation[@"peerID"];
489 if(peerID) {
490 peers[peerID] = peerInformation;
491 [allPeerIDs addObject:peerID];
492 }
493 }
494
495 NSDictionary* egoInformation = contextDump[@"self"];
496 NSString * egoPeerID = egoInformation[@"peerID"];
497 NSDictionary* egoDynamicInfo = egoInformation[@"dynamicInfo"];
498
499 if(egoPeerID) {
500 NSMutableArray *otherPeers = [allPeerIDs mutableCopy];
501 [self printPeer:egoInformation prefix:@" Self: "];
502 printf("\n");
503
504 // The self peer is technically a peer, so, shove it on in there
505 peers[egoPeerID] = egoInformation;
506
507 NSArray<NSString *>* includedPeers = egoDynamicInfo[@"included"];
508 printf("Trusted peers (by me):\n");
509 if(includedPeers && includedPeers.count > 0) {
510 [self printPeers:includedPeers egoPeerID:egoPeerID informationOnPeers:peers];
511 [otherPeers removeObjectsInArray:includedPeers];
512 } else {
513 printf(" No trusted peers.\n");
514 }
515 printf("\n");
516
517 NSArray<NSString *>* excludedPeers = egoDynamicInfo[@"excluded"];
518 printf("Excluded peers (by me):\n");
519 if(excludedPeers && excludedPeers.count > 0) {
520 [self printPeers:excludedPeers egoPeerID:egoPeerID informationOnPeers:peers];
521 [otherPeers removeObjectsInArray:excludedPeers];
522 } else {
523 printf(" No excluded peers.\n");
524 }
525 printf("\n");
526
527 printf("Other peers (included/excluded by others):\n");
528 if(otherPeers.count > 0) {
529 [self printPeers:otherPeers egoPeerID:egoPeerID informationOnPeers:peers];
530 } else {
531 printf(" No other peers.\n");
532 }
533 printf("\n");
534
535 } else {
536 printf("No current identity for this device.\n\n");
537
538 if(allPeerIDs.count > 0) {
539 printf("All peers currently in this account:\n");
540 [self printPeers:allPeerIDs egoPeerID:nil informationOnPeers:peers];
541 } else {
542 printf("No peers currently exist for this account.\n");
543 }
544 }
545
546 printf("\n");
547 }
548 }
549 }];
550
551 return ret;
552 #else
553 printf("Unimplemented.\n");
554 return -1;
555 #endif
556 }
557
558 - (long)recoverUsingBottleID:(NSString *)bottleID
559 entropy:(NSData*)entropy
560 altDSID:(NSString *)altDSID
561 containerName:(NSString *)containerName
562 context:(NSString *)context
563 control:(OTControl*)control {
564 __block long ret = 0;
565
566 #if OCTAGON
567 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
568
569 [control restore:containerName
570 contextID:context
571 bottleSalt:altDSID
572 entropy:entropy
573 bottleID:bottleID
574 reply:^(NSError* _Nullable error) {
575 if(error) {
576 ret = -1;
577 printf("Error recovering: %s\n", [[error description] UTF8String]);
578 } else {
579 printf("Succeeded recovering bottled peer %s\n", [[bottleID description] UTF8String]);
580 }
581 dispatch_semaphore_signal(sema);
582 }];
583
584 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
585 printf("timed out waiting for restore/recover\n");
586 ret = -1;
587 }
588
589 return ret;
590 #else
591 ret = -1;
592 return ret;
593 #endif
594 }
595
596 - (long)fetchAllBottles:(NSString *)altDSID
597 containerName:(NSString *)containerName
598 context:(NSString *)context
599 control:(OTControl*)control {
600 __block long ret = 0;
601
602 #if OCTAGON
603 __block NSError* localError = nil;
604
605 __block NSArray<NSString *>* localViableBottleIDs = nil;
606 __block NSArray<NSString *>* localPartiallyViableBottleIDs = nil;
607
608 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
609
610 [control fetchAllViableBottles:containerName
611 context:context
612 reply:^(NSArray<NSString *>* _Nullable sortedBottleIDs,
613 NSArray<NSString *>* _Nullable sortedPartialBottleIDs,
614 NSError* _Nullable controlError) {
615 if(controlError) {
616 secnotice("clique", "findOptimalBottleIDsWithContextData errored: %@\n", controlError);
617 } else {
618 secnotice("clique", "findOptimalBottleIDsWithContextData succeeded: %@, %@\n", sortedBottleIDs, sortedPartialBottleIDs);
619 }
620 localError = controlError;
621 localViableBottleIDs = sortedBottleIDs;
622 localPartiallyViableBottleIDs = sortedPartialBottleIDs;
623 dispatch_semaphore_signal(sema);
624 }];
625
626 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
627 secnotice("clique", "findOptimalBottleIDsWithContextData failed to fetch bottles\n");
628 return -1;
629 }
630
631 [localViableBottleIDs enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL* stop) {
632 printf("preferred bottleID: %s\n", [obj UTF8String]);
633 }];
634
635 [localPartiallyViableBottleIDs enumerateObjectsUsingBlock:^(NSString * obj, NSUInteger idx, BOOL* stop) {
636 printf("partial recovery bottleID: %s\n", [obj UTF8String]);
637 }];
638
639 return ret;
640 #else
641 ret = -1;
642 return ret;
643 #endif
644 }
645
646 - (long)healthCheck:(NSString * _Nullable)container context:(NSString *)contextID skipRateLimitingCheck:(BOOL)skipRateLimitingCheck
647 {
648 #if OCTAGON
649 __block long ret = -1;
650
651 [self.control healthCheck:container
652 context:contextID
653 skipRateLimitingCheck:skipRateLimitingCheck
654 reply:^(NSError* _Nullable error) {
655 if(error) {
656 printf("Error checking health: %s\n", [[error description] UTF8String]);
657 } else {
658 printf("Checking Octagon Health completed.\n");
659 ret = 0;
660 }
661 }];
662 return ret;
663 #else
664 printf("Unimplemented.\n");
665 return -1;
666 #endif
667 }
668
669 - (long)refetchCKKSPolicy:(NSString *)container context:(NSString *)contextID
670 {
671 #if OCTAGON
672 __block long ret = 1;
673
674 [self.control refetchCKKSPolicy:container
675 contextID:contextID
676 reply:^(NSError * _Nullable error) {
677 if(error) {
678 printf("Error refetching CKKS policy: %s\n", [[error description] UTF8String]);
679 } else {
680 printf("CKKS refetch completed.\n");
681 ret = 0;
682 }
683 }];
684 return ret;
685 #else
686 printf("Unimplemented.\n");
687 return 1;
688 #endif
689 }
690
691 - (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar
692 {
693 #if OCTAGON
694 __block long ret = 1;
695
696 [self.control tapToRadar:action
697 description:description
698 radar:radar
699 reply:^(NSError* _Nullable error) {
700 if(error) {
701 printf("Error trigger TTR: %s\n", [[error description] UTF8String]);
702 } else {
703 printf("Trigger TTR completed.\n");
704 ret = 0;
705 }
706 }];
707 return ret;
708 #else
709 printf("Unimplemented.\n");
710 return 1;
711 #endif
712 }
713
714 - (long)setUserControllableViewsSyncStatus:(NSString * _Nullable)containerName
715 contextID:(NSString *)contextID
716 enabled:(BOOL)enabled
717 {
718 #if OCTAGON
719 __block long ret = 1;
720
721 [self.control setUserControllableViewsSyncStatus:containerName
722 contextID:contextID
723 enabled:enabled
724 reply:^(BOOL nowSyncing, NSError * _Nullable error) {
725 if(error) {
726 printf("Error setting user controllable views: %s\n", [[error description] UTF8String]);
727 } else {
728 printf("User controllable views are now %s.", [(nowSyncing ? @"enabled" : @"paused") UTF8String]);
729 ret = 0;
730 }
731 }];
732 return ret;
733 #else
734 printf("Unimplemented.\n");
735 return 1;
736 #endif
737 }
738
739 - (long)fetchUserControllableViewsSyncStatus:(NSString * _Nullable)containerName
740 contextID:(NSString *)contextID
741 {
742 #if OCTAGON
743 __block long ret = 1;
744
745 [self.control fetchUserControllableViewsSyncStatus:containerName
746 contextID:contextID
747 reply:^(BOOL nowSyncing, NSError * _Nullable error) {
748 if(error) {
749 printf("Error setting user controllable views: %s\n", [[error description] UTF8String]);
750 } else {
751 printf("User controllable views are currently %s.", [(nowSyncing ? @"enabled" : @"paused") UTF8String]);
752 ret = 0;
753 }
754 }];
755 return ret;
756 #else
757 printf("Unimplemented.\n");
758 return 1;
759 #endif
760 }
761
762
763 @end