5 // Created by J Osborne on 2/20/13.
9 #import "KDSecCircle.h"
10 #import "KDCirclePeer.h"
12 #include <dispatch/dispatch.h>
13 #import "SecureObjectSync/SOSCloudCircle.h"
14 #include "SecureObjectSync/SOSPeerInfo.h"
16 @interface KDSecCircle ()
17 @property (retain) NSMutableArray *callbacks;
19 @property (readwrite) unsigned long long changeCount;
21 @property (readwrite) SOSCCStatus rawStatus;
23 @property (readwrite) NSString *status;
24 @property (readwrite) NSError *error;
26 @property (readwrite) NSArray *peers;
27 @property (readwrite) NSArray *applicants;
29 @property (readwrite) dispatch_queue_t queue_;
33 @implementation KDSecCircle
37 // XXX: assert not on main_queue
38 CFErrorRef err = NULL;
39 SOSCCStatus newRawStatus = SOSCCThisDeviceIsInCircle(&err);
41 NSArray *peerInfos = (__bridge NSArray *)(SOSCCCopyApplicantPeerInfo(&err));
42 NSMutableArray *newApplicants = [[NSMutableArray alloc] initWithCapacity:peerInfos.count];
43 [peerInfos enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
44 [newApplicants addObject:[[KDCirclePeer alloc] initWithPeerObject:obj]];
47 peerInfos = (__bridge NSArray *)(SOSCCCopyPeerPeerInfo(&err));
48 NSMutableArray *newPeers = [[NSMutableArray alloc] initWithCapacity:peerInfos.count];
49 [peerInfos enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
50 [newPeers addObject:[[KDCirclePeer alloc] initWithPeerObject:obj]];
53 NSLog(@"rawStatus %d, #applicants %lu, #peers %lu, err=%@", newRawStatus, (unsigned long)[newApplicants count], (unsigned long)[newPeers count], err);
55 dispatch_async(dispatch_get_main_queue(), ^{
56 self.rawStatus = newRawStatus;
58 switch (newRawStatus) {
60 self.status = @"In Circle";
63 case kSOSCCNotInCircle:
64 self.status = @"Not In Circle";
67 case kSOSCCRequestPending:
68 self.status = @"Request Pending";
71 case kSOSCCCircleAbsent:
72 self.status = @"Circle Absent";
76 self.status = [NSString stringWithFormat:@"Error: %@", err];
80 self.status = [NSString stringWithFormat:@"ParamError: %@", err];
84 self.status = [NSString stringWithFormat:@"Unknown status code %d", self.rawStatus];
88 self.applicants = [newApplicants copy];
89 self.peers = [newPeers copy];
90 self.error = (__bridge NSError *)(err);
93 for (dispatch_block_t callback in self.callbacks) {
99 // XXX It's a botch to use the "name" and not applicant, but
100 // it is hard to get anythign else to survive a serialastion
101 // trip thoguth NSUserNotificationCenter.
103 // Er, now that I look more closely maybe SOSPeerInfoGetPeerID...
105 typedef void (^applicantBlock)(id applicant);
107 -(void)forApplicantId:(NSString*)applicantId run:(applicantBlock)applicantBlock
109 dispatch_async(self.queue_, ^{
110 for (KDCirclePeer *applicant in self.applicants) {
111 if ([applicantId isEqualToString:applicantId]) {
112 applicantBlock(applicant.peerObject);
119 -(void)acceptApplicantId:(NSString*)applicantId
121 [self forApplicantId:applicantId run:^void(id applicant) {
123 bool ok = SOSCCAcceptApplicants((__bridge CFArrayRef)(@[applicant]), &err);
124 NSAssert(ok, @"Error %@ while accepting %@ (%@)", err, applicantId, applicant);
128 -(void)rejectApplicantId:(NSString*)applicantId
130 [self forApplicantId:applicantId run:^void(id applicant) {
132 bool ok = SOSCCRejectApplicants((__bridge CFArrayRef)(@[applicant]), &err);
133 NSAssert(ok, @"Error %@ while rejecting %@ (%@)", err, applicantId, applicant);
142 self->_queue_ = dispatch_queue_create([[NSString stringWithFormat:@"KDSecCircle@%p", self] UTF8String], NULL);
143 self->_callbacks = [NSMutableArray new];
144 // Replace "com.apple.security.secureobjectsync.circlechanged" with kSOSCCCircleChangedNotification once it is exported
145 notify_register_dispatch("com.apple.security.secureobjectsync.circlechanged", &token, self.queue_, ^(int token){
152 -(void)addChangeCallback:(dispatch_block_t)callback
154 [self.callbacks addObject:callback];
155 if (self.changeCount) {
156 dispatch_async(dispatch_get_main_queue(), callback);
157 } else if (self.callbacks.count == 1) {
158 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
166 return (self.rawStatus == kSOSCCInCircle) ? YES : NO;
171 return (self.rawStatus == kSOSCCNotInCircle || self.rawStatus == kSOSCCCircleAbsent);
177 if (self.rawStatus == kSOSCCCircleAbsent) {
178 SOSCCResetToOffering(&err);
180 SOSCCRequestToJoinCircle(&err);
187 SOSCCRemoveThisDeviceFromCircle(&err);