]>
Commit | Line | Data |
---|---|---|
866f8763 A |
1 | /* |
2 | * Copyright (c) 2016 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 | #if OCTAGON | |
25 | ||
26 | #import "keychain/ckks/CKKSAPSReceiver.h" | |
27 | #import "keychain/ckks/CKKSCondition.h" | |
28 | #import <CloudKit/CloudKit_Private.h> | |
29 | #include <utilities/debugging.h> | |
30 | ||
31 | @interface CKKSAPSReceiver() | |
32 | // If we receive >0 notifications for a CKRecordZoneID that hasn't been registered yet, give them a fake update when they register | |
33 | @property NSMutableSet<CKRecordZoneID*>* undeliveredUpdates; | |
34 | @end | |
35 | ||
36 | @implementation CKKSAPSReceiver | |
37 | ||
38 | + (instancetype)receiverForEnvironment:(NSString *)environmentName | |
39 | namedDelegatePort:(NSString*)namedDelegatePort | |
40 | apsConnectionClass:(Class<CKKSAPSConnection>)apsConnectionClass { | |
41 | static NSMutableDictionary<NSString*, CKKSAPSReceiver*>* environmentMap = nil; | |
42 | ||
43 | if(environmentName == nil) { | |
44 | secnotice("ckkspush", "No push environment; not bringing up APS."); | |
45 | return nil; | |
46 | } | |
47 | ||
48 | @synchronized([self class]) { | |
49 | if(environmentMap == nil) { | |
50 | environmentMap = [[NSMutableDictionary alloc] init]; | |
51 | } | |
52 | ||
53 | CKKSAPSReceiver* recv = [environmentMap valueForKey: environmentName]; | |
54 | ||
55 | if(recv == nil) { | |
56 | recv = [[CKKSAPSReceiver alloc] initWithEnvironmentName: environmentName namedDelegatePort:namedDelegatePort apsConnectionClass: apsConnectionClass]; | |
57 | [environmentMap setValue: recv forKey: environmentName]; | |
58 | } | |
59 | ||
60 | return recv; | |
61 | } | |
62 | } | |
63 | ||
64 | + (dispatch_queue_t)apsDeliveryQueue { | |
65 | static dispatch_queue_t aps_dispatch_queue; | |
66 | static dispatch_once_t onceToken; | |
67 | dispatch_once(&onceToken, ^{ | |
68 | aps_dispatch_queue = dispatch_queue_create("aps-callback-queue", DISPATCH_QUEUE_SERIAL); | |
69 | }); | |
70 | return aps_dispatch_queue; | |
71 | } | |
72 | ||
73 | - (instancetype)initWithEnvironmentName:(NSString*)environmentName | |
74 | namedDelegatePort:(NSString*)namedDelegatePort | |
75 | apsConnectionClass:(Class<CKKSAPSConnection>)apsConnectionClass { | |
76 | if(self = [super init]) { | |
77 | _apsConnectionClass = apsConnectionClass; | |
78 | _apsConnection = NULL; | |
79 | ||
80 | _undeliveredUpdates = [[NSMutableSet alloc] init]; | |
81 | ||
82 | // APS might be slow. This doesn't need to happen immediately, so let it happen later. | |
83 | __weak __typeof(self) weakSelf = self; | |
84 | dispatch_async([CKKSAPSReceiver apsDeliveryQueue], ^{ | |
85 | __strong __typeof(self) strongSelf = weakSelf; | |
86 | if(!strongSelf) { | |
87 | return; | |
88 | } | |
89 | strongSelf.apsConnection = [[strongSelf.apsConnectionClass alloc] initWithEnvironmentName:environmentName namedDelegatePort:namedDelegatePort queue:[CKKSAPSReceiver apsDeliveryQueue]]; | |
90 | strongSelf.apsConnection.delegate = strongSelf; | |
91 | ||
92 | // The following string should match: [[NSBundle mainBundle] bundleIdentifier] | |
93 | NSString* topic = [kCKPushTopicPrefix stringByAppendingString:@"com.apple.securityd"]; | |
94 | [strongSelf.apsConnection setEnabledTopics:@[topic]]; | |
95 | }); | |
96 | ||
97 | _zoneMap = [NSMapTable strongToWeakObjectsMapTable]; | |
98 | } | |
99 | return self; | |
100 | } | |
101 | ||
102 | - (CKKSCondition*)register:(id<CKKSZoneUpdateReceiver>)receiver forZoneID:(CKRecordZoneID *)zoneID { | |
103 | CKKSCondition* finished = [[CKKSCondition alloc] init]; | |
104 | ||
105 | __weak __typeof(self) weakSelf = self; | |
106 | dispatch_async([CKKSAPSReceiver apsDeliveryQueue], ^{ | |
107 | __strong __typeof(self) strongSelf = weakSelf; | |
108 | if(!strongSelf) { | |
109 | secerror("ckks: received registration for released CKKSAPSReceiver"); | |
110 | return; | |
111 | } | |
112 | ||
113 | [self.zoneMap setObject:receiver forKey: zoneID]; | |
114 | if([strongSelf.undeliveredUpdates containsObject:zoneID]) { | |
115 | [strongSelf.undeliveredUpdates removeObject:zoneID]; | |
116 | ||
117 | // Now, send the receiver its fake notification! | |
118 | secerror("ckks: sending fake push to newly-registered zone(%@): %@", zoneID, receiver); | |
119 | [receiver notifyZoneChange:nil]; | |
120 | } | |
121 | ||
122 | [finished fulfill]; | |
123 | }); | |
124 | ||
125 | return finished; | |
126 | } | |
127 | ||
128 | #pragma mark - APS Delegate callbacks | |
129 | ||
130 | - (void)connection:(APSConnection *)connection didReceivePublicToken:(NSData *)publicToken { | |
131 | // no-op. | |
132 | secnotice("ckkspush", "CKKSAPSDelegate initiated: %@", connection); | |
133 | } | |
134 | ||
135 | - (void)connection:(APSConnection *)connection didReceiveToken:(NSData *)token forTopic:(NSString *)topic identifier:(NSString *)identifier { | |
136 | secnotice("ckkspush", "Received per-topic push token \"%@\" for topic \"%@\" identifier \"%@\" on connection %@", token, topic, identifier, connection); | |
137 | } | |
138 | ||
139 | - (void)connection:(APSConnection *)connection didReceiveIncomingMessage:(APSIncomingMessage *)message { | |
140 | secnotice("ckkspush", "CKKSAPSDelegate received a message: %@ on connection %@", message, connection); | |
141 | ||
142 | CKNotification* notification = [CKNotification notificationFromRemoteNotificationDictionary:message.userInfo]; | |
143 | ||
144 | if(notification.notificationType == CKNotificationTypeRecordZone) { | |
145 | CKRecordZoneNotification* rznotification = (CKRecordZoneNotification*) notification; | |
146 | ||
147 | // Find receiever in map | |
148 | id<CKKSZoneUpdateReceiver> recv = [self.zoneMap objectForKey:rznotification.recordZoneID]; | |
149 | if(recv) { | |
150 | [recv notifyZoneChange:rznotification]; | |
151 | } else { | |
152 | secerror("ckks: received push for unregistered zone: %@", rznotification); | |
153 | if(rznotification.recordZoneID) { | |
154 | [self.undeliveredUpdates addObject: rznotification.recordZoneID]; | |
155 | } | |
156 | } | |
157 | } else { | |
158 | secerror("ckks: unexpected notification: %@", notification); | |
159 | } | |
160 | } | |
161 | ||
162 | @end | |
163 | ||
164 | #endif // OCTAGON |