5 // Created by Mitch Adler on 6/10/16.
10 #include <CoreFoundation/CoreFoundation.h>
12 #include <Security/SecureObjectSync/SOSAccount.h>
14 #include "SOSAccountPriv.h"
16 #include <utilities/SecCFWrappers.h>
22 static CFMutableSetRef
SOSAccountCopyOtherPeersViews(SOSAccountRef account
) {
23 __block CFMutableSetRef otherPeersViews
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
24 SOSAccountForEachCirclePeerExceptMe(account
, ^(SOSPeerInfoRef peer
) {
25 SOSPeerInfoWithEnabledViewSet(peer
, ^(CFSetRef enabled
) {
26 CFSetUnion(otherPeersViews
, enabled
);
30 return otherPeersViews
;
34 // MARK: Outstanding tracking
37 CFMutableSetRef
SOSAccountCopyOutstandingViews(SOSAccountRef account
) {
38 CFSetRef initialSyncViews
= SOSViewCopyViewSet(kViewSetAll
);
39 CFMutableSetRef result
= SOSAccountCopyIntersectionWithOustanding(account
, initialSyncViews
);
40 CFReleaseNull(initialSyncViews
);
45 bool SOSAccountIsViewOutstanding(SOSAccountRef account
, CFStringRef view
) {
46 bool isOutstandingView
;
48 require_action_quiet(SOSAccountIsInCircle(account
, NULL
), done
, isOutstandingView
= true);
50 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
51 require_action_quiet(unsyncedObject
, done
, isOutstandingView
= false);
53 CFBooleanRef unsyncedBool
= asBoolean(unsyncedObject
, NULL
);
55 isOutstandingView
= CFBooleanGetValue(unsyncedBool
);
57 CFSetRef unsyncedSet
= asSet(unsyncedObject
, NULL
);
58 isOutstandingView
= unsyncedSet
&& CFSetContainsValue(unsyncedSet
, view
);
62 return isOutstandingView
;
65 CFMutableSetRef
SOSAccountCopyIntersectionWithOustanding(SOSAccountRef account
, CFSetRef inSet
) {
66 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
67 CFMutableSetRef result
= NULL
;
69 require_quiet(SOSAccountIsInCircle(account
, NULL
), done
);
71 CFBooleanRef unsyncedBool
= asBoolean(unsyncedObject
, NULL
);
73 if (!CFBooleanGetValue(unsyncedBool
)) {
74 result
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
77 CFSetRef unsyncedSet
= asSet(unsyncedObject
, NULL
);
79 result
= CFSetCreateIntersection(kCFAllocatorDefault
, unsyncedSet
, inSet
);
81 result
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
87 result
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, inSet
);
93 bool SOSAccountIntersectsWithOutstanding(SOSAccountRef account
, CFSetRef views
) {
94 CFSetRef nonInitiallySyncedViews
= SOSAccountCopyIntersectionWithOustanding(account
, views
);
95 bool intersects
= !CFSetIsEmpty(nonInitiallySyncedViews
);
96 CFReleaseNull(nonInitiallySyncedViews
);
100 bool SOSAccountHasOustandingViews(SOSAccountRef account
) {
101 bool hasOutstandingViews
;
103 require_action_quiet(SOSAccountIsInCircle(account
, NULL
), done
, hasOutstandingViews
= true);
105 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
106 require_action_quiet(unsyncedObject
, done
, hasOutstandingViews
= false);
108 CFBooleanRef unsyncedBool
= asBoolean(unsyncedObject
, NULL
);
110 hasOutstandingViews
= CFBooleanGetValue(unsyncedBool
);
112 hasOutstandingViews
= isSet(unsyncedBool
);
116 return hasOutstandingViews
;
121 // MARK: Initial sync functions
124 static bool SOSAccountHasCompletedInitialySyncWithSetKind(SOSAccountRef account
, ViewSetKind setKind
) {
125 CFSetRef viewSet
= SOSViewCopyViewSet(setKind
);
126 bool completedSync
= !SOSAccountIntersectsWithOutstanding(account
, viewSet
);
127 CFReleaseNull(viewSet
);
129 return completedSync
;
132 bool SOSAccountHasCompletedInitialSync(SOSAccountRef account
) {
133 return SOSAccountHasCompletedInitialySyncWithSetKind(account
, kViewSetInitial
);
136 bool SOSAccountHasCompletedRequiredBackupSync(SOSAccountRef account
) {
137 return SOSAccountHasCompletedInitialySyncWithSetKind(account
, kViewSetRequiredForBackup
);
144 // MARK: Handling initial sync being done
147 static bool SOSAccountResolvePendingViewSets(SOSAccountRef account
, CFErrorRef
*error
) {
148 bool status
= SOSAccountUpdateViewSets(account
,
149 asSet(SOSAccountGetValue(account
, kSOSPendingEnableViewsToBeSetKey
, NULL
), NULL
),
150 asSet(SOSAccountGetValue(account
, kSOSPendingDisableViewsToBeSetKey
, NULL
), NULL
));
152 SOSAccountClearValue(account
, kSOSPendingEnableViewsToBeSetKey
, NULL
);
153 SOSAccountClearValue(account
, kSOSPendingDisableViewsToBeSetKey
, NULL
);
155 secnotice("views","updated view sets!");
158 secerror("Could not update view sets");
163 static void SOSAccountCallInitialSyncBlocks(SOSAccountRef account
) {
164 CFDictionaryRef syncBlocks
= NULL
;
165 CFTransferRetained(syncBlocks
, account
->waitForInitialSync_blocks
);
168 CFDictionaryForEach(syncBlocks
, ^(const void *key
, const void *value
) {
169 secnotice("updates", "calling in sync block [%@]", key
);
170 ((SOSAccountWaitForInitialSyncBlock
)value
)(account
);
173 CFReleaseNull(syncBlocks
);
177 static void SOSAccountHandleRequiredBackupSyncDone(SOSAccountRef account
) {
178 secnotice("initial-sync", "Handling Required Backup Sync done");
181 static void SOSAccountHandleInitialSyncDone(SOSAccountRef account
) {
182 secnotice("initial-sync", "Handling initial sync done.");
184 if(!SOSAccountResolvePendingViewSets(account
, NULL
))
185 secnotice("initial-sync", "Account could not add the pending view sets");
187 SOSAccountCallInitialSyncBlocks(account
);
193 // MARK: Waiting for in-sync
195 static CFStringRef
CreateUUIDString() {
196 CFUUIDRef uuid
= CFUUIDCreate(kCFAllocatorDefault
);
197 CFStringRef result
= CFUUIDCreateString(kCFAllocatorDefault
, uuid
);
202 CFStringRef
SOSAccountCallWhenInSync(SOSAccountRef account
, SOSAccountWaitForInitialSyncBlock syncBlock
) {
203 //if we are not initially synced
204 CFStringRef id
= NULL
;
205 CFTypeRef unSyncedViews
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
206 if (unSyncedViews
!= NULL
) {
207 id
= CreateUUIDString();
208 secnotice("initial-sync", "adding sync block [%@] to array!", id
);
209 SOSAccountWaitForInitialSyncBlock copy
= Block_copy(syncBlock
);
210 if (account
->waitForInitialSync_blocks
== NULL
) {
211 account
->waitForInitialSync_blocks
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
213 CFDictionarySetValue(account
->waitForInitialSync_blocks
, id
, copy
);
222 bool SOSAccountUnregisterCallWhenInSync(SOSAccountRef account
, CFStringRef id
) {
223 if (account
->waitForInitialSync_blocks
== NULL
) return false;
225 bool removed
= CFDictionaryGetValueIfPresent(account
->waitForInitialSync_blocks
, id
, NULL
);
226 CFDictionaryRemoveValue(account
->waitForInitialSync_blocks
, id
);
230 static void performWithInitialSyncDescription(CFTypeRef object
, void (^action
)(CFStringRef description
)) {
231 CFSetRef setObject
= asSet(object
, NULL
);
233 CFStringSetPerformWithDescription(setObject
, action
);
235 CFStringRef format
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@"), object
);
237 CFReleaseNull(format
);
241 static bool CFSetIntersectionWentEmpty(CFSetRef interestingSet
, CFSetRef before
, CFSetRef after
) {
242 return ((before
!= NULL
) && !CFSetIntersectionIsEmpty(interestingSet
, before
)) &&
243 ((after
== NULL
) || CFSetIntersectionIsEmpty(interestingSet
, after
));
246 static bool SOSViewIntersectionWentEmpty(ViewSetKind kind
, CFSetRef before
, CFSetRef after
) {
247 CFSetRef kindSet
= SOSViewCopyViewSet(kind
);
248 bool result
= CFSetIntersectionWentEmpty(kindSet
, before
, after
);
249 CFReleaseNull(kindSet
);
253 bool SOSAccountHandleOutOfSyncUpdate(SOSAccountRef account
, CFSetRef oldOOSViews
, CFSetRef newOOSViews
) {
254 bool actionTaken
= false;
256 if (SOSViewIntersectionWentEmpty(kViewSetInitial
, oldOOSViews
, newOOSViews
)) {
257 SOSAccountHandleInitialSyncDone(account
);
261 if (SOSViewIntersectionWentEmpty(kViewSetRequiredForBackup
, oldOOSViews
, newOOSViews
)) {
262 SOSAccountHandleRequiredBackupSyncDone(account
);
268 void SOSAccountUpdateOutOfSyncViews(SOSAccountTransactionRef aTxn
, CFSetRef viewsInSync
) {
269 SOSAccountRef account
= aTxn
->account
;
270 SOSCCStatus circleStatus
= SOSAccountGetCircleStatus(account
, NULL
);
271 bool inOrApplying
= (circleStatus
== kSOSCCInCircle
) || (circleStatus
== kSOSCCRequestPending
);
273 CFTypeRef unsyncedObject
= SOSAccountGetValue(account
, kSOSUnsyncedViewsKey
, NULL
);
274 __block CFTypeRef newUnsyncedObject
= CFRetainSafe(unsyncedObject
);
276 CFSetRef unsyncedSet
= NULL
;
277 CFMutableSetRef newUnsyncedSet
= NULL
;
279 performWithInitialSyncDescription(viewsInSync
, ^(CFStringRef viewsInSyncDescription
) {
280 secnotice("initial-sync", "Views in sync: %@", viewsInSyncDescription
);
285 if (unsyncedObject
!= NULL
) {
286 secnotice("initial-sync", "not in circle nor applying: clearing pending");
287 CFReleaseNull(newUnsyncedObject
);
289 } else if (circleStatus
== kSOSCCInCircle
) {
290 if (unsyncedObject
== kCFBooleanTrue
) {
291 unsyncedSet
= SOSViewCopyViewSet(kViewSetAll
);
292 CFAssignRetained(newUnsyncedObject
, CFSetCreateCopy(kCFAllocatorDefault
, unsyncedSet
));
294 secnotice("initial-sync", "Pending views setting to all we can expect.");
295 } else if (isSet(unsyncedObject
)) {
296 unsyncedSet
= (CFSetRef
) CFRetainSafe(unsyncedObject
);
300 CFSetRef otherPeersViews
= SOSAccountCopyOtherPeersViews(account
);
302 newUnsyncedSet
= CFSetCreateIntersection(kCFAllocatorDefault
, unsyncedSet
, otherPeersViews
);
305 CFSetSubtract(newUnsyncedSet
, viewsInSync
);
308 CFRetainAssign(newUnsyncedObject
, newUnsyncedSet
);
309 CFReleaseNull(otherPeersViews
);
312 performWithInitialSyncDescription(newUnsyncedSet
, ^(CFStringRef unsynced
) {
313 secnotice("initial-sync", "Unsynced: %@", unsynced
);
318 if (isSet(newUnsyncedObject
) && CFSetIsEmpty((CFSetRef
) newUnsyncedObject
)) {
319 secnotice("initial-sync", "Empty set, using NULL instead");
320 CFReleaseNull(newUnsyncedObject
);
323 CFErrorRef localError
= NULL
;
324 if (!SOSAccountSetValue(account
, kSOSUnsyncedViewsKey
, newUnsyncedObject
, &localError
)) {
325 secnotice("initial-sync", "Failure saving new unsynced value: %@ value: %@", localError
, newUnsyncedObject
);
327 CFReleaseNull(localError
);
329 CFReleaseNull(newUnsyncedObject
);
330 CFReleaseNull(newUnsyncedSet
);
331 CFReleaseNull(unsyncedSet
);
334 void SOSAccountPeerGotInSync(SOSAccountTransactionRef aTxn
, CFStringRef peerID
, CFSetRef views
) {
335 SOSAccountRef account
= aTxn
->account
;
336 secnotice("initial-sync", "Peer %@ synced views: %@", peerID
, views
);
337 if (account
->trusted_circle
&& SOSAccountIsInCircle(account
, NULL
) && SOSCircleHasActivePeerWithID(account
->trusted_circle
, peerID
, NULL
)) {
338 SOSAccountUpdateOutOfSyncViews(aTxn
, views
);
342 static SOSEngineRef
SOSAccountGetDataSourceEngine(SOSAccountRef account
) {
343 return SOSDataSourceFactoryGetEngineForDataSourceName(account
->factory
, SOSCircleGetName(account
->trusted_circle
), NULL
);
346 void SOSAccountEnsureSyncChecking(SOSAccountRef account
) {
347 if (!account
->isListeningForSync
) {
348 SOSEngineRef engine
= SOSAccountGetDataSourceEngine(account
);
351 secnotice("initial-sync", "Setting up notifications to monitor in-sync");
352 SOSEngineSetSyncCompleteListenerQueue(engine
, account
->queue
);
353 SOSEngineSetSyncCompleteListener(engine
, ^(CFStringRef peerID
, CFSetRef views
) {
354 SOSAccountWithTransaction_Locked(account
, ^(SOSAccountRef account
, SOSAccountTransactionRef txn
) {
355 SOSAccountPeerGotInSync(txn
, peerID
, views
);
358 account
->isListeningForSync
= true;
360 secerror("Couldn't find engine to setup notifications!!!");
365 void SOSAccountCancelSyncChecking(SOSAccountRef account
) {
366 if (account
->isListeningForSync
) {
367 SOSEngineRef engine
= SOSAccountGetDataSourceEngine(account
);
370 secnotice("initial-sync", "Cancelling notifications to monitor in-sync");
371 SOSEngineSetSyncCompleteListenerQueue(engine
, NULL
);
372 SOSEngineSetSyncCompleteListener(engine
, NULL
);
374 secnotice("initial-sync", "No engine to cancel notification from.");
376 account
->isListeningForSync
= false;