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