feat: remove rn-fetch-blob (#5669)

* feat: remove rn-fetch-blob

* fix types

* jest
This commit is contained in:
Gleidson Daniel Silva 2024-05-20 15:21:31 -04:00 committed by GitHub
parent 0ffa528e2a
commit 02b3afda3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 140 additions and 399 deletions

View File

@ -32,8 +32,6 @@ import android.security.KeyChainAliasCallback;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import com.RNFetchBlob.RNFetchBlob;
import com.reactnativecommunity.webview.RNCWebViewManager;
import com.dylanvann.fastimage.FastImageOkHttpUrlLoader;
@ -104,8 +102,6 @@ public class SSLPinningModule extends ReactContextBaseJavaModule implements KeyC
WebSocketModule.setCustomClientBuilder(new CustomClient());
// Image networking react-native layer
ReactOkHttpNetworkFetcher.setOkHttpClient(getOkHttpClient());
// RNFetchBlob networking layer
RNFetchBlob.applyCustomOkHttpClient(getOkHttpClient());
// RNCWebView onReceivedClientCertRequest
RNCWebViewManager.setCertificateAlias(data);
// FastImage Glide network layer

View File

@ -13,8 +13,9 @@ const styles = StyleSheet.create({
},
text: {
fontSize: 14,
...sharedStyles.textRegular,
...sharedStyles.textAlignCenter
// jest error: TypeError: Cannot read property 'textRegular' of undefined
...sharedStyles?.textRegular,
...sharedStyles?.textAlignCenter
}
});

View File

@ -1,21 +1,21 @@
import { dequal } from 'dequal';
import moment from 'moment';
import React, { useContext, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import moment from 'moment';
import { dequal } from 'dequal';
import FastImage from 'react-native-fast-image';
import Touchable from './Touchable';
import Markdown from '../markdown';
import openLink from '../../lib/methods/helpers/openLink';
import sharedStyles from '../../views/Styles';
import { themes } from '../../lib/constants';
import MessageContext from './Context';
import { fileDownloadAndPreview } from './helpers/fileDownload';
import { IAttachment, TGetCustomEmoji } from '../../definitions';
import RCActivityIndicator from '../ActivityIndicator';
import Attachments from './Attachments';
import { TSupportedThemes, useTheme } from '../../theme';
import { themes } from '../../lib/constants';
import { fileDownloadAndPreview } from '../../lib/methods/helpers';
import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl';
import openLink from '../../lib/methods/helpers/openLink';
import { TSupportedThemes, useTheme } from '../../theme';
import sharedStyles from '../../views/Styles';
import RCActivityIndicator from '../ActivityIndicator';
import Markdown from '../markdown';
import Attachments from './Attachments';
import MessageContext from './Context';
import Touchable from './Touchable';
import messageStyles from './styles';
const styles = StyleSheet.create({

View File

@ -14,7 +14,7 @@ import {
isDownloadActive,
resumeMediaFile
} from '../../lib/methods/handleMediaDownload';
import { isIOS } from '../../lib/methods/helpers';
import { fileDownload, isIOS } from '../../lib/methods/helpers';
import EventEmitter from '../../lib/methods/helpers/events';
import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl';
import { useTheme } from '../../theme';
@ -24,7 +24,6 @@ import Markdown from '../markdown';
import BlurComponent from './Components/OverlayComponent';
import MessageContext from './Context';
import Touchable from './Touchable';
import { fileDownload } from './helpers/fileDownload';
import { DEFAULT_MESSAGE_HEIGHT } from './utils';
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];

View File

@ -1,53 +0,0 @@
import RNFetchBlob, { FetchBlobResponse } from 'rn-fetch-blob';
import FileViewer from 'react-native-file-viewer';
import EventEmitter from '../../../../lib/methods/helpers/events';
import { LISTENER } from '../../../Toast';
import I18n from '../../../../i18n';
import { DOCUMENTS_PATH, DOWNLOAD_PATH } from '../../../../lib/constants';
import { IAttachment } from '../../../../definitions';
export const getLocalFilePathFromFile = (localPath: string, attachment: IAttachment): string => `${localPath}${attachment.title}`;
export const fileDownload = (url: string, attachment: IAttachment): Promise<FetchBlobResponse> => {
const path = getLocalFilePathFromFile(DOWNLOAD_PATH, attachment);
const options = {
path,
timeout: 10000,
indicator: true,
overwrite: true,
addAndroidDownloads: {
path,
notification: true,
useDownloadManager: true
}
};
return RNFetchBlob.config(options).fetch('GET', url);
};
export const fileDownloadAndPreview = async (url: string, attachment: IAttachment): Promise<void> => {
try {
const path = getLocalFilePathFromFile(DOCUMENTS_PATH, attachment);
const file = await RNFetchBlob.config({
timeout: 10000,
indicator: true,
path
}).fetch('GET', url);
FileViewer.open(file.data, {
showOpenWithDialog: true,
showAppsSuggestions: true
})
.then(res => res)
.catch(async () => {
const file = await fileDownload(url, attachment);
file
? EventEmitter.emit(LISTENER, { message: I18n.t('Downloaded_file') })
: EventEmitter.emit(LISTENER, { message: I18n.t('Error_Download_file') });
});
} catch (e) {
EventEmitter.emit(LISTENER, { message: I18n.t('Error_Download_file') });
}
};

View File

@ -6,7 +6,6 @@ export * from './environment';
export * from './keys';
export * from './links';
export * from './localAuthentication';
export * from './localPath';
export * from './messagesStatus';
export * from './messageTypeLoad';
export * from './notifications';

View File

@ -1,4 +0,0 @@
import RNFetchBlob from 'rn-fetch-blob';
export const DOCUMENTS_PATH = `${RNFetchBlob.fs.dirs.DocumentDir}/`;
export const DOWNLOAD_PATH = `${RNFetchBlob.fs.dirs.DownloadDir}/`;

View File

@ -1,4 +1,3 @@
import RNFetchBlob from 'rn-fetch-blob';
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
import { KJUR } from 'jsrsasign';
import moment from 'moment';
@ -45,12 +44,12 @@ const verifyJWT = (jwt?: string): ISupportedVersionsData | null => {
export async function getServerInfo(server: string): Promise<TServerInfoResult> {
try {
const response = await RNFetchBlob.fetch('GET', `${server}/api/info`, {
const response = await fetch(`${server}/api/info`, {
...RocketChatSettings.customHeaders
});
try {
const jsonRes: IApiServerInfo = response.json();
if (!jsonRes?.success) {
const serverInfo: IApiServerInfo = await response.json();
if (!serverInfo?.success) {
return {
success: false,
message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') })
@ -58,7 +57,7 @@ export async function getServerInfo(server: string): Promise<TServerInfoResult>
}
// Makes use of signed JWT to get supported versions
const supportedVersions = verifyJWT(jsonRes.supportedVersions?.signed);
const supportedVersions = verifyJWT(serverInfo.supportedVersions?.signed);
// if backend doesn't have supported versions or JWT is invalid, request from cloud
if (!supportedVersions) {
@ -69,7 +68,7 @@ export async function getServerInfo(server: string): Promise<TServerInfoResult>
moment(new Date()).diff(serverRecord?.supportedVersionsUpdatedAt, 'hours') <= SV_CLOUD_UPDATE_INTERVAL
) {
return {
...jsonRes,
...serverInfo,
success: true
};
}
@ -79,7 +78,7 @@ export async function getServerInfo(server: string): Promise<TServerInfoResult>
// Allows airgapped servers to use the app until enforcementStartDate
if (!cloudInfo) {
return {
...jsonRes,
...serverInfo,
success: true
};
}
@ -88,14 +87,14 @@ export async function getServerInfo(server: string): Promise<TServerInfoResult>
const supportedVersionsCloud = verifyJWT(cloudInfo?.signed);
return {
...jsonRes,
...serverInfo,
success: true,
supportedVersions: supportedVersionsCloud
};
}
return {
...jsonRes,
...serverInfo,
success: true,
supportedVersions
};

View File

@ -0,0 +1,33 @@
import * as FileSystem from 'expo-file-system';
import FileViewer from 'react-native-file-viewer';
import { LISTENER } from '../../../containers/Toast';
import { IAttachment } from '../../../definitions';
import i18n from '../../../i18n';
import EventEmitter from './events';
export const getLocalFilePathFromFile = (localPath: string, attachment: IAttachment): string => `${localPath}${attachment.title}`;
export const fileDownload = async (url: string, attachment?: IAttachment, fileName?: string): Promise<string> => {
let path = `${FileSystem.documentDirectory}`;
if (fileName) {
path = `${path}${fileName}`;
}
if (attachment) {
path = `${path}${attachment.title}`;
}
const file = await FileSystem.downloadAsync(url, path);
return file.uri;
};
export const fileDownloadAndPreview = async (url: string, attachment: IAttachment): Promise<void> => {
try {
const file = await fileDownload(url, attachment);
FileViewer.open(file, {
showOpenWithDialog: true,
showAppsSuggestions: true
});
} catch (e) {
EventEmitter.emit(LISTENER, { message: i18n.t('Error_Download_file') });
}
};

View File

@ -0,0 +1,66 @@
export interface IFileUpload {
name: string;
uri?: string;
type?: string;
filename?: string;
data?: any;
}
export class Upload {
public xhr: XMLHttpRequest;
public formData: FormData;
constructor() {
this.xhr = new XMLHttpRequest();
this.formData = new FormData();
}
public setupRequest(url: string, headers: { [key: string]: string }): void {
this.xhr.open('POST', url);
Object.keys(headers).forEach(key => {
this.xhr.setRequestHeader(key, headers[key]);
});
}
public appendFile(item: IFileUpload): void {
if (item.uri) {
this.formData.append(item.name, {
uri: item.uri,
type: item.type,
name: item.filename
} as any);
} else {
this.formData.append(item.name, item.data);
}
}
public then(callback: (param: { respInfo: XMLHttpRequest }) => void): void {
this.xhr.onload = () => callback({ respInfo: this.xhr });
this.xhr.send(this.formData);
}
public catch(callback: ((this: XMLHttpRequest, ev: ProgressEvent<EventTarget>) => any) | null): void {
this.xhr.onerror = callback;
}
public uploadProgress(callback: (param: number, arg1: number) => any): void {
this.xhr.upload.onprogress = ({ total, loaded }) => callback(loaded, total);
}
public cancel(): Promise<void> {
this.xhr.abort();
return Promise.resolve();
}
}
class FileUpload {
public uploadFile(url: string, headers: { [x: string]: string }, data: IFileUpload[]) {
const upload = new Upload();
upload.setupRequest(url, headers);
data.forEach(item => upload.appendFile(item));
return upload;
}
}
const fileUpload = new FileUpload();
export default fileUpload;

View File

@ -1,60 +0,0 @@
import { IFileUpload } from './interfaces';
class Upload {
public xhr: XMLHttpRequest;
public formData: FormData;
constructor() {
this.xhr = new XMLHttpRequest();
this.formData = new FormData();
}
then = (callback: (param: { respInfo: XMLHttpRequest }) => XMLHttpRequest) => {
this.xhr.onload = () => callback({ respInfo: this.xhr });
this.xhr.send(this.formData);
};
catch = (callback: ((this: XMLHttpRequest, ev: ProgressEvent<EventTarget>) => any) | null) => {
this.xhr.onerror = callback;
};
uploadProgress = (callback: (param: number, arg1: number) => any) => {
this.xhr.upload.onprogress = ({ total, loaded }) => callback(loaded, total);
};
cancel = () => {
this.xhr.abort();
return Promise.resolve();
};
}
class FileUpload {
fetch = (method: string, url: string, headers: { [x: string]: string }, data: IFileUpload[]) => {
const upload = new Upload();
upload.xhr.open(method, url);
Object.keys(headers).forEach(key => {
upload.xhr.setRequestHeader(key, headers[key]);
});
data.forEach(item => {
if (item.uri) {
upload.formData.append(item.name, {
// @ts-ignore
uri: item.uri,
// @ts-ignore
type: item.type,
name: item.filename
});
} else {
upload.formData.append(item.name, item.data);
}
});
return upload;
};
}
const fileUpload = new FileUpload();
export default fileUpload;

View File

@ -1,25 +0,0 @@
import RNFetchBlob from 'rn-fetch-blob';
import { TMethods } from '../fetch';
import { IFileUpload } from './interfaces';
class FileUpload {
fetch = (method: TMethods, url: string, headers: { [key: string]: string }, data: IFileUpload[]) => {
const formData = data.map(item => {
if (item.uri) {
return {
name: item.name,
type: item.type,
filename: item.filename,
data: RNFetchBlob.wrap(decodeURI(item.uri))
};
}
return item;
});
return RNFetchBlob.fetch(method, url, headers, formData);
};
}
const fileUpload = new FileUpload();
export default fileUpload;

View File

@ -1,7 +0,0 @@
export interface IFileUpload {
name: string;
uri?: string;
type?: string;
filename?: string;
data?: any;
}

View File

@ -18,3 +18,4 @@ export * from './image';
export * from './askAndroidMediaPermissions';
export * from './emitter';
export * from './parseJson';
export * from './fileDownload';

View File

@ -1,17 +1,16 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
import isEmpty from 'lodash/isEmpty';
import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob';
import { Alert } from 'react-native';
import { IUpload, IUser, TUploadModel } from '../../definitions';
import i18n from '../../i18n';
import database from '../database';
import type { IFileUpload, Upload } from './helpers/fileUpload';
import FileUpload from './helpers/fileUpload';
import { IFileUpload } from './helpers/fileUpload/interfaces';
import log from './helpers/log';
const uploadQueue: { [index: string]: StatefulPromise<FetchBlobResponse> } = {};
const uploadQueue: { [index: string]: Upload } = {};
const getUploadPath = (path: string, rid: string) => `${path}-${rid}`;
@ -48,7 +47,7 @@ export function sendFileMessage(
server: string,
user: Partial<Pick<IUser, 'id' | 'token'>>,
isForceTryAgain?: boolean
): Promise<FetchBlobResponse | void> {
): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
const { id, token } = user;
@ -121,7 +120,7 @@ export function sendFileMessage(
'X-User-Id': id
};
uploadQueue[uploadPath] = FileUpload.fetch('POST', uploadUrl, headers, formData);
uploadQueue[uploadPath] = FileUpload.uploadFile(uploadUrl, headers, formData);
uploadQueue[uploadPath].uploadProgress(async (loaded: number, total: number) => {
try {
@ -137,12 +136,11 @@ export function sendFileMessage(
uploadQueue[uploadPath].then(async response => {
if (response.respInfo.status >= 200 && response.respInfo.status < 400) {
// If response is all good...
try {
await db.write(async () => {
await uploadRecord.destroyPermanently();
});
resolve(response);
resolve();
} catch (e) {
log(e);
}

View File

@ -1,13 +1,9 @@
import RNFetchBlob from 'rn-fetch-blob';
export const getServerTimeSync = async (server: string) => {
try {
const response = await Promise.race([
RNFetchBlob.fetch('GET', `${server}/_timesync`),
new Promise<undefined>(res => setTimeout(res, 2000))
]);
if (response?.data) {
return parseInt(response.data);
const response = await Promise.race([fetch(`${server}/_timesync`), new Promise<undefined>(res => setTimeout(res, 2000))]);
const data = await response?.json();
if (data?.data) {
return parseInt(data.data);
}
return null;
} catch {

View File

@ -6,7 +6,7 @@ import React from 'react';
import { PermissionsAndroid, useWindowDimensions, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { shallowEqual } from 'react-redux';
import RNFetchBlob from 'rn-fetch-blob';
import * as FileSystem from 'expo-file-system';
import { isImageBase64 } from '../lib/methods';
import RCActivityIndicator from '../containers/ActivityIndicator';
@ -18,7 +18,7 @@ import { IAttachment } from '../definitions';
import I18n from '../i18n';
import { useAppSelector } from '../lib/hooks';
import { useAppNavigation, useAppRoute } from '../lib/hooks/navigation';
import { formatAttachmentUrl, isAndroid } from '../lib/methods/helpers';
import { formatAttachmentUrl, isAndroid, fileDownload } from '../lib/methods/helpers';
import EventEmitter from '../lib/methods/helpers/events';
import { getUserSelector } from '../selectors/login';
import { TNavigation } from '../stacks/stackType';
@ -177,11 +177,9 @@ const AttachmentView = (): React.ReactElement => {
} else {
filename = getFilename({ title: attachment.title, type: 'video', mimeType: video_type, url });
}
const documentDir = `${RNFetchBlob.fs.dirs.DocumentDir}/`;
const path = `${documentDir + filename}`;
const file = await RNFetchBlob.config({ path }).fetch('GET', mediaAttachment);
await CameraRoll.save(path, { album: 'Rocket.Chat' });
file.flush();
const file = await fileDownload(mediaAttachment, {}, filename);
await CameraRoll.save(file, { album: 'Rocket.Chat' });
FileSystem.deleteAsync(file, { idempotent: true });
}
EventEmitter.emit(LISTENER, { message: I18n.t('saved_to_gallery') });
} catch (e) {

View File

@ -484,8 +484,6 @@ PODS:
- React
- rn-extensions-share (2.4.1):
- React
- rn-fetch-blob (0.12.0):
- React-Core
- RNBootSplash (4.3.3):
- React-Core
- RNCAsyncStorage (1.17.11):
@ -653,7 +651,6 @@ DEPENDENCIES:
- "ReactNativeART (from `../node_modules/@react-native-community/art`)"
- ReactNativeUiLib (from `../node_modules/react-native-ui-lib`)
- rn-extensions-share (from `../node_modules/rn-extensions-share`)
- rn-fetch-blob (from `../node_modules/rn-fetch-blob`)
- RNBootSplash (from `../node_modules/react-native-bootsplash`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
@ -827,8 +824,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-ui-lib"
rn-extensions-share:
:path: "../node_modules/rn-extensions-share"
rn-fetch-blob:
:path: "../node_modules/rn-fetch-blob"
RNBootSplash:
:path: "../node_modules/react-native-bootsplash"
RNCAsyncStorage:
@ -962,7 +957,6 @@ SPEC CHECKSUMS:
ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab
ReactNativeUiLib: 33521c0747ea376d292b62b6415e0f1d75bd3c10
rn-extensions-share: 5fd84a80e6594706f0dfa1884f2d6d591b382cf5
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNBootSplash: 7e91ea56c7010aae487489789dbe212e8c905a0c
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
RNCClipboard: cc054ad1e8a33d2a74cd13e565588b4ca928d8fd

View File

@ -29,18 +29,6 @@ jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock
jest.mock('@react-native-clipboard/clipboard', () => mockClipboard);
jest.mock('rn-fetch-blob', () => ({
fs: {
dirs: {
DocumentDir: '/data/com.rocket.chat/documents',
DownloadDir: '/data/com.rocket.chat/downloads'
},
exists: jest.fn(() => null)
},
fetch: jest.fn(() => null),
config: jest.fn(() => null)
}));
jest.mock('react-native-file-viewer', () => ({
open: jest.fn(() => null)
}));

View File

@ -137,7 +137,6 @@
"remove-markdown": "^0.3.0",
"reselect": "4.0.0",
"rn-extensions-share": "RocketChat/rn-extensions-share",
"rn-fetch-blob": "^0.12.0",
"rn-root-view": "RocketChat/rn-root-view",
"semver": "7.3.8",
"transliteration": "^2.3.5",

View File

@ -1,153 +0,0 @@
diff --git a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java
index 602d51d..920d975 100644
--- a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java
+++ b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java
@@ -38,7 +38,7 @@ import static com.RNFetchBlob.RNFetchBlobConst.GET_CONTENT_INTENT;
public class RNFetchBlob extends ReactContextBaseJavaModule {
- private final OkHttpClient mClient;
+ static private OkHttpClient mClient;
static ReactApplicationContext RCTContext;
private static LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
@@ -75,6 +75,10 @@ public class RNFetchBlob extends ReactContextBaseJavaModule {
});
}
+ public static void applyCustomOkHttpClient(OkHttpClient client) {
+ mClient = client;
+ }
+
@Override
public String getName() {
return "RNFetchBlob";
diff --git a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
index cdbe6b1..04e5e7b 100644
--- a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
+++ b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m
@@ -15,6 +15,9 @@
#import "IOS7Polyfill.h"
#import <CommonCrypto/CommonDigest.h>
+#import "SecureStorage.h"
+#import <MMKV/MMKV.h>
+
typedef NS_ENUM(NSUInteger, ResponseFormat) {
UTF8,
@@ -450,16 +453,108 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen
}
}
-
-- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
+-(NSURLCredential *)getUrlCredential:(NSURLAuthenticationChallenge *)challenge path:(NSString *)path password:(NSString *)password
{
- if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- } else {
- completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+ NSString *authMethod = [[challenge protectionSpace] authenticationMethod];
+ SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
+
+ if ([authMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || path == nil || password == nil) {
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+ } else if (path && password) {
+ NSMutableArray *policies = [NSMutableArray array];
+ [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)];
+ SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
+
+ SecTrustResultType result;
+ SecTrustEvaluate(serverTrust, &result);
+
+ if (![[NSFileManager defaultManager] fileExistsAtPath:path])
+ {
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+ }
+
+ NSData *p12data = [NSData dataWithContentsOfFile:path];
+ NSDictionary* options = @{ (id)kSecImportExportPassphrase:password };
+ CFArrayRef rawItems = NULL;
+ OSStatus status = SecPKCS12Import((__bridge CFDataRef)p12data,
+ (__bridge CFDictionaryRef)options,
+ &rawItems);
+
+ if (status != noErr) {
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+ }
+
+ NSArray* items = (NSArray*)CFBridgingRelease(rawItems);
+ NSDictionary* firstItem = nil;
+ if ((status == errSecSuccess) && ([items count]>0)) {
+ firstItem = items[0];
}
+
+ SecIdentityRef identity = (SecIdentityRef)CFBridgingRetain(firstItem[(id)kSecImportItemIdentity]);
+ SecCertificateRef certificate = NULL;
+ if (identity) {
+ SecIdentityCopyCertificate(identity, &certificate);
+ if (certificate) { CFRelease(certificate); }
+ }
+
+ NSMutableArray *certificates = [[NSMutableArray alloc] init];
+ [certificates addObject:CFBridgingRelease(certificate)];
+
+ return [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceNone];
+ }
+
+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+}
+
+- (NSString *)stringToHex:(NSString *)string
+{
+ char *utf8 = (char *)[string UTF8String];
+ NSMutableString *hex = [NSMutableString string];
+ while (*utf8) [hex appendFormat:@"%02X", *utf8++ & 0x00FF];
+
+ return [[NSString stringWithFormat:@"%@", hex] lowercaseString];
}
+-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
+{
+ NSString *host = challenge.protectionSpace.host;
+
+ // Read the clientSSL info from MMKV
+ __block NSString *clientSSL;
+ SecureStorage *secureStorage = [[SecureStorage alloc] init];
+
+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
+ NSString *key = [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"]];
+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+
+ if (key == NULL) {
+ return completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, credential);
+ }
+
+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding];
+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess];
+ clientSSL = [mmkv getStringForKey:host];
+
+ if ([clientSSL length] != 0) {
+ NSData *data = [clientSSL dataUsingEncoding:NSUTF8StringEncoding];
+ id dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
+ NSString *path = [dict objectForKey:@"path"];
+ NSString *password = [dict objectForKey:@"password"];
+ credential = [self getUrlCredential:challenge path:path password:password];
+ }
+
+ completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
+}
+
+// - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
+// {
+// if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
+// completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+// } else {
+// completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+// }
+// }
+
- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{

View File

@ -8171,11 +8171,6 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base-64@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==
base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.3, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -8619,7 +8614,6 @@ builtins@^1.0.3:
resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==
bunyamin@^1.5.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/bunyamin/-/bunyamin-1.5.2.tgz#681db204c0b16531369d5c1f6c89dc8d760b7558"
@ -12208,18 +12202,6 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
glob@7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
integrity sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.2"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@7.1.6, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
@ -18798,14 +18780,6 @@ rn-extensions-share@RocketChat/rn-extensions-share:
version "2.4.1"
resolved "https://codeload.github.com/RocketChat/rn-extensions-share/tar.gz/4d7c0e4c2f300e4fb116af7b7cc0dbbc8169150c"
rn-fetch-blob@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/rn-fetch-blob/-/rn-fetch-blob-0.12.0.tgz#ec610d2f9b3f1065556b58ab9c106eeb256f3cba"
integrity sha512-+QnR7AsJ14zqpVVUbzbtAjq0iI8c9tCg49tIoKO2ezjzRunN7YL6zFSFSWZm6d+mE/l9r+OeDM3jmb2tBb2WbA==
dependencies:
base-64 "0.1.0"
glob "7.0.6"
rn-host-detect@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/rn-host-detect/-/rn-host-detect-1.2.0.tgz#8b0396fc05631ec60c1cb8789e5070cdb04d0da0"
@ -19677,6 +19651,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
strip-ansi "^6.0.0"
string-width@^4.2.3:
name string-width-cjs
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -19840,6 +19815,7 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
ansi-regex "^4.1.0"
strip-ansi@^6.0.1:
name strip-ansi-cjs
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==