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"
39 #import "keychain/ckks/CKKSFixups.h"
42 @implementation CKKSZoneStateEntry
44 - (instancetype)initWithCKZone:(NSString*)ckzone
45 zoneCreated:(bool)ckzonecreated
46 zoneSubscribed:(bool)ckzonesubscribed
47 changeToken:(NSData*)changetoken
48 lastFetch:(NSDate*)lastFetch
49 lastFixup:(CKKSFixup)lastFixup
50 encodedRateLimiter:(NSData*)encodedRateLimiter
52 if(self = [super init]) {
54 _ckzonecreated = ckzonecreated;
55 _ckzonesubscribed = ckzonesubscribed;
56 _encodedChangeToken = changetoken;
57 _lastFetchTime = lastFetch;
58 _lastFixup = lastFixup;
60 self.encodedRateLimiter = encodedRateLimiter;
65 - (BOOL)isEqual: (id) object {
66 if(![object isKindOfClass:[CKKSZoneStateEntry class]]) {
70 CKKSZoneStateEntry* obj = (CKKSZoneStateEntry*) object;
72 return ([self.ckzone isEqualToString: obj.ckzone] &&
73 self.ckzonecreated == obj.ckzonecreated &&
74 self.ckzonesubscribed == obj.ckzonesubscribed &&
75 ((self.encodedChangeToken == nil && obj.encodedChangeToken == nil) || [self.encodedChangeToken isEqual: obj.encodedChangeToken]) &&
76 ((self.lastFetchTime == nil && obj.lastFetchTime == nil) || [self.lastFetchTime isEqualToDate: obj.lastFetchTime]) &&
77 ((self.rateLimiter == nil && obj.rateLimiter == nil) || [self.rateLimiter isEqual: obj.rateLimiter]) &&
78 self.lastFixup == obj.lastFixup &&
82 + (instancetype) state: (NSString*) ckzone {
84 CKKSZoneStateEntry* ret = [CKKSZoneStateEntry tryFromDatabase:ckzone error:&error];
87 secerror("CKKS: error fetching CKState(%@): %@", ckzone, error);
91 ret = [[CKKSZoneStateEntry alloc] initWithCKZone:ckzone
96 lastFixup:CKKSCurrentFixupNumber
97 encodedRateLimiter:nil];
102 - (CKServerChangeToken*) getChangeToken {
103 if(self.encodedChangeToken) {
104 NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:self.encodedChangeToken];
105 unarchiver.requiresSecureCoding = YES;
106 return [unarchiver decodeObjectOfClass:[CKServerChangeToken class] forKey:NSKeyedArchiveRootObjectKey];
112 - (void) setChangeToken: (CKServerChangeToken*) token {
113 self.encodedChangeToken = token ? [NSKeyedArchiver archivedDataWithRootObject:token] : nil;
116 - (NSData*)encodedRateLimiter {
117 if(self.rateLimiter == nil) {
120 return [NSKeyedArchiver archivedDataWithRootObject: self.rateLimiter];
123 - (void)setEncodedRateLimiter:(NSData *)encodedRateLimiter {
124 if(encodedRateLimiter == nil) {
125 self.rateLimiter = nil;
129 NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedRateLimiter];
130 unarchiver.requiresSecureCoding = YES;
131 self.rateLimiter = [unarchiver decodeObjectOfClass: [CKKSRateLimiter class] forKey:NSKeyedArchiveRootObjectKey];
134 #pragma mark - Database Operations
136 + (instancetype) fromDatabase: (NSString*) ckzone error: (NSError * __autoreleasing *) error {
137 return [self fromDatabaseWhere: @{@"ckzone": CKKSNilToNSNull(ckzone)} error: error];
140 + (instancetype) tryFromDatabase: (NSString*) ckzone error: (NSError * __autoreleasing *) error {
141 return [self tryFromDatabaseWhere: @{@"ckzone": CKKSNilToNSNull(ckzone)} error: error];
144 #pragma mark - CKKSSQLDatabaseObject methods
146 + (NSString*) sqlTable {
150 + (NSArray<NSString*>*) sqlColumns {
151 return @[@"ckzone", @"ckzonecreated", @"ckzonesubscribed", @"changetoken", @"lastfetch", @"ratelimiter", @"lastFixup"];
154 - (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
155 return @{@"ckzone": self.ckzone};
158 - (NSDictionary<NSString*,NSString*>*) sqlValues {
159 NSISO8601DateFormatter* dateFormat = [[NSISO8601DateFormatter alloc] init];
161 return @{@"ckzone": self.ckzone,
162 @"ckzonecreated": [NSNumber numberWithBool:self.ckzonecreated],
163 @"ckzonesubscribed": [NSNumber numberWithBool:self.ckzonesubscribed],
164 @"changetoken": CKKSNilToNSNull([self.encodedChangeToken base64EncodedStringWithOptions:0]),
165 @"lastfetch": CKKSNilToNSNull(self.lastFetchTime ? [dateFormat stringFromDate: self.lastFetchTime] : nil),
166 @"ratelimiter": CKKSNilToNSNull([self.encodedRateLimiter base64EncodedStringWithOptions:0]),
167 @"lastFixup": [NSNumber numberWithLong:self.lastFixup],
171 + (instancetype) fromDatabaseRow: (NSDictionary*) row {
172 NSISO8601DateFormatter* dateFormat = [[NSISO8601DateFormatter alloc] init];
174 return [[CKKSZoneStateEntry alloc] initWithCKZone: row[@"ckzone"]
175 zoneCreated: [row[@"ckzonecreated"] boolValue]
176 zoneSubscribed: [row[@"ckzonesubscribed"] boolValue]
177 changeToken: ![row[@"changetoken"] isEqual: [NSNull null]] ?
178 [[NSData alloc] initWithBase64EncodedString: row[@"changetoken"] options:0] :
180 lastFetch: [row[@"lastfetch"] isEqual: [NSNull null]] ? nil : [dateFormat dateFromString: row[@"lastfetch"]]
181 lastFixup:(CKKSFixup)[row[@"lastFixup"] integerValue]
182 encodedRateLimiter: [row[@"ratelimiter"] isEqual: [NSNull null]] ? nil : [[NSData alloc] initWithBase64EncodedString: row[@"ratelimiter"] options:0]