]>
Commit | Line | Data |
---|---|---|
427c49bc | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2012,2014 Apple Inc. All Rights Reserved. |
427c49bc A |
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 | * SOSCloudTransport.c - Implementation of the transport layer from CKBridge to SOSAccount/SOSCircle | |
27 | */ | |
28 | /* | |
29 | This XPC service is essentially just a proxy to iCloud KVS, which exists since | |
30 | the main security code cannot link against Foundation. | |
31 | ||
32 | See sendTSARequestWithXPC in tsaSupport.c for how to call the service | |
33 | ||
34 | The client of an XPC service does not get connection events, nor does it | |
35 | need to deal with transactions. | |
36 | */ | |
37 | ||
38 | #include <SOSCloudTransport.h> | |
39 | #include <utilities/SecCFError.h> | |
40 | #include <dispatch/dispatch.h> | |
41 | #include <stdlib.h> | |
42 | ||
43 | static CFStringRef sErrorDomain = CFSTR("com.apple.security.sos.transport.error"); | |
44 | ||
45 | enum | |
46 | { | |
47 | kSOSObjectMallocFailed = 1, | |
48 | kAddDuplicateEntry, | |
49 | kSOSObjectNotFoundError = 1, | |
50 | kSOSObjectCantBeConvertedToXPCObject, | |
51 | kSOSOUnexpectedConnectionEvent, | |
52 | kSOSOUnexpectedXPCEvent, | |
53 | kSOSConnectionNotOpen | |
54 | }; | |
55 | ||
56 | /* SOSCloudTransport, a statically initialized transport singleton. */ | |
57 | struct SOSCloudTransport | |
58 | { | |
59 | struct CloudTransport t; | |
60 | unsigned version; | |
61 | CFStringRef kvsID; | |
62 | dispatch_once_t once; | |
63 | }; | |
64 | ||
65 | #pragma mark ---------- Communication with XPC ---------- | |
66 | ||
67 | ||
68 | //------------------------------------------------------------------------------------------------ | |
69 | ||
70 | #include <AssertMacros.h> | |
71 | ||
72 | #include <xpc/xpc.h> | |
73 | #include <CoreFoundation/CoreFoundation.h> | |
74 | #include <CoreFoundation/CFXPCBridge.h> | |
75 | #include <sysexits.h> | |
76 | #include <syslog.h> | |
77 | ||
78 | #include <utilities/debugging.h> | |
79 | #include <utilities/SecCFWrappers.h> | |
80 | ||
81 | #include "CKConstants.h" | |
82 | ||
83 | #define zsecdebug(format...) secerror(format) | |
84 | ||
85 | static xpc_connection_t serviceConnection = NULL; | |
86 | static dispatch_queue_t xpc_queue = NULL; | |
87 | static CloudKeychainReplyBlock itemsChangedBlock; | |
88 | ||
89 | static bool handle_xpc_event(const xpc_connection_t peer, xpc_object_t event); | |
90 | static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error); | |
91 | ||
92 | // extern CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xo); | |
93 | // extern xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef cf); | |
94 | ||
95 | // Debug | |
96 | static void describeXPCObject(char *prefix, xpc_object_t object); | |
97 | static void describeXPCType(char *prefix, xpc_type_t xtype); | |
98 | ||
99 | ||
100 | ||
101 | #define WANTXPCREPLY 0 | |
102 | ||
103 | #pragma mark ----- utilities ----- | |
104 | ||
105 | static CFErrorRef makeError(CFIndex which) | |
106 | { | |
107 | CFDictionaryRef userInfo = NULL; | |
108 | return CFErrorCreate(kCFAllocatorDefault, sErrorDomain, which, userInfo); | |
109 | } | |
110 | ||
111 | #pragma mark ----- SPI ----- | |
112 | ||
113 | static void initXPCConnection() | |
114 | { | |
115 | zsecdebug("initXPCConnection\n"); | |
116 | ||
117 | xpc_queue = dispatch_queue_create(xpcServiceName, DISPATCH_QUEUE_SERIAL); | |
118 | ||
119 | serviceConnection = xpc_connection_create_mach_service(xpcServiceName, xpc_queue, 0); | |
120 | ||
121 | // serviceConnection = xpc_connection_create(xpcServiceName, xpc_queue); | |
122 | zsecdebug("serviceConnection: %p\n", serviceConnection); | |
123 | ||
124 | xpc_connection_set_event_handler(serviceConnection, ^(xpc_object_t event) | |
125 | { | |
126 | zsecdebug("xpc_connection_set_event_handler\n"); | |
127 | handle_xpc_event(serviceConnection, event); | |
128 | }); | |
129 | ||
130 | xpc_connection_resume(serviceConnection); | |
131 | xpc_retain(serviceConnection); | |
132 | } | |
133 | ||
134 | #if 0 // unused | |
135 | static void closeXPCConnection() | |
136 | { | |
137 | zsecdebug("closeXPCConnection\n"); | |
138 | xpc_release(serviceConnection); | |
139 | } | |
140 | #endif | |
141 | ||
142 | static void setItemsChangedBlock(CloudKeychainReplyBlock icb) | |
143 | { | |
144 | if (icb != itemsChangedBlock) | |
145 | { | |
146 | if (itemsChangedBlock) | |
147 | Block_release(itemsChangedBlock); | |
148 | itemsChangedBlock = icb; | |
149 | Block_copy(itemsChangedBlock); | |
150 | } | |
151 | } | |
152 | ||
153 | // typedef void (^CloudKeychainReplyBlock)(CFDictionaryRef returnedValues, CFErrorRef error); | |
154 | ||
155 | static bool handle_xpc_event(const xpc_connection_t peer, xpc_object_t event) | |
156 | { | |
157 | CFErrorRef localError = NULL; | |
158 | zsecdebug(">>>>> handle_connection_event via event_handler <<<<<\n"); | |
159 | bool result = false; | |
160 | if ((result = xpc_event_filter(peer, event, &localError))) | |
161 | { | |
162 | const char *operation = xpc_dictionary_get_string(event, kMessageKeyOperation); | |
163 | if (!operation || strcmp(operation, kMessageOperationItemChanged)) // some op we don't care about | |
164 | { | |
165 | zsecdebug("operation: %s", operation); | |
166 | return result; | |
167 | } | |
168 | ||
169 | xpc_object_t xrv = xpc_dictionary_get_value(event, kMessageKeyValue); | |
170 | if (!xrv) | |
171 | { | |
172 | zsecdebug("xrv null for kMessageKeyValue"); | |
173 | return result; | |
174 | } | |
175 | describeXPCObject("xrv", xrv); | |
176 | ||
177 | CFDictionaryRef returnedValues = _CFXPCCreateCFObjectFromXPCObject(xrv); | |
178 | zsecdebug("returnedValues: %@", returnedValues); | |
179 | ||
180 | if (itemsChangedBlock) | |
181 | itemsChangedBlock(returnedValues, localError); | |
182 | } | |
183 | CFReleaseSafe(localError); | |
184 | ||
185 | return result; | |
186 | } | |
187 | ||
188 | static bool xpc_event_filter(const xpc_connection_t peer, xpc_object_t event, CFErrorRef *error) | |
189 | { | |
190 | // return true if the type is XPC_TYPE_DICTIONARY (and therefore something more to process) | |
191 | zsecdebug("handle_connection_event\n"); | |
192 | xpc_type_t xtype = xpc_get_type(event); | |
193 | describeXPCType("handle_xpc_event", xtype); | |
194 | if (XPC_TYPE_CONNECTION == xtype) | |
195 | { | |
196 | zsecdebug("handle_xpc_event: XPC_TYPE_CONNECTION (unexpected)"); | |
197 | // The client of an XPC service does not get connection events | |
198 | // For nwo, we log this and keep going | |
199 | describeXPCObject("handle_xpc_event: XPC_TYPE_CONNECTION, obj : ", event); | |
200 | #if 0 | |
201 | if (error) | |
202 | *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX | |
203 | assert(true); | |
204 | #endif | |
205 | } | |
206 | else | |
207 | if (XPC_TYPE_ERROR == xtype) | |
208 | { | |
209 | zsecdebug("default: xpc error: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); | |
210 | if (error) | |
211 | *error = makeError(kSOSOUnexpectedConnectionEvent); // FIX | |
212 | } | |
213 | else | |
214 | if (XPC_TYPE_DICTIONARY == xtype) | |
215 | { | |
216 | zsecdebug("received dictionary event %p\n", event); | |
217 | return true; | |
218 | } | |
219 | else | |
220 | { | |
221 | zsecdebug("default: unexpected connection event %p\n", event); | |
222 | describeXPCObject("handle_xpc_event: obj : ", event); | |
223 | if (error) | |
224 | *error = makeError(kSOSOUnexpectedXPCEvent); | |
225 | } | |
226 | return false; | |
227 | } | |
228 | ||
229 | static void talkWithKVS(xpc_object_t message, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
230 | { | |
231 | secerror("start"); | |
232 | __block CFErrorRef error = NULL; | |
233 | __block CFTypeRef object = NULL; | |
234 | ||
235 | dispatch_block_t callback = ^{ | |
236 | secerror("callback"); | |
237 | if (replyBlock) | |
238 | replyBlock(object, error); | |
239 | // if (object) | |
240 | // CFRelease(object); | |
241 | if (error) | |
242 | { | |
243 | secerror("callback error: %@", error); | |
244 | // CFRelease(error); | |
245 | } | |
246 | dispatch_release(processQueue); | |
247 | }; | |
248 | ||
249 | require_action(serviceConnection, xit, error = makeError(kSOSConnectionNotOpen)); | |
250 | require_action(message, xit, error = makeError(kSOSObjectNotFoundError)); | |
251 | dispatch_retain(processQueue); | |
252 | secerror("xpc_connection_send_message_with_reply called"); | |
253 | ||
254 | Block_copy(callback); // TODO -- this should not be needed, I think | |
255 | ||
256 | //#if !WANTXPCREPLY | |
257 | // xpc_connection_send_message(serviceConnection, message); // Send message; don't want a reply | |
258 | //#else | |
259 | xpc_connection_send_message_with_reply(serviceConnection, message, xpc_queue, ^(xpc_object_t reply) | |
260 | { | |
261 | secerror("xpc_connection_send_message_with_reply handler called back"); | |
262 | if (xpc_event_filter(serviceConnection, reply, &error) && reply) | |
263 | { | |
264 | describeXPCObject("getValuesFromKVS: reply : ", reply); | |
265 | xpc_object_t xrv = xpc_dictionary_get_value(reply, kMessageKeyValue); | |
266 | if (xrv) | |
267 | { | |
268 | describeXPCObject("talkWithKVS: xrv: ", xrv); | |
269 | /* | |
270 | * The given XPC object must be one that was previously returned by | |
271 | * _CFXPCCreateXPCMessageWithCFObject(). | |
272 | */ | |
273 | object = _CFXPCCreateCFObjectFromXPCObject(xrv); // CF object is retained; release in callback | |
274 | secerror("converted CF object: %@", object); | |
275 | } | |
276 | else | |
277 | secerror("missing value reply"); | |
278 | } | |
279 | dispatch_async(processQueue, callback); | |
280 | }); | |
281 | //#endif | |
282 | ||
283 | //sleep(5); // DEBUG DEBUG FIX | |
284 | // xpc_release(message); | |
285 | return; | |
286 | ||
287 | xit: | |
288 | secerror("talkWithKVS error: %@", error); | |
289 | if (replyBlock) | |
290 | dispatch_async(processQueue, callback); | |
291 | } | |
292 | ||
293 | static void putValuesWithXPC(CFDictionaryRef values, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
294 | { | |
295 | CFErrorRef error = NULL; | |
296 | ||
297 | require_action(values, xit, error = makeError(kSOSObjectNotFoundError)); | |
298 | ||
299 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); | |
300 | xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion); | |
301 | xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationPUTDictionary); | |
302 | ||
303 | xpc_object_t xobject = _CFXPCCreateXPCObjectFromCFObject(values); | |
304 | require_action(xobject, xit, error = makeError(kSOSObjectCantBeConvertedToXPCObject)); | |
305 | xpc_dictionary_set_value(message, kMessageKeyValue, xobject); | |
306 | ||
307 | talkWithKVS(message, processQueue, replyBlock); | |
308 | return; | |
309 | ||
310 | xit: | |
311 | if (replyBlock) | |
312 | replyBlock(NULL, error); | |
313 | } | |
314 | ||
315 | static void synchronizeKVS(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
316 | { | |
317 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); | |
318 | xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion); | |
319 | xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationSynchronize); | |
320 | talkWithKVS(message, processQueue, replyBlock); | |
321 | } | |
322 | ||
323 | static void clearAll(dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
324 | { | |
325 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); | |
326 | xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion); | |
327 | xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationClearStore); | |
328 | talkWithKVS(message, processQueue, replyBlock); | |
329 | } | |
330 | ||
331 | static void getValuesFromKVS(CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
332 | { | |
333 | secerror("start"); | |
334 | CFErrorRef error = NULL; | |
335 | xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0); | |
336 | xpc_object_t xkeysToGet = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create(); | |
337 | ||
338 | require_action(xkeysToGet, xit, error = makeError(kSOSObjectNotFoundError)); | |
339 | ||
340 | xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToGet); | |
341 | ||
342 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); | |
343 | xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion); | |
344 | xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationGETv2); | |
345 | xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest); | |
346 | ||
347 | talkWithKVS(message, processQueue, replyBlock); | |
348 | ||
349 | xpc_release(message); | |
350 | return; | |
351 | ||
352 | xit: | |
353 | if (replyBlock) | |
354 | replyBlock(NULL, error); | |
355 | } | |
356 | ||
357 | static void registerKeysForKVS(CFArrayRef keysToGet, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
358 | { | |
359 | secerror("start"); | |
360 | ||
361 | xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0); | |
362 | xpc_object_t xkeysToRegister = keysToGet ? _CFXPCCreateXPCObjectFromCFObject(keysToGet) : xpc_null_create(); | |
363 | xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToRegister); | |
364 | ||
365 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); | |
366 | xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion); | |
367 | xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationRegisterKeysAndGet); | |
368 | xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest); | |
369 | ||
370 | if (clientIdentifier) | |
371 | { | |
372 | char *clientid = CFStringToCString(clientIdentifier); | |
373 | if (clientid) | |
374 | { | |
375 | xpc_dictionary_set_string(message, kMessageKeyClientIdentifier, clientid); | |
376 | free(clientid); | |
377 | } | |
378 | } | |
379 | ||
380 | setItemsChangedBlock(replyBlock); | |
381 | talkWithKVS(message, processQueue, replyBlock); | |
382 | ||
383 | xpc_release(message); | |
384 | } | |
385 | ||
386 | static void unregisterKeysForKVS(CFArrayRef keysToUnregister, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
387 | { | |
388 | #if NO_SERVERz | |
389 | if (gCKD->unregisterKeys) { | |
390 | return gCKD->unregisterKeys(...); | |
391 | } | |
392 | #endif | |
393 | ||
394 | secerror("start"); | |
395 | ||
396 | xpc_object_t xkeysOfInterest = xpc_dictionary_create(NULL, NULL, 0); | |
397 | xpc_object_t xkeysToUnregister = keysToUnregister ? _CFXPCCreateXPCObjectFromCFObject(keysToUnregister) : xpc_null_create(); | |
398 | xpc_dictionary_set_value(xkeysOfInterest, kMessageKeyKeysToGet, xkeysToUnregister); | |
399 | ||
400 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); | |
401 | xpc_dictionary_set_uint64(message, kMessageKeyVersion, kCKDXPCVersion); | |
402 | xpc_dictionary_set_string(message, kMessageKeyOperation, kOperationUnregisterKeys); | |
403 | xpc_dictionary_set_value(message, kMessageKeyValue, xkeysOfInterest); | |
404 | ||
405 | if (clientIdentifier) | |
406 | { | |
407 | char *clientid = CFStringToCString(clientIdentifier); | |
408 | if (clientid) | |
409 | { | |
410 | xpc_dictionary_set_string(message, kMessageKeyClientIdentifier, clientid); | |
411 | free(clientid); | |
412 | } | |
413 | } | |
414 | ||
415 | talkWithKVS(message, processQueue, replyBlock); | |
416 | ||
417 | xpc_release(message); | |
418 | } | |
419 | ||
420 | #pragma mark ----- DEBUG Utilities ----- | |
421 | ||
422 | //------------------------------------------------------------------------------------------------ | |
423 | // DEBUG only | |
424 | //------------------------------------------------------------------------------------------------ | |
425 | ||
426 | static void describeXPCObject(char *prefix, xpc_object_t object) | |
427 | { | |
428 | //#ifndef NDEBUG | |
429 | // This is useful for debugging. | |
430 | if (object) | |
431 | { | |
432 | char *desc = xpc_copy_description(object); | |
433 | zsecdebug("%s%s\n", prefix, desc); | |
434 | free(desc); | |
435 | } | |
436 | else | |
437 | zsecdebug("%s<NULL>\n", prefix); | |
438 | //#endif | |
439 | } | |
440 | ||
441 | static void describeXPCType(char *prefix, xpc_type_t xtype) | |
442 | { | |
443 | /* | |
444 | Add these as necessary: | |
445 | XPC_TYPE_ENDPOINT | |
446 | XPC_TYPE_NULL | |
447 | XPC_TYPE_BOOL | |
448 | XPC_TYPE_INT64 | |
449 | XPC_TYPE_UINT64 | |
450 | XPC_TYPE_DOUBLE | |
451 | XPC_TYPE_DATE | |
452 | XPC_TYPE_DATA | |
453 | XPC_TYPE_STRING | |
454 | XPC_TYPE_UUID | |
455 | XPC_TYPE_FD | |
456 | XPC_TYPE_SHMEM | |
457 | XPC_TYPE_ARRAY | |
458 | */ | |
459 | ||
460 | #ifndef NDEBUG | |
461 | // This is useful for debugging. | |
462 | char msg[256]={0,}; | |
463 | if (XPC_TYPE_CONNECTION == xtype) | |
464 | strcpy(msg, "XPC_TYPE_CONNECTION"); | |
465 | else if (XPC_TYPE_ERROR == xtype) | |
466 | strcpy(msg, "XPC_TYPE_ERROR"); | |
467 | else if (XPC_TYPE_DICTIONARY == xtype) | |
468 | strcpy(msg, "XPC_TYPE_DICTIONARY"); | |
469 | else | |
470 | strcpy(msg, "<unknown>"); | |
471 | ||
472 | zsecdebug("%s type:%s\n", prefix, msg); | |
473 | #endif | |
474 | } | |
475 | ||
476 | #pragma mark ---------- SOSCloudTransport ---------- | |
477 | ||
478 | static void SOSCloudTransportPut(SOSCloudTransportRef transport, CFDictionaryRef valuesToPut, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
479 | { | |
480 | secerror("%@", valuesToPut); | |
481 | putValuesWithXPC(valuesToPut, processQueue, replyBlock); | |
482 | } | |
483 | ||
484 | static void SOSCloudTransportGet(SOSCloudTransportRef transport, CFArrayRef keysToGet, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
485 | { | |
486 | // struct SOSCloudTransport *t = (struct SOSCloudTransport *)transport; | |
487 | /* Get from KVS */ | |
488 | secerror("%@", keysToGet); | |
489 | getValuesFromKVS(keysToGet, processQueue, replyBlock); | |
490 | } | |
491 | ||
492 | static void SOSCloudTransportRegisterKeys(SOSCloudTransportRef transport, CFArrayRef keysToGet, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
493 | { | |
494 | /* RegisterKeys with KVS */ | |
495 | registerKeysForKVS(keysToGet, clientIdentifier, processQueue, replyBlock); | |
496 | } | |
497 | ||
498 | static void SOSCloudTransportUnregisterKeys(SOSCloudTransportRef transport, CFArrayRef keysToUnregister, CFStringRef clientIdentifier, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
499 | { | |
500 | /* unregister from KVS */ | |
501 | unregisterKeysForKVS(keysToUnregister, clientIdentifier, processQueue, replyBlock); | |
502 | } | |
503 | ||
504 | static void SOSCloudTransportGetAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
505 | { | |
506 | /* Get from KVS */ | |
507 | getValuesFromKVS(NULL, processQueue, replyBlock); | |
508 | } | |
509 | ||
510 | static void SOSCloudTransportSync(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
511 | { | |
512 | // struct SOSCloudTransport *t = (struct SOSCloudTransport *)transport; | |
513 | /* Sync KVS */ | |
514 | synchronizeKVS(processQueue, replyBlock); | |
515 | } | |
516 | ||
517 | static void SOSCloudTransportClearAll(SOSCloudTransportRef transport, dispatch_queue_t processQueue, CloudKeychainReplyBlock replyBlock) | |
518 | { | |
519 | // struct SOSCloudTransport *t = (struct SOSCloudTransport *)transport; | |
520 | /* clear KVS */ | |
521 | clearAll(processQueue, replyBlock); | |
522 | } | |
523 | ||
524 | static void SOSCloudTransportSetItemsChangedBlock(SOSCloudTransportRef transport, CloudKeychainReplyBlock itemsChangedBlock) | |
525 | { | |
526 | // struct SOSCloudTransport *t = (struct SOSCloudTransport *)transport; | |
527 | /* clear KVS */ | |
528 | setItemsChangedBlock(itemsChangedBlock); | |
529 | } | |
530 | ||
531 | static void SOSCloudTransportInit(void *ctx) | |
532 | { | |
533 | struct SOSCloudTransport *st = (struct SOSCloudTransport *)ctx; | |
534 | st->t.put = SOSCloudTransportPut; | |
535 | st->t.registerKeys = SOSCloudTransportRegisterKeys; | |
536 | st->t.unregisterKeys = SOSCloudTransportUnregisterKeys; | |
537 | st->t.get = SOSCloudTransportGet; | |
538 | st->t.getAll = SOSCloudTransportGetAll; | |
539 | st->t.synchronize = SOSCloudTransportSync; | |
540 | st->t.clearAll = SOSCloudTransportClearAll; | |
541 | st->t.setItemsChangedBlock = SOSCloudTransportSetItemsChangedBlock; | |
542 | ||
543 | if (st->kvsID) | |
544 | CFRetain(st->kvsID); | |
545 | ||
546 | st->version = 0; | |
547 | initXPCConnection(); | |
548 | } | |
549 | ||
550 | SOSCloudTransportRef SOSCloudTransportDefaultTransport(CFStringRef kvsID, uint32_t options) | |
551 | { | |
552 | static struct SOSCloudTransport cloudTransport = {}; | |
553 | cloudTransport.kvsID = kvsID; | |
554 | dispatch_once_f(&cloudTransport.once, &cloudTransport, SOSCloudTransportInit); | |
555 | return &cloudTransport.t; | |
556 | } | |
557 | ||
558 |