summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@kvadrat.se>2023-10-05 14:47:36 +0200
committerBug Magnet <marco.nikic@mullvad.net>2023-10-09 14:54:58 +0200
commit845d2586b5c10b9a47d3deced295d57c944ce932 (patch)
treed94465ce975e9eaa372ca4fa5d243a02a509b5c2
parentbb2caa8fa93b9272817d22c08ed6f4e5371a35ac (diff)
downloadmullvadvpn-845d2586b5c10b9a47d3deced295d57c944ce932.tar.xz
mullvadvpn-845d2586b5c10b9a47d3deced295d57c944ce932.zip
Add UI for blocked state
-rw-r--r--ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift44
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManager.swift35
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift19
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift4
4 files changed, 70 insertions, 32 deletions
diff --git a/ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift b/ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift
index 87164e0eaa..d36c41303c 100644
--- a/ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift
+++ b/ios/MullvadVPN/Notifications/Notification Providers/TunnelStatusNotificationProvider.swift
@@ -7,11 +7,12 @@
//
import Foundation
+import PacketTunnelCore
final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotificationProvider {
private var isWaitingForConnectivity = false
private var noNetwork = false
- private var packetTunnelError: String?
+ private var packetTunnelError: BlockedStateReason?
private var tunnelManagerError: Error?
private var tunnelObserver: TunnelBlockObserver?
@@ -57,7 +58,7 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
private func handleTunnelStatus(_ tunnelStatus: TunnelStatus) {
let invalidateForTunnelError: Bool
if case let .error(blockStateReason) = tunnelStatus.state {
- invalidateForTunnelError = updateLastTunnelError(blockStateReason.rawValue)
+ invalidateForTunnelError = updateLastTunnelError(blockStateReason)
} else {
invalidateForTunnelError = updateLastTunnelError(nil)
}
@@ -71,7 +72,7 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
}
}
- private func updateLastTunnelError(_ lastTunnelError: String?) -> Bool {
+ private func updateLastTunnelError(_ lastTunnelError: BlockedStateReason?) -> Bool {
if packetTunnelError != lastTunnelError {
packetTunnelError = lastTunnelError
@@ -120,22 +121,21 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
return false
}
- private func notificationDescription(for packetTunnelError: String) -> InAppNotificationDescriptor {
+ private func notificationDescription(for packetTunnelError: BlockedStateReason) -> InAppNotificationDescriptor {
InAppNotificationDescriptor(
identifier: identifier,
style: .error,
title: NSLocalizedString(
- "TUNNEL_LEAKING_INAPP_NOTIFICATION_TITLE",
- value: "NETWORK TRAFFIC MIGHT BE LEAKING",
+ "TUNNEL_BLOCKED_INAPP_NOTIFICATION_TITLE",
+ value: "BLOCKING INTERNET",
comment: ""
),
body: .init(string: String(
format: NSLocalizedString(
- "PACKET_TUNNEL_ERROR_INAPP_NOTIFICATION_BODY",
- value: "Could not configure VPN: %@",
+ "TUNNEL_BLOCKED_INAPP_NOTIFICATION_BODY",
+ value: localizedReasonForBlockedStateError(packetTunnelError),
comment: ""
- ),
- packetTunnelError
+ )
))
)
}
@@ -220,4 +220,28 @@ final class TunnelStatusNotificationProvider: NotificationProvider, InAppNotific
)
)
}
+
+ private func localizedReasonForBlockedStateError(_ error: BlockedStateReason) -> String {
+ let errorString: String
+
+ switch error {
+ case .outdatedSchema:
+ errorString = "Unable to start tunnel connection after update. Please send a problem report."
+ case .noRelaysSatisfyingConstraints:
+ errorString = "No servers match your settings, try changing server or other settings."
+ case .invalidAccount:
+ errorString = "You are logged in with an invalid account number. Please log out and try another one."
+ case .deviceRevoked, .deviceLoggedOut:
+ errorString = "Unable to authenticate account. Please log out and log back in."
+ default:
+ errorString = "Unable to start tunnel connection. Please send a problem report."
+ }
+
+ return NSLocalizedString(
+ "BLOCKED_STATE_ERROR_TITLE",
+ tableName: "Main",
+ value: errorString,
+ comment: ""
+ )
+ }
}
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
index 9966b40fc7..1f27c66395 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
@@ -289,6 +289,12 @@ final class TunnelManager: StorePaymentObserver {
finish(result.error)
}
} catch {
+ if let error = error as? NoRelaysSatisfyingConstraintsError {
+ _ = self.setTunnelStatus { tunnelStatus in
+ tunnelStatus.state = .error(.noRelaysSatisfyingConstraints)
+ }
+ }
+
finish(error)
return AnyCancellable()
@@ -703,22 +709,30 @@ final class TunnelManager: StorePaymentObserver {
lastPacketTunnelKeyRotation = newPacketTunnelKeyRotation
refreshDeviceState()
}
-
- // TODO: handle blocked state (error state). See how handleRestError() manages invalid account or revoked device.
-
switch newTunnelStatus.state {
case .connecting, .reconnecting:
// Start polling tunnel status to keep the relay information up to date
// while the tunnel process is trying to connect.
startPollingTunnelStatus(interval: establishingTunnelStatusPollInterval)
- case .connected, .waitingForConnectivity(.noConnection), .error:
+ case .connected, .waitingForConnectivity(.noConnection):
// Start polling tunnel status to keep connectivity status up to date.
startPollingTunnelStatus(interval: establishedTunnelStatusPollInterval)
case .pendingReconnect, .disconnecting, .disconnected, .waitingForConnectivity(.noNetwork):
// Stop polling tunnel status once connection moved to final state.
cancelPollingTunnelStatus()
+
+ case let .error(blockedStateReason):
+ switch blockedStateReason {
+ case .deviceRevoked, .invalidAccount:
+ handleBlockedState(reason: blockedStateReason)
+ default:
+ break
+ }
+
+ // Stop polling tunnel status once blocked state has been determined.
+ cancelPollingTunnelStatus()
}
DispatchQueue.main.async {
@@ -1084,13 +1098,24 @@ final class TunnelManager: StorePaymentObserver {
guard let restError = error as? REST.Error else { return }
if restError.compareErrorCode(.deviceNotFound) {
- setDeviceState(.revoked, persist: true)
+ handleBlockedState(reason: .deviceRevoked)
} else if restError.compareErrorCode(.invalidAccount) {
+ handleBlockedState(reason: .invalidAccount)
+ }
+ }
+
+ private func handleBlockedState(reason: BlockedStateReason) {
+ switch reason {
+ case .deviceRevoked:
+ setDeviceState(.revoked, persist: true)
+ case .invalidAccount:
unsetTunnelConfiguration {
self.setDeviceState(.revoked, persist: true)
self.operationQueue.cancelAllOperations()
self.wipeAllUserData()
}
+ default:
+ break
}
}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
index cf76e00d3e..dc6669fb47 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
@@ -496,7 +496,7 @@ private extension TunnelState {
comment: ""
)
- case .waitingForConnectivity(.noConnection):
+ case .waitingForConnectivity(.noConnection), .error:
return NSLocalizedString(
"TUNNEL_STATE_WAITING_FOR_CONNECTIVITY",
tableName: "Main",
@@ -511,10 +511,6 @@ private extension TunnelState {
value: "No network",
comment: ""
)
-
- case let .error(blockedStateReason):
- // TODO: Fix me
- return ""
}
}
@@ -535,17 +531,14 @@ private extension TunnelState {
value: "Select location",
comment: ""
)
- case .connecting, .connected, .reconnecting, .waitingForConnectivity:
+
+ case .connecting, .connected, .reconnecting, .waitingForConnectivity, .error:
return NSLocalizedString(
"SWITCH_LOCATION_BUTTON_TITLE",
tableName: "Main",
value: "Switch location",
comment: ""
)
-
- case let .error(blockedStateReason):
- // TODO: Fix me
- return ""
}
}
@@ -591,7 +584,7 @@ private extension TunnelState {
tunnelInfo.location.country
)
- case .waitingForConnectivity(.noConnection):
+ case .waitingForConnectivity(.noConnection), .error:
return NSLocalizedString(
"TUNNEL_STATE_WAITING_FOR_CONNECTIVITY_ACCESSIBILITY_LABEL",
tableName: "Main",
@@ -622,10 +615,6 @@ private extension TunnelState {
value: "Reconnecting",
comment: ""
)
-
- case let .error(blockedStateReason):
- // TODO: Fix me
- return ""
}
}
diff --git a/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift b/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift
index a31c508288..f7d22d16db 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/BlockedStateErrorMapper.swift
@@ -17,8 +17,8 @@ import WireGuardKit
/**
Struct responsible for mapping errors that may occur in the packet tunnel to the `BlockedStateReason`.
*/
-struct BlockedStateErrorMapper: BlockedStateErrorMapperProtocol {
- func mapError(_ error: Error) -> BlockedStateReason {
+public struct BlockedStateErrorMapper: BlockedStateErrorMapperProtocol {
+ public func mapError(_ error: Error) -> BlockedStateReason {
switch error {
case let error as ReadDeviceDataError:
// Such error is thrown by implementations of `SettingsReaderProtocol`.