Ta android + ios

This commit is contained in:
LYP
2024-08-07 20:22:28 +08:00
parent 69fee08c61
commit 76458664d8
722 changed files with 41709 additions and 1 deletions

View File

@@ -0,0 +1,27 @@
#import <Foundation/Foundation.h>
#import "ThinkingAnalyticsSDKPrivate.h"
NS_ASSUME_NONNULL_BEGIN
typedef void (^TDFlushConfigBlock)(NSDictionary *result, NSError * _Nullable error);
@interface TANetwork : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
@property (nonatomic, copy) NSString *appid;
@property (nonatomic, strong) NSURL *serverURL;
@property (nonatomic, strong) NSURL *serverDebugURL;
@property (nonatomic, assign) ThinkingAnalyticsDebugMode debugMode;
@property (nonatomic, strong) TDSecurityPolicy *securityPolicy;
@property (nonatomic, copy) TDURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
- (BOOL)flushEvents:(NSArray<NSDictionary *> *)events;
//- (void)flushEvents:(NSArray<NSDictionary *> *)recordArray completion:(nullable void(^)(BOOL))completion;
- (void)fetchRemoteConfig:(NSString *)appid handler:(TDFlushConfigBlock)handler;
- (int)flushDebugEvents:(NSDictionary *)record withAppid:(NSString *)appid;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 43b25c6a08dffb94091064201bb4bbfc
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,309 @@
#import "TANetwork.h"
#import "NSData+TDGzip.h"
#import "TDJSONUtil.h"
#import "TDLogging.h"
#import "TDSecurityPolicy.h"
#import "TDAppState.h"
#if TARGET_OS_IOS
#import "TDToastView.h"
#endif
static NSString *kTAIntegrationType = @"TA-Integration-Type";
static NSString *kTAIntegrationVersion = @"TA-Integration-Version";
static NSString *kTAIntegrationCount = @"TA-Integration-Count";
static NSString *kTAIntegrationExtra = @"TA-Integration-Extra";
static NSString *kTADatasType = @"TA-Datas-Type";
@implementation TANetwork
- (NSURLSession *)sharedURLSession {
static NSURLSession *sharedSession = nil;
@synchronized(self) {
if (sharedSession == nil) {
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sharedSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
}
}
return sharedSession;
}
- (NSString *)URLEncode:(NSString *)string {
NSString *encodedString = [string stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@"?!@#$^&%*+,:;='\"`<>()[]{}/\\| "] invertedSet]];
return encodedString;
}
- (int)flushDebugEvents:(NSDictionary *)record withAppid:(NSString *)appid {
__block int debugResult = -1;
NSMutableDictionary *recordDic = [record mutableCopy];
NSMutableDictionary *properties = [[recordDic objectForKey:@"properties"] mutableCopy];
if ([ThinkingAnalyticsSDK isTrackEvent:[record objectForKey:@"#type"]]) {
@synchronized ([TDDeviceInfo sharedManager]) {
[properties addEntriesFromDictionary:[[TDDeviceInfo sharedManager] getAutomaticData]];
}
}
[recordDic setObject:properties forKey:@"properties"];
NSString *jsonString = [TDJSONUtil JSONStringForObject:recordDic];
NSMutableURLRequest *request = [self buildDebugRequestWithJSONString:jsonString withAppid:appid withDeviceId:[[[TDDeviceInfo sharedManager] getAutomaticData] objectForKey:@"#device_id"]];
dispatch_semaphore_t flushSem = dispatch_semaphore_create(0);
void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) {
if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) {
debugResult = -2;
TDLogError(@"Debug Networking error:%@", error);
[self callbackNetworkErrorWithRequest:jsonString error:error.debugDescription];
dispatch_semaphore_signal(flushSem);
return;
}
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *)response;
if ([urlResponse statusCode] == 200) {
NSError *err;
if (!data) {
return;
}
NSDictionary *retDic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err];
if (err) {
TDLogError(@"Debug data json error:%@", err);
debugResult = -2;
} else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:1]]) {
debugResult = 1;
NSArray* errorProperties = [retDic objectForKey:@"errorProperties"];
NSMutableString *errorStr = [NSMutableString string];
for (id obj in errorProperties) {
NSString *errorReasons = [obj objectForKey:@"errorReason"];
NSString *propertyName = [obj objectForKey:@"propertyName"];
[errorStr appendFormat:@" propertyName:%@ errorReasons:%@\n", propertyName, errorReasons];
}
TDLogError(@"Debug data error:%@", errorStr);
} else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:2]]) {
debugResult = 2;
NSString *errorReasons = [[retDic objectForKey:@"errorReasons"] componentsJoinedByString:@" "];
TDLogError(@"Debug data error:%@", errorReasons);
} else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:0]]) {
debugResult = 0;
TDLogDebug(@"Verify data success.");
} else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:-1]]) {
debugResult = -1;
NSString *errorReasons = [[retDic objectForKey:@"errorReasons"] componentsJoinedByString:@" "];
TDLogError(@"Debug mode error:%@", errorReasons);
}
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (debugResult == 0 || debugResult == 1 || debugResult == 2) {
#if TARGET_OS_IOS
dispatch_async(dispatch_get_main_queue(), ^{
UIApplication *application = [TDAppState sharedApplication];
if (![application isKindOfClass:UIApplication.class]) {
return;
}
UIWindow *window = application.keyWindow;
[TDToastView showInWindow:window text:[NSString stringWithFormat:@"The current mode is:%@", self.debugMode == ThinkingAnalyticsDebugOnly ? @"DebugOnly(Data is not persisted) \n The test joint debugging stage is allowed to open \n Please turn off the Debug function before the official launch" : @"Debug"] duration:2.0];
});
#endif
}
});
@try {
if ([retDic isKindOfClass:[NSDictionary class]]) {
if ([[(NSDictionary *)retDic objectForKey:@"errorLevel"] integerValue] != 0) {
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:retDic options:NSJSONWritingPrettyPrinted error:NULL];
NSString *string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[self callbackNetworkErrorWithRequest:jsonString error:string];
}
}
} @catch (NSException *exception) {
}
} else {
debugResult = -2;
NSString *urlResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
TDLogError(@"%@", [NSString stringWithFormat:@"Debug %@ network failed with response '%@'.", self, urlResponse]);
[self callbackNetworkErrorWithRequest:jsonString error:urlResponse];
}
dispatch_semaphore_signal(flushSem);
};
NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block];
[task resume];
dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER);
return debugResult;
}
- (BOOL)flushEvents:(NSArray<NSDictionary *> *)recordArray {
__block BOOL flushSucc = YES;
UInt64 time = [[NSDate date] timeIntervalSince1970] * 1000;
NSDictionary *flushDic = @{
@"data": recordArray,
@"#app_id": self.appid,
@"#flush_time": @(time),
};
__block BOOL isEncrypt;
[recordArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.allKeys containsObject:@"ekey"]) {
isEncrypt = YES;
*stop = YES;
}
}];
NSString *jsonString = [TDJSONUtil JSONStringForObject:flushDic];
NSMutableURLRequest *request = [self buildRequestWithJSONString:jsonString];
[request addValue:[TDDeviceInfo sharedManager].libName forHTTPHeaderField:kTAIntegrationType];
[request addValue:[TDDeviceInfo sharedManager].libVersion forHTTPHeaderField:kTAIntegrationVersion];
[request addValue:@(recordArray.count).stringValue forHTTPHeaderField:kTAIntegrationCount];
[request addValue:@"iOS" forHTTPHeaderField:kTAIntegrationExtra];
if (isEncrypt) {
[request addValue:@"1" forHTTPHeaderField:kTADatasType];
}
// [request addValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"];
// [request addValue:@"timeout=15,max=100" forHTTPHeaderField:@"Keep-Alive"];
dispatch_semaphore_t flushSem = dispatch_semaphore_create(0);
void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) {
if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) {
flushSucc = NO;
TDLogError(@"Networking error:%@", error);
[self callbackNetworkErrorWithRequest:jsonString error:error.debugDescription];
dispatch_semaphore_signal(flushSem);
return;
}
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *)response;
if ([urlResponse statusCode] == 200) {
flushSucc = YES;
TDLogDebug(@"flush success sendContent---->:%@",flushDic);
if (!data) {
return;
}
id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
TDLogDebug(@"flush success responseData---->%@",result);
@try {
if ([result isKindOfClass:[NSDictionary class]]) {
if ([[(NSDictionary *)result objectForKey:@"code"] integerValue] != 0) {
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:NULL];
NSString *string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[self callbackNetworkErrorWithRequest:jsonString error:string];
}
}
} @catch (NSException *exception) {
}
} else {
flushSucc = NO;
NSString *urlResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
TDLogError(@"%@", [NSString stringWithFormat:@"%@ network failed with response '%@'.", self, urlResponse]);
[self callbackNetworkErrorWithRequest:jsonString error:urlResponse];
}
dispatch_semaphore_signal(flushSem);
};
NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block];
[task resume];
dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER);
return flushSucc;
}
- (void)callbackNetworkErrorWithRequest:(NSString *)request error:(NSString *)error {
if (request == nil && error == nil) return;
ThinkingAnalyticsSDK *tdSDK = [ThinkingAnalyticsSDK sharedInstanceWithAppid:self.appid];
if (tdSDK.errorCallback) {
NSInteger code = 10001;
NSString *errorMsg = error;
NSString *ext = request;
tdSDK.errorCallback(code, errorMsg, ext);
}
}
- (NSMutableURLRequest *)buildRequestWithJSONString:(NSString *)jsonString {
NSData *zippedData = [NSData td_gzipData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
NSString *postBody = [zippedData base64EncodedStringWithOptions:0];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverURL];
// NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.20.23:8991/sync"]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]];
NSString *contentType = [NSString stringWithFormat:@"text/plain"];
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];
[request setTimeoutInterval:60.0];
return request;
}
- (NSMutableURLRequest *)buildDebugRequestWithJSONString:(NSString *)jsonString withAppid:(NSString *)appid withDeviceId:(NSString *)deviceId {
// dryRun=0, if the verification is passed, it will be put into storage. dryRun=1, no storage
int dryRun = _debugMode == ThinkingAnalyticsDebugOnly ? 1 : 0;
NSString *postData = [NSString stringWithFormat:@"appid=%@&source=client&dryRun=%d&deviceId=%@&data=%@", appid, dryRun, deviceId, [self URLEncode:jsonString]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverDebugURL];
[request setHTTPMethod:@"POST"];
request.HTTPBody = [postData dataUsingEncoding:NSUTF8StringEncoding];
return request;
}
- (void)fetchRemoteConfig:(NSString *)appid handler:(TDFlushConfigBlock)handler {
void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) {
if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) {
TDLogError(@"Fetch remote config network failed:%@", error);
return;
}
NSError *err;
if (!data) {
return;
}
NSDictionary *ret = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err];
if (err) {
TDLogError(@"Fetch remote config json error:%@", err);
} else if ([ret isKindOfClass:[NSDictionary class]] && [ret[@"code"] isEqualToNumber:[NSNumber numberWithInt:0]]) {
TDLogDebug(@"Fetch remote config for %@ : %@", appid, [ret objectForKey:@"data"]);
handler([ret objectForKey:@"data"], error);
} else {
TDLogError(@"Fetch remote config failed");
}
};
NSString *urlStr = [NSString stringWithFormat:@"%@?appid=%@", self.serverURL, appid];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
[request setHTTPMethod:@"Get"];
NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block];
[task resume];
}
#pragma mark - NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
@end

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: d2b161a17028d0d419e63bf51c436166
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
//
// TAReachability.h
// ThinkingSDK
//
// Created by Yangxiongon 2022/6/1.
//
#import <Foundation/Foundation.h>
#import "TDConstant.h"
NS_ASSUME_NONNULL_BEGIN
@interface TAReachability : NSObject
+ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType;
+ (instancetype)shareInstance;
- (void)startMonitoring;
- (void)stopMonitoring;
- (NSString *)networkState;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 3bf8629734a44364eb0dd013447864dc
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,192 @@
//
// TAReachability.m
// ThinkingSDK
//
// Created by Yangxiongon 2022/6/1.
//
#import "TAReachability.h"
#import <SystemConfiguration/SystemConfiguration.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#if __has_include(<ThinkingSDK/TDLogging.h>)
#import <ThinkingSDK/TDLogging.h>
#else
#import "TDLogging.h"
#endif
@interface TAReachability ()
#if TARGET_OS_IOS
@property (atomic, assign) SCNetworkReachabilityRef reachability;
#endif
@property (nonatomic, assign) BOOL isWifi;
@property (nonatomic, assign) BOOL isWwan;
@end
@implementation TAReachability
#if TARGET_OS_IOS
static void ThinkingReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
TAReachability *instance = (__bridge TAReachability *)info;
if (instance && [instance isKindOfClass:[TAReachability class]]) {
[instance reachabilityChanged:flags];
}
}
#endif
//MARK: - Public Methods
+ (instancetype)shareInstance {
static dispatch_once_t onceToken;
static TAReachability *reachability = nil;
dispatch_once(&onceToken, ^{
reachability = [[TAReachability alloc] init];
});
return reachability;
}
#if TARGET_OS_IOS
- (NSString *)networkState {
if (self.isWifi) {
return @"WIFI";
} else if (self.isWwan) {
return [self currentRadio];
} else {
return @"NULL";
}
}
- (void)startMonitoring {
[self stopMonitoring];
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL,"thinkingdata.cn");
self.reachability = reachability;
if (self.reachability != NULL) {
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(self.reachability, &flags);
if (didRetrieveFlags) {
self.isWifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN);
self.isWwan = (flags & kSCNetworkReachabilityFlagsIsWWAN);
}
SCNetworkReachabilityContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
if (SCNetworkReachabilitySetCallback(self.reachability, ThinkingReachabilityCallback, &context)) {
if (!SCNetworkReachabilityScheduleWithRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes)) {
SCNetworkReachabilitySetCallback(self.reachability, NULL, NULL);
}
}
}
}
- (void)stopMonitoring {
if (!self.reachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
+ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType {
if ([@"NULL" isEqualToString:networkType]) {
return ThinkingNetworkTypeALL;
} else if ([@"WIFI" isEqualToString:networkType]) {
return ThinkingNetworkTypeWIFI;
} else if ([@"2G" isEqualToString:networkType]) {
return ThinkingNetworkType2G;
} else if ([@"3G" isEqualToString:networkType]) {
return ThinkingNetworkType3G;
} else if ([@"4G" isEqualToString:networkType]) {
return ThinkingNetworkType4G;
}else if([@"5G"isEqualToString:networkType])
{
return ThinkingNetworkType5G;
}
return ThinkingNetworkTypeNONE;
}
//MARK: - Private Methods
- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags {
self.isWifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN);
self.isWwan = (flags & kSCNetworkReachabilityFlagsIsWWAN);
}
- (NSString *)currentRadio {
NSString *networkType = @"NULL";
@try {
static CTTelephonyNetworkInfo *info = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
info = [[CTTelephonyNetworkInfo alloc] init];
});
NSString *currentRadio = nil;
#ifdef __IPHONE_12_0
if (@available(iOS 12.0, *)) {
NSDictionary *serviceCurrentRadio = [info serviceCurrentRadioAccessTechnology];
if ([serviceCurrentRadio isKindOfClass:[NSDictionary class]] && serviceCurrentRadio.allValues.count>0) {
currentRadio = serviceCurrentRadio.allValues[0];
}
}
#endif
if (currentRadio == nil && [info.currentRadioAccessTechnology isKindOfClass:[NSString class]]) {
currentRadio = info.currentRadioAccessTechnology;
}
if ([currentRadio isEqualToString:CTRadioAccessTechnologyLTE]) {
networkType = @"4G";
} else if ([currentRadio isEqualToString:CTRadioAccessTechnologyeHRPD] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyCDMA1x] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyHSUPA] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyHSDPA] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyWCDMA]) {
networkType = @"3G";
} else if ([currentRadio isEqualToString:CTRadioAccessTechnologyEdge] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyGPRS]) {
networkType = @"2G";
}
#ifdef __IPHONE_14_1
else if (@available(iOS 14.1, *)) {
if ([currentRadio isKindOfClass:[NSString class]]) {
if([currentRadio isEqualToString:CTRadioAccessTechnologyNRNSA] ||
[currentRadio isEqualToString:CTRadioAccessTechnologyNR]) {
networkType = @"5G";
}
}
}
#endif
} @catch (NSException *exception) {
TDLogError(@"%@: %@", self, exception);
}
return networkType;
}
#elif TARGET_OS_OSX
+ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType {
return ThinkingNetworkTypeWIFI;
}
- (void)startMonitoring {
}
- (void)stopMonitoring {
}
- (NSString *)currentRadio {
return @"WIFI";
}
- (NSString *)networkState {
return @"WIFI";
}
#endif
@end

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 166a2da05be55d24899e432265dcb5af
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
/**
Thinks AFNetworking: https://github.com/AFNetworking/AFNetworking
*/
#import <Foundation/Foundation.h>
#if __has_include(<ThinkingSDK/TDConstant.h>)
#import <ThinkingSDK/TDConstant.h>
#else
#import "TDConstant.h"
#endif
NS_ASSUME_NONNULL_BEGIN
@interface TDSecurityPolicy: NSObject<NSCopying>
@property (nonatomic, assign) BOOL allowInvalidCertificates;
@property (nonatomic, assign) BOOL validatesDomainName;
@property (nonatomic, copy) TDURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
+ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode;
+ (instancetype)defaultPolicy;
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain;
@end
#ifndef __Require_Quiet
#define __Require_Quiet(assertion, exceptionLabel) \
do \
{ \
if ( __builtin_expect(!(assertion), 0) ) \
{ \
goto exceptionLabel; \
} \
} while ( 0 )
#endif
#ifndef __Require_noErr_Quiet
#define __Require_noErr_Quiet(errorCode, exceptionLabel) \
do \
{ \
if ( __builtin_expect(0 != (errorCode), 0) ) \
{ \
goto exceptionLabel; \
} \
} while ( 0 )
#endif
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: b6067047a0ad2a043a6724e06a1e7a16
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,248 @@
#import "TDSecurityPolicy.h"
#import "TDLogging.h"
static id TDPublicKeyForCertificate(NSData *certificate) {
id allowedPublicKey = nil;
SecCertificateRef allowedCertificate;
SecPolicyRef policy = nil;
SecTrustRef allowedTrust = nil;
SecTrustResultType result;
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
__Require_Quiet(allowedCertificate != NULL, _out);
policy = SecPolicyCreateBasicX509();
__Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
_out:
if (allowedTrust) {
CFRelease(allowedTrust);
}
if (policy) {
CFRelease(policy);
}
if (allowedCertificate) {
CFRelease(allowedCertificate);
}
return allowedPublicKey;
}
static BOOL TDServerTrustIsValid(SecTrustRef serverTrust) {
BOOL isValid = NO;
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
_out:
return isValid;
}
static NSArray * TDCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
[trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
}
return [NSArray arrayWithArray:trustChain];
}
static NSArray * TDPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
SecPolicyRef policy = SecPolicyCreateBasicX509();
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
SecCertificateRef someCertificates[] = {certificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
SecTrustRef trust;
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
_out:
if (trust) {
CFRelease(trust);
}
if (certificates) {
CFRelease(certificates);
}
continue;
}
CFRelease(policy);
return [NSArray arrayWithArray:trustChain];
}
static BOOL TDSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
return [(__bridge id)key1 isEqual:(__bridge id)key2];
}
@interface TDSecurityPolicy ()
@property (nonatomic, assign) TDSSLPinningMode SSLPinningMode;
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;
@end
@implementation TDSecurityPolicy
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.validatesDomainName = YES;
return self;
}
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
return [NSSet setWithSet:certificates];
}
+ (NSSet *)defaultPinnedCertificates {
static NSSet *_defaultPinnedCertificates = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
});
return _defaultPinnedCertificates;
}
+ (instancetype)defaultPolicy {
TDSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = TDSSLPinningModeNone;
return securityPolicy;
}
+ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode {
return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}
+ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
TDSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = pinningMode;
[securityPolicy setPinnedCertificates:pinnedCertificates];
return securityPolicy;
}
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;
if (self.pinnedCertificates) {
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
for (NSData *certificate in self.pinnedCertificates) {
id publicKey = TDPublicKeyForCertificate(certificate);
if (publicKey) {
[mutablePinnedPublicKeys addObject:publicKey];
}
}
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == TDSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
TDLogDebug(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == TDSSLPinningModeNone) {
return self.allowInvalidCertificates || TDServerTrustIsValid(serverTrust);
} else if (!TDServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
switch (self.SSLPinningMode) {
case TDSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!TDServerTrustIsValid(serverTrust)) {
return NO;
}
NSArray *serverCertificates = TDCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case TDSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = TDPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (TDSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
default:
return NO;
}
return NO;
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone {
TDSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
securityPolicy.SSLPinningMode = self.SSLPinningMode;
securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates;
securityPolicy.validatesDomainName = self.validatesDomainName;
securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone];
return securityPolicy;
}
@end

View File

@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 9f66440e92e611647afe05768069d1fd
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
userData:
assetBundleName:
assetBundleVariant: