Randomize provider server (#263)

* Pick random server within location

* Add toggle to provider section in profile
This commit is contained in:
Davide De Rosa 2023-03-19 08:19:32 +01:00 committed by GitHub
parent c85f3d894e
commit 17ae9793df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 125 additions and 64 deletions

View File

@ -77,7 +77,7 @@ class DefaultLightProfileManager: LightProfileManager {
$0.header < $1.header
}.map {
let server: ProviderServer?
if let serverId = $0.providerServerId() {
if let serverId = $0.providerServerId {
server = providerManager.server(withId: serverId)
} else {
server = nil

View File

@ -92,13 +92,13 @@ extension EndpointView {
}
_customEndpoint = .init {
if currentProfile.value.isProvider {
return currentProfile.value.providerCustomEndpoint()
return currentProfile.value.providerCustomEndpoint
} else {
return currentProfile.value.hostOpenVPNSettings?.customEndpoint
}
} set: {
if currentProfile.value.isProvider {
currentProfile.value.setProviderCustomEndpoint($0)
currentProfile.value.providerCustomEndpoint = $0
} else {
currentProfile.value.hostOpenVPNSettings?.customEndpoint = $0
}

View File

@ -35,16 +35,6 @@ extension ProfileView {
}
var body: some View {
if currentProfile.value.isProvider {
Section {
Toggle(
L10n.Profile.Items.VpnResolvesHostname.caption,
isOn: $currentProfile.value.networkSettings.resolvesHostname
)
} footer: {
Text(L10n.Profile.Sections.VpnResolvesHostname.footer)
}
}
Section {
Toggle(
L10n.Profile.Items.VpnSurvivesSleep.caption,

View File

@ -77,6 +77,18 @@ extension ProfileView {
} footer: {
currentProviderServerDescription.map(Text.init)
}
Section {
Toggle(
L10n.Profile.Items.RandomizesServer.caption,
isOn: $currentProfile.value.providerRandomizesServer ?? false
)
Toggle(
L10n.Profile.Items.VpnResolvesHostname.caption,
isOn: $currentProfile.value.networkSettings.resolvesHostname
)
} footer: {
Text(L10n.Profile.Sections.VpnResolvesHostname.footer)
}
Section {
NavigationLink {
ProviderPresetView(currentProfile: currentProfile)
@ -107,7 +119,14 @@ extension ProfileView {
}
private var currentProviderServerDescription: String? {
profile.providerServer(providerManager)?.localizedLongDescription(withCategory: true)
guard let server = profile.providerServer(providerManager) else {
return nil
}
if currentProfile.value.providerRandomizesServer ?? false {
return server.localizedCountry(withCategory: true)
} else {
return server.localizedLongDescription(withCategory: true)
}
}
private var currentProviderCountryImage: Image? {

View File

@ -71,7 +71,7 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
self.isEditable = isEditable
_selectedServer = .init {
guard let serverId = currentProfile.value.providerServerId() else {
guard let serverId = currentProfile.value.providerServerId else {
return nil
}
return providerManager.server(withId: serverId)
@ -84,9 +84,9 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
isPresented.wrappedValue = false
}
_favoriteLocationIds = .init {
currentProfile.value.providerFavoriteLocationIds()
currentProfile.value.providerFavoriteLocationIds
} set: {
currentProfile.value.setProviderFavoriteLocationIds($0)
currentProfile.value.providerFavoriteLocationIds = $0
}
}
@ -151,6 +151,8 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
private func locationRow(_ location: ProviderLocation) -> some View {
if let onlyServer = location.onlyServer {
singleServerRow(location, onlyServer)
} else if profile.providerRandomizesServer ?? false {
singleServerRow(location, nil)
} else {
multipleServersRow(location)
}
@ -170,9 +172,9 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
})
}
private func singleServerRow(_ location: ProviderLocation, _ server: ProviderServer) -> some View {
private func singleServerRow(_ location: ProviderLocation, _ server: ProviderServer?) -> some View {
Button {
selectedServer = server
selectedServer = server ?? location.servers?.randomElement()
} label: {
LocationRow(
location: location,

View File

@ -46,7 +46,7 @@ struct ProviderPresetView: View {
server = currentProfile.value.providerServer(providerManager)
_selectedPreset = .init {
guard let serverId = currentProfile.value.providerServerId() else {
guard let serverId = currentProfile.value.providerServerId else {
return nil
}
guard let server = providerManager.server(withId: serverId) else {

View File

@ -795,6 +795,10 @@ internal enum L10n {
internal static let caption = L10n.tr("Localizable", "profile.items.provider.refresh.caption", fallback: "Refresh infrastructure")
}
}
internal enum RandomizesServer {
/// Randomize server
internal static let caption = L10n.tr("Localizable", "profile.items.randomizes_server.caption", fallback: "Randomize server")
}
internal enum UseProfile {
/// Use this profile
internal static let caption = L10n.tr("Localizable", "profile.items.use_profile.caption", fallback: "Use this profile")

View File

@ -70,6 +70,14 @@ extension ProviderServer {
countryCode.localizedAsCountryCode
}
func localizedCountry(withCategory: Bool) -> String {
let desc = localizedCountry
if withCategory, !categoryName.isEmpty {
return "\(categoryName.uppercased()): \(desc)"
}
return desc
}
var localizedShortDescription: String? {
var comps = localizedName.map { [$0] } ?? []
if let serverIndex = serverIndex {

View File

@ -152,6 +152,7 @@
"profile.items.vpn.turn_off.caption" = "Disable VPN";
"profile.items.connection_status.caption" = "Status";
"profile.items.data_count.caption" = "Exchanged data";
"profile.items.randomizes_server.caption" = "Randomize server";
"profile.items.provider.refresh.caption" = "Refresh infrastructure";
"profile.items.category.caption" = "Category";
"profile.items.only_shows_favorites.caption" = "Only show favorite locations";

View File

@ -27,23 +27,24 @@ import Foundation
import TunnelKitCore
extension Profile {
public func hostAccount() -> Profile.Account? {
switch currentVPNProtocol {
case .openVPN:
return host?.ovpnSettings?.account
public var hostAccount: Profile.Account? {
get {
switch currentVPNProtocol {
case .openVPN:
return host?.ovpnSettings?.account
case .wireGuard:
return nil
case .wireGuard:
return nil
}
}
}
set {
switch currentVPNProtocol {
case .openVPN:
host?.ovpnSettings?.account = newValue
public mutating func setHostAccount(_ account: Profile.Account?) {
switch currentVPNProtocol {
case .openVPN:
host?.ovpnSettings?.account = account
case .wireGuard:
break
case .wireGuard:
break
}
}
}

View File

@ -41,16 +41,16 @@ extension Profile {
public var account: Profile.Account {
get {
if isProvider {
return providerAccount() ?? .init()
return providerAccount ?? .init()
} else {
return hostAccount() ?? .init()
return hostAccount ?? .init()
}
}
set {
if isProvider {
setProviderAccount(newValue)
providerAccount = newValue
} else {
setHostAccount(newValue)
hostAccount = newValue
}
}
}

View File

@ -45,7 +45,7 @@ extension Profile {
provider?.name
}
public func providerServerId() -> String? {
public var providerServerId: String? {
provider?.vpnSettings[currentVPNProtocol]?.serverId
}
@ -71,28 +71,40 @@ extension Profile {
provider?.vpnSettings[currentVPNProtocol]?.presetId = preset.id
}
public func providerFavoriteLocationIds() -> Set<String>? {
provider?.vpnSettings[currentVPNProtocol]?.favoriteLocationIds
public var providerFavoriteLocationIds: Set<String>? {
get {
provider?.vpnSettings[currentVPNProtocol]?.favoriteLocationIds
}
set {
provider?.vpnSettings[currentVPNProtocol]?.favoriteLocationIds = newValue
}
}
public mutating func setProviderFavoriteLocationIds(_ ids: Set<String>?) {
provider?.vpnSettings[currentVPNProtocol]?.favoriteLocationIds = ids
public var providerCustomEndpoint: Endpoint? {
get {
provider?.vpnSettings[currentVPNProtocol]?.customEndpoint
}
set {
provider?.vpnSettings[currentVPNProtocol]?.customEndpoint = newValue
}
}
public func providerCustomEndpoint() -> Endpoint? {
provider?.vpnSettings[currentVPNProtocol]?.customEndpoint
public var providerAccount: Profile.Account? {
get {
provider?.vpnSettings[currentVPNProtocol]?.account
}
set {
provider?.vpnSettings[currentVPNProtocol]?.account = newValue
}
}
public mutating func setProviderCustomEndpoint(_ endpoint: Endpoint?) {
provider?.vpnSettings[currentVPNProtocol]?.customEndpoint = endpoint
}
public func providerAccount() -> Profile.Account? {
provider?.vpnSettings[currentVPNProtocol]?.account
}
public mutating func setProviderAccount(_ account: Profile.Account?) {
provider?.vpnSettings[currentVPNProtocol]?.account = account
public var providerRandomizesServer: Bool? {
get {
provider?.randomizesServer
}
set {
provider?.randomizesServer = newValue
}
}
}

View File

@ -47,6 +47,8 @@ extension Profile {
public var vpnSettings: [VPNProtocolType: Settings] = [:]
public var randomizesServer: Bool?
public init(_ name: ProviderName) {
self.name = name
}

View File

@ -39,7 +39,7 @@ extension Profile {
extension Profile {
public func providerServer(_ providerManager: ProviderManager) -> ProviderServer? {
guard let serverId = providerServerId() else {
guard let serverId = providerServerId else {
return nil
}
return providerManager.server(withId: serverId)
@ -51,9 +51,21 @@ extension Profile {
}
// infer remotes from preset + server
guard let server = providerServer(providerManager) else {
guard let selectedServer = providerServer(providerManager) else {
throw PassepartoutError.missingProviderServer
}
let server: ProviderServer
if providerRandomizesServer ?? false {
let location = selectedServer.location(withVPNProtocol: currentVPNProtocol)
let servers = providerManager.servers(forLocation: location)
guard let randomServerId = servers.randomElement()?.id,
let randomServer = providerManager.server(withId: randomServerId) else {
throw PassepartoutError.missingProviderServer
}
server = randomServer
} else {
server = selectedServer
}
guard let preset = providerPreset(server) else {
throw PassepartoutError.missingProviderPreset
}
@ -68,8 +80,8 @@ extension Profile {
// apply provider settings (username, custom endpoint)
let cfg = builder.build()
var settings = OpenVPNSettings(configuration: cfg)
settings.account = providerAccount()
settings.customEndpoint = providerCustomEndpoint()
settings.account = providerAccount
settings.customEndpoint = providerCustomEndpoint
return settings
}

View File

@ -45,6 +45,16 @@ extension ProviderServer {
public var locationId: String {
"\(providerMetadata.name):\(categoryName):\(countryCode)"
}
public func location(withVPNProtocol vpnProtocol: VPNProtocolType) -> ProviderLocation {
ProviderLocation(
providerMetadata: providerMetadata,
vpnProtocol: vpnProtocol,
categoryName: categoryName,
countryCode: countryCode,
servers: nil
)
}
}
extension ProviderServer {

View File

@ -85,7 +85,7 @@ extension VPNManager {
try await profileManager.makeProfileReady(profile)
}
let oldServerId = profile.providerServerId()
let oldServerId = profile.providerServerId
guard let newServer = providerManager.server(withId: newServerId) else {
pp_log.warning("Server \(newServerId) not found")
throw PassepartoutError.missingProviderServer

View File

@ -206,15 +206,15 @@ extension VPNManager {
if newProfile.isProvider {
// server changed?
if newProfile.providerServerId() != lastProfile.providerServerId() {
pp_log.info("Provider server changed: \(newProfile.providerServerId()?.description ?? "nil")")
if newProfile.providerServerId != lastProfile.providerServerId {
pp_log.info("Provider server changed: \(newProfile.providerServerId?.description ?? "nil")")
isHandled = true
shouldReconnect = notDisconnected
}
// endpoint changed?
else if newProfile.providerCustomEndpoint() != lastProfile.providerCustomEndpoint() {
pp_log.info("Provider endpoint changed: \(newProfile.providerCustomEndpoint()?.description ?? "automatic")")
else if newProfile.providerCustomEndpoint != lastProfile.providerCustomEndpoint {
pp_log.info("Provider endpoint changed: \(newProfile.providerCustomEndpoint?.description ?? "automatic")")
isHandled = true
shouldReconnect = notDisconnected
}