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 "CKKSOutgoingQueueEntry.h"
38 #import "CKKSMirrorEntry.h"
41 @implementation CKKSMirrorEntry
43 -(instancetype)initWithCKKSItem:(CKKSItem*)item {
44 if((self = [super init])) {
51 -(instancetype)initWithCKRecord:(CKRecord*)record {
52 if((self = [super init])) {
53 _item = [[CKKSItem alloc] initWithCKRecord:record];
55 _wasCurrent = [record[SecCKRecordServerWasCurrent] unsignedLongLongValue];
60 - (NSString*)description {
61 return [NSString stringWithFormat: @"<%@(%@): %@>",
62 NSStringFromClass([self class]),
63 self.item.zoneID.zoneName,
67 -(void)setFromCKRecord: (CKRecord*) record {
68 [self.item setFromCKRecord: record];
69 _wasCurrent = [record[SecCKRecordServerWasCurrent] unsignedLongLongValue];
72 - (bool)matchesCKRecord: (CKRecord*) record {
73 bool matches = [self.item matchesCKRecord: record];
77 // Why is obj-c nullable equality so difficult?
78 if(!((record[SecCKRecordServerWasCurrent] == nil && self.wasCurrent == 0) ||
79 [record[SecCKRecordServerWasCurrent] isEqual: [NSNumber numberWithUnsignedLongLong:self.wasCurrent]])) {
80 secinfo("ckksitem", "was_current does not match");
87 #pragma mark - Database Operations
89 + (instancetype) fromDatabase: (NSString*) uuid zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
90 return [self fromDatabaseWhere: @{@"UUID": CKKSNilToNSNull(uuid), @"ckzone":CKKSNilToNSNull(zoneID.zoneName)} error: error];
93 + (instancetype) tryFromDatabase: (NSString*) uuid zoneID:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
94 return [self tryFromDatabaseWhere: @{@"UUID": CKKSNilToNSNull(uuid), @"ckzone":CKKSNilToNSNull(zoneID.zoneName)} error: error];
97 #pragma mark - Property access to underlying CKKSItem
100 return self.item.uuid;
103 -(void)setUuid:(NSString *)uuid {
104 self.item.uuid = uuid;
107 #pragma mark - CKKSSQLDatabaseObject methods
109 + (NSString*) sqlTable {
113 + (NSArray<NSString*>*)sqlColumns {
114 return [[CKKSItem sqlColumns] arrayByAddingObjectsFromArray: @[@"wascurrent"]];
117 - (NSDictionary<NSString*,NSString*>*) whereClauseToFindSelf {
118 return [self.item whereClauseToFindSelf];
121 - (NSDictionary<NSString*,NSString*>*)sqlValues {
122 NSMutableDictionary* values = [[self.item sqlValues] mutableCopy];
123 values[@"wascurrent"] = [NSNumber numberWithUnsignedLongLong:self.wasCurrent];
127 + (instancetype) fromDatabaseRow: (NSDictionary*) row {
128 CKKSMirrorEntry* ckme = [[CKKSMirrorEntry alloc] initWithCKKSItem: [CKKSItem fromDatabaseRow:row]];
130 // This appears to be the best way to get an unsigned long long out of a string.
131 ckme.wasCurrent = [[[[NSNumberFormatter alloc] init] numberFromString:CKKSNSNullToNil(row[@"wascurrent"])] unsignedLongLongValue];
135 + (NSDictionary<NSString*,NSNumber*>*)countsByParentKey:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
136 NSMutableDictionary* results = [[NSMutableDictionary alloc] init];
138 [CKKSSQLDatabaseObject queryDatabaseTable: [[self class] sqlTable]
139 where: @{@"ckzone": CKKSNilToNSNull(zoneID.zoneName)}
140 columns: @[@"parentKeyUUID", @"count(rowid)"]
141 groupBy: @[@"parentKeyUUID"]
144 processRow: ^(NSDictionary* row) {
145 results[row[@"parentKeyUUID"]] = [NSNumber numberWithInteger: [row[@"count(rowid)"] integerValue]];