Skip to content

Commit 5724eae

Browse files
committed
Add dialog to ask user if he wants to download a file. Add tapjacking prevention for it
1 parent f818a76 commit 5724eae

File tree

1 file changed

+71
-20
lines changed

1 file changed

+71
-20
lines changed

apple/RNCWebViewImpl.m

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -972,28 +972,29 @@ - (void)downloadBase64File:(NSString *)base64String {
972972

973973
NSData *fileData = [[NSData alloc] initWithBase64EncodedString:base64ContentPart options:NSDataBase64DecodingIgnoreUnknownCharacters];
974974
NSString *fileExtension = [self fileExtensionFromBase64String:base64String];
975+
[self showDownloadAlert:fileExtension invokeDownload:^{
976+
NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"File.%@", fileExtension]];
977+
[fileData writeToFile:tempFilePath atomically:YES];
978+
979+
NSURL *tempFileURL = [NSURL fileURLWithPath:tempFilePath];
980+
981+
UIDocumentPickerViewController *documentPicker = nil;
982+
if (@available(iOS 14.0, *)) {
983+
documentPicker = [[UIDocumentPickerViewController alloc] initForExportingURLs:@[tempFileURL] asCopy:YES];
984+
} else {
985+
// Usage of initWithURL:inMode: might lose file's extension and user has to type it manually
986+
// Problem was solved for iOS 14 and higher with initForExportingURLs
987+
documentPicker = [[UIDocumentPickerViewController alloc] initWithURL:tempFileURL inMode:UIDocumentPickerModeExportToService];
988+
}
989+
documentPicker.delegate = self;
990+
documentPicker.modalPresentationStyle = UIModalPresentationFullScreen;
975991

976-
NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"File.%@", fileExtension]];
977-
[fileData writeToFile:tempFilePath atomically:YES];
978-
979-
NSURL *tempFileURL = [NSURL fileURLWithPath:tempFilePath];
980-
981-
UIDocumentPickerViewController *documentPicker = nil;
982-
if (@available(iOS 14.0, *)) {
983-
documentPicker = [[UIDocumentPickerViewController alloc] initForExportingURLs:@[tempFileURL] asCopy:YES];
984-
} else {
985-
// Usage of initWithURL:inMode: might lose file's extension and user has to type it manually
986-
// Problem was solved for iOS 14 and higher with initForExportingURLs
987-
documentPicker = [[UIDocumentPickerViewController alloc] initWithURL:tempFileURL inMode:UIDocumentPickerModeExportToService];
988-
}
989-
documentPicker.delegate = self;
990-
documentPicker.modalPresentationStyle = UIModalPresentationFullScreen;
991-
992-
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
993-
while (rootViewController.presentedViewController) {
994-
rootViewController = rootViewController.presentedViewController;
995-
}
992+
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
993+
while (rootViewController.presentedViewController) {
994+
rootViewController = rootViewController.presentedViewController;
995+
}
996996
[rootViewController presentViewController:documentPicker animated:YES completion:nil];
997+
}];
997998
}
998999

9991000
- (NSString *)fileExtensionFromBase64String:(NSString *)base64String {
@@ -2103,6 +2104,56 @@ - (void)handleRegularFileDownload:(NSString *)urlString {
21032104
}];
21042105
}
21052106

2107+
- (void)showDownloadAlert:(NSString *)fileExtension
2108+
invokeDownload:(void (^)(void))invokeDownload {
2109+
NSString *title =
2110+
[NSString stringWithFormat:@"Do you want to download File.%@?",
2111+
fileExtension ?: @""];
2112+
2113+
UIAlertController *alert = [UIAlertController
2114+
alertControllerWithTitle:title
2115+
message:nil
2116+
preferredStyle:UIAlertControllerStyleAlert];
2117+
2118+
UIAlertAction *cancelAction =
2119+
[UIAlertAction actionWithTitle:@"Cancel"
2120+
style:UIAlertActionStyleCancel
2121+
handler:nil];
2122+
2123+
UIAlertAction *downloadAction =
2124+
[UIAlertAction actionWithTitle:@"Download"
2125+
style:UIAlertActionStyleDefault
2126+
handler:^(UIAlertAction *_Nonnull action) {
2127+
if (invokeDownload) {
2128+
invokeDownload();
2129+
}
2130+
}];
2131+
2132+
[alert addAction:cancelAction];
2133+
[alert addAction:downloadAction];
2134+
2135+
UIViewController *rootViewController =
2136+
[UIApplication sharedApplication].keyWindow.rootViewController;
2137+
if (rootViewController) {
2138+
[rootViewController presentViewController:alert
2139+
animated:YES
2140+
completion:nil];
2141+
} else {
2142+
NSLog(@"Error: No root view controller to present alert");
2143+
}
2144+
2145+
// Disable download button initially to prevent Tap jacking
2146+
downloadAction.enabled = NO;
2147+
2148+
// Enable button after 500ms
2149+
dispatch_after(
2150+
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(500 * NSEC_PER_MSEC)),
2151+
dispatch_get_main_queue(), ^{
2152+
downloadAction.enabled = YES;
2153+
});
2154+
}
2155+
2156+
21062157
@end
21072158

21082159
@implementation RNCWeakScriptMessageDelegate

0 commit comments

Comments
 (0)