diff --git a/include/mbgl/ios/MGLMapboxEvents.h b/include/mbgl/ios/MGLMapboxEvents.h index b68b9b1b3..2c6f8151b 100644 --- a/include/mbgl/ios/MGLMapboxEvents.h +++ b/include/mbgl/ios/MGLMapboxEvents.h @@ -25,7 +25,7 @@ extern NSString *const MGLEventGesturePanStart; extern NSString *const MGLEventGesturePinchStart; extern NSString *const MGLEventGestureRotateStart; -@interface MGLMapboxEvents : NSObject +@interface MGLMapboxEvents : NSObject // You must call these methods from the main thread. // diff --git a/platform/ios/MGLMapboxEvents.m b/platform/ios/MGLMapboxEvents.m index e4014d494..4b010463d 100644 --- a/platform/ios/MGLMapboxEvents.m +++ b/platform/ios/MGLMapboxEvents.m @@ -73,6 +73,7 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; @property (atomic) NSUInteger flushAt; @property (atomic) NSDateFormatter *rfc3339DateFormatter; @property (atomic) CGFloat scale; +@property (atomic) NSData *geoTrustCert; // The isPaused state tracker is only ever accessed from the main thread. @@ -132,11 +133,29 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; _serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); // Configure Events Infrastructure + // =============================== + + // Load Local Copy of Server's Public Key + NSString *cerPath = nil; + NSArray *bundles = [NSBundle allFrameworks]; + for (int lc = 0; lc < bundles.count; lc++) { + NSBundle *b = [bundles objectAtIndex:lc]; + cerPath = [[NSBundle mainBundle] pathForResource:@"api_mapbox_com-geotrust" ofType:@"der"]; + if (cerPath != nil) { + break; + } + } + if (cerPath != nil) { + _geoTrustCert = [NSData dataWithContentsOfFile:cerPath]; + } + + // Events Control _eventQueue = [[NSMutableArray alloc] init]; _flushAt = 20; _flushAfter = 60; _token = nil; _instanceID = [[NSUUID UUID] UUIDString]; + // Dynamic detection of ASIdentifierManager from Mixpanel // https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE _advertiserId = @""; @@ -382,10 +401,13 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; __weak MGLMapboxEvents *weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + MGLMapboxEvents *strongSelf = weakSelf; + if (!strongSelf) return; + // Setup URL Request - NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", MGLMapboxEventsAPIBase, weakSelf.token]; + NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", MGLMapboxEventsAPIBase, strongSelf.token]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]]; - [request setValue:[weakSelf getUserAgent] forHTTPHeaderField:@"User-Agent"]; + [request setValue:[strongSelf getUserAgent] forHTTPHeaderField:@"User-Agent"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setHTTPMethod:@"POST"]; @@ -395,9 +417,9 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; [request setHTTPBody:jsonData]; // Send non blocking HTTP Request to server - [NSURLConnection sendAsynchronousRequest:request - queue:nil - completionHandler:nil]; + NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:strongSelf startImmediately:NO]; + [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + [connection start]; } }); } @@ -638,4 +660,48 @@ NSString *const MGLEventGestureRotateStart = @"Rotation"; return result; } +#pragma mark NSURLConnectionDelegate +- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { + + if (_geoTrustCert == nil) { + [[challenge sender] cancelAuthenticationChallenge:challenge]; + return; + } + + // Get Server's Public Key + SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; + + SecTrustResultType trustResult; + SecTrustEvaluate(serverTrust, &trustResult); + switch (trustResult) { + case kSecTrustResultProceed: + break; + case kSecTrustResultUnspecified: + break; + default: + [[challenge sender] cancelAuthenticationChallenge:challenge]; + return; + } + + long numKeys = SecTrustGetCertificateCount(serverTrust); + + BOOL found = false; + for (int lc = 0; lc < numKeys; lc++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc); + NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate)); + + // Compare Remote Key With Local Version + if ([remoteCertificateData isEqualToData:_geoTrustCert]) { + NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + found = true; + break; + } + } + + if (!found) { + [[challenge sender] cancelAuthenticationChallenge:challenge]; + } +} + @end diff --git a/platform/ios/resources/api_mapbox_com-geotrust.der b/platform/ios/resources/api_mapbox_com-geotrust.der new file mode 100644 index 000000000..116144bc0 Binary files /dev/null and b/platform/ios/resources/api_mapbox_com-geotrust.der differ