]> git.saurik.com Git - apple/security.git/blob - Analytics/SFAnalyticsSQLiteStore.m
Security-58286.230.21.tar.gz
[apple/security.git] / Analytics / SFAnalyticsSQLiteStore.m
1 /*
2 * Copyright (c) 2017 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 __OBJC2__
25
26 #import "SFAnalyticsSQLiteStore.h"
27 #import "SFAnalyticsDefines.h"
28 #import "debugging.h"
29
30 NSString* const SFAnalyticsColumnEventType = @"event_type";
31 NSString* const SFAnalyticsColumnDate = @"timestamp";
32 NSString* const SFAnalyticsColumnData = @"data";
33 NSString* const SFAnalyticsUploadDate = @"upload_date";
34
35 @implementation SFAnalyticsSQLiteStore
36
37 + (instancetype)storeWithPath:(NSString*)path schema:(NSString*)schema
38 {
39 if (![path length]) {
40 seccritical("Cannot init db with empty path");
41 return nil;
42 }
43 if (![schema length]) {
44 seccritical("Cannot init db without schema");
45 return nil;
46 }
47
48 SFAnalyticsSQLiteStore* store = nil;
49 @synchronized([SFAnalyticsSQLiteStore class]) {
50 static NSMutableDictionary* loggingStores = nil;
51 static dispatch_once_t onceToken;
52 dispatch_once(&onceToken, ^{
53 loggingStores = [[NSMutableDictionary alloc] init];
54 });
55
56 NSString* standardizedPath = path.stringByStandardizingPath;
57 store = loggingStores[standardizedPath];
58 if (!store) {
59 store = [[self alloc] initWithPath:standardizedPath schema:schema];
60 loggingStores[standardizedPath] = store;
61 }
62 NSError* error = nil;
63 if (![store openWithError:&error] && !(error && error.code == SQLITE_AUTH)) {
64 secerror("SFAnalytics: could not open db at init, will try again later. Error: %@", error);
65 }
66 }
67
68 return store;
69 }
70
71 - (void)dealloc
72 {
73 [self close];
74 }
75
76 - (BOOL)tryToOpenDatabase
77 {
78 if (!self.isOpen) {
79 NSError* error = nil;
80 if (![self openWithError:&error]) {
81 return NO;
82 }
83 secnotice("SFAnalytics", "successfully opened analytics db");
84 }
85 return YES;
86 }
87
88 - (NSInteger)successCountForEventType:(NSString*)eventType
89 {
90 if (![self tryToOpenDatabase]) {
91 return 0;
92 }
93 return [[[[self select:@[SFAnalyticsColumnSuccessCount] from:SFAnalyticsTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsColumnSuccessCount] integerValue];
94 }
95
96 - (void)incrementSuccessCountForEventType:(NSString*)eventType
97 {
98 if (![self tryToOpenDatabase]) {
99 return;
100 }
101 NSInteger successCount = [self successCountForEventType:eventType];
102 NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
103 NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
104 [self insertOrReplaceInto:SFAnalyticsTableSuccessCount values:@{SFAnalyticsColumnEventType : eventType, SFAnalyticsColumnSuccessCount : @(successCount + 1), SFAnalyticsColumnHardFailureCount : @(hardFailureCount), SFAnalyticsColumnSoftFailureCount : @(softFailureCount)}];
105 }
106
107 - (NSInteger)hardFailureCountForEventType:(NSString*)eventType
108 {
109 if (![self tryToOpenDatabase]) {
110 return 0;
111 }
112 return [[[[self select:@[SFAnalyticsColumnHardFailureCount] from:SFAnalyticsTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsColumnHardFailureCount] integerValue];
113 }
114
115 - (NSInteger)softFailureCountForEventType:(NSString*)eventType
116 {
117 if (![self tryToOpenDatabase]) {
118 return 0;
119 }
120 return [[[[self select:@[SFAnalyticsColumnSoftFailureCount] from:SFAnalyticsTableSuccessCount where:@"event_type = ?" bindings:@[eventType]] firstObject] valueForKey:SFAnalyticsColumnSoftFailureCount] integerValue];
121 }
122
123 - (void)incrementHardFailureCountForEventType:(NSString*)eventType
124 {
125 if (![self tryToOpenDatabase]) {
126 return;
127 }
128 NSInteger successCount = [self successCountForEventType:eventType];
129 NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
130 NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
131 [self insertOrReplaceInto:SFAnalyticsTableSuccessCount values:@{SFAnalyticsColumnEventType : eventType, SFAnalyticsColumnSuccessCount : @(successCount), SFAnalyticsColumnHardFailureCount : @(hardFailureCount + 1), SFAnalyticsColumnSoftFailureCount : @(softFailureCount)}];
132 }
133
134 - (void)incrementSoftFailureCountForEventType:(NSString*)eventType
135 {
136 if (![self tryToOpenDatabase]) {
137 return;
138 }
139 NSInteger successCount = [self successCountForEventType:eventType];
140 NSInteger hardFailureCount = [self hardFailureCountForEventType:eventType];
141 NSInteger softFailureCount = [self softFailureCountForEventType:eventType];
142 [self insertOrReplaceInto:SFAnalyticsTableSuccessCount values:@{SFAnalyticsColumnEventType : eventType, SFAnalyticsColumnSuccessCount : @(successCount), SFAnalyticsColumnHardFailureCount : @(hardFailureCount), SFAnalyticsColumnSoftFailureCount : @(softFailureCount + 1)}];
143 }
144
145 - (NSDictionary*)summaryCounts
146 {
147 if (![self tryToOpenDatabase]) {
148 return [NSDictionary new];
149 }
150 NSMutableDictionary* successCountsDict = [NSMutableDictionary dictionary];
151 NSArray* rows = [self selectAllFrom:SFAnalyticsTableSuccessCount where:nil bindings:nil];
152 for (NSDictionary* rowDict in rows) {
153 NSString* eventName = rowDict[SFAnalyticsColumnEventType];
154 if (!eventName) {
155 secinfo("SFAnalytics", "ignoring entry in success counts table without an event name");
156 continue;
157 }
158
159 successCountsDict[eventName] = @{SFAnalyticsTableSuccessCount : rowDict[SFAnalyticsColumnSuccessCount], SFAnalyticsColumnHardFailureCount : rowDict[SFAnalyticsColumnHardFailureCount], SFAnalyticsColumnSoftFailureCount : rowDict[SFAnalyticsColumnSoftFailureCount]};
160 }
161
162 return successCountsDict;
163 }
164
165 - (NSArray*)deserializedRecords:(NSArray*)recordBlobs
166 {
167 if (![self tryToOpenDatabase]) {
168 return [NSArray new];
169 }
170 NSMutableArray* records = [NSMutableArray new];
171 for (NSDictionary* row in recordBlobs) {
172 NSMutableDictionary* deserializedRecord = [NSPropertyListSerialization propertyListWithData:row[SFAnalyticsColumnData] options:NSPropertyListMutableContainers format:nil error:nil];
173 [records addObject:deserializedRecord];
174 }
175 return records;
176 }
177
178 - (NSArray*)hardFailures
179 {
180 if (![self tryToOpenDatabase]) {
181 return [NSArray new];
182 }
183 return [self deserializedRecords:[self select:@[SFAnalyticsColumnData] from:SFAnalyticsTableHardFailures]];
184 }
185
186 - (NSArray*)softFailures
187 {
188 if (![self tryToOpenDatabase]) {
189 return [NSArray new];
190 }
191 return [self deserializedRecords:[self select:@[SFAnalyticsColumnData] from:SFAnalyticsTableSoftFailures]];
192 }
193
194 - (NSArray*)allEvents
195 {
196 if (![self tryToOpenDatabase]) {
197 return [NSArray new];
198 }
199 return [self deserializedRecords:[self select:@[SFAnalyticsColumnData] from:SFAnalyticsTableAllEvents]];
200 }
201
202 - (NSArray*)samples
203 {
204 if (![self tryToOpenDatabase]) {
205 return [NSArray new];
206 }
207 return [self select:@[SFAnalyticsColumnSampleName, SFAnalyticsColumnSampleValue] from:SFAnalyticsTableSamples];
208 }
209
210 - (void)addEventDict:(NSDictionary*)eventDict toTable:(NSString*)table
211 {
212 if (![self tryToOpenDatabase]) {
213 return;
214 }
215 NSError* error = nil;
216 NSData* serializedRecord = [NSPropertyListSerialization dataWithPropertyList:eventDict format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
217 if(!error && serializedRecord) {
218 [self insertOrReplaceInto:table values:@{SFAnalyticsColumnDate : @([[NSDate date] timeIntervalSince1970]), SFAnalyticsColumnData : serializedRecord}];
219 }
220 if(error && !serializedRecord) {
221 secerror("Couldn't serialize failure record: %@", error);
222 }
223 }
224
225 - (void)addSample:(NSNumber*)value forName:(NSString*)name
226 {
227 if (![self tryToOpenDatabase]) {
228 return;
229 }
230 [self insertOrReplaceInto:SFAnalyticsTableSamples values:@{SFAnalyticsColumnDate : @([[NSDate date] timeIntervalSince1970]), SFAnalyticsColumnSampleName : name, SFAnalyticsColumnSampleValue : value}];
231 }
232
233 - (void)removeAllSamplesForName:(NSString*)name
234 {
235 if (![self tryToOpenDatabase]) {
236 return;
237 }
238 [self deleteFrom:SFAnalyticsTableSamples where:[NSString stringWithFormat:@"name == '%@'", name] bindings:nil];
239 }
240
241 - (NSDate*)uploadDate
242 {
243 if (![self tryToOpenDatabase]) {
244 return nil; // In other cases return default object but nil is better here to avoid entering the upload flow
245 }
246 return [self datePropertyForKey:SFAnalyticsUploadDate];
247 }
248
249 - (void)setUploadDate:(NSDate*)uploadDate
250 {
251 if (![self tryToOpenDatabase]) {
252 return;
253 }
254 [self setDateProperty:uploadDate forKey:SFAnalyticsUploadDate];
255 }
256
257 - (void)clearAllData
258 {
259 if (![self tryToOpenDatabase]) {
260 return;
261 }
262 [self deleteFrom:SFAnalyticsTableSuccessCount where:@"event_type like ?" bindings:@[@"%"]];
263 [self deleteFrom:SFAnalyticsTableHardFailures where:@"id >= 0" bindings:nil];
264 [self deleteFrom:SFAnalyticsTableSoftFailures where:@"id >= 0" bindings:nil];
265 [self deleteFrom:SFAnalyticsTableSamples where:@"id >= 0" bindings:nil];
266 [self deleteFrom:SFAnalyticsTableAllEvents where:@"id >= 0" bindings:nil];
267 }
268
269 @end
270
271 #endif // OBJC2