]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
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 | ||
427c49bc A |
24 | |
25 | #include "SOSTestDataSource.h" | |
26 | ||
27 | #include <corecrypto/ccder.h> | |
d8f41ccd A |
28 | #include <SecureObjectSync/SOSDataSource.h> |
29 | #include <SecureObjectSync/SOSDigestVector.h> | |
427c49bc A |
30 | #include <utilities/array_size.h> |
31 | #include <utilities/der_plist.h> | |
32 | #include <utilities/SecCFError.h> | |
33 | #include <utilities/SecCFWrappers.h> | |
d8f41ccd | 34 | #include <Security/SecItem.h> |
427c49bc | 35 | #include <Security/SecItemPriv.h> |
d8f41ccd | 36 | #include <AssertMacros.h> |
427c49bc | 37 | |
d8f41ccd | 38 | CFStringRef sSOSDataSourceErrorDomain = CFSTR("com.apple.datasource"); |
427c49bc A |
39 | |
40 | typedef struct SOSTestDataSource *SOSTestDataSourceRef; | |
41 | ||
42 | struct SOSTestDataSource { | |
43 | struct SOSDataSource ds; | |
44 | unsigned gm_count; | |
45 | unsigned cm_count; | |
46 | unsigned co_count; | |
d8f41ccd A |
47 | CFMutableDictionaryRef d2database; |
48 | CFMutableDictionaryRef p2database; | |
49 | CFMutableDictionaryRef statedb; | |
427c49bc A |
50 | uint8_t manifest_digest[SOSDigestSize]; |
51 | bool clean; | |
d8f41ccd A |
52 | |
53 | struct SOSDigestVector removals; | |
54 | struct SOSDigestVector additions; | |
55 | SOSDataSourceNotifyBlock notifyBlock; | |
427c49bc A |
56 | }; |
57 | ||
58 | typedef struct SOSTestDataSourceFactory *SOSTestDataSourceFactoryRef; | |
59 | ||
60 | struct SOSTestDataSourceFactory { | |
61 | struct SOSDataSourceFactory dsf; | |
62 | CFMutableDictionaryRef data_sources; | |
63 | }; | |
64 | ||
65 | ||
66 | /* DataSource protocol. */ | |
427c49bc A |
67 | static SOSManifestRef copy_manifest(SOSDataSourceRef data_source, CFErrorRef *error) { |
68 | struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; | |
69 | ds->cm_count++; | |
70 | __block struct SOSDigestVector dv = SOSDigestVectorInit; | |
d8f41ccd | 71 | CFDictionaryForEach(ds->d2database, ^(const void *key, const void *value) { |
427c49bc A |
72 | SOSDigestVectorAppend(&dv, CFDataGetBytePtr((CFDataRef)key)); |
73 | }); | |
74 | SOSDigestVectorSort(&dv); | |
d8f41ccd | 75 | SOSManifestRef manifest = SOSManifestCreateWithDigestVector(&dv, error); |
427c49bc A |
76 | SOSDigestVectorFree(&dv); |
77 | ccdigest(ccsha1_di(), SOSManifestGetSize(manifest), SOSManifestGetBytePtr(manifest), ds->manifest_digest); | |
78 | ds->clean = true; | |
79 | ||
80 | return manifest; | |
81 | } | |
82 | ||
d8f41ccd | 83 | static bool foreach_object(SOSDataSourceRef data_source, SOSManifestRef manifest, CFErrorRef *error, void (^handle_object)(CFDataRef key, SOSObjectRef object, bool *stop)) { |
427c49bc A |
84 | struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; |
85 | ds->co_count++; | |
86 | __block bool result = true; | |
d8f41ccd A |
87 | SOSManifestForEach(manifest, ^(CFDataRef key, bool *stop) { |
88 | handle_object(key, (SOSObjectRef)CFDictionaryGetValue(ds->d2database, key), stop); | |
427c49bc A |
89 | }); |
90 | return result; | |
91 | } | |
92 | ||
d8f41ccd | 93 | static bool dispose(SOSDataSourceRef data_source, CFErrorRef *error) { |
427c49bc | 94 | struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; |
d8f41ccd A |
95 | CFReleaseSafe(ds->d2database); |
96 | CFReleaseSafe(ds->p2database); | |
97 | CFReleaseSafe(ds->statedb); | |
427c49bc | 98 | free(ds); |
d8f41ccd | 99 | return true; |
427c49bc A |
100 | } |
101 | ||
d8f41ccd | 102 | static SOSObjectRef createWithPropertyList(CFDictionaryRef plist, CFErrorRef *error) { |
427c49bc A |
103 | return (SOSObjectRef)CFDictionaryCreateCopy(kCFAllocatorDefault, plist); |
104 | } | |
105 | ||
106 | static CFDataRef SOSObjectCopyDER(SOSObjectRef object, CFErrorRef *error) { | |
107 | CFDictionaryRef dict = (CFDictionaryRef)object; | |
108 | size_t size = der_sizeof_plist(dict, error); | |
109 | CFMutableDataRef data = CFDataCreateMutable(0, size); | |
110 | if (data) { | |
111 | CFDataSetLength(data, size); | |
112 | uint8_t *der = (uint8_t *)CFDataGetMutableBytePtr(data); | |
113 | uint8_t *der_end = der + size; | |
114 | der_end = der_encode_plist(dict, error, der, der_end); | |
115 | assert(der_end == der); | |
d8f41ccd | 116 | (void)der_end; |
427c49bc | 117 | } else if (error && *error == NULL) { |
d8f41ccd | 118 | *error = CFErrorCreate(0, sSOSDataSourceErrorDomain, kSOSDataSourceObjectMallocFailed, NULL); |
427c49bc A |
119 | } |
120 | return data; | |
121 | } | |
122 | ||
123 | static CFDataRef ccdigest_copy_data(const struct ccdigest_info *di, size_t len, | |
124 | const void *data, CFErrorRef *error) { | |
125 | CFMutableDataRef digest = CFDataCreateMutable(0, di->output_size); | |
126 | if (digest) { | |
127 | CFDataSetLength(digest, di->output_size); | |
128 | ccdigest(di, len, data, CFDataGetMutableBytePtr(digest)); | |
129 | } else if (error && *error == NULL) { | |
d8f41ccd | 130 | *error = CFErrorCreate(0, sSOSDataSourceErrorDomain, kSOSDataSourceObjectMallocFailed, NULL); |
427c49bc A |
131 | } |
132 | return digest; | |
133 | } | |
134 | ||
135 | static CFDataRef copyDigest(SOSObjectRef object, CFErrorRef *error) { | |
136 | CFMutableDictionaryRef ocopy = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)object); | |
137 | CFDictionaryRemoveValue(ocopy, kSecClass); | |
138 | CFDataRef der = SOSObjectCopyDER((SOSObjectRef)ocopy, error); | |
139 | CFRelease(ocopy); | |
140 | CFDataRef digest = NULL; | |
141 | if (der) { | |
142 | digest = ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der), CFDataGetBytePtr(der), error); | |
143 | CFRelease(der); | |
144 | } | |
145 | return digest; | |
146 | } | |
147 | ||
148 | static CFDataRef copyPrimaryKey(SOSObjectRef object, CFErrorRef *error) { | |
149 | CFMutableDictionaryRef ocopy = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); | |
150 | CFTypeRef pkNames[] = { | |
151 | CFSTR("acct"), | |
152 | CFSTR("agrp"), | |
153 | CFSTR("svce"), | |
154 | CFSTR("sync"), | |
155 | CFSTR("sdmn"), | |
156 | CFSTR("srvr"), | |
157 | CFSTR("ptcl"), | |
158 | CFSTR("atyp"), | |
159 | CFSTR("port"), | |
160 | CFSTR("path"), | |
161 | CFSTR("ctyp"), | |
162 | CFSTR("issr"), | |
163 | CFSTR("slnr"), | |
164 | CFSTR("kcls"), | |
165 | CFSTR("klbl"), | |
166 | CFSTR("atag"), | |
167 | CFSTR("crtr"), | |
168 | CFSTR("type"), | |
169 | CFSTR("bsiz"), | |
170 | CFSTR("esiz"), | |
171 | CFSTR("sdat"), | |
172 | CFSTR("edat"), | |
173 | }; | |
174 | CFSetRef pkAttrs = CFSetCreate(kCFAllocatorDefault, pkNames, array_size(pkNames), &kCFTypeSetCallBacks); | |
175 | CFDictionaryForEach((CFDictionaryRef)object, ^(const void *key, const void *value) { | |
176 | if (CFSetContainsValue(pkAttrs, key)) | |
177 | CFDictionaryAddValue(ocopy, key, value); | |
178 | }); | |
179 | CFRelease(pkAttrs); | |
180 | CFDataRef der = SOSObjectCopyDER((SOSObjectRef)ocopy, error); | |
181 | CFRelease(ocopy); | |
182 | CFDataRef digest = NULL; | |
183 | if (der) { | |
184 | digest = ccdigest_copy_data(ccsha1_di(), CFDataGetLength(der), CFDataGetBytePtr(der), error); | |
185 | CFRelease(der); | |
186 | } | |
187 | return digest; | |
188 | } | |
189 | ||
190 | static CFDictionaryRef copyPropertyList(SOSObjectRef object, CFErrorRef *error) { | |
191 | return (CFDictionaryRef) CFRetain(object); | |
192 | } | |
193 | ||
194 | // Return the newest object | |
195 | static SOSObjectRef copyMergedObject(SOSObjectRef object1, SOSObjectRef object2, CFErrorRef *error) { | |
196 | CFDictionaryRef dict1 = (CFDictionaryRef)object1; | |
197 | CFDictionaryRef dict2 = (CFDictionaryRef)object2; | |
198 | SOSObjectRef result = NULL; | |
199 | CFDateRef m1, m2; | |
200 | m1 = CFDictionaryGetValue(dict1, kSecAttrModificationDate); | |
201 | m2 = CFDictionaryGetValue(dict2, kSecAttrModificationDate); | |
202 | switch (CFDateCompare(m1, m2, NULL)) { | |
203 | case kCFCompareGreaterThan: | |
204 | result = (SOSObjectRef)dict1; | |
205 | break; | |
206 | case kCFCompareLessThan: | |
207 | result = (SOSObjectRef)dict2; | |
208 | break; | |
209 | case kCFCompareEqualTo: | |
210 | { | |
211 | // Return the item with the smallest digest. | |
212 | CFDataRef digest1 = copyDigest(object1, error); | |
213 | CFDataRef digest2 = copyDigest(object2, error); | |
214 | if (digest1 && digest2) switch (CFDataCompare(digest1, digest2)) { | |
215 | case kCFCompareGreaterThan: | |
216 | case kCFCompareEqualTo: | |
217 | result = (SOSObjectRef)dict2; | |
218 | break; | |
219 | case kCFCompareLessThan: | |
220 | result = (SOSObjectRef)dict1; | |
221 | break; | |
222 | } | |
223 | CFReleaseSafe(digest2); | |
224 | CFReleaseSafe(digest1); | |
225 | break; | |
226 | } | |
227 | } | |
228 | CFRetainSafe(result); | |
229 | return result; | |
230 | } | |
231 | ||
d8f41ccd A |
232 | static SOSMergeResult mergeObject(SOSTransactionRef txn, SOSObjectRef object, SOSObjectRef *createdObject, CFErrorRef *error) { |
233 | SOSTestDataSourceRef ds = (SOSTestDataSourceRef)txn; | |
234 | SOSMergeResult mr = kSOSMergeFailure; | |
235 | CFDataRef pk = copyPrimaryKey(object, error); | |
236 | if (!pk) return mr; | |
237 | SOSObjectRef myObject = (SOSObjectRef)CFDictionaryGetValue(ds->p2database, pk); | |
238 | if (myObject) { | |
239 | SOSObjectRef merged = copyMergedObject(object, myObject, error); | |
240 | if (CFEqualSafe(merged, myObject)) { | |
241 | mr = kSOSMergeLocalObject; | |
242 | } else if (CFEqualSafe(merged, object)) { | |
243 | mr = kSOSMergePeersObject; | |
244 | } else { | |
245 | if (createdObject) { | |
246 | *createdObject = merged; | |
247 | merged = NULL; | |
248 | } | |
249 | mr = kSOSMergeCreatedObject; | |
250 | } | |
251 | if (mr != kSOSMergeLocalObject) { | |
252 | CFDataRef myKey = copyDigest(myObject, error); | |
253 | CFDictionaryRemoveValue(ds->d2database, myKey); | |
254 | SOSDigestVectorAppend(&ds->removals, CFDataGetBytePtr(myKey)); | |
255 | CFReleaseSafe(myKey); | |
256 | CFDataRef key = copyDigest(merged, error); | |
257 | CFDictionarySetValue(ds->d2database, key, merged); | |
258 | SOSDigestVectorAppend(&ds->additions, CFDataGetBytePtr(key)); | |
259 | CFReleaseSafe(key); | |
260 | CFDictionarySetValue(ds->p2database, pk, merged); | |
261 | } | |
262 | CFReleaseSafe(merged); | |
263 | } else { | |
264 | SOSTestDataSourceAddObject((SOSDataSourceRef)ds, object, error); | |
265 | mr = kSOSMergePeersObject; | |
266 | } | |
267 | CFReleaseSafe(pk); | |
268 | return mr; | |
269 | } | |
427c49bc | 270 | |
d8f41ccd A |
271 | static CFStringRef dsGetName(SOSDataSourceRef ds) { |
272 | return CFSTR("The sky is made of butterflies"); | |
273 | } | |
427c49bc | 274 | |
60c433a9 | 275 | static void dsSetNotifyPhaseBlock(SOSDataSourceRef ds, dispatch_queue_t queue, SOSDataSourceNotifyBlock notifyBlock) { |
d8f41ccd A |
276 | ((SOSTestDataSourceRef)ds)->notifyBlock = Block_copy(notifyBlock); |
277 | } | |
427c49bc | 278 | |
d8f41ccd A |
279 | static CFDataRef dsCopyStateWithKey(SOSDataSourceRef ds, CFStringRef key, CFStringRef pdmn, CFErrorRef *error) { |
280 | SOSTestDataSourceRef tds = (SOSTestDataSourceRef)ds; | |
281 | CFStringRef dbkey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-%@"), pdmn, key); | |
282 | CFDataRef state = CFDictionaryGetValue(tds->statedb, dbkey); | |
283 | CFReleaseSafe(dbkey); | |
284 | return CFRetainSafe(state); | |
285 | } | |
286 | ||
287 | static bool dsWith(SOSDataSourceRef ds, CFErrorRef *error, SOSDataSourceTransactionSource source, void(^transaction)(SOSTransactionRef txn, bool *commit)) { | |
288 | SOSTestDataSourceRef tds = (SOSTestDataSourceRef)ds; | |
289 | bool commit = true; | |
290 | transaction((SOSTransactionRef)ds, &commit); | |
291 | if (commit && ((SOSTestDataSourceRef)ds)->notifyBlock && (tds->removals.count || tds->additions.count)) { | |
292 | ((SOSTestDataSourceRef)ds)->notifyBlock(ds, (SOSTransactionRef)ds, kSOSDataSourceTransactionWillCommit, source, &tds->removals, &tds->additions); | |
293 | SOSDigestVectorFree(&tds->removals); | |
294 | SOSDigestVectorFree(&tds->additions); | |
295 | } | |
296 | return true; | |
297 | } | |
298 | ||
299 | static bool dsSetStateWithKey(SOSDataSourceRef ds, SOSTransactionRef txn, CFStringRef pdmn, CFStringRef key, CFDataRef state, CFErrorRef *error) { | |
300 | SOSTestDataSourceRef tds = (SOSTestDataSourceRef)ds; | |
301 | CFStringRef dbkey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-%@"), pdmn, key); | |
302 | CFDictionarySetValue(tds->statedb, dbkey, state); | |
303 | CFReleaseSafe(dbkey); | |
304 | return true; | |
305 | } | |
306 | ||
307 | static bool dsRestoreObject(SOSTransactionRef txn, uint64_t handle, CFDictionaryRef item, CFErrorRef *error) { | |
308 | // TODO: Just call merge, probably doesn't belong in protocol at all | |
309 | assert(false); | |
310 | return true; | |
311 | } | |
312 | ||
313 | static CFDictionaryRef objectCopyBackup(SOSObjectRef object, uint64_t handle, CFErrorRef *error) { | |
314 | // OMG We failed without an error. | |
315 | assert(false); | |
316 | return NULL; | |
317 | } | |
318 | ||
319 | SOSDataSourceRef SOSTestDataSourceCreate(void) { | |
320 | SOSTestDataSourceRef ds = calloc(1, sizeof(struct SOSTestDataSource)); | |
321 | ||
322 | ds->ds.engine = NULL; | |
323 | ds->ds.dsGetName = dsGetName; | |
324 | ds->ds.dsSetNotifyPhaseBlock = dsSetNotifyPhaseBlock; | |
325 | ds->ds.dsCopyManifest = copy_manifest; | |
326 | ds->ds.dsForEachObject = foreach_object; | |
327 | ds->ds.dsCopyStateWithKey = dsCopyStateWithKey; | |
328 | ds->ds.dsWith = dsWith; | |
329 | ds->ds.dsRelease = dispose; | |
330 | ||
331 | ds->ds.dsMergeObject = mergeObject; | |
332 | ds->ds.dsSetStateWithKey = dsSetStateWithKey; | |
333 | ds->ds.dsRestoreObject = dsRestoreObject; | |
334 | ||
335 | ds->ds.objectCopyDigest = copyDigest; | |
336 | ds->ds.objectCopyPrimaryKey = copyPrimaryKey; | |
337 | ds->ds.objectCreateWithPropertyList = createWithPropertyList; | |
338 | ds->ds.objectCopyPropertyList = copyPropertyList; | |
339 | ds->ds.objectCopyBackup = objectCopyBackup; | |
340 | ||
341 | ds->d2database = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
342 | ds->p2database = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
343 | ds->statedb = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
427c49bc A |
344 | ds->clean = false; |
345 | ||
346 | return (SOSDataSourceRef)ds; | |
347 | } | |
348 | ||
349 | static CFArrayRef SOSTestDataSourceFactoryCopyNames(SOSDataSourceFactoryRef factory) | |
350 | { | |
351 | SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory; | |
352 | CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); | |
353 | ||
354 | CFDictionaryForEach(dsf->data_sources, ^(const void*key, const void*value) { CFArrayAppendValue(result, key); }); | |
355 | ||
356 | return result; | |
357 | } | |
358 | ||
d8f41ccd | 359 | static SOSDataSourceRef SOSTestDataSourceFactoryCreateDataSource(SOSDataSourceFactoryRef factory, CFStringRef dataSourceName, CFErrorRef *error) |
427c49bc A |
360 | { |
361 | SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory; | |
362 | ||
363 | return (SOSDataSourceRef) CFDictionaryGetValue(dsf->data_sources, dataSourceName); | |
364 | } | |
365 | ||
366 | static void SOSTestDataSourceFactoryDispose(SOSDataSourceFactoryRef factory) | |
367 | { | |
368 | SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory; | |
369 | ||
370 | CFReleaseNull(dsf->data_sources); | |
371 | free(dsf); | |
372 | } | |
373 | ||
374 | SOSDataSourceFactoryRef SOSTestDataSourceFactoryCreate() { | |
375 | SOSTestDataSourceFactoryRef dsf = calloc(1, sizeof(struct SOSTestDataSourceFactory)); | |
376 | ||
377 | dsf->dsf.copy_names = SOSTestDataSourceFactoryCopyNames; | |
378 | dsf->dsf.create_datasource = SOSTestDataSourceFactoryCreateDataSource; | |
379 | dsf->dsf.release = SOSTestDataSourceFactoryDispose; | |
380 | dsf->data_sources = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); | |
381 | ||
382 | return &(dsf->dsf); | |
383 | } | |
384 | ||
d8f41ccd A |
385 | static bool do_nothing(SOSDataSourceRef ds, CFErrorRef *error) { |
386 | return true; | |
427c49bc A |
387 | } |
388 | ||
389 | void SOSTestDataSourceFactoryAddDataSource(SOSDataSourceFactoryRef factory, CFStringRef name, SOSDataSourceRef ds) | |
390 | { | |
391 | SOSTestDataSourceFactoryRef dsf = (SOSTestDataSourceFactoryRef) factory; | |
392 | ||
393 | // TODO This hack sucks. It leaks now. | |
d8f41ccd | 394 | ds->dsRelease = do_nothing; |
427c49bc A |
395 | |
396 | CFDictionarySetValue(dsf->data_sources, name, ds); | |
397 | ||
398 | } | |
399 | ||
400 | SOSMergeResult SOSTestDataSourceAddObject(SOSDataSourceRef data_source, SOSObjectRef object, CFErrorRef *error) { | |
401 | struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; | |
402 | bool result = false; | |
403 | CFDataRef key = copyDigest(object, error); | |
d8f41ccd A |
404 | CFDataRef pk = copyPrimaryKey(object, error); |
405 | if (key && pk) { | |
406 | SOSObjectRef myObject = (SOSObjectRef)CFDictionaryGetValue(ds->p2database, pk); | |
427c49bc A |
407 | SOSObjectRef merged = NULL; |
408 | if (myObject) { | |
409 | merged = copyMergedObject(object, myObject, error); | |
410 | } else { | |
411 | merged = object; | |
412 | CFRetain(merged); | |
413 | } | |
414 | if (merged) { | |
415 | result = true; | |
416 | if (!CFEqualSafe(merged, myObject)) { | |
d8f41ccd A |
417 | if (myObject) { |
418 | CFDataRef myKey = copyDigest(myObject, error); | |
419 | CFDictionaryRemoveValue(ds->d2database, myKey); | |
420 | SOSDigestVectorAppend(&ds->removals, CFDataGetBytePtr(myKey)); | |
421 | CFReleaseSafe(myKey); | |
422 | } | |
423 | CFDictionarySetValue(ds->d2database, key, merged); | |
424 | CFDictionarySetValue(ds->p2database, pk, merged); | |
425 | SOSDigestVectorAppend(&ds->additions, CFDataGetBytePtr(key)); | |
427c49bc A |
426 | ds->clean = false; |
427 | } | |
428 | CFRelease(merged); | |
429 | } | |
427c49bc | 430 | } |
d8f41ccd A |
431 | CFReleaseSafe(pk); |
432 | CFReleaseSafe(key); | |
427c49bc A |
433 | return result; |
434 | } | |
435 | ||
436 | bool SOSTestDataSourceDeleteObject(SOSDataSourceRef data_source, CFDataRef key, CFErrorRef *error) { | |
437 | //struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; | |
438 | return false; | |
439 | } | |
440 | ||
441 | CFMutableDictionaryRef SOSTestDataSourceGetDatabase(SOSDataSourceRef data_source) { | |
442 | struct SOSTestDataSource *ds = (struct SOSTestDataSource *)data_source; | |
d8f41ccd | 443 | return ds->d2database; |
427c49bc A |
444 | } |
445 | ||
446 | // This works for any datasource, not just the test one, but it's only used in testcases, so it's here for now. | |
447 | SOSObjectRef SOSDataSourceCreateGenericItemWithData(SOSDataSourceRef ds, CFStringRef account, CFStringRef service, bool is_tomb, CFDataRef data) { | |
427c49bc A |
448 | int32_t value = 0; |
449 | CFNumberRef zero = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); | |
450 | value = 1; | |
451 | CFNumberRef one = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); | |
452 | CFAbsoluteTime timestamp = 3700000; | |
453 | CFDateRef now = CFDateCreate(kCFAllocatorDefault, timestamp); | |
454 | CFDictionaryRef dict = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, | |
455 | kSecClass, kSecClassGenericPassword, | |
456 | kSecAttrSynchronizable, one, | |
457 | kSecAttrTombstone, is_tomb ? one : zero, | |
458 | kSecAttrAccount, account, | |
459 | kSecAttrService, service, | |
460 | kSecAttrCreationDate, now, | |
461 | kSecAttrModificationDate, now, | |
462 | kSecAttrAccessGroup, CFSTR("test"), | |
463 | kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, | |
464 | !is_tomb && data ? kSecValueData : NULL,data, | |
465 | NULL); | |
466 | CFRelease(one); | |
467 | CFRelease(zero); | |
468 | CFReleaseSafe(now); | |
469 | CFErrorRef localError = NULL; | |
d8f41ccd | 470 | SOSObjectRef object = ds->objectCreateWithPropertyList(dict, &localError); |
427c49bc A |
471 | if (!object) { |
472 | secerror("createWithPropertyList: %@ failed: %@", dict, localError); | |
473 | CFRelease(localError); | |
474 | } | |
475 | CFRelease(dict); | |
476 | return object; | |
427c49bc A |
477 | } |
478 | ||
479 | SOSObjectRef SOSDataSourceCreateGenericItem(SOSDataSourceRef ds, CFStringRef account, CFStringRef service) { | |
480 | return SOSDataSourceCreateGenericItemWithData(ds, account, service, false, NULL); | |
481 | } | |
d8f41ccd A |
482 | |
483 | SOSObjectRef SOSDataSourceCopyObject(SOSDataSourceRef ds, SOSObjectRef match, CFErrorRef *error) | |
484 | { | |
485 | __block SOSObjectRef result = NULL; | |
486 | ||
487 | CFDataRef digest = SOSObjectCopyDigest(ds, match, error); | |
488 | SOSManifestRef manifest = NULL; | |
489 | ||
490 | require(digest, exit); | |
491 | manifest = SOSManifestCreateWithData(digest, error); | |
492 | ||
493 | SOSDataSourceForEachObject(ds, manifest, error, ^void (CFDataRef key, SOSObjectRef object, bool *stop) { | |
494 | if (object == NULL) { | |
495 | if (error && !*error) { | |
496 | SecCFCreateErrorWithFormat(kSOSDataSourceObjectNotFoundError, sSOSDataSourceErrorDomain, NULL, error, 0, CFSTR("key %@ not in database"), key); | |
497 | } | |
498 | } else if (result == NULL) { | |
499 | result = CFRetainSafe(object); | |
500 | } | |
501 | }); | |
502 | ||
503 | exit: | |
504 | CFReleaseNull(manifest); | |
505 | CFReleaseNull(digest); | |
506 | return result; | |
507 | } |