diff --git a/android/app/build.gradle b/android/app/build.gradle index 638128bbcd100535e990d8672328cd6185676aa9..95bf15e975b41f69bd72c8e6f344a896665c2c83 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -184,6 +184,9 @@ dependencies { } debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") + + implementation 'dnsjava:dnsjava:3.5.2' + if (hermesEnabled.toBoolean()) { implementation("com.facebook.react:hermes-android") } else { diff --git a/android/app/src/main/java/com/vereign/vcm/MyAppPackage.java b/android/app/src/main/java/com/vereign/vcm/MyAppPackage.java index dd5146146ac126ef03a6f857808484bc7e1a84b8..559cd453428fd021352538acf6a458aacaae7a77 100644 --- a/android/app/src/main/java/com/vereign/vcm/MyAppPackage.java +++ b/android/app/src/main/java/com/vereign/vcm/MyAppPackage.java @@ -23,6 +23,7 @@ public class MyAppPackage implements ReactPackage { modules.add(new EmailLauncherModule(reactContext)); modules.add(new VereignImapModule(reactContext)); + modules.add(new VereignDnsModule(reactContext)); return modules; } diff --git a/android/app/src/main/java/com/vereign/vcm/VereignDnsModule.java b/android/app/src/main/java/com/vereign/vcm/VereignDnsModule.java new file mode 100644 index 0000000000000000000000000000000000000000..7f99898c99ddcd646e19ec8486a693d6c19f6a36 --- /dev/null +++ b/android/app/src/main/java/com/vereign/vcm/VereignDnsModule.java @@ -0,0 +1,205 @@ +package com.vereign.vcm; + +import android.util.Log; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableNativeArray; +import com.facebook.react.bridge.WritableNativeMap; + +import org.xbill.DNS.*; +import org.xbill.DNS.lookup.LookupSession; +import org.xbill.DNS.lookup.NoSuchDomainException; + + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class VereignDnsModule extends ReactContextBaseJavaModule { + + private static final String TAG = "VereignDnsModule"; + + public VereignDnsModule(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public String getName() { + return "VereignDnsModule"; + } + + @ReactMethod + public void lookup(String domain, double timeout, final Promise promise) { + Log.d(TAG, "lookup called with domain: " + domain + ", timeout: " + timeout); + + sslImapQuery(domain, timeout, new QueryCallback() { + @Override + public void onSuccess(List<DnsRecord> records) { + Log.d(TAG, "sslImapQuery successful"); + promise.resolve(convertRecordsToWritableArray(records)); + } + + @Override + public void onFailure(String code, String message, Throwable error) { + Log.d(TAG, "sslImapQuery failed: " + message); + imapQuery(domain, timeout, new QueryCallback() { + @Override + public void onSuccess(List<DnsRecord> records) { + promise.resolve(records); + } + + @Override + public void onFailure(String code, String message, Throwable error) { + promise.reject(message, error); + } + }); + } + }); + } + + private void sslImapQuery(String domain, double timeout, final QueryCallback callback) { + String queryDomain = "_imaps._tcp." + domain; + Log.d(TAG, "Performing sslImapQuery with domain: " + queryDomain + ", timeout: " + timeout); + performSRVQuery(queryDomain, timeout, new QueryCallback() { + @Override + public void onSuccess(List<DnsRecord> records) { + Log.d(TAG, "sslImapQuery performSRVQuery success"); + callback.onSuccess(records); + } + + @Override + public void onFailure(String code, String message, Throwable error) { + Log.d(TAG, "sslImapQuery performSRVQuery failure: " + message); + callback.onFailure(code, message, error); + } + }); + } + + private void imapQuery(String domain, double timeout, final QueryCallback callback) { + String queryDomain = "_imap._tcp." + domain; + Log.d(TAG, "Performing imapQuery with domain: " + queryDomain + ", timeout: " + timeout); + performSRVQuery(queryDomain, timeout, new QueryCallback() { + @Override + public void onSuccess(List<DnsRecord> records) { + Log.d(TAG, "imapQuery performSRVQuery success"); + callback.onSuccess(records); + } + + @Override + public void onFailure(String code, String message, Throwable error) { + Log.d(TAG, "imapQuery performSRVQuery failure: " + message); + callback.onFailure(code, message, error); + } + }); + } + + private void performSRVQuery(String domain, double timeout, final QueryCallback callback) { + Log.d(TAG, "Performing SRV query for domain: " + domain + " with timeout: " + timeout); + try { + LookupSession s = LookupSession + .defaultBuilder() + .resolver(new SimpleResolver("8.8.8.8")) + .build(); + + Name srvLookup = Name.fromString(domain); + + s.lookupAsync(srvLookup, Type.SRV) + .whenComplete( + (answers, ex) -> { + if (ex == null) { + if (answers.getRecords().isEmpty()) { + Log.d(TAG, domain + " has no SRV records"); + callback.onFailure("dns_lookup_error", "No SRV records found", new Exception("No SRV records found")); + } else { + List<DnsRecord> dnsRecords = new ArrayList<>(); + for (Record rec : answers.getRecords()) { + if (rec instanceof SRVRecord) { + SRVRecord srv = (SRVRecord) rec; + + String target = srv.getTarget().toString(); + dnsRecords.add(new DnsRecord( + target.endsWith(".") ? target.substring(0, target.length() - 1) : target, + srv.getPort(), + srv.getTTL(), + srv.getPriority() + )); + } + } + Log.d(TAG, "SRV query successful"); + callback.onSuccess(dnsRecords); + } + } else { + Log.e(TAG, "Error during SRV query", ex); + callback.onFailure("dns_lookup_error", "Error during DNS lookup", ex); + } + }) + .exceptionally(ex -> { + Log.e(TAG, "Exception during SRV lookup", ex); + callback.onFailure("dns_lookup_error", "Exception during DNS lookup", ex); + return null; + }) + .toCompletableFuture() + .get(); + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "Exception during SRV query execution", e); + callback.onFailure("dns_lookup_error", "Error during DNS lookup", e); + } catch (TextParseException | UnknownHostException e) { + throw new RuntimeException(e); + } + } + + private WritableArray convertRecordsToWritableArray(List<DnsRecord> records) { + Log.d(TAG, "Converting records to WritableArray"); + WritableArray writableArray = new WritableNativeArray(); + for (DnsRecord record : records) { + WritableNativeMap recordMap = new WritableNativeMap(); + recordMap.putString("target", record.getTarget()); + recordMap.putInt("port", record.getPort()); + recordMap.putDouble("ttl", record.getTtl()); // Using putDouble instead of putLong + recordMap.putInt("priority", record.getPriority()); + writableArray.pushMap(recordMap); + } + return writableArray; + } + + private interface QueryCallback { + void onSuccess(List<DnsRecord> records); + + void onFailure(String code, String message, Throwable error); + } + + private static class DnsRecord { + private final String target; + private final int port; + private final long ttl; + private final int priority; + + public DnsRecord(String target, int port, long ttl, int priority) { + this.target = target; + this.port = port; + this.ttl = ttl; + this.priority = priority; + } + + public String getTarget() { + return target; + } + + public int getPort() { + return port; + } + + public long getTtl() { + return ttl; + } + + public int getPriority() { + return priority; + } + } +} diff --git a/ios/Podfile b/ios/Podfile index 3414861e9f0e029d2892fbba0d89c914dd1cf48d..a5495eb2e610e5c7a5f0165055a09a2551f6e431 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -5,7 +5,6 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ platform :ios, 13 prepare_react_native_project! - # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded # @@ -18,6 +17,7 @@ prepare_react_native_project! # flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled # use_frameworks! :linkage => :static target 'VereignCredentialManager' do + pod 'srvresolver' config = use_native_modules! # Flags change depending on the env values. diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6c3211b17e9071b53b3fc5840755b74f3e7d52da..7902cc5cbee1ba0c683972438224f437b45d345f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -507,6 +507,7 @@ PODS: - RNVectorIcons (9.2.0): - React-Core - SocketRocket (0.6.1) + - srvresolver (1.0.0) - Yoga (1.14.0) DEPENDENCIES: @@ -579,6 +580,7 @@ DEPENDENCIES: - RNShare (from `../node_modules/react-native-share`) - RNSVG (from `../node_modules/react-native-svg`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`) + - srvresolver - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: @@ -587,6 +589,7 @@ SPEC REPOS: - fmt - libevent - SocketRocket + - srvresolver EXTERNAL SOURCES: anoncreds: @@ -799,8 +802,9 @@ SPEC CHECKSUMS: RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396 RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 + srvresolver: ae7bf7fe569b0d5849e6f3325da6aba1f8361f31 Yoga: 86fed2e4d425ee4c6eab3813ba1791101ee153c6 -PODFILE CHECKSUM: f132b3478cf1326c4b3b919d11de3d5ba6bb2341 +PODFILE CHECKSUM: b3f875916024a40fc96d60701b981fee76d23c50 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/ios/VereignCredentialManager.xcodeproj/project.pbxproj b/ios/VereignCredentialManager.xcodeproj/project.pbxproj index 2a34f22f4d311cb66924f3a8c5f187962eda4aa5..bb66af11ab4ce53ab152587ba9ab02c94b901deb 100644 --- a/ios/VereignCredentialManager.xcodeproj/project.pbxproj +++ b/ios/VereignCredentialManager.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; B89EBF06EC1149C6A7A0B91E /* antfill.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E329655F7F8247C4AA1220C1 /* antfill.ttf */; }; BD211E3DFD554642A53882D4 /* TitilliumWeb-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CDFADC7BA94A49E8BD7C41B7 /* TitilliumWeb-Light.ttf */; }; + FA529BA92BF65D01000768B1 /* RCTVereignDnsModule.m in Sources */ = {isa = PBXBuildFile; fileRef = FB7B5C332BF64B0A00CEF61D /* RCTVereignDnsModule.m */; }; FB1934E72AEAAE9F00138B0E /* MailCore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB1934E62AEAAE9F00138B0E /* MailCore.xcframework */; }; FB1934E82AEAAEA800138B0E /* MailCore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB1934E62AEAAE9F00138B0E /* MailCore.xcframework */; }; FB1934E92AEAAEA800138B0E /* MailCore.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FB1934E62AEAAE9F00138B0E /* MailCore.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -91,6 +92,8 @@ EF7CF2CD60574ADD8E0AFAD9 /* TitilliumWeb-SemiBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "TitilliumWeb-SemiBold.ttf"; path = "../src/assets/fonts/TitilliumWeb-SemiBold.ttf"; sourceTree = "<group>"; }; FB0F03C52BA33CA0009B97B1 /* VereignCredentialManager.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = VereignCredentialManager.entitlements; path = VereignCredentialManager/VereignCredentialManager.entitlements; sourceTree = "<group>"; }; FB1934E62AEAAE9F00138B0E /* MailCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = MailCore.xcframework; sourceTree = "<group>"; }; + FB7B5C322BF64AF100CEF61D /* RCTVereignDnsModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTVereignDnsModule.h; sourceTree = "<group>"; }; + FB7B5C332BF64B0A00CEF61D /* RCTVereignDnsModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTVereignDnsModule.m; sourceTree = "<group>"; }; FB82ADE32AE7C8F200767A3B /* VereignImapModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VereignImapModule.m; sourceTree = "<group>"; }; FB82ADE52AE7C91600767A3B /* VereignImapModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VereignImapModule.h; sourceTree = "<group>"; }; FB82ADE92AE7CBC100767A3B /* RCTVereignImapModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTVereignImapModule.m; sourceTree = "<group>"; }; @@ -139,6 +142,7 @@ 13B07FAE1A68108700A75B9A /* VereignCredentialManager */ = { isa = PBXGroup; children = ( + FB7B5C312BF64ACE00CEF61D /* VereignDnsModule */, FB0F03C52BA33CA0009B97B1 /* VereignCredentialManager.entitlements */, FB82ADE22AE7C8C700767A3B /* VereignImapModule */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, @@ -233,6 +237,15 @@ path = Pods; sourceTree = "<group>"; }; + FB7B5C312BF64ACE00CEF61D /* VereignDnsModule */ = { + isa = PBXGroup; + children = ( + FB7B5C322BF64AF100CEF61D /* RCTVereignDnsModule.h */, + FB7B5C332BF64B0A00CEF61D /* RCTVereignDnsModule.m */, + ); + path = VereignDnsModule; + sourceTree = "<group>"; + }; FB82ADE22AE7C8C700767A3B /* VereignImapModule */ = { isa = PBXGroup; children = ( @@ -506,6 +519,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FA529BA92BF65D01000768B1 /* RCTVereignDnsModule.m in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, FB82ADE42AE7C8F200767A3B /* VereignImapModule.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, @@ -800,6 +814,13 @@ "-ld_classic", "-Wl", "-ld_classic", + "-Wl", + "-ld_classic", + "-Wl", + "-ld_classic", + "-Wl", + "-ld_classic", + "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; @@ -898,6 +919,13 @@ "-ld_classic", "-Wl", "-ld_classic", + "-Wl", + "-ld_classic", + "-Wl", + "-ld_classic", + "-Wl", + "-ld_classic", + "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; diff --git a/ios/VereignDnsModule/RCTVereignDnsModule.h b/ios/VereignDnsModule/RCTVereignDnsModule.h new file mode 100644 index 0000000000000000000000000000000000000000..212f499f5346da984bfc076ad5b437b404f5a5fe --- /dev/null +++ b/ios/VereignDnsModule/RCTVereignDnsModule.h @@ -0,0 +1,4 @@ +#import <React/RCTBridgeModule.h> +@interface RCTVereignDnsModule : NSObject <RCTBridgeModule> +@end + diff --git a/ios/VereignDnsModule/RCTVereignDnsModule.m b/ios/VereignDnsModule/RCTVereignDnsModule.m new file mode 100644 index 0000000000000000000000000000000000000000..8f3d7e5a479c9978924020f5a519a7405b5f8a5b --- /dev/null +++ b/ios/VereignDnsModule/RCTVereignDnsModule.m @@ -0,0 +1,48 @@ +#import <Foundation/Foundation.h> +#import "RCTVereignDnsModule.h" +#import "srvresolver/SRVQueryResolver.h" +#import "srvresolver/SRVQueryRecord.h" + +@implementation RCTVereignDnsModule + +RCT_EXPORT_MODULE(VereignDnsModule); + +RCT_EXPORT_METHOD(lookup:(NSString *)domain timeout:(nonnull NSNumber *)timeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + [self sslImapQuery:domain timeout:timeout resolver:resolve rejecter:^(NSString *code, NSString *message, NSError *error) { + [self imapQuery:domain timeout:timeout resolver:resolve rejecter:reject]; + }]; +} + +- (void)sslImapQuery:(NSString *)domain timeout:(NSNumber *)timeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject { + NSString *queryDomain = [NSString stringWithFormat:@"_imaps._tcp.%@", domain]; + [self performSRVQuery:queryDomain timeout:timeout resolver:resolve rejecter:^(NSString *code, NSString *message, NSError *error) { + reject(code, message, error); + }]; +} + +- (void)imapQuery:(NSString *)domain timeout:(NSNumber *)timeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject { + NSString *queryDomain = [NSString stringWithFormat:@"_imap._tcp.%@", domain]; + [self performSRVQuery:queryDomain timeout:timeout resolver:resolve rejecter:reject]; +} + +- (void)performSRVQuery:(NSString *)domain timeout:(NSNumber *)timeout resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject { + [SRVQueryResolver query:domain timeout:timeout.doubleValue completion:^(NSArray<SRVQueryRecord *> *records, NSError *err) { + if (records) { + NSMutableArray *results = [NSMutableArray array]; + for (SRVQueryRecord *record in records) { + NSDictionary *recordDict = @{ + @"target": record.target, + @"port": @(record.port), + @"ttl": @(record.ttl), + @"priority": @(record.priority) + }; + [results addObject:recordDict]; + } + resolve(results); + } else if (err) { + NSLog(@"DNS Lookup Error: %@", err.localizedDescription); + reject(@"dns_lookup_error", [NSString stringWithFormat:@"Error: %@. Please try again later.", err.localizedDescription], err); + } + }]; +} +@end diff --git a/src/screens/ImapConfiguration/index.tsx b/src/screens/ImapConfiguration/index.tsx index 50e6fa26ffd502b18e6477369412decf4ec1d9cc..f48bf3f5e25c1f0786845723b9a766f8e13c8dd2 100644 --- a/src/screens/ImapConfiguration/index.tsx +++ b/src/screens/ImapConfiguration/index.tsx @@ -1,76 +1,114 @@ -import React, {useEffect, useState} from 'react'; +import React, { useEffect, useState } from "react"; import { NativeModules, StatusBar, StyleSheet, Keyboard, ScrollView, - SafeAreaView + SafeAreaView, } from "react-native"; import TextInput from "src/components/TextInput"; -import Button, {ButtonType} from "src/components/Button"; -import {getImapConfig, ImapConfiguration as ImapConfigurationInterface, setImapConfig} from "src/utils/keychain"; -import {successToast, warningToast} from "src/utils/toast"; -import {ColorPallet} from "src/theme/theme"; +import Button, { ButtonType } from "src/components/Button"; +import { + getImapConfig, + ImapConfiguration as ImapConfigurationInterface, + setImapConfig, +} from "src/utils/keychain"; +import { successToast, warningToast } from "src/utils/toast"; +import { ColorPallet } from "src/theme/theme"; import useKeyboard from "../../utils/keyboard"; import rootStore from "../../store/rootStore"; +import { DNSRecord, srvLookup } from "src/utils/dnsLookUp"; -const {VereignImapModule} = NativeModules; +const { VereignImapModule } = NativeModules; const ImapConfiguration = () => { const [loading, setLoading] = useState(false); const { keyboardHeight, isKeyBoardOpen } = useKeyboard(); const [imapValues, setImapValues] = useState({ enableSsl: true, - host: '', - username: '', - password: '', - port: 993 + host: "", + username: "", + password: "", + port: 993, }); - const loadConfig = async () => { - const config = await getImapConfig(); - if (config) { - setImapValues(config); - } - }; - useEffect(() => { - loadConfig(); - }, []); + + const [isVisible, setIsVisible] = useState(false); + + //FIXME: filling up data after app is uninstalled and installed again looks strange to end users. + //FIXME: this experience should be improved. + // const loadConfig = async () => { + // const config = await getImapConfig(); + // if (config) { + // setImapValues(config); + // } + // }; + // useEffect(() => { + // loadConfig(); + // }, []); const saveConfig = async () => { Keyboard.dismiss(); setLoading(true); + let resolved = [] as DNSRecord[]; + let host: string; + let port: number; + + if (isVisible === false) { + try { + const domain = imapValues.username.split("@").pop()!; + resolved = await srvLookup(domain); + } catch (e) { + setLoading(false); + if (isVisible === false) { + warningToast( + "Unable to automatically configure your mail server. Please manually set up the IMAP and port settings." + ); + } + setIsVisible(true); + return; + } + } + try { + host = resolved[0]?.target || imapValues.host; + port = Number(resolved[0]?.port || imapValues.port); + await setImapConfig({ verified: false, enableSsl: true, - host: imapValues.host, + host, + port, username: imapValues.username, password: imapValues.password, - port: Number(imapValues.port) }); - const config = await getImapConfig() as ImapConfigurationInterface; - VereignImapModule.checkConfiguration(config.host, config.username, config.password, (success: boolean, error: any) => { + const config = (await getImapConfig()) as ImapConfigurationInterface; - setLoading(false); - if (success) { - setImapConfig({ - ...config, - verified: true - }); - successToast("Imap config is valid"); - rootStore.agentStore.checkNotSavedEmailMessages(); + VereignImapModule.checkConfiguration( + config.host, + config.username, + config.password, + (success: boolean, error: any) => { + setLoading(false); + if (success) { + setImapConfig({ + ...config, + verified: true, + }); + successToast("Imap config is valid"); + rootStore.agentStore.checkNotSavedEmailMessages(); + } + if (error) { + warningToast("Imap config is invalid " + error); + } } - if (error) { - warningToast("Imap config is invalid " + error); - } - }); + ); } catch (e: any) { setLoading(false); warningToast(e.message); } - } + }; return ( <SafeAreaView @@ -78,27 +116,11 @@ const ImapConfiguration = () => { styles.scroll, { paddingBottom: isKeyBoardOpen ? keyboardHeight : 0, - } + }, ]} > <ScrollView contentContainerStyle={styles.container}> <StatusBar barStyle="light-content" /> - - <TextInput - label="host" - placeholder="host" - accessible - accessibilityLabel="host" - labelBold - labelCenter - autoFocus - value={imapValues.host} - onChangeText={(value) => setImapValues({ - ...imapValues, - host: value - })} - returnKeyType="done" - /> <TextInput label="username" placeholder="username" @@ -107,10 +129,12 @@ const ImapConfiguration = () => { labelBold labelCenter value={imapValues.username} - onChangeText={(value) => setImapValues({ - ...imapValues, - username: value - })} + onChangeText={(value) => + setImapValues({ + ...imapValues, + username: value, + }) + } returnKeyType="done" /> <TextInput @@ -122,26 +146,51 @@ const ImapConfiguration = () => { labelCenter secureTextEntry value={imapValues.password} - onChangeText={(value) => setImapValues({ - ...imapValues, - password: value - })} - returnKeyType="done" - /> - <TextInput - label="port" - placeholder="port" - accessible - accessibilityLabel="port" - labelBold - labelCenter - value={imapValues.port.toString()} - onChangeText={(value) => setImapValues({ - ...imapValues, - port: Number(value) - })} + onChangeText={(value) => + setImapValues({ + ...imapValues, + password: value, + }) + } returnKeyType="done" /> + {isVisible && ( + <> + <TextInput + label="host" + placeholder="host" + accessible + accessibilityLabel="host" + labelBold + labelCenter + autoFocus + value={imapValues.host} + onChangeText={(value) => + setImapValues({ + ...imapValues, + host: value, + }) + } + returnKeyType="done" + /> + <TextInput + label="port" + placeholder="port" + accessible + accessibilityLabel="port" + labelBold + labelCenter + value={imapValues.port.toString()} + onChangeText={(value) => + setImapValues({ + ...imapValues, + port: Number(value), + }) + } + returnKeyType="done" + /> + </> + )} <Button title="Save" @@ -158,12 +207,12 @@ export default ImapConfiguration; const styles = StyleSheet.create({ scroll: { - flex: 1 + flex: 1, }, container: { backgroundColor: ColorPallet.grayscale.white, margin: 20, - paddingBottom: 80 + paddingBottom: 80, }, button: { marginTop: 16, diff --git a/src/utils/dnsLookUp.ts b/src/utils/dnsLookUp.ts new file mode 100644 index 0000000000000000000000000000000000000000..2d1610598347d9ea1be0cb094e6f6f4036a484f7 --- /dev/null +++ b/src/utils/dnsLookUp.ts @@ -0,0 +1,14 @@ +import { NativeModules } from 'react-native'; + +const { VereignDnsModule } = NativeModules; + +export interface DNSRecord { + target: string; + port: number; + ttl: number; + priority: number; +} + +export const srvLookup = (domain: string, timeout: number = 2): Promise<DNSRecord[]> => { + return VereignDnsModule.lookup(domain, timeout); +};