2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <AssertMacros.h>
26 #import <Foundation/Foundation.h>
28 #import "CKKSKeychainView.h"
30 #include <utilities/SecDb.h>
31 #include <securityd/SecDbItem.h>
32 #include <securityd/SecItemSchema.h>
36 #import <CloudKit/CloudKit.h>
37 #import "CKKSZoneStateEntry.h"
38 #import "keychain/ckks/CKKSRateLimiter.h"
41 @implementation CKKSZoneStateEntry
43 - (instancetype) initWithCKZone: (NSString*) ckzone zoneCreated: (bool) ckzonecreated zoneSubscribed: (bool) ckzonesubscribed changeToken: (NSData*) changetoken lastFetch: (NSDate*) lastFetch encodedRateLimiter: (NSData*) encodedRateLimiter {
44 if(self = [super init]) {
46 _ckzonecreated = ckzonecreated;
47 _ckzonesubscribed = ckzonesubscribed;
48 _encodedChangeToken = changetoken;
49 _lastFetchTime = lastFetch;
51 self.encodedRateLimiter = encodedRateLimiter;
56 - (BOOL)isEqual: (id) object {
57 if(![object isKindOfClass:[CKKSZoneStateEntry class]]) {
61 CKKSZoneStateEntry* obj = (CKKSZoneStateEntry*) object;
63 return ([self.ckzone isEqualToString: obj.ckzone] &&
64 self.ckzonecreated == obj.ckzonecreated &&
65 self.ckzonesubscribed == obj.ckzonesubscribed &&
66 ((self.encodedChangeToken == nil && obj.encodedChangeToken == nil) || [self.encodedChangeToken isEqual: obj.encodedChangeToken]) &&
67 ((self.lastFetchTime == nil && obj.lastFetchTime == nil) || [self.lastFetchTime isEqualToDate: obj.lastFetchTime]) &&
68 ((self.rateLimiter == nil && obj.rateLimiter == nil) || [self.rateLimiter isEqual: obj.rateLimiter])
72 + (instancetype) state: (NSString*) ckzone {
74 CKKSZoneStateEntry* ret = [CKKSZoneStateEntry tryFromDatabase:ckzone error:&error];
77 secerror("CKKS: error fetching CKState(%@): %@", ckzone, error);
81 ret = [[CKKSZoneStateEntry alloc] initWithCKZone: ckzone zoneCreated: false zoneSubscribed: false changeToken: nil lastFetch:nil encodedRateLimiter: nil];
86 - (CKServerChangeToken*) getChangeToken {
87 if(self.encodedChangeToken) {
88 NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.encodedChangeToken];
89 unarchiver.requiresSecureCoding = YES;
90 return [unarchiver decodeObjectOfClass:[CKServerChangeToken class] forKey:NSKeyedArchiveRootObjectKey];
96 - (void) setChangeToken: (CKServerChangeToken*) token {
97 self.encodedChangeToken = token ? [NSKeyedArchiver archivedDataWithRootObject:token] : nil;
100 - (NSData*)encodedRateLimiter {
101 if(self.rateLimiter == nil) {
104 return [NSKeyedArchiver archivedDataWithRootObject: self.rateLimiter];
107 - (void)setEncodedRateLimiter:(NSData *)encodedRateLimiter {
108 if(encodedRateLimiter == nil) {
109 self.rateLimiter = nil;
113 NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedRateLimiter];
114 unarchiver.requiresSecureCoding = YES;
115 self.rateLimiter = [unarchiver decodeObjectOfClass: [CKKSRateLimiter class] forKey:NSKeyedArchiveRootObjectKey];
118 #pragma mark - Database Operations
120 + (instancetype) fromDatabase: (NSString*) ckzone error: (NSError * __autoreleasing *) error {
121 return [self fromDatabaseWhere: @{@"ckzone": CKKSNilToNSNull(ckzone)} error: error];
124 + (instancetype) tryFromDatabase: (NSString*) ckzone error: (NSError * __autoreleasing *) error {
125 return [self tryFromDatabaseWhere: @{@"ckzone": CKKSNilToNSNull(ckzone)} error: error];
128 #pragma mark - CKKSSQLDatabaseObject methods
130 + (NSString*) sqlTable {
134 + (NSArray<NSString*>*) sqlColumns {
135 return @[@"ckzone", @"ckzonecreated", @"ckzonesubscribed", @"changetoken", @"lastfetch", @"ratelimiter"];
138 - (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
139 return @{@"ckzone": self.ckzone};
142 - (NSDictionary<NSString*,NSString*>*) sqlValues {
143 NSISO8601DateFormatter* dateFormat = [[NSISO8601DateFormatter alloc] init];
145 return @{@"ckzone": self.ckzone,
146 @"ckzonecreated": [NSNumber numberWithBool:self.ckzonecreated],
147 @"ckzonesubscribed": [NSNumber numberWithBool:self.ckzonesubscribed],
148 @"changetoken": CKKSNilToNSNull([self.encodedChangeToken base64EncodedStringWithOptions:0]),
149 @"lastfetch": CKKSNilToNSNull(self.lastFetchTime ? [dateFormat stringFromDate: self.lastFetchTime] : nil),
150 @"ratelimiter": CKKSNilToNSNull([self.encodedRateLimiter base64EncodedStringWithOptions:0])
154 + (instancetype) fromDatabaseRow: (NSDictionary*) row {
155 NSISO8601DateFormatter* dateFormat = [[NSISO8601DateFormatter alloc] init];
157 return [[CKKSZoneStateEntry alloc] initWithCKZone: row[@"ckzone"]
158 zoneCreated: [row[@"ckzonecreated"] boolValue]
159 zoneSubscribed: [row[@"ckzonesubscribed"] boolValue]
160 changeToken: ![row[@"changetoken"] isEqual: [NSNull null]] ?
161 [[NSData alloc] initWithBase64EncodedString: row[@"changetoken"] options:0] :
163 lastFetch: [row[@"lastfetch"] isEqual: [NSNull null]] ? nil : [dateFormat dateFromString: row[@"lastfetch"]]
164 encodedRateLimiter: [row[@"ratelimiter"] isEqual: [NSNull null]] ? nil : [[NSData alloc] initWithBase64EncodedString: row[@"ratelimiter"] options:0]