summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/MullvadSettings/IPOverride.swift4
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj4
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift2
-rw-r--r--ios/MullvadVPN/SceneDelegate.swift174
-rw-r--r--ios/MullvadVPN/Supporting Files/Info.plist21
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManager.swift10
6 files changed, 208 insertions, 7 deletions
diff --git a/ios/MullvadSettings/IPOverride.swift b/ios/MullvadSettings/IPOverride.swift
index ec59ebc384..f530a7c373 100644
--- a/ios/MullvadSettings/IPOverride.swift
+++ b/ios/MullvadSettings/IPOverride.swift
@@ -11,6 +11,10 @@ import Network
public struct RelayOverrides: Codable, Sendable {
public let overrides: [IPOverride]
+ public init(overrides: [IPOverride]) {
+ self.overrides = overrides
+ }
+
private enum CodingKeys: String, CodingKey {
case overrides = "relay_overrides"
}
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 26406d4230..d8c3254d97 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -1108,8 +1108,8 @@
F910A4312D4A1B41002FF3BB /* InAppPurchaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F910A4302D4A1B3B002FF3BB /* InAppPurchaseCoordinator.swift */; };
F910A43A2D4A283D002FF3BB /* InAppPurchaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F910A4392D4A2839002FF3BB /* InAppPurchaseViewController.swift */; };
F910A8572D523812002FF3BB /* TunnelSettingsV7.swift in Sources */ = {isa = PBXBuildFile; fileRef = F910A8562D523812002FF3BB /* TunnelSettingsV7.swift */; };
- F924C65F2DAE4554001F4660 /* ServerRelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F924C65E2DAE4554001F4660 /* ServerRelayTests.swift */; };
F924C4532D70692E001F4660 /* MullvadApiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F924C4522D706929001F4660 /* MullvadApiTests.swift */; };
+ F924C65F2DAE4554001F4660 /* ServerRelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F924C65E2DAE4554001F4660 /* ServerRelayTests.swift */; };
F998EFF82D359C4600D88D01 /* SKProduct+Formatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */; };
F998EFFA2D3656BA00D88D01 /* SKProduct+Sorting.swift in Sources */ = {isa = PBXBuildFile; fileRef = F998EFF92D3656B100D88D01 /* SKProduct+Sorting.swift */; };
/* End PBXBuildFile section */
@@ -2520,8 +2520,8 @@
F910A4302D4A1B3B002FF3BB /* InAppPurchaseCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPurchaseCoordinator.swift; sourceTree = "<group>"; };
F910A4392D4A2839002FF3BB /* InAppPurchaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPurchaseViewController.swift; sourceTree = "<group>"; };
F910A8562D523812002FF3BB /* TunnelSettingsV7.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsV7.swift; sourceTree = "<group>"; };
- F924C65E2DAE4554001F4660 /* ServerRelayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRelayTests.swift; sourceTree = "<group>"; };
F924C4522D706929001F4660 /* MullvadApiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadApiTests.swift; sourceTree = "<group>"; };
+ F924C65E2DAE4554001F4660 /* ServerRelayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerRelayTests.swift; sourceTree = "<group>"; };
F998EFF92D3656B100D88D01 /* SKProduct+Sorting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SKProduct+Sorting.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
diff --git a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift
index f4076787e3..bdf8399a96 100644
--- a/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/IPOverride/IPOverrideInteractor.swift
@@ -54,7 +54,7 @@ final class IPOverrideInteractor {
resetToDefaultStatus()
}
- private func handleImport(of data: Data, context: IPOverrideStatus.Context) {
+ func handleImport(of data: Data, context: IPOverrideStatus.Context) {
do {
let overrides = try repository.parse(data: data)
diff --git a/ios/MullvadVPN/SceneDelegate.swift b/ios/MullvadVPN/SceneDelegate.swift
index feec718687..b5839e6af1 100644
--- a/ios/MullvadVPN/SceneDelegate.swift
+++ b/ios/MullvadVPN/SceneDelegate.swift
@@ -10,7 +10,9 @@ import MullvadLogging
import MullvadREST
import MullvadSettings
import MullvadTypes
+import Network
import Operations
+import Routing
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate, @preconcurrency SettingsMigrationUIHandler {
@@ -180,6 +182,178 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, @preconcurrency Setting
}
}
+ // swiftlint:disable:next function_body_length
+ func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
+ do {
+ let url = URLContexts.first!.url
+
+ guard
+ let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
+ let albumPath = components.path,
+ let params = components.queryItems
+ else {
+ throw NSError(domain: "", code: 1, userInfo: nil)
+ }
+
+ var currentSettings = tunnelManager.settings
+
+ switch albumPath {
+ case "settings":
+ try params.forEach { param in
+ switch param.name {
+ case "daita":
+ currentSettings.daita.daitaState = param.value == "on" ? .on : .off
+ case "directOnly":
+ currentSettings.daita.directOnlyState = param.value == "on" ? .on : .off
+ case "multihop":
+ currentSettings.tunnelMultihopState = param.value == "on" ? .on : .off
+ case "quantumResistance":
+ currentSettings.tunnelQuantumResistance = param.value == "on" ? .on : .off
+ case "obfuscation":
+ var state: WireGuardObfuscationState = .automatic
+ var port: Int?
+
+ let components = param.value!.split(separator: ",")
+
+ try components.forEach { component in
+ let keyValue = component.split(separator: "=")
+
+ switch keyValue.first! {
+ case "state":
+ switch keyValue.last! {
+ case "automatic":
+ state = .automatic
+ case "off":
+ state = .off
+ case "shadowsocks":
+ state = .shadowsocks
+ case "udpOverTcp":
+ state = .udpOverTcp
+ default:
+ throw NSError(domain: "", code: 2, userInfo: nil)
+ }
+ case "port":
+ port = Int(keyValue.last!)
+ default:
+ throw NSError(domain: "", code: 3, userInfo: nil)
+ }
+ }
+
+ currentSettings.wireGuardObfuscation.state = state
+
+ switch state {
+ case .shadowsocks:
+ let shadowSocksPort = port.flatMap {
+ WireGuardObfuscationShadowsocksPort.custom(UInt16($0))
+ } ?? WireGuardObfuscationShadowsocksPort.automatic
+
+ currentSettings.wireGuardObfuscation.shadowsocksPort = shadowSocksPort
+ case .udpOverTcp:
+ let udpTcpPort: WireGuardObfuscationUdpOverTcpPort = switch port {
+ case nil:
+ .automatic
+ case 80:
+ .port80
+ case 5001:
+ .port5001
+ default:
+ throw NSError(domain: "", code: 4, userInfo: nil)
+ }
+
+ currentSettings.wireGuardObfuscation.udpOverTcpPort = udpTcpPort
+ default:
+ break
+ }
+ default:
+ throw NSError(domain: "", code: 5, userInfo: nil)
+ }
+ }
+ case "ipOverrides":
+ var overrides: [IPOverride] = []
+
+ try params.forEach { param in
+ var hostname = ""
+ var ipv4: IPv4Address?
+ var ipv6: IPv6Address?
+
+ let hostComponents = param.value!.split(separator: ",")
+
+ try hostComponents.forEach { component in
+ let keyValue = component.split(separator: "=")
+
+ switch keyValue.first! {
+ case "hostname":
+ hostname = String(keyValue.last!)
+ case "ipv4_addr_in":
+ ipv4 = keyValue.last.flatMap { IPv4Address(String($0)) }
+ case "ipv6_addr_in":
+ ipv6 = keyValue.last.flatMap { IPv6Address(String($0)) }
+ default:
+ throw NSError(domain: "", code: 6, userInfo: nil)
+ }
+ }
+
+ try overrides.append(IPOverride(hostname: hostname, ipv4Address: ipv4, ipv6Address: ipv6))
+ }
+
+ let interactor = IPOverrideInteractor(repository: IPOverrideRepository(), tunnelManager: tunnelManager)
+ try interactor.handleImport(of: JSONEncoder().encode(RelayOverrides(overrides: overrides)), context: .text)
+ default:
+ throw NSError(domain: "", code: 7, userInfo: nil)
+ }
+
+ tunnelManager.updateSettings(currentSettings)
+
+ let presentation = AlertPresentation(
+ id: "import-successful",
+ icon: .info,
+ title: NSLocalizedString(
+ "SHARE_IMPORT_SUCCESSFUL_TITLE",
+ tableName: "Settings",
+ value: "Success!",
+ comment: ""
+ ),
+ message: NSLocalizedString(
+ "SHARE_IMPORT_SUCCESSFUL_MESSAGE",
+ tableName: "Settings",
+ value: "The new settings were successfully applied.",
+ comment: ""
+ ),
+ buttons: [
+ AlertAction(title: "Got it!", style: .default)
+ ]
+ )
+
+ let alert = AlertPresenter(context: appCoordinator)
+ alert.showAlert(presentation: presentation, animated: true)
+ } catch {
+ print(error)
+
+ let presentation = AlertPresentation(
+ id: "import-unsuccessful",
+ icon: .warning,
+ title: NSLocalizedString(
+ "SHARE_IMPORT_SUCCESSFUL_TITLE",
+ tableName: "Settings",
+ value: "Failure!",
+ comment: ""
+ ),
+ message: NSLocalizedString(
+ "SHARE_IMPORT_SUCCESSFUL_MESSAGE",
+ tableName: "Settings",
+ value: "The new settings could not be applied.",
+ comment: ""
+ ),
+ buttons: [
+ AlertAction(title: "Got it!", style: .default)
+ ]
+ )
+
+ let alert = AlertPresenter(context: appCoordinator)
+ alert.showAlert(presentation: presentation, animated: true)
+ }
+ }
+
func sceneDidDisconnect(_ scene: UIScene) {}
func sceneDidBecomeActive(_ scene: UIScene) {
diff --git a/ios/MullvadVPN/Supporting Files/Info.plist b/ios/MullvadVPN/Supporting Files/Info.plist
index 14af71cdc7..b8ba272e94 100644
--- a/ios/MullvadVPN/Supporting Files/Info.plist
+++ b/ios/MullvadVPN/Supporting Files/Info.plist
@@ -2,10 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>HostName</key>
- <string>$(HOST_NAME)</string>
- <key>NSLocalNetworkUsageDescription</key>
- <string>The app needs this to connect and test a new method.</string>
<key>ApplicationSecurityGroupIdentifier</key>
<string>$(SECURITY_GROUP_IDENTIFIER)</string>
<key>BGTaskSchedulerPermittedIdentifiers</key>
@@ -30,10 +26,25 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
+ <key>CFBundleURLTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ <key>CFBundleURLName</key>
+ <string>net.mullvad.MullvadVPN</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>mullvad</string>
+ </array>
+ </dict>
+ </array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>HasTimeAccountNumber</key>
<string>$(HAS_TIME_ACCOUNT_NUMBER)</string>
+ <key>HostName</key>
+ <string>$(HOST_NAME)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
@@ -45,6 +56,8 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
+ <key>NSLocalNetworkUsageDescription</key>
+ <string>The app needs this to connect and test a new method.</string>
<key>NSUserActivityTypes</key>
<array>
<string>StartVPNIntent</string>
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
index af1a41f124..50f132b52a 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
@@ -558,6 +558,16 @@ final class TunnelManager: StorePaymentObserver, @unchecked Sendable {
)
}
+ func updateSettings(_ newSettings: LatestTunnelSettings, completionHandler: (@Sendable () -> Void)? = nil) {
+ scheduleSettingsUpdate(
+ taskName: "Update tunnel settings",
+ modificationBlock: { settings in
+ settings = newSettings
+ },
+ completionHandler: completionHandler
+ )
+ }
+
func refreshRelayCacheTracker() throws {
try relayCacheTracker.refreshCachedRelays()
}