Skip to content

Commit

Permalink
MM-15216 Fix crash when cookie does not set expiration date on iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
enahum committed Apr 18, 2019
1 parent 689ef9c commit 9ba81fb
Show file tree
Hide file tree
Showing 2 changed files with 277 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ post-install:
@cp ./native_modules/RNCWebViewManager.java node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
@cp ./native_modules/RNCWKWebView.m node_modules/react-native-webview/ios/RNCWKWebView.m

# Need to copy custom RNCookieManagerIOS.m that fixes a crash when cookies does not have expiration date set
@cp ./native_modules/RNCookieManagerIOS.m node_modules/react-native-cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.m

@rm -f node_modules/intl/.babelrc
@# Hack to get react-intl and its dependencies to work with react-native
@# Based off of https://github.com/este/este/blob/master/gulp/native-fix.js
Expand Down
274 changes: 274 additions & 0 deletions native_modules/RNCookieManagerIOS.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
#import "RNCookieManagerIOS.h"
#if __has_include("RCTConvert.h")
#import "RCTConvert.h"
#else
#import <React/RCTConvert.h>
#endif

static NSString * const NOT_AVAILABLE_ERROR_MESSAGE = @"WebKit/WebKit-Components are only available with iOS11 and higher!";

@implementation RNCookieManagerIOS

- (instancetype)init
{
self = [super init];
if (self) {
self.formatter = [NSDateFormatter new];
[self.formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
}
return self;
}

+ (BOOL)requiresMainQueueSetup
{
return NO;
}

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(
set:(NSDictionary *)props
useWebKit:(BOOL)useWebKit
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSString *name = [RCTConvert NSString:props[@"name"]];
NSString *value = [RCTConvert NSString:props[@"value"]];
NSString *domain = [RCTConvert NSString:props[@"domain"]];
NSString *origin = [RCTConvert NSString:props[@"origin"]];
NSString *path = [RCTConvert NSString:props[@"path"]];
NSString *version = [RCTConvert NSString:props[@"version"]];
NSDate *expiration = [RCTConvert NSDate:props[@"expiration"]];

NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
[cookieProperties setObject:name forKey:NSHTTPCookieName];
[cookieProperties setObject:value forKey:NSHTTPCookieValue];
[cookieProperties setObject:domain forKey:NSHTTPCookieDomain];
[cookieProperties setObject:origin forKey:NSHTTPCookieOriginURL];
[cookieProperties setObject:path forKey:NSHTTPCookiePath];
[cookieProperties setObject:version forKey:NSHTTPCookieVersion];
[cookieProperties setObject:expiration forKey:NSHTTPCookieExpires];

NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];

NSLog(@"SETTING COOKIE");
NSLog(@"%@", cookie);

if (useWebKit) {
if (@available(iOS 11.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^(){
WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
[cookieStore setCookie:cookie completionHandler:nil];
resolve(nil);
});
} else {
reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil);
}
} else {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
resolve(nil);
}
}

RCT_EXPORT_METHOD(setFromResponse:(NSURL *)url
value:(NSString *)value
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:@{@"Set-Cookie": value} forURL:url];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:NULL];
resolve(nil);
}

RCT_EXPORT_METHOD(getFromResponse:(NSURL *)url
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:response.URL];
NSMutableDictionary *dics = [NSMutableDictionary dictionary];

for (int i = 0; i < cookies.count; i++) {
NSHTTPCookie *cookie = [cookies objectAtIndex:i];
[dics setObject:cookie.value forKey:cookie.name];
NSLog(@"cookie: name=%@, value=%@", cookie.name, cookie.value);
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}
resolve(dics);
}];
}

-(NSString *)getDomainName:(NSURL *) url
{
NSString *separator = @".";
NSInteger maxLength = 2;

NSURLComponents *components = [[NSURLComponents alloc]initWithURL:url resolvingAgainstBaseURL:FALSE];
NSArray<NSString *> *separatedHost = [components.host componentsSeparatedByString:separator];
NSInteger count = [separatedHost count];
NSInteger endPosition = count;
NSInteger startPosition = count - maxLength;

NSMutableString *result = [[NSMutableString alloc]init];
for (NSUInteger i = startPosition; i != endPosition; i++) {
[result appendString:separator];
[result appendString:[separatedHost objectAtIndex:i]];
}
return result;
}

RCT_EXPORT_METHOD(
get:(NSURL *) url
useWebKit:(BOOL)useWebKit
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
if (useWebKit) {
if (@available(iOS 11.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^(){
NSString *topLevelDomain = [self getDomainName:url];

WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *allCookies) {
NSMutableDictionary *cookies = [NSMutableDictionary dictionary];
for(NSHTTPCookie *currentCookie in allCookies) {
if([currentCookie.domain containsString:topLevelDomain]) {
[cookies setObject:currentCookie.value forKey:currentCookie.name];
}
}
resolve(cookies);
}];
});
} else {
reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil);
}
} else {
NSMutableDictionary *cookies = [NSMutableDictionary dictionary];
for (NSHTTPCookie *c in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url]) {
NSMutableDictionary *d = [NSMutableDictionary dictionary];
[d setObject:c.value forKey:@"value"];
[d setObject:c.name forKey:@"name"];
[d setObject:c.domain forKey:@"domain"];
[d setObject:c.path forKey:@"path"];
NSString *expires = [self.formatter stringFromDate:c.expiresDate];
if (expires != nil) {
[d setObject:expires forKey:@"expiresDate"];
}
[cookies setObject:d forKey:c.name];
}
resolve(cookies);
}
}

RCT_EXPORT_METHOD(
clearAll:(BOOL)useWebKit
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
if (useWebKit) {
if (@available(iOS 11.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^(){
WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *allCookies) {
for(NSHTTPCookie *currentCookie in allCookies) {
// Uses the NSHTTPCookie directly has no effect, nor deleted the cookie nor thrown an error.
// Create a new cookie with the given values and delete this one do the work.
NSMutableDictionary<NSHTTPCookiePropertyKey, id> *cookieData = [NSMutableDictionary dictionary];
[cookieData setValue:currentCookie.name forKey:NSHTTPCookieName];
[cookieData setValue:currentCookie.value forKey:NSHTTPCookieValue];
[cookieData setValue:currentCookie.domain forKey:NSHTTPCookieDomain];
[cookieData setValue:currentCookie.path forKey:NSHTTPCookiePath];

NSHTTPCookie *newCookie = [NSHTTPCookie cookieWithProperties:cookieData];
[cookieStore deleteCookie:newCookie completionHandler:^{}];
}
resolve(nil);
}];
});
} else {
reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil);
}
} else {
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *c in cookieStorage.cookies) {
[cookieStorage deleteCookie:c];
}
resolve(nil);
}
}

RCT_EXPORT_METHOD(clearByName:(NSString *) name
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *c in cookieStorage.cookies) {
if ([[c name] isEqualToString:name]) {
[cookieStorage deleteCookie:c];
}
}
resolve(nil);
}

RCT_EXPORT_METHOD(
getAll:(BOOL)useWebKit
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
if (useWebKit) {
if (@available(iOS 11.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^(){
WKHTTPCookieStore *cookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore];
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *allCookies) {
resolve([self createCookieList: allCookies]);
}];
});
} else {
reject(@"", NOT_AVAILABLE_ERROR_MESSAGE, nil);
}
} else {
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
resolve([self createCookieList:cookieStorage.cookies]);
}
}

RCT_EXPORT_METHOD(getAll:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSMutableDictionary *cookies = [NSMutableDictionary dictionary];
for (NSHTTPCookie *c in cookieStorage.cookies) {
NSMutableDictionary *d = [NSMutableDictionary dictionary];
[d setObject:c.value forKey:@"value"];
[d setObject:c.name forKey:@"name"];
[d setObject:c.domain forKey:@"domain"];
[d setObject:c.path forKey:@"path"];
NSString *expires = [self.formatter stringFromDate:c.expiresDate];
if (expires != nil) {
[d setObject:expires forKey:@"expiresDate"];
}
[cookies setObject:d forKey:c.name];
}
}

-(NSDictionary *)createCookieList:(NSArray<NSHTTPCookie *>*)cookies
{
NSMutableDictionary *cookieList = [NSMutableDictionary dictionary];
for (NSHTTPCookie *cookie in cookies) {
// NSLog(@"COOKIE: %@", cookie);
[cookieList setObject:[self createCookieData:cookie] forKey:cookie.name];
}
return cookieList;
}

-(NSDictionary *)createCookieData:(NSHTTPCookie *)cookie
{
NSMutableDictionary *cookieData = [NSMutableDictionary dictionary];
[cookieData setObject:cookie.value forKey:@"value"];
[cookieData setObject:cookie.name forKey:@"name"];
[cookieData setObject:cookie.domain forKey:@"domain"];
[cookieData setObject:cookie.path forKey:@"path"];
return cookieData;
}

@end

0 comments on commit 9ba81fb

Please sign in to comment.