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@
26 #include <AssertMacros.h>
28 #import <Foundation/Foundation.h>
30 #import "CKKSKeychainView.h"
32 #include <utilities/SecDb.h>
33 #include "keychain/securityd/SecDbItem.h"
34 #include "keychain/securityd/SecItemSchema.h"
37 #import <CloudKit/CloudKit.h>
38 #import "CKKSOutgoingQueueEntry.h"
39 #import "CKKSMirrorEntry.h"
42 @implementation CKKSMirrorEntry
44 -(instancetype)initWithCKKSItem:(CKKSItem*)item {
45 if((self = [super init])) {
52 -(instancetype)initWithCKRecord:(CKRecord*)record {
53 if((self = [super init])) {
54 _item = [[CKKSItem alloc] initWithCKRecord:record];
56 _wasCurrent = [record[SecCKRecordServerWasCurrent] unsignedLongLongValue];
61 - (NSString*)description {
62 return [NSString stringWithFormat: @"<%@(%@): %@>",
63 NSStringFromClass([self class]),
64 self.item.zoneID.zoneName,
68 -(void)setFromCKRecord: (CKRecord*) record {
69 [self.item setFromCKRecord: record];
70 _wasCurrent = [record[SecCKRecordServerWasCurrent] unsignedLongLongValue];
73 - (bool)matchesCKRecord: (CKRecord*) record {
74 bool matches = [self.item matchesCKRecord: record];
78 // Why is obj-c nullable equality so difficult?
79 if(!((record[SecCKRecordServerWasCurrent] == nil && self.wasCurrent == 0) ||
80 [record[SecCKRecordServerWasCurrent] isEqual: [NSNumber numberWithUnsignedLongLong:self.wasCurrent]])) {
81 secinfo("ckksitem", "was_current does not match");
88 #pragma mark - Database Operations
90 + (instancetype) fromDatabase: (NSString*) uuid zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
91 return [self fromDatabaseWhere: @{@"UUID": CKKSNilToNSNull(uuid), @"ckzone":CKKSNilToNSNull(zoneID.zoneName)} error: error];
94 + (instancetype) tryFromDatabase: (NSString*) uuid zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
95 return [self tryFromDatabaseWhere: @{@"UUID": CKKSNilToNSNull(uuid), @"ckzone":CKKSNilToNSNull(zoneID.zoneName)} error: error];
98 #pragma mark - Property access to underlying CKKSItem
101 return self.item.uuid;
104 -(void)setUuid:(NSString *)uuid {
105 self.item.uuid = uuid;
108 #pragma mark - CKKSSQLDatabaseObject methods
110 + (NSString*) sqlTable {
114 + (NSArray<NSString*>*)sqlColumns {
115 return [[CKKSItem sqlColumns] arrayByAddingObjectsFromArray: @[@"wascurrent"]];
118 - (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
119 return [self.item whereClauseToFindSelf];
122 - (NSDictionary<NSString*,NSString*>*)sqlValues {
123 NSMutableDictionary* values = [[self.item sqlValues] mutableCopy];
124 values[@"wascurrent"] = [[NSNumber numberWithUnsignedLongLong:self.wasCurrent] stringValue];
128 + (instancetype)fromDatabaseRow:(NSDictionary<NSString*, CKKSSQLResult*>*)row {
129 CKKSMirrorEntry* ckme = [[CKKSMirrorEntry alloc] initWithCKKSItem: [CKKSItem fromDatabaseRow:row]];
131 // This appears to be the best way to get an unsigned long long out of a string.
132 ckme.wasCurrent = [[[[NSNumberFormatter alloc] init] numberFromString:row[@"wascurrent"].asString] unsignedLongLongValue];
136 + (NSDictionary<NSString*,NSNumber*>*)countsByParentKey:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
137 NSMutableDictionary* results = [[NSMutableDictionary alloc] init];
139 [CKKSSQLDatabaseObject queryDatabaseTable: [[self class] sqlTable]
140 where: @{@"ckzone": CKKSNilToNSNull(zoneID.zoneName)}
141 columns: @[@"parentKeyUUID", @"count(rowid)"]
142 groupBy: @[@"parentKeyUUID"]
145 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
146 results[row[@"parentKeyUUID"].asString] = row[@"count(rowid)"].asNSNumberInteger;
152 + (NSNumber*)counts:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
153 __block NSNumber *result = nil;
155 [CKKSSQLDatabaseObject queryDatabaseTable: [[self class] sqlTable]
156 where: @{@"ckzone": CKKSNilToNSNull(zoneID.zoneName)}
157 columns: @[@"count(rowid)"]
161 processRow: ^(NSDictionary<NSString*, CKKSSQLResult*>* row) {
162 result = row[@"count(rowid)"].asNSNumberInteger;