]> git.saurik.com Git - apple/security.git/blob - Security/sec/SOSCircle/Regressions/CKDKeyValueStore.m
Security-57031.10.10.tar.gz
[apple/security.git] / Security / sec / SOSCircle / Regressions / CKDKeyValueStore.m
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 // CKDKeyValueStore.m
26 // sec
27 //
28
29 #import "CKDKeyValueStore.h"
30 #import "CKDPersistentState.h"
31
32 /*
33 pseudo-code
34
35 g = [CKDKeyValueStore defaultStore:mystoreID];
36 [g setObject:obj forKey:@"foo"];
37 [g synchronize];
38 */
39
40 #define DONTUSENOTIFICATIONS true
41
42 static const int verboseCKDKVSDebugging = false;
43
44 #ifndef NDEBUG
45 #define pdebug(format...) \
46 do { \
47 if (verboseCKDKVSDebugging) \
48 NSLog(format); \
49 } while (0)
50 #else
51 //empty
52 #define pdebug(format...)
53 #endif
54
55
56 extern CFStringRef kCKDKVSRemoteStoreID;
57 CFStringRef kCKDKVSRemoteStoreID = CFSTR("REMOTE");
58 CFStringRef kCKDAWSRemoteStoreID = CFSTR("AWS");
59
60 NSString * const kCKDKVSWhoChangedItemKey = @"WhoChangedItemKey";
61
62 static NSString * const ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification = @"ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification";
63
64 // MARK: ----- CKDKeyValueStore -----
65
66 @implementation CKDKeyValueStore
67
68 - (id)initWithIdentifier:(NSString *)identifier itemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
69 {
70 if (self = [super init])
71 {
72 self.delegate = self;
73 self.identifier = identifier;
74 self->localKVS = true;
75
76 // copy blocks onto heap
77 itemsChangedCallback = Block_copy(itemsChangedBlock);
78
79 [[NSNotificationCenter defaultCenter] addObserver: self
80 selector: @selector (iCloudAccountAvailabilityChanged:)
81 name: NSUbiquityIdentityDidChangeNotification
82 object: nil];
83
84 [[NSNotificationCenter defaultCenter] addObserver:self
85 selector:@selector(cloudChanged:)
86 name:ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification
87 object:nil];
88 }
89 return self;
90 }
91
92 + (CKDKeyValueStore *)defaultStore:(NSString *)identifier itemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
93 {
94 return [CKDKeyValueStoreCollection defaultStore:identifier itemsChangedBlock:itemsChangedBlock];
95 }
96
97 - (BOOL)synchronize
98 {
99 BOOL value = NO;
100 value = [CKDKeyValueStoreCollection enqueueSyncWithReply];
101
102 return value;
103 }
104
105 - (BOOL)isLocalKVS
106 {
107 return YES;
108 }
109
110 - (id)objectForKey:(NSString *)aKey
111 {
112 pdebug(@"retrieving value for key \"%@\"", aKey);
113 id value = [CKDKeyValueStoreCollection enqueueWithReply:aKey];
114 pdebug(@"retrieved value for key \"%@\": %@", aKey, value);
115 return value;
116 }
117
118 - (void)setObject:(id)anObject forKey:(NSString *)aKey
119 {
120 pdebug(@"setting value for key \"%@\"", aKey);
121 [CKDKeyValueStoreCollection enqueueWrite:anObject forKey:aKey from:self.identifier];
122 }
123
124 - (void)removeObjectForKey:(NSString *)aKey
125 {
126 pdebug(@"removing value for key \"%@\"", aKey);
127 [CKDKeyValueStoreCollection enqueueWrite:NULL forKey:aKey from:self.identifier];
128 }
129
130 - (NSDictionary *)dictionaryRepresentation
131 {
132 pdebug(@"retrieving dictionaryRepresentation");
133 id value = [CKDKeyValueStoreCollection enqueueWithReply:NULL];
134 pdebug(@"retrieved dictionaryRepresentation: %@", value);
135 return value;
136 }
137
138 - (void)setDictionaryRepresentation:(NSMutableDictionary *)initialValue
139 {
140 // DEBUG
141 [CKDKeyValueStoreCollection enqueueWrite:initialValue forKey:NULL from:self.identifier];
142 }
143
144 - (void)clearPersistentStores
145 {
146 }
147
148 + (CFStringRef)remoteStoreID
149 {
150 return kCKDKVSRemoteStoreID;
151 }
152
153 // MARK: ----- copied from real kvs -----
154
155 - (id)initWithItemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
156 {
157 if (self = [super init])
158 {
159 // copy blocks onto heap
160 itemsChangedCallback = Block_copy(itemsChangedBlock);
161
162 [[NSNotificationCenter defaultCenter] addObserver: self
163 selector: @selector (iCloudAccountAvailabilityChanged:)
164 name: NSUbiquityIdentityDidChangeNotification
165 object: nil];
166
167 [[NSNotificationCenter defaultCenter] addObserver:self
168 selector:@selector(cloudChanged:)
169 name:ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification
170 object:nil];
171 }
172 return self;
173 }
174
175 - (void)dealloc
176 {
177
178 [[NSNotificationCenter defaultCenter] removeObserver:self
179 name:ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification object:nil];
180
181 [[NSNotificationCenter defaultCenter] removeObserver:self
182 name:NSUbiquityIdentityDidChangeNotification object:nil];
183
184 Block_release(itemsChangedCallback);
185 [super dealloc];
186 }
187
188 - (void)cloudChanged:(NSNotification*)notification
189 {
190 /*
191 Posted when the value of one or more keys in the local key-value store changed due to incoming data pushed from iCloud.
192 This notification is sent only upon a change received from iCloud; it is not sent when your app sets a value.
193
194 The user info dictionary can contain the reason for the notification as well as a list of which values changed, as follows:
195
196 The value of the NSUbiquitousKeyValueStoreChangeReasonKey key, when present, indicates why the key-value store changed.
197 Its value is one of the constants in “Change Reason Values .” The value of the NSUbiquitousKeyValueStoreChangedKeysKey,
198 when present, is an array of strings, each the name of a key whose value changed. The notification object is the
199 NSUbiquitousKeyValueStore object whose contents changed.
200
201 enum {
202 NSUbiquitousKeyValueStoreServerChange NS_ENUM_AVAILABLE(10_7, 5_0),
203 NSUbiquitousKeyValueStoreInitialSyncChange NS_ENUM_AVAILABLE(10_7, 5_0),
204 NSUbiquitousKeyValueStoreQuotaViolationChange NS_ENUM_AVAILABLE(10_7, 5_0),
205 NSUbiquitousKeyValueStoreAccountChange NS_ENUM_AVAILABLE(10_8, 6_0)
206 };
207
208 */
209
210 NSDictionary *userInfo = [notification userInfo];
211 NSNumber *reason = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey];
212 NSInteger reasonValue = -1;
213
214 pdebug(@"cloudChanged notification: %@", notification);
215
216 NSString *whoChangedIt = [userInfo objectForKey:kCKDKVSWhoChangedItemKey];
217
218 if (self.identifier && whoChangedIt && [self.identifier isEqualToString:whoChangedIt])
219 {
220 pdebug(@"cloudChanged by us (%@), ignoring event", self.identifier);
221 return;
222 }
223
224 if (reason)
225 {
226 reasonValue = [reason integerValue];
227 NSArray *reasonStrings = [NSArray arrayWithObjects:@"Server", @"InitialSync", @"QuotaViolation", @"Account", @"unknown", nil];
228 long ridx = (NSUbiquitousKeyValueStoreServerChange <= reasonValue && reasonValue <= NSUbiquitousKeyValueStoreAccountChange)?reasonValue : 5;
229 pdebug(@"cloudChanged with reason %ld (%@ Change)", (long)reasonValue, [reasonStrings objectAtIndex:ridx]);
230 }
231
232 if ((reasonValue == NSUbiquitousKeyValueStoreServerChange) ||
233 (reasonValue == NSUbiquitousKeyValueStoreInitialSyncChange))
234 {
235 NSArray *keysChangedInCloud = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
236 pdebug(@"keysChangedInCloud: %@", keysChangedInCloud);
237 NSMutableDictionary *changedValues = [NSMutableDictionary dictionaryWithCapacity:0];
238 [keysChangedInCloud enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
239 {
240 NSString *key = (NSString *)obj;
241 // itemChangedCallback(key, [self.store objectForKey:key]);
242 id anObject = @"FIXME"; //[self.store objectForKey:key];
243 [changedValues setObject:anObject forKey:key];
244
245 pdebug(@"storeChanged updated value for %@", key);
246 }];
247 itemsChangedCallback((CFDictionaryRef)changedValues); // fix me *************************
248 }
249 }
250
251
252
253 @end
254
255 // MARK: ----- CKDKeyValueStoreCollection -----
256
257 @implementation CKDKeyValueStoreCollection
258
259 - (id)init
260 {
261 if (self = [super init])
262 {
263 self.collection = [NSMutableDictionary dictionaryWithCapacity:0];
264 self->syncrequestqueue = dispatch_queue_create("syncrequestqueue", DISPATCH_QUEUE_SERIAL);
265 self->store = [NSMutableDictionary dictionaryWithCapacity:0];
266 }
267 return self;
268 }
269
270 // maybe should return (CKDKeyValueStore *), main thing is that it matches the protocol
271 + (id <CKDKVSDelegate>)defaultStore:(NSString *)identifier itemsChangedBlock:(CloudItemsChangedBlock)itemsChangedBlock
272 {
273 // look it up in the collection and return singleton
274 if (identifier == NULL)
275 return (id <CKDKVSDelegate>)[NSUbiquitousKeyValueStore defaultStore];
276
277 CKDKeyValueStoreCollection *mall = [CKDKeyValueStoreCollection sharedInstance];
278 id <CKDKVSDelegate> store = mall.collection[identifier];
279 if (!store)
280 {
281 store = [[CKDKeyValueStore alloc] initWithIdentifier:identifier itemsChangedBlock:itemsChangedBlock];
282 mall->_collection[identifier] = store;
283 }
284 return store;
285
286 }
287
288 + (id)sharedInstance
289 {
290 static dispatch_once_t once;
291 static CKDKeyValueStoreCollection *sharedStoreCollection;
292 dispatch_once(&once, ^ { sharedStoreCollection = [[self alloc] init]; });
293 return sharedStoreCollection;
294 }
295
296 + (void)enqueueWrite:(id)anObject forKey:(NSString *)aKey from:(NSString *)identifier
297 {
298 CKDKeyValueStoreCollection *mall = [CKDKeyValueStoreCollection sharedInstance];
299 dispatch_async(mall->syncrequestqueue, ^void ()
300 {
301 if (aKey==NULL && (CFGetTypeID(anObject)==CFDictionaryGetTypeID()))
302 {
303 [mall->store setDictionary:anObject];
304 [self postItemsChangedNotification:[anObject allKeys] from:identifier];
305 }
306 else
307 {
308 if (anObject)
309 [mall->store setObject:anObject forKey:aKey];
310 else
311 [mall->store removeObjectForKey:aKey];
312 [CKDKeyValueStoreCollection postItemChangedNotification:aKey from:identifier];
313 }
314
315 });
316 }
317
318 + (id)enqueueWithReply:(NSString *)aKey
319 {
320 __block id value = NULL;
321 CKDKeyValueStoreCollection *mall = [CKDKeyValueStoreCollection sharedInstance];
322 dispatch_sync(mall->syncrequestqueue, ^void ()
323 {
324 value = (aKey==NULL)?mall->store:[mall->store objectForKey:aKey];
325 });
326 return value;
327 }
328
329 + (BOOL)enqueueSyncWithReply
330 {
331 // basically a barrier
332 __block BOOL value = false;
333 CKDKeyValueStoreCollection *mall = [CKDKeyValueStoreCollection sharedInstance];
334 dispatch_sync(mall->syncrequestqueue, ^void ()
335 {
336 value = true;
337 });
338 return value;
339 }
340
341 + (void)postItemChangedNotification:(NSString *)keyThatChanged from:(NSString *)identifier
342 {
343 // convenience routine when a single key changes
344 NSArray *keysThatChanged = [NSArray arrayWithObject:keyThatChanged];
345 [self postItemsChangedNotification:keysThatChanged from:identifier];
346 // [keysThatChanged release];
347 }
348
349 + (void)postItemsChangedNotification:(NSArray *)keysThatChanged from:(NSString *)identifier
350 {
351 // add in array of keys plus the id of who changed it
352 NSDictionary *aUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
353 keysThatChanged, NSUbiquitousKeyValueStoreChangedKeysKey,
354 @(NSUbiquitousKeyValueStoreServerChange), NSUbiquitousKeyValueStoreChangeReasonKey,
355 identifier, kCKDKVSWhoChangedItemKey,
356 nil];
357
358 [[NSNotificationCenter defaultCenter] postNotificationName:ourNSUbiquitousKeyValueStoreDidChangeExternallyNotification
359 object:nil userInfo:aUserInfo];
360 // NSArray *keysChangedInCloud = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
361 }
362
363
364 @end
365