1/*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25/*
26 * SOSPeer.c -  Implementation of a secure object syncing peer
27 */
28#include <SecureObjectSync/SOSPeer.h>
29
30#include <SecureObjectSync/SOSCoder.h>
31#include <SecureObjectSync/SOSDigestVector.h>
32#include <SecureObjectSync/SOSEngine.h>
33#include <SecureObjectSync/SOSFullPeerInfo.h>
34#include <SecureObjectSync/SOSInternal.h>
35#include <SecureObjectSync/SOSPeerInfo.h>
36#include <SecureObjectSync/SOSTransport.h>
37#include <CommonCrypto/CommonDigest.h>
38#include <CommonCrypto/CommonDigestSPI.h>
39#include <utilities/SecCFError.h>
40#include <utilities/SecCFRelease.h>
41#include <utilities/SecCFWrappers.h>
42#include <utilities/SecDb.h>
43#include <utilities/SecFileLocations.h>
44#include <utilities/SecIOFormat.h>
45#include <utilities/array_size.h>
46#include <utilities/debugging.h>
47#include <utilities/der_plist.h>
48#include <utilities/der_plist_internal.h>
49
50#include <securityd/SOSCloudCircleServer.h>
51
52#include <CoreFoundation/CoreFoundation.h>
53
54#include <stdlib.h>
55
56#include <AssertMacros.h>
57
58//
59// MARK: - SOSPeerPersistence code
60//
61static CFStringRef kSOSPeerSequenceNumberKey = CFSTR("sequence-number");
62static CFStringRef kSOSPeerGetObjectsKey = CFSTR("get-objects");
63static CFStringRef kSOSPeerReceivedUnknownConfirmedDigestKey = CFSTR("received-unknown");
64static CFStringRef kSOSPeerJoinRequestedKey = CFSTR("join-requested");
65static CFStringRef kSOSPeerSkipHelloKey = CFSTR("skip-hello");
66
67CFStringRef kSOSPeerDataLabel = CFSTR("iCloud Peer Data Meta-data");
68
69//
70// MARK: SOSPeerState (dictionary keys)
71//
72
73// PeerState dictionary keys
74static CFStringRef kSOSPeerSendObjectsKey = CFSTR("send-objects"); // bool
75static CFStringRef kSOSPeerMustSendMessageKey = CFSTR("must-send"); // bool
76static CFStringRef kSOSPeerPendingObjectsKey = CFSTR("pending-objects"); // digest
77static CFStringRef kSOSPeerPendingDeletesKey = CFSTR("pending-deletes"); // digest
78static CFStringRef kSOSPeerConfirmedManifestKey = CFSTR("confirmed-manifest");  //digest
79static CFStringRef kSOSPeerProposedManifestKey = CFSTR("pending-manifest"); // array of digests
80static CFStringRef kSOSPeerLocalManifestKey = CFSTR("local-manifest"); // array of digests
81static CFStringRef kSOSPeerVersionKey = CFSTR("version"); // int
82
83enum {
84    kSOSPeerMaxManifestWindowDepth = 4
85};
86
87//
88// MARK: - SOSPeer
89//
90
91struct __OpaqueSOSPeer {
92    CFRuntimeBase _base;
93    SOSEngineRef engine;
94
95    SOSCoderRef coder;
96    CFStringRef peer_id;
97    CFIndex version;
98    uint64_t sequenceNumber;
99    bool mustSendMessage;
100};
101
102CFGiblisWithCompareFor(SOSPeer)
103
104static CFStringRef SOSManifestCreateOptionalDescriptionWithLabel(SOSManifestRef manifest, CFStringRef label) {
105    if (!manifest) return CFSTR(" -  ");
106    //return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@[%zu]"), label, SOSManifestGetCount(manifest));
107    return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(" %@%@"), label, manifest);
108}
109
110static CFMutableDictionaryRef SOSPeerGetState(SOSPeerRef peer) {
111    return SOSEngineGetPeerState(peer->engine, peer->peer_id);
112}
113
114static CFStringRef SOSPeerCreateManifestArrayDescriptionWithKey(SOSPeerRef peer, CFStringRef key, CFStringRef label) {
115    CFMutableArrayRef digests = (CFMutableArrayRef)CFDictionaryGetValue(SOSPeerGetState(peer), key);
116    CFIndex count = digests ? CFArrayGetCount(digests) : 0;
117    if (count == 0) return CFSTR(" -  ");
118    CFDataRef digest = CFArrayGetValueAtIndex(digests, 0);
119    return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(" %@[%" PRIdCFIndex "]%@"), label, count, SOSEngineGetManifestForDigest(peer->engine, digest));
120}
121
122static CFStringRef SOSPeerCopyDescription(CFTypeRef cf) {
123    SOSPeerRef peer = (SOSPeerRef)cf;
124    if(peer){
125        CFStringRef po = SOSManifestCreateOptionalDescriptionWithLabel(SOSPeerGetPendingObjects(peer), CFSTR("O"));
126        CFStringRef de = SOSManifestCreateOptionalDescriptionWithLabel(SOSPeerGetPendingDeletes(peer), CFSTR("D"));
127        CFStringRef co = SOSManifestCreateOptionalDescriptionWithLabel(SOSPeerGetConfirmedManifest(peer), CFSTR("C"));
128        CFStringRef pe = SOSPeerCreateManifestArrayDescriptionWithKey(peer, kSOSPeerProposedManifestKey, CFSTR("P"));
129        CFStringRef lo = SOSPeerCreateManifestArrayDescriptionWithKey(peer, kSOSPeerLocalManifestKey, CFSTR("L"));
130        CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%@ %s%s%@%@%@%@%@>"),
131                                                    SOSPeerGetID(peer),
132                                                    SOSPeerMustSendMessage(peer) ? "F" : "f",
133                                                    SOSPeerSendObjects(peer) ? "S" : "s",
134                                                    po, de, co, pe, lo);
135        CFReleaseSafe(lo);
136        CFReleaseSafe(pe);
137        CFReleaseSafe(co);
138        CFReleaseSafe(de);
139        CFReleaseSafe(po);
140
141        return desc;
142    }
143    else
144        return CFSTR("NULL");
145}
146
147static Boolean SOSPeerCompare(CFTypeRef cfA, CFTypeRef cfB)
148{
149    SOSPeerRef peerA = (SOSPeerRef)cfA, peerB = (SOSPeerRef)cfB;
150    // Use mainly to see if peerB is actually this device (peerA)
151    return CFStringCompare(SOSPeerGetID(peerA), SOSPeerGetID(peerB), 0) == kCFCompareEqualTo;
152}
153
154static CFMutableArrayRef SOSPeerGetDigestsWithKey(SOSPeerRef peer, CFStringRef key) {
155    CFMutableDictionaryRef peerState = SOSPeerGetState(peer);
156    CFMutableArrayRef digests = (CFMutableArrayRef)CFDictionaryGetValue(peerState, key);
157    if (!digests) {
158        digests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
159        CFDictionarySetValue(peerState, key, digests);
160        CFReleaseSafe(digests);
161    }
162    return digests;
163}
164
165static void SOSPeerStateSetDigestForKey(CFMutableDictionaryRef peerState, CFStringRef key, CFDataRef digest) {
166    if (digest)
167        CFDictionarySetValue(peerState, key, digest);
168    else
169        CFDictionaryRemoveValue(peerState, key);
170}
171
172static void SOSPeerAddManifestWithKey(SOSPeerRef peer, CFStringRef key, SOSManifestRef manifest) {
173    CFMutableArrayRef digests = SOSPeerGetDigestsWithKey(peer, key);
174    CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
175    if (digest) {
176        CFIndex count = CFArrayGetCount(digests);
177        SOSEngineAddManifest(peer->engine, manifest);
178        CFIndex ixOfDigest = CFArrayGetFirstIndexOfValue(digests, CFRangeMake(0, count), digest);
179        if (ixOfDigest != 0) {
180            if (ixOfDigest != kCFNotFound) {
181                CFArrayRemoveValueAtIndex(digests, ixOfDigest);
182            } else {
183                while (count >= kSOSPeerMaxManifestWindowDepth)
184                    CFArrayRemoveValueAtIndex(digests, --count);
185            }
186
187            CFArrayInsertValueAtIndex(digests, 0, digest);
188        }
189    } else {
190        // pending == NULL => nothing clear history
191        CFArrayRemoveAllValues(digests);
192    }
193}
194
195static SOSPeerRef SOSPeerCreate_Internal(SOSEngineRef engine, CFDictionaryRef persisted, CFStringRef theirPeerID, CFIndex version, CFErrorRef *error) {
196    SOSPeerRef p = CFTypeAllocate(SOSPeer, struct __OpaqueSOSPeer, kCFAllocatorDefault);
197    p->engine = engine;
198    p->peer_id = CFRetainSafe(theirPeerID);
199    p->version = version;
200
201    if (persisted) {
202        CFDictionaryRef peer_dict = (CFDictionaryRef) persisted;
203
204        int64_t sequenceNumber;
205        CFNumberRef seqNo = CFDictionaryGetValue(peer_dict, kSOSPeerSequenceNumberKey);
206        if (seqNo) {
207            CFNumberGetValue(seqNo, kCFNumberSInt64Type, &sequenceNumber);
208            p->sequenceNumber = sequenceNumber;
209        }
210        CFNumberRef version = CFDictionaryGetValue(peer_dict, kSOSPeerVersionKey);
211        if (version)
212            CFNumberGetValue(version, kCFNumberCFIndexType, &p->version);
213    }
214
215    return p;
216}
217
218SOSPeerRef SOSPeerCreateWithEngine(SOSEngineRef engine, CFStringRef peer_id) {
219    CFMutableDictionaryRef state = SOSEngineGetPeerState(engine, peer_id);
220    return SOSPeerCreate_Internal(engine, state, peer_id, 0, NULL);
221}
222
223static bool SOSPeerPersistData(SOSPeerRef peer, CFErrorRef *error)
224{
225    CFMutableDictionaryRef data_dict = SOSPeerGetState(peer);
226
227    int64_t sequenceNumber = peer->sequenceNumber;
228    CFNumberRef seqNo = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &sequenceNumber);
229    CFDictionarySetValue(data_dict, kSOSPeerSequenceNumberKey, seqNo);
230    CFReleaseNull(seqNo);
231    CFDictionarySetValue(data_dict, kSOSPeerMustSendMessageKey, peer->mustSendMessage ? kCFBooleanTrue : kCFBooleanFalse);
232    if (peer->version) {
233        CFNumberRef version = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &peer->version);
234        CFDictionarySetValue(data_dict, kSOSPeerVersionKey, version);
235        CFReleaseSafe(version);
236    }
237    return true;
238}
239
240SOSPeerRef SOSPeerCreate(SOSEngineRef engine, SOSPeerInfoRef peerInfo,
241                         CFErrorRef *error) {
242    if (peerInfo == NULL) {
243        SOSCreateError(kSOSErrorUnsupported, CFSTR("Can't create peer without their peer info!"), NULL, error);
244        return NULL;
245    }
246
247    CFStringRef peer_id = SOSPeerInfoGetPeerID(peerInfo);
248    CFDictionaryRef persisted = SOSEngineGetPeerState(engine, peer_id);
249    SOSPeerRef peer = SOSPeerCreate_Internal(engine,
250                                             persisted,
251                                             peer_id,
252                                             SOSPeerInfoGetVersion(peerInfo),
253                                             error);
254    if (peer)
255        SOSPeerPersistData(peer, error);
256    return peer;
257}
258
259SOSPeerRef SOSPeerCreateSimple(SOSEngineRef engine, CFStringRef peer_id, CFIndex version, CFErrorRef *error) {
260
261    CFDictionaryRef persisted = SOSEngineGetPeerState(engine, peer_id);
262    SOSPeerRef peer = SOSPeerCreate_Internal(engine, persisted, peer_id, version, error);
263    if (peer)
264        SOSPeerPersistData(peer, error);
265    return peer;
266}
267
268static void SOSPeerDestroy(CFTypeRef cf) {
269    SOSPeerRef peer = (SOSPeerRef)cf;
270    SOSPeerPersistData(peer, NULL);
271    CFReleaseSafe(peer->peer_id);
272}
273
274void SOSPeerDidConnect(SOSPeerRef peer) {
275    SOSPeerSetMustSendMessage(peer, true);
276    SOSPeerSetProposedManifest(peer, SOSPeerGetConfirmedManifest(peer));
277}
278
279CFIndex SOSPeerGetVersion(SOSPeerRef peer) {
280    return peer->version;
281}
282
283CFStringRef SOSPeerGetID(SOSPeerRef peer) {
284    return peer->peer_id;
285}
286
287SOSEngineRef SOSPeerGetEngine(SOSPeerRef peer){
288    return peer->engine;
289}
290SOSCoderRef SOSPeerGetCoder(SOSPeerRef peer){
291        return peer->coder;
292}
293void SOSPeerSetCoder(SOSPeerRef peer, SOSCoderRef coder){
294        peer->coder = coder;
295}
296
297uint64_t SOSPeerNextSequenceNumber(SOSPeerRef peer) {
298    return ++peer->sequenceNumber;
299}
300
301uint64_t SOSPeerGetMessageVersion(SOSPeerRef peer) {
302    return SOSPeerGetVersion(peer);
303
304}
305
306bool SOSPeerMustSendMessage(SOSPeerRef peer) {
307    CFBooleanRef must = CFDictionaryGetValue(SOSPeerGetState(peer), kSOSPeerMustSendMessageKey);
308    return must && CFBooleanGetValue(must);
309}
310
311void SOSPeerSetMustSendMessage(SOSPeerRef peer, bool sendMessage) {
312    CFDictionarySetValue(SOSPeerGetState(peer), kSOSPeerMustSendMessageKey, sendMessage ? kCFBooleanTrue : kCFBooleanFalse);
313}
314
315bool SOSPeerSendObjects(SOSPeerRef peer) {
316    CFBooleanRef send = CFDictionaryGetValue(SOSPeerGetState(peer), kSOSPeerSendObjectsKey);
317    return send && CFBooleanGetValue(send);
318}
319
320void SOSPeerSetSendObjects(SOSPeerRef peer, bool sendObjects) {
321    CFDictionarySetValue(SOSPeerGetState(peer), kSOSPeerSendObjectsKey, sendObjects ? kCFBooleanTrue : kCFBooleanFalse);
322}
323
324SOSManifestRef SOSPeerGetProposedManifest(SOSPeerRef peer) {
325    CFDataRef digest = NULL;
326    CFMutableArrayRef proposedDigests = (CFMutableArrayRef)CFDictionaryGetValue(SOSPeerGetState(peer), kSOSPeerProposedManifestKey);
327    if (proposedDigests && CFArrayGetCount(proposedDigests) > 0)
328        digest = CFArrayGetValueAtIndex(proposedDigests, 0);
329    return SOSEngineGetManifestForDigest(peer->engine, digest);
330}
331
332#if 0
333static SOSManifestRef SOSPeerGetLocalManifest(SOSPeerRef peer) {
334    CFDataRef digest = NULL;
335    CFMutableArrayRef localDigests = (CFMutableArrayRef)CFDictionaryGetValue(SOSPeerGetState(peer), kSOSPeerLocalManifestKey);
336    if (localDigests && CFArrayGetCount(localDigests) > 0)
337        digest = CFArrayGetValueAtIndex(localDigests, 0);
338    return SOSEngineGetManifestForDigest(peer->engine, digest);
339}
340#endif
341
342SOSManifestRef SOSPeerGetConfirmedManifest(SOSPeerRef peer) {
343    return SOSEngineGetManifestForDigest(peer->engine, CFDictionaryGetValue(SOSPeerGetState(peer), kSOSPeerConfirmedManifestKey));
344}
345
346void SOSPeerSetConfirmedManifest(SOSPeerRef peer, SOSManifestRef confirmed) {
347    SOSEngineAddManifest(peer->engine, confirmed);
348    SOSPeerStateSetDigestForKey(SOSPeerGetState(peer), kSOSPeerConfirmedManifestKey, SOSManifestGetDigest(confirmed, NULL));
349
350    // TODO: Clear only expired pending and local manifests from the array - this clears them all
351    // To do so we'd have to track the messageIds we sent to our peer and when we proposed a particular manifest.
352    // Then we simply remove the entires from messages older that the one we are confirming now
353    //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerProposedManifestKey));
354    //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerLocalManifestKey));
355}
356
357void SOSPeerAddProposedManifest(SOSPeerRef peer, SOSManifestRef pending) {
358    SOSPeerAddManifestWithKey(peer, kSOSPeerProposedManifestKey, pending);
359}
360
361void SOSPeerSetProposedManifest(SOSPeerRef peer, SOSManifestRef pending) {
362    SOSEngineAddManifest(peer->engine, pending);
363    CFMutableArrayRef proposedDigests = SOSPeerGetDigestsWithKey(peer, kSOSPeerProposedManifestKey);
364    CFArrayRemoveAllValues(proposedDigests);
365    if (pending)
366        CFArrayAppendValue(proposedDigests, SOSManifestGetDigest(pending, NULL));
367}
368
369void SOSPeerAddLocalManifest(SOSPeerRef peer, SOSManifestRef local) {
370    SOSPeerAddManifestWithKey(peer, kSOSPeerLocalManifestKey, local);
371}
372
373SOSManifestRef SOSPeerGetPendingObjects(SOSPeerRef peer) {
374    return SOSEngineGetManifestForDigest(peer->engine, CFDictionaryGetValue(SOSPeerGetState(peer), kSOSPeerPendingObjectsKey));
375}
376
377void SOSPeerSetPendingObjects(SOSPeerRef peer, SOSManifestRef pendingObjects) {
378    SOSEngineAddManifest(peer->engine, pendingObjects);
379    SOSPeerStateSetDigestForKey(SOSPeerGetState(peer), kSOSPeerPendingObjectsKey, SOSManifestGetDigest(pendingObjects, NULL));
380}
381
382SOSManifestRef SOSPeerGetPendingDeletes(SOSPeerRef peer) {
383    return SOSEngineGetManifestForDigest(peer->engine, CFDictionaryGetValue(SOSPeerGetState(peer), kSOSPeerPendingDeletesKey));
384}
385
386void SOSPeerSetPendingDeletes(SOSPeerRef peer, SOSManifestRef pendingDeletes) {
387    SOSEngineAddManifest(peer->engine, pendingDeletes);
388    SOSPeerStateSetDigestForKey(SOSPeerGetState(peer), kSOSPeerPendingDeletesKey, SOSManifestGetDigest(pendingDeletes, NULL));
389}
390
391static void SOSMarkDigestInUse(struct SOSDigestVector *mdInUse, CFDataRef digest) {
392    if (!isData(digest)) return;
393    SOSDigestVectorAppend(mdInUse, CFDataGetBytePtr(digest));
394}
395
396static void SOSMarkDigestsInUse(struct SOSDigestVector *mdInUse, CFArrayRef digests) {
397    if (!isArray(digests)) return;
398    CFDataRef digest = NULL;
399    CFArrayForEachC(digests, digest) {
400        SOSMarkDigestInUse(mdInUse, digest);
401    }
402}
403
404// Add all digests we are using to mdInUse
405void SOSPeerMarkDigestsInUse(SOSPeerRef peer, struct SOSDigestVector *mdInUse) {
406    CFMutableDictionaryRef peerState = SOSPeerGetState(peer);
407    SOSMarkDigestInUse(mdInUse, CFDictionaryGetValue(peerState, kSOSPeerPendingObjectsKey));
408    SOSMarkDigestInUse(mdInUse, CFDictionaryGetValue(peerState, kSOSPeerPendingDeletesKey));
409    SOSMarkDigestInUse(mdInUse, CFDictionaryGetValue(peerState, kSOSPeerConfirmedManifestKey));
410    SOSMarkDigestsInUse(mdInUse, CFDictionaryGetValue(peerState, kSOSPeerLocalManifestKey));
411    SOSMarkDigestsInUse(mdInUse, CFDictionaryGetValue(peerState, kSOSPeerProposedManifestKey));
412}
413
414
415// absentFromRemote
416// AbsentLocally
417// additionsFromRemote
418// original intent was that digests only got added to pendingObjects. We only know for sure if it is something added locally via api call
419
420
421bool SOSPeerDidReceiveRemovalsAndAdditions(SOSPeerRef peer, SOSManifestRef absentFromRemote, SOSManifestRef additionsFromRemote,
422                                           SOSManifestRef local, CFErrorRef *error) {
423    // We assume that incoming manifests are all sorted, and absentFromRemote is disjoint from additionsFromRemote
424    bool ok = true;
425    SOSManifestRef remoteRemovals = NULL, sharedRemovals = NULL, sharedAdditions = NULL, remoteAdditions = NULL;
426    CFDataRef pendingObjectsDigest, pendingDeletesDigest;
427
428    // TODO: Simplyfy -- a lot.
429    ok = ok && (remoteRemovals = SOSManifestCreateIntersection(absentFromRemote, local, error));           // remoteRemovals = absentFromRemote <Intersected> local
430    ok = ok && (sharedRemovals = SOSManifestCreateComplement(remoteRemovals, absentFromRemote, error));    // sharedRemovals = absentFromRemote - remoteRemovals
431    ok = ok && (sharedAdditions = SOSManifestCreateIntersection(additionsFromRemote, local, error));         // sharedAdditions = additionsFromRemote <Intersected> local
432    ok = ok && (remoteAdditions = SOSManifestCreateComplement(sharedAdditions, additionsFromRemote, error)); // remoteAdditions = additionsFromRemote - sharedAdditions
433
434    secnotice("peer", "%@ R:%@ A:%@ C:%@ D:%@ O:%@", peer, absentFromRemote, additionsFromRemote, SOSPeerGetConfirmedManifest(peer), SOSPeerGetPendingDeletes(peer), SOSPeerGetPendingObjects(peer));
435
436    // TODO: Does the value of SOSPeerSendObjects() matter here?
437    pendingObjectsDigest = SOSEnginePatchRecordAndCopyDigest(peer->engine, SOSPeerGetPendingObjects(peer), sharedAdditions, NULL, error);  // PO = PO - sharedAdditions
438    pendingDeletesDigest = SOSEnginePatchRecordAndCopyDigest(peer->engine, SOSPeerGetPendingDeletes(peer), sharedRemovals, NULL, error);  // D = D - sharedRemovals
439
440    CFMutableDictionaryRef peerState = SOSPeerGetState(peer);
441    SOSPeerStateSetDigestForKey(peerState, kSOSPeerPendingObjectsKey, pendingObjectsDigest);
442    SOSPeerStateSetDigestForKey(peerState, kSOSPeerPendingDeletesKey, pendingDeletesDigest);
443
444    CFReleaseSafe(pendingDeletesDigest);
445    CFReleaseSafe(pendingObjectsDigest);
446    CFReleaseSafe(remoteRemovals);
447    CFReleaseSafe(sharedRemovals);
448    CFReleaseSafe(sharedAdditions);
449    CFReleaseSafe(remoteAdditions);
450
451    secnotice("peer", "%@ C:%@ D:%@ O:%@", peer, SOSPeerGetConfirmedManifest(peer), SOSPeerGetPendingDeletes(peer), SOSPeerGetPendingObjects(peer));
452
453    return ok;
454}
455
456bool SOSPeerDidReceiveConfirmedManifest(SOSPeerRef peer, SOSManifestRef confirmed, SOSManifestRef local, CFErrorRef *error) {
457    bool ok = true;
458    if (!confirmed) return ok;
459    SOSManifestRef confirmedRemovals = NULL, confirmedAdditions = NULL;
460
461    // confirmedAdditions = confirmed - previous_confirmed, confirmedRemovals = previous_confirmed - confirmed
462    ok &= SOSManifestDiff(SOSPeerGetConfirmedManifest(peer), confirmed, &confirmedRemovals, &confirmedAdditions, error);
463    ok &= SOSPeerDidReceiveRemovalsAndAdditions(peer, confirmedRemovals, confirmedAdditions, local, error);
464
465    CFReleaseSafe(confirmedRemovals);
466    CFReleaseSafe(confirmedAdditions);
467    return ok;
468}
469
470bool SOSPeerDataSourceWillCommit(SOSPeerRef peer, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
471    SOSManifestRef unconfirmedAdditions = NULL;
472    CFDataRef pendingObjectsDigest, pendingDeletesDigest = NULL;
473
474    secnotice("peer", "%@ R:%@ A:%@ C:%@ D:%@ O:%@", peer, removals, additions, SOSPeerGetConfirmedManifest(peer), SOSPeerGetPendingDeletes(peer), SOSPeerGetPendingObjects(peer));
475
476    // Remove confirmed from additions
477    // TODO: Add require and check for error
478    unconfirmedAdditions = SOSManifestCreateComplement(SOSPeerGetConfirmedManifest(peer), additions, error);
479    secnotice("peer", "%@ UA: %@ source: %s", peer, unconfirmedAdditions, source == kSOSDataSourceSOSTransaction ? "sos" : "api");
480
481    pendingObjectsDigest = SOSEnginePatchRecordAndCopyDigest(peer->engine, SOSPeerGetPendingObjects(peer), removals, source == kSOSDataSourceAPITransaction ? unconfirmedAdditions : NULL, error);
482    // TODO: Figure out how to update pendingDeletes...
483    //pendingDeletesDigest = SOSEnginePatchRecordAndCopyDigest(peer->engine, SOSPeerGetPendingDeletes(peer), removals, NULL, error);
484
485    CFMutableDictionaryRef peerState = SOSPeerGetState(peer);
486
487    SOSPeerStateSetDigestForKey(peerState, kSOSPeerPendingObjectsKey, pendingObjectsDigest);
488    SOSPeerStateSetDigestForKey(peerState, kSOSPeerPendingDeletesKey, pendingDeletesDigest);
489
490    CFReleaseSafe(pendingDeletesDigest);
491    CFReleaseSafe(pendingObjectsDigest);
492    CFReleaseSafe(unconfirmedAdditions);
493
494    secnotice("peer", "%@ C:%@ D:%@ P:%@", peer, SOSPeerGetConfirmedManifest(peer), SOSPeerGetPendingDeletes(peer), SOSPeerGetPendingObjects(peer));
495
496    return true;
497}
498
499