/*
* Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#import "KDSecCircle.h"
#import "KDCirclePeer.h"
#include <notify.h>
#include <dispatch/dispatch.h>
#import <Security/SecureObjectSync/SOSCloudCircle.h>
#import <Security/SecureObjectSync/SOSPeerInfo.h>
#import <CloudServices/SecureBackup.h>
#include <utilities/debugging.h>
@interface KDSecCircle ()
@property (retain) NSMutableArray *callbacks;
@property (readwrite) unsigned long long changeCount;
@property (readwrite) SOSCCStatus rawStatus;
@property (readwrite) NSString *status;
@property (readwrite) NSError *error;
@property (readwrite) NSArray *peers;
@property (readwrite) NSArray *applicants;
@property (readwrite) dispatch_queue_t queue_;
@end
@implementation KDSecCircle
-(void)updateCheck
{
// XXX: assert not on main_queue
CFErrorRef err = NULL;
SOSCCValidateUserPublic(NULL); // requires the account queue - makes the rest of this wait for fresh info. This used to happen in SOSCCThisDeviceIsInCircle(below) before we made it use cached info.
SOSCCStatus newRawStatus = SOSCCThisDeviceIsInCircle(&err);
NSArray *peerInfos = (__bridge NSArray *) SOSCCCopyApplicantPeerInfo(&err);
NSMutableArray *newApplicants = [[NSMutableArray alloc] initWithCapacity:peerInfos.count];
[peerInfos enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[newApplicants addObject:[[KDCirclePeer alloc] initWithPeerObject:obj]];
}];
peerInfos = (__bridge NSArray *) SOSCCCopyPeerPeerInfo(&err);
NSMutableArray *newPeers = [[NSMutableArray alloc] initWithCapacity:peerInfos.count];
[peerInfos enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[newPeers addObject:[[KDCirclePeer alloc] initWithPeerObject:obj]];
}];
secdebug("kcn", "rawStatus
dispatch_async(dispatch_get_main_queue(), ^{
self.rawStatus = newRawStatus;
switch (newRawStatus) {
case kSOSCCInCircle:
self.status = @"In Circle";
break;
case kSOSCCNotInCircle:
self.status = @"Not In Circle";
break;
case kSOSCCRequestPending:
self.status = @"Request Pending";
break;
case kSOSCCCircleAbsent:
self.status = @"Circle Absent";
break;
case kSOSCCError:
self.status = [NSString stringWithFormat:@"Error: break;
default:
self.status = [NSString stringWithFormat:@"Unknown status code break;
}
self.applicants = [newApplicants copy];
self.peers = [newPeers copy];
self.error = (__bridge NSError *)(err);
self.changeCount++;
for (dispatch_block_t callback in self.callbacks) {
callback();
}
});
}
// XXX It's a botch to use the "name" and not applicant, but
// it is hard to get anythign else to survive a serialastion
// trip thoguth NSUserNotificationCenter.
//
// Er, now that I look more closely maybe SOSPeerInfoGetPeerID...
typedef void (^applicantBlock)(id applicant);
-(void)forApplicantId:(NSString*)applicantId run:(applicantBlock)applicantBlock
{
dispatch_async(self.queue_, ^{
for (KDCirclePeer *applicant in self.applicants) {
if ([applicantId isEqualToString:applicantId]) {
applicantBlock(applicant.peerObject);
break;
}
}
});
}
// Tell clang that these bools are okay, even if NSAssert doesn't use them
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
-(void)acceptApplicantId:(NSString*)applicantId
{
[self forApplicantId:applicantId run:^void(id applicant) {
CFErrorRef err = NULL;
bool ok = SOSCCAcceptApplicants((__bridge CFArrayRef)(@[applicant]), &err);
NSAssert(ok, @"Error }];
}
-(void)rejectApplicantId:(NSString*)applicantId
{
[self forApplicantId:applicantId run:^void(id applicant) {
CFErrorRef err = NULL;
bool ok = SOSCCRejectApplicants((__bridge CFArrayRef)(@[applicant]), &err);
NSAssert(ok, @"Error }];
}
#pragma clang diagnostic pop
-(id)init
{
if ((self = [super init])) {
int token;
self->_queue_ = dispatch_queue_create([[NSString stringWithFormat:@"KDSecCircle@ self->_callbacks = [NSMutableArray new];
notify_register_dispatch(kSOSCCCircleChangedNotification, &token, self.queue_, ^(int token1) {
[self updateCheck];
});
}
return self;
}
-(void)addChangeCallback:(dispatch_block_t)callback
{
[self.callbacks addObject:callback];
if (self.changeCount) {
dispatch_async(dispatch_get_main_queue(), callback);
} else if (self.callbacks.count == 1) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self updateCheck];
});
}
}
-(BOOL)isInCircle
{
return (self.rawStatus == kSOSCCInCircle);
}
-(BOOL)isOutOfCircle
{
return (self.rawStatus == kSOSCCNotInCircle || self.rawStatus == kSOSCCCircleAbsent);
}
-(void)enableSync
{
CFErrorRef err = NULL;
if (self.rawStatus == kSOSCCCircleAbsent) {
SOSCCResetToOffering(&err);
} else {
SOSCCRequestToJoinCircle(&err);
}
CFMutableSetRef viewsToEnable = CFSetCreateMutable(NULL, 0, NULL);
CFMutableSetRef viewsToDisable = CFSetCreateMutable(NULL, 0, NULL);
CFSetAddValue(viewsToEnable, (void*)kSOSViewWiFi);
CFSetAddValue(viewsToEnable, (void*)kSOSViewAutofillPasswords);
CFSetAddValue(viewsToEnable, (void*)kSOSViewSafariCreditCards);
CFSetAddValue(viewsToEnable, (void*)kSOSViewOtherSyncable);
SOSCCViewSet(viewsToEnable, viewsToDisable);
CFRelease(viewsToEnable);
CFRelease(viewsToDisable);
}
-(void)disableSync
{
CFErrorRef err = NULL;
SOSCCRemoveThisDeviceFromCircle(&err);
}
@end