Commit 00998add authored by Thomas Markiewicz's avatar Thomas Markiewicz

Merge branch 'master' of github-purism:t0mmar/client-library-apple

parents d28c54a4 cb5134e1
Pod::Spec.new do |s|
s.name = "PIALibrary"
s.version = "2.8.2"
s.version = "2.9.0"
s.summary = "PIA client library in Swift."
s.homepage = "https://www.privateinternetaccess.com/"
......
......@@ -246,6 +246,12 @@
82C374F82514DE7D00E391EE /* EndpointManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C374F42514DC6D00E391EE /* EndpointManagerTests.swift */; };
82C374F92514DE8200E391EE /* server.json in Resources */ = {isa = PBXBuildFile; fileRef = 82C374F62514DC7200E391EE /* server.json */; };
82C374FB2514DEC700E391EE /* EndpointManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C374FA2514DEC700E391EE /* EndpointManager.swift */; };
82C4962925642D5800233CB1 /* String+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C4962825642D5800233CB1 /* String+Random.swift */; };
82C4962A25642D5800233CB1 /* String+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82C4962825642D5800233CB1 /* String+Random.swift */; };
82CAB808255A9ACB00BB08EF /* InAppMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB807255A9ACB00BB08EF /* InAppMessage.swift */; };
82CAB809255A9ACB00BB08EF /* InAppMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAB807255A9ACB00BB08EF /* InAppMessage.swift */; };
82CDC2E2257A5B440001669D /* DateUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CDC2E1257A5B440001669D /* DateUtil.swift */; };
82CDC2E3257A5B440001669D /* DateUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CDC2E1257A5B440001669D /* DateUtil.swift */; };
82DDD5302539CFDC0049E79E /* DIPTokenKeychainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82DDD52F2539CFDC0049E79E /* DIPTokenKeychainTests.swift */; };
82E20B1124F652ED0065EFE3 /* AccountInfo+Kotlin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82E20B1024F652ED0065EFE3 /* AccountInfo+Kotlin.swift */; };
82E20B1224F652ED0065EFE3 /* AccountInfo+Kotlin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82E20B1024F652ED0065EFE3 /* AccountInfo+Kotlin.swift */; };
......@@ -568,6 +574,9 @@
82C374F42514DC6D00E391EE /* EndpointManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointManagerTests.swift; sourceTree = "<group>"; };
82C374F62514DC7200E391EE /* server.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = server.json; sourceTree = "<group>"; };
82C374FA2514DEC700E391EE /* EndpointManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndpointManager.swift; sourceTree = "<group>"; };
82C4962825642D5800233CB1 /* String+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Random.swift"; sourceTree = "<group>"; };
82CAB807255A9ACB00BB08EF /* InAppMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppMessage.swift; sourceTree = "<group>"; };
82CDC2E1257A5B440001669D /* DateUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateUtil.swift; sourceTree = "<group>"; };
82DDD52F2539CFDC0049E79E /* DIPTokenKeychainTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DIPTokenKeychainTests.swift; sourceTree = "<group>"; };
82E20B1024F652ED0065EFE3 /* AccountInfo+Kotlin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountInfo+Kotlin.swift"; sourceTree = "<group>"; };
82E20B1524F6AA110065EFE3 /* RegionData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionData.swift; sourceTree = "<group>"; };
......@@ -695,6 +704,7 @@
DDD824E4218996CD00151709 /* Pages.swift */,
DD76292D21ECEC3F0092DF50 /* DataManipulation.swift */,
DD6FB0362224355600A84F05 /* UIDevice+WiFi.swift */,
82CDC2E1257A5B440001669D /* DateUtil.swift */,
);
path = Util;
sourceTree = "<group>";
......@@ -719,6 +729,7 @@
0E492C591FE5EA06007F23DF /* CMacros.h */,
0E492C5A1FE5EA06007F23DF /* CMacros.m */,
DD58F4BC21AEF76100D043F7 /* String+Components.swift */,
82C4962825642D5800233CB1 /* String+Random.swift */,
);
path = iOS;
sourceTree = "<group>";
......@@ -865,6 +876,7 @@
DD6768E222FAB19D00B9FDD0 /* AppStoreInformation.swift */,
82E20B1524F6AA110065EFE3 /* RegionData.swift */,
829EB63E2535C432003E74DD /* DedicatedIP.swift */,
82CAB807255A9ACB00BB08EF /* InAppMessage.swift */,
);
path = WebServices;
sourceTree = "<group>";
......@@ -1646,6 +1658,7 @@
buildActionMask = 2147483647;
files = (
0E492C5F1FE5F7C0007F23DF /* MockAccountProvider.swift in Sources */,
82CDC2E3257A5B440001669D /* DateUtil.swift in Sources */,
0E392DBC1FE34B5A0002160D /* NSString+URL.m in Sources */,
84125E0C213D7E0E001BCC19 /* PIAFonts.swift in Sources */,
0E3D13DA1F9E273300434A48 /* GlossSignup.swift in Sources */,
......@@ -1659,6 +1672,7 @@
0E3D13D41F9E270A00434A48 /* GlossAccountInfo.swift in Sources */,
0E9D62D31FDEBBFD009A90CF /* GlossServer.swift in Sources */,
82FD5D672521F32B00E390CB /* PIAAccountStagingClientStateProvider.swift in Sources */,
82C4962A25642D5800233CB1 /* String+Random.swift in Sources */,
0E9D62DB1FDEE3FE009A90CF /* ServerProvider.swift in Sources */,
0E7BC6FA1F96B1120035C8B2 /* Notification+Core.swift in Sources */,
0EA4C4321FDDD48F0041C3D8 /* Server.swift in Sources */,
......@@ -1747,6 +1761,7 @@
DD8C3E652327EF6000BAD18E /* IKEv2EncryptionAlgorithm.swift in Sources */,
0EF14E4C1FEAE6350007485A /* Client+Providers.swift in Sources */,
0E7BC6E61F96B1000035C8B2 /* Client.swift in Sources */,
82CAB809255A9ACB00BB08EF /* InAppMessage.swift in Sources */,
0E392D7E1FE2E4C10002160D /* MemoryStore.swift in Sources */,
DD76292921ECDFF80092DF50 /* Usage.swift in Sources */,
0E2ADD381FE14F0000BB170C /* DefaultVPNProvider.swift in Sources */,
......@@ -1775,6 +1790,7 @@
DD76292E21ECEC3F0092DF50 /* DataManipulation.swift in Sources */,
DDFCFABA21E929660081F235 /* MockTileProvider.swift in Sources */,
0EA4C4391FDDE24B0041C3D8 /* ServersBundle.swift in Sources */,
82C4962925642D5800233CB1 /* String+Random.swift in Sources */,
0E53A84A1FE5BA52000C2A18 /* Daemon.swift in Sources */,
0E492C641FE5F949007F23DF /* MockServerProvider.swift in Sources */,
0E53A8471FE5BA0B000C2A18 /* ServersDaemon.swift in Sources */,
......@@ -1819,6 +1835,7 @@
0EC849C91F82329F002480CA /* WebServices.swift in Sources */,
0E9D62711FDE83BD009A90CF /* GlossServer.swift in Sources */,
0EE14D1B1FF16B39008D9AC2 /* InvalidatingFlowLayout.swift in Sources */,
82CAB808255A9ACB00BB08EF /* InAppMessage.swift in Sources */,
82183D7F25011D200033023F /* MagicLinkLoginViewController.swift in Sources */,
0EE1068C1F8250A1009514E9 /* Plan.swift in Sources */,
DD36CB7E21CCFFFB00FC815A /* CAGradientLayer+Image.swift in Sources */,
......@@ -1890,6 +1907,7 @@
0EFEB4C12007784A00F81029 /* PIATunnelProvider+Profile.swift in Sources */,
DD8C3E612327EF4C00BAD18E /* IKEv2IntegrityAlgorithm.swift in Sources */,
0E392DA31FE3247E0002160D /* Endpoint.swift in Sources */,
82CDC2E2257A5B440001669D /* DateUtil.swift in Sources */,
0E53A8581FE5DA16000C2A18 /* MockInAppProvider.swift in Sources */,
822BC1D024BF20C90041BF9A /* UIControl+Action.swift in Sources */,
DDD824E32189969400151709 /* Preset.swift in Sources */,
......@@ -2294,7 +2312,7 @@
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MARKETING_VERSION = 2.8.0;
MARKETING_VERSION = 2.9.0;
PRODUCT_BUNDLE_IDENTIFIER = com.privateinternetaccess.apple.PIALibrary;
PRODUCT_NAME = PIALibrary;
PROVISIONING_PROFILE_SPECIFIER = "";
......@@ -2322,7 +2340,7 @@
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MARKETING_VERSION = 2.8.0;
MARKETING_VERSION = 2.9.0;
PRODUCT_BUNDLE_IDENTIFIER = com.privateinternetaccess.apple.PIALibrary;
PRODUCT_NAME = PIALibrary;
PROVISIONING_PROFILE_SPECIFIER = "";
......
......@@ -144,7 +144,14 @@ public protocol AccountProvider: class {
*/
func featureFlags(_ callback: SuccessLibraryCallback?)
/**
Returns the available messages from the API.
- Parameter version: App version.
- Parameter callback: Returns the message`InAppMessage` on success.
*/
func inAppMessages(forAppVersion version: String, _ callback: LibraryCallback<InAppMessage>?)
#if os(iOS)
/**
Lists the available plans with their corresponding product to purchase in order to get them.
......
......@@ -26,6 +26,8 @@ import Foundation
public struct NotificationKey: Hashable {
static let products = NotificationKey("ProductsKey")
public static let token = NotificationKey("TokenKey")
/// An `UserAccount` object.
public static let user = NotificationKey("UserKey")
......
......@@ -54,5 +54,9 @@ protocol SecureStore: class {
func removeDIPTokens()
func setPassword(_ password: String?, forDipToken dip: String)
func passwordReference(forDipToken dip: String) -> Data?
func clear(for username: String)
}
......@@ -31,6 +31,11 @@ public enum AvailableTiles: Int, EnumsBuilder {
case quickSettings
case favoriteServers
case connectionTile
case messages
public static func fixedTiles() -> [AvailableTiles] {
return [.messages]
}
public static func allTiles() -> [AvailableTiles] {
return [.region, .quickConnect, .ip, .subscription, .usage, .quickSettings, .favoriteServers, .connectionTile]
......
......@@ -28,6 +28,9 @@ public protocol TileProvider: class {
/// the visible tiles in the dashboard.
var visibleTiles: [AvailableTiles] { get set }
/// the fixed tiles in the dashboard.
var fixedTiles: [AvailableTiles] { get }
/// the order of the tiles to appear in the dashboard.
var orderedTiles: [AvailableTiles] { get set }
......
......@@ -20,9 +20,25 @@
//
import Foundation
import PIAAccount
public enum DedicatedIPStatus {
case active
case expired
case invalid
case error
init(fromAPIStatus dipStatus: DedicatedIPInformationResponse.Status) {
switch dipStatus {
case .invalid:
self = .invalid
case .expired:
self = .expired
case .error:
self = .error
default:
self = .active
}
}
}
//
// InAppMessage.swift
// PIALibrary
//
// Created by Jose Blaya on 10/11/2020.
// Copyright © 2020 Private Internet Access, Inc.
//
// This file is part of the Private Internet Access iOS Client.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import Foundation
import PIAAccount
public enum InAppMessageType {
case none
case action
case view
case link
}
public enum InAppMessageLevel {
case system
case api
}
public struct InAppMessage {
public let id: String
public let message: [String: String]
public let linkMessage: [String: String]?
public let type: InAppMessageType
public let level: InAppMessageLevel
public let settingAction: [String: Bool]?
public let settingView: String?
public let settingLink: String?
public init(withMessage message: [String: String], id: String, link: [String: String], type: InAppMessageType, level: InAppMessageLevel, actions: [String:Bool]?, view: String?, uri: String?) {
self.id = id
self.message = message
self.linkMessage = link
self.type = type
self.level = level
self.settingAction = actions
self.settingView = view
self.settingLink = uri
}
}
extension InAppMessage {
init(withMessage messageInformation: MessageInformation, andLevel level: InAppMessageLevel) {
self.id = "\(messageInformation.id)"
self.message = messageInformation.message
if let link = messageInformation.link {
self.linkMessage = link.text
if !link.action.settings.isEmpty {
self.type = .action
var actions = [String: Bool]()
for setting in link.action.settings {
actions[setting.key] = setting.value.boolValue
}
self.settingAction = actions
self.settingLink = nil
self.settingView = nil
} else if let uri = link.action.uri, !uri.isEmpty {
self.type = .link
self.settingLink = link.action.uri
self.settingAction = nil
self.settingView = nil
} else {
self.type = .view
self.settingView = link.action.view
self.settingLink = nil
self.settingAction = nil
}
} else {
self.type = .none
self.linkMessage = nil
self.settingLink = nil
self.settingAction = nil
self.settingView = nil
}
self.level = level
}
}
......@@ -86,7 +86,13 @@ public class Server: Hashable {
/// The response time for this address.
private(set) var responseTime: Int?
private(set) var available: Bool = true
public var description: String {
return "\(ip):0"
}
/// :nodoc:
public init(ip: String, cn: String) {
self.ip = ip
......@@ -97,6 +103,14 @@ public class Server: Hashable {
self.responseTime = time
}
func markServerAsUnavailable() {
available = false
}
func reset() {
available = true
}
}
/// The server name.
......@@ -117,11 +131,8 @@ public class Server: Hashable {
/// The server is virtually located.
public let geo: Bool
/// The best address for establishing an OpenVPN connection over TCP.
public let bestOpenVPNAddressForTCP: Address?
/// The best address for establishing an OpenVPN connection over UDP.
public let bestOpenVPNAddressForUDP: Address?
/// The server is unavailable.
public let offline: Bool
/// The best address for establishing an OpenVPN connection over TCP.
public let openVPNAddressesForTCP: [ServerAddressIP]?
......@@ -148,6 +159,8 @@ public class Server: Hashable {
public let dipToken: String?
public let dipStatus: DedicatedIPStatus?
public let dipUsername: String?
var isAutomatic: Bool
......@@ -157,8 +170,6 @@ public class Server: Hashable {
name: String,
country: String,
hostname: String,
bestOpenVPNAddressForTCP: Address?,
bestOpenVPNAddressForUDP: Address?,
openVPNAddressesForTCP: [ServerAddressIP]? = nil,
openVPNAddressesForUDP: [ServerAddressIP]? = nil,
wireGuardAddressesForUDP: [ServerAddressIP]? = nil,
......@@ -166,10 +177,12 @@ public class Server: Hashable {
pingAddress: Address?,
responseTime: Int? = 0,
geo: Bool = false,
offline: Bool = false,
meta: ServerAddressIP? = nil,
dipExpire: Date? = nil,
dipToken: String? = nil,
dipStatus: DedicatedIPStatus? = nil,
dipUsername: String? = nil,
regionIdentifier: String) {
self.serial = serial
......@@ -177,11 +190,10 @@ public class Server: Hashable {
self.country = country
self.hostname = hostname
self.geo = geo
self.offline = offline
self.regionIdentifier = regionIdentifier
identifier = hostname.components(separatedBy: ".").first ?? ""
self.bestOpenVPNAddressForTCP = bestOpenVPNAddressForTCP
self.bestOpenVPNAddressForUDP = bestOpenVPNAddressForUDP
self.openVPNAddressesForTCP = openVPNAddressesForTCP
self.openVPNAddressesForUDP = openVPNAddressesForUDP
self.wireGuardAddressesForUDP = wireGuardAddressesForUDP
......@@ -193,8 +205,9 @@ public class Server: Hashable {
self.dipExpire = dipExpire
self.dipToken = dipToken
self.dipStatus = dipStatus
isAutomatic = true
self.dipUsername = dipUsername
isAutomatic = false
}
// MARK: Hashable
......@@ -213,86 +226,70 @@ public class Server: Hashable {
extension Server {
func bestAddressForOpenVPNTCP() -> Address? {
if let addresses = openVPNAddressesForTCP {
let sorted = addresses.sorted(by: { $0.responseTime ?? 0 > $1.responseTime ?? 0 })
return nil
}
public func addresses() -> [ServerAddressIP] {
return bestOpenVPNAddressForTCP
}
func bestAddressForOpenVPNUDP() -> Address? {
if let addresses = openVPNAddressesForUDP {
let sorted = addresses.sorted(by: { $0.responseTime ?? 0 > $1.responseTime ?? 0 })
return nil
switch Client.providers.vpnProvider.currentVPNType {
case IKEv2Profile.vpnType:
return iKEv2AddressesForUDP ?? []
case PIATunnelProfile.vpnType:
return openVPNAddressesForTCP ?? []
case PIAWGTunnelProfile.vpnType:
return wireGuardAddressesForUDP ?? []
case "Mock":
return iKEv2AddressesForUDP ?? []
default:
return []
}
return bestOpenVPNAddressForUDP
}
func bestAddressForIKEv2() -> ServerAddressIP? {
public func ovpnAddresses(tcp: Bool) -> [ServerAddressIP] {
if let addresses = iKEv2AddressesForUDP {
let sorted = addresses.sorted(by: { $0.responseTime ?? 0 > $1.responseTime ?? 0 })
return sorted.first
if tcp {
return openVPNAddressesForTCP ?? []
} else {
return openVPNAddressesForUDP ?? []
}
return nil // currently using DNS
}
func bestAddressForWireGuard() -> ServerAddressIP? {
if let addresses = wireGuardAddressesForUDP {
let sorted = addresses.sorted(by: { $0.responseTime ?? 0 > $1.responseTime ?? 0 })
return sorted.first
public func bestAddress() -> ServerAddressIP? {
guard !addresses().isEmpty else {
return nil
}
return nil
}
public func bestPingAddress() -> [Address] {
switch Client.providers.vpnProvider.currentVPNType {
case IKEv2Profile.vpnType:
var addresses: [Address] = []
for address in iKEv2AddressesForUDP ?? [] {
addresses.append(Address(hostname: address.ip, port: 0))
}
return addresses
case PIATunnelProfile.vpnType:
var addresses: [Address] = []
for address in openVPNAddressesForUDP ?? [] {
addresses.append(Address(hostname: address.ip, port: 0))
}
return addresses
case PIAWGTunnelProfile.vpnType:
var addresses: [Address] = []
for address in wireGuardAddressesForUDP ?? [] {
addresses.append(Address(hostname: address.ip, port: 0))
}
return addresses
default:
return []
let availableServer = addresses().first(where: {$0.available})
if availableServer == nil {
addresses().map({$0.reset()})
return bestAddress()
}
return availableServer
}
public func bestAddressForOVPN(tcp: Bool) -> ServerAddressIP? {
guard !ovpnAddresses(tcp: tcp).isEmpty else {
return nil
}
let availableServer = ovpnAddresses(tcp: tcp).first(where: {$0.available})
if availableServer == nil {
ovpnAddresses(tcp: tcp).map({$0.reset()})
return bestAddress()
}
return availableServer
}
}
extension Server {
func updateResponseTime(_ time: Int, forAddress address: Address) {
func updateResponseTime(_ time: Int, forAddress address: ServerAddressIP) {
switch Client.providers.vpnProvider.currentVPNType {
case IKEv2Profile.vpnType:
let serverAddressIP = iKEv2AddressesForUDP?.first(where: {$0.ip == address.hostname })
let serverAddressIP = iKEv2AddressesForUDP?.first(where: {$0.ip == address.ip })
serverAddressIP?.updateResponseTime(time)
case PIATunnelProfile.vpnType:
let serverAddressIP = openVPNAddressesForUDP?.first(where: {$0.ip == address.hostname })
let serverAddressIP = openVPNAddressesForUDP?.first(where: {$0.ip == address.ip })
serverAddressIP?.updateResponseTime(time)
case PIAWGTunnelProfile.vpnType:
let serverAddressIP = wireGuardAddressesForUDP?.first(where: {$0.ip == address.hostname })
let serverAddressIP = wireGuardAddressesForUDP?.first(where: {$0.ip == address.ip })
serverAddressIP?.updateResponseTime(time)
default:
break
......@@ -300,3 +297,14 @@ extension Server {
}
}
extension Server {
func dipPassword() -> Data? {
if let dipUsername = dipUsername {
return Client.database.secure.passwordReference(forDipToken: dipUsername)
}
return nil
}
}
......@@ -72,4 +72,7 @@ protocol WebServices: class {
func featureFlags(_ callback: LibraryCallback<[String]>?)
// MARK: Messages
func messages(forAppVersion version: String, _ callback: LibraryCallback<InAppMessage>?)
}
......@@ -215,7 +215,7 @@ class DefaultAccountProvider: AccountProvider, ConfigurationAccess, DatabaseAcce
webServices.token(credentials: request.credentials) { (token, error) in
guard let token = token else {
callback?(nil, error)
callback?(nil, ClientError.unauthorized)
return
}
......@@ -224,7 +224,9 @@ class DefaultAccountProvider: AccountProvider, ConfigurationAccess, DatabaseAcce
self.webServices.info(token: token) { (accountInfo, error) in
guard let accountInfo = accountInfo else {
callback?(nil, error)
self.webServices.logout(nil)
self.cleanDatabase()
callback?(nil, ClientError.unauthorized)
return
}
......@@ -328,6 +330,12 @@ class DefaultAccountProvider: AccountProvider, ConfigurationAccess, DatabaseAcce
}
}
func inAppMessages(forAppVersion version: String, _ callback: LibraryCallback<InAppMessage>?) {
webServices.messages(forAppVersion: version) { (message, error) in
callback?(message, error)
}
}
#if os(iOS)
func subscriptionInformation(_ callback: LibraryCallback<AppStoreInformation>?) {
log.debug("Fetching available product keys...")
......
......@@ -109,6 +109,15 @@ extension Client {
/// Sets the maximum number of failed connectivity checks before giving up.
public var connectivityMaxAttempts: Int
/// Sets the timeout for VPN connectivity checks.
public var vpnConnectivityTimeout: TimeInterval
/// Sets the delay after which to retry VPN connectivity checks.
public var vpnConnectivityRetryDelay: TimeInterval
/// Sets the maximum number of failed VPN connectivity attempts before giving up.
public var vpnConnectivityMaxAttempts: Int
let maceHostname: String
let macePort: UInt16
......@@ -198,9 +207,13 @@ extension Client {
enablesConnectivityUpdates = false
connectivityVPNLag = 1000
connectivityTimeout = 3000
connectivityRetryDelay = 5000
connectivityRetryDelay = 10000
connectivityMaxAttempts = 3
vpnConnectivityTimeout = 2.0
vpnConnectivityRetryDelay = 5.0
vpnConnectivityMaxAttempts = 3
maceHostname = "209.222.18.222"
macePort = 1111
maceDelay = 5000
......
......@@ -88,6 +88,10 @@ public final class Client {
ServersDaemon.shared.forceUpdates(completionBlock: completionBlock)
}
public static func resetWebServices() {
Client.webServices = PIAWebServices()
}
/**
Refresh the list of plan products
*/
......
......@@ -33,7 +33,7 @@ class PingTask {
let identifier: String
let server: Server
let address: Server.Address
let address: Server.ServerAddressIP
let stateUpdateHandler: (PingTask) -> ()
var state = PingTaskState.pending {
didSet {
......@@ -41,7 +41,7 @@ class PingTask {
}
}
init(identifier: String, server: Server, address: Server.Address, stateUpdateHandler: @escaping (PingTask) -> ()) {
init(identifier: String, server: Server, address: Server.ServerAddressIP, stateUpdateHandler: @escaping (PingTask) -> ()) {
self.identifier = identifier
self.server = server
self.address = address
......@@ -54,7 +54,7 @@ class PingTask {
let persistence = Client.database.plain
self.state = .pending
log.debug("Starting to Ping \(server.identifier) with address: \(address.hostname)")
log.debug("Starting to Ping \(server.identifier) with address: \(address.ip)")
queue.async() { [weak self] in
......@@ -62,7 +62,7 @@ class PingTask {
return
}
let tcpAddress = Server.Address(hostname: address.hostname, port: 443)
let tcpAddress = Server.Address(hostname: address.ip, port: 443)
response = server.ping(toAddress: tcpAddress, withProtocol: .TCP)
DispatchQueue.main.async {
self?.parsePingResponse(response: response, withServer: server)
......
......@@ -61,7 +61,7 @@ class ServersPinger: DatabaseAccess {