Commit 407945f8 authored by Thomas Markiewicz's avatar Thomas Markiewicz

Merged upstream master

parents 80de5205 a7d02a4a
source "https://rubygems.org"
gem "fastlane"
gem "cocoapods", "1.7.5"
gem "dotenv"
#CI_HOSTNAME=`[[ $CI_PROJECT_URL =~ ^https:\/\/([^\/]+)\/.*$ ]] && echo ${BASH_REMATCH[1]}`
ci_project_url = ENV['CI_PROJECT_URL']
ENV['CI_HOSTNAME'] = ci_project_url.gsub(/^https:\/\/([^\/]+)\/.*$/, '\1') unless ci_project_url.nil?
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
This diff is collapsed.
......@@ -126,7 +126,7 @@ struct AppConfiguration {
static let successConnectionsUntilPrompt: Int = 3
static let successConnectionsUntilPromptAgain: Int = 50
static let errorInConnectionsUntilPrompt: Int = 2
static let errorInConnectionsUntilPrompt: Int = 1
static let timeIntervalUntilPromptAgain: Double = 2592000
}
......
......@@ -135,8 +135,6 @@ struct AppConstants {
name: name,
country: country,
hostname: hostname,
bestOpenVPNAddressForTCP: Server.Address(hostname: address, port: tcpPort),
bestOpenVPNAddressForUDP: Server.Address(hostname: address, port: udpPort),
pingAddress: nil,
regionIdentifier: ""
))
......
......@@ -65,7 +65,8 @@ class AppPreferences {
static let todayWidgetVpnProtocol = "vpn.widget.protocol"
static let todayWidgetVpnPort = "vpn.widget.port"
static let todayWidgetVpnSocket = "vpn.widget.socket"
static let todayWidgetTrustedNetwork = "vpn.widget.trusted.network"
// Quick Settings options
static let quickSettingThemeVisible = "quickSettingThemeVisible"
static let quickSettingKillswitchVisible = "quickSettingKillswitchVisible"
......@@ -81,6 +82,15 @@ class AppPreferences {
// GEO servers
static let showGeoServers = "ShowGeoServers"
// Dismissed messages
static let dismissedMessages = "DismissedMessages"
// Dedicated IP relations
static let tokenIPRelation = "TokenIPRelation"
// In app messages
static let stopInAppMessages = "stopInAppMessages"
}
static let shared = AppPreferences()
......@@ -249,6 +259,15 @@ class AppPreferences {
}
}
var todayWidgetTrustedNetwork: Bool {
get {
return defaults.bool(forKey: Entries.todayWidgetTrustedNetwork) ?? false
}
set {
defaults.set(newValue, forKey: Entries.todayWidgetTrustedNetwork)
}
}
var useSmallPackets: Bool {
get {
return defaults.bool(forKey: Entries.useSmallPackets)
......@@ -258,6 +277,17 @@ class AppPreferences {
}
}
var dedicatedTokenIPReleation: [String: String] {
get {
if let relation = defaults.dictionary(forKey: Entries.tokenIPRelation) as? [String: String] {
return relation
}
return [String:String]()
}
set {
defaults.set(newValue, forKey: Entries.tokenIPRelation)
}
}
@available(iOS 12.0, *)
var connectShortcut: INVoiceShortcut? {
......@@ -381,6 +411,24 @@ class AppPreferences {
defaults.set(newValue, forKey: Entries.appVersion)
}
}
var dismissedMessages: [String]{
get {
return defaults.array(forKey: Entries.dismissedMessages) as? [String] ?? []
}
set {
defaults.set(newValue, forKey: Entries.dismissedMessages)
}
}
var stopInAppMessages: Bool {
get {
return defaults.bool(forKey: Entries.stopInAppMessages) ?? false
}
set {
defaults.set(newValue, forKey: Entries.stopInAppMessages)
}
}
private init() {
guard let defaults = UserDefaults(suiteName: AppConstants.appGroup) else {
......@@ -402,6 +450,7 @@ class AppPreferences {
Entries.todayWidgetVpnProtocol: IKEv2Profile.vpnType,
Entries.todayWidgetVpnPort: "500",
Entries.todayWidgetVpnSocket: "UDP",
Entries.todayWidgetTrustedNetwork: false,
Entries.quickSettingThemeVisible: true,
Entries.quickSettingKillswitchVisible: true,
Entries.quickSettingNetworkToolVisible: true,
......@@ -410,7 +459,9 @@ class AppPreferences {
Entries.canAskAgainForReview: false,
Entries.successConnections: 0,
Entries.failureConnections: 0,
Entries.showGeoServers: true
Entries.showGeoServers: true,
Entries.dismissedMessages: [],
Entries.stopInAppMessages: false
])
}
......@@ -561,9 +612,13 @@ class AppPreferences {
todayWidgetVpnProtocol = IKEv2Profile.vpnType
todayWidgetVpnPort = "500"
todayWidgetVpnSocket = "UDP"
todayWidgetTrustedNetwork = false
Client.resetServers(completionBlock: {_ in })
failureConnections = 0
showGeoServers = true
stopInAppMessages = false
dedicatedTokenIPReleation = [:]
MessagesManager.shared.reset()
}
func clean() {
......@@ -581,6 +636,7 @@ class AppPreferences {
todayWidgetVpnProtocol = IKEv2Profile.vpnType
todayWidgetVpnPort = "500"
todayWidgetVpnSocket = "UDP"
todayWidgetTrustedNetwork = false
quickSettingThemeVisible = true
quickSettingKillswitchVisible = true
quickSettingNetworkToolVisible = true
......@@ -591,6 +647,10 @@ class AppPreferences {
Client.resetServers(completionBlock: {_ in })
failureConnections = 0
showGeoServers = true
stopInAppMessages = false
dismissedMessages = []
dedicatedTokenIPReleation = [:]
MessagesManager.shared.reset()
}
// + (void)eraseForTesting;
......
......@@ -124,14 +124,17 @@ class Bootstrapper {
PIAWGTunnelProfile.vpnType: PIAWireguardConfiguration(customDNSServers: [])
]
//Client.providers.accountProvider.featureFlags(nil)
#if PIA_DEV
Client.configuration.featureFlags.append(contentsOf: ["dedicated-ip"])
#else
Client.providers.accountProvider.featureFlags(nil)
#endif
MessagesManager.shared.refreshMessages()
Client.providers.serverProvider.downloadRegionStaticData { (error) in
NotificationCenter.default.post(name: .PIAServerHasBeenUpdated,
object: self,
userInfo: nil)
Macros.postNotification(.PIAServerHasBeenUpdated)
//FORCE THE MIGRATION TO GEN4
if Client.providers.vpnProvider.needsMigrationToGEN4() {
......
......@@ -60,8 +60,6 @@ class CustomServerSettingsViewController: AutolayoutViewController {
name: name,
country: country,
hostname: hostname,
bestOpenVPNAddressForTCP: Server.Address(hostname: address, port: tcpPort),
bestOpenVPNAddressForUDP: Server.Address(hostname: address, port: udpPort),
pingAddress: nil,
regionIdentifier: ""
)
......
......@@ -23,6 +23,29 @@
import UIKit
enum DashboardSections: Int {
case fixedTiles = 0
case tiles
}
enum FixedCells: Int, EnumsBuilder {
case messages = 0
var identifier: String {
switch self {
case .messages: return "MessagesTileCell"
}
}
var className: String {
switch self {
case .messages: return "MessagesTileCollectionViewCell"
}
}
}
enum Cells: Int, EnumsBuilder {
case region = 0
......@@ -64,6 +87,9 @@ enum Cells: Int, EnumsBuilder {
class DashboardCollectionViewUtil: NSObject {
func registerCellsFor(_ collectionView: UICollectionView) {
collectionView.register(UINib(nibName: FixedCells.messages.className,
bundle: nil),
forCellWithReuseIdentifier: FixedCells.messages.identifier)
collectionView.register(UINib(nibName: Cells.ipTile.className,
bundle: nil),
forCellWithReuseIdentifier: Cells.ipTile.identifier)
......
This diff is collapsed.
......@@ -30,7 +30,7 @@ class DedicatedIPTitleHeaderViewCell: UITableViewCell {
super.awakeFromNib()
titleLabel.style(style: TextStyle.textStyle9)
titleLabel.font = UIFont.mediumFontWith(size: 14.0)
titleLabel.text = "Your Dedicated IPs".uppercased()
titleLabel.text = L10n.Dedicated.Ip.Plural.title.uppercased()
self.backgroundColor = .clear
}
......
......@@ -35,9 +35,10 @@ class DedicatedIpEmptyHeaderViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = .clear
self.title.text = "Dedicated IP"
self.subtitle.text = "Activate your Dedicated IP by pasting your token in the form below. If you've recently purchased a dedicated IP, you can generate the token by going to the PIA website."
self.addTokenTextfield.placeholder = "Paste in your token here"
self.title.text = L10n.Dedicated.Ip.title
self.subtitle.text = L10n.Dedicated.Ip.Activation.description
self.addTokenTextfield.accessibilityLabel = L10n.Dedicated.Ip.Token.Textfield.accessibility
self.addTokenTextfield.placeholder = L10n.Dedicated.Ip.Token.Textfield.placeholder
self.addTokenTextfield.delegate = self
}
......@@ -58,7 +59,7 @@ class DedicatedIpEmptyHeaderViewCell: UITableViewCell {
private func styleButton() {
addTokenButton.setRounded()
addTokenButton.style(style: TextStyle.Buttons.piaGreenButton)
addTokenButton.setTitle("Activate",
addTokenButton.setTitle(L10n.Dedicated.Ip.Activate.Button.title,
for: [])
}
......@@ -77,24 +78,45 @@ class DedicatedIpEmptyHeaderViewCell: UITableViewCell {
NotificationCenter.default.post(name: .DedicatedIpHideAnimation, object: nil)
self?.addTokenTextfield.text = ""
guard let dipServer = server else {
Macros.displayStickyNote(withMessage: "Your token is invalid. Please make sure you have entered the token correctly.",
Macros.displayStickyNote(withMessage: L10n.Dedicated.Ip.Message.Invalid.token,
andImage: Asset.iconWarning.image)
return
}
switch dipServer?.dipStatus {
case .active:
Macros.displaySuccessImageNote(withImage: Asset.iconWarning.image, message: "Your Dedicated IP has been activated successfully. It will be available in your Region selection list.")
Macros.displaySuccessImageNote(withImage: Asset.iconWarning.image, message: L10n.Dedicated.Ip.Message.Valid.token)
guard let token = dipServer?.dipToken, let address = dipServer?.bestAddress() else {
return
}
var relation = AppPreferences.shared.dedicatedTokenIPReleation
if relation.isEmpty {
//no data
relation[token] = address.ip
} else {
if relation[token] != address.ip {
//changes
relation[token] = address.ip
let message = InAppMessage(withMessage: ["en": L10n.Dedicated.Ip.Message.Ip.updated], id: token, link: ["en":""], type: .none, level: .system, actions: nil, view: nil, uri: nil)
MessagesManager.shared.postSystemMessage(message: message)
}
}
AppPreferences.shared.dedicatedTokenIPReleation[token] = address.ip
case .expired:
Macros.displayWarningImageNote(withImage: Asset.iconWarning.image, message: "Your token is expired. Please generate a new one from your Account page in the website.")
Macros.displayWarningImageNote(withImage: Asset.iconWarning.image, message: L10n.Dedicated.Ip.Message.Expired.token)
case .error:
Macros.displayWarningImageNote(withImage: Asset.iconWarning.image, message: L10n.Dedicated.Ip.Message.Error.token)
default:
Macros.displayStickyNote(withMessage: "Your token is invalid. Please make sure you have entered the token correctly.",
Macros.displayStickyNote(withMessage: L10n.Dedicated.Ip.Message.Invalid.token,
andImage: Asset.iconWarning.image)
}
NotificationCenter.default.post(name: .DedicatedIpReload, object: nil)
NotificationCenter.default.post(name: .PIAThemeDidChange, object: nil)
}
} else {
Macros.displayStickyNote(withMessage: "Please make sure you have entered the token correctly.",
Macros.displayStickyNote(withMessage: L10n.Dedicated.Ip.Message.Incorrect.token,
andImage: Asset.iconWarning.image)
}
}
......@@ -109,4 +131,14 @@ extension DedicatedIpEmptyHeaderViewCell: UITextFieldDelegate {
return false
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
activateView.layer.borderColor = Theme.current.palette.emphasis.cgColor
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
activateView.layer.borderColor = UIColor.piaGrey4.cgColor
return true
}
}
......@@ -38,6 +38,7 @@ class DedicatedIpRowViewCell: UITableViewCell, Restylable {
self.server = server
imvFlag.setImage(fromServer: server)
imvFlag.accessibilityLabel = L10n.Dedicated.Ip.Country.Flag.accessibility(server.name)
if let _ = Client.providers.serverProvider.regionStaticData {
labelRegion.text = Client.providers.serverProvider.regionStaticData.localisedServerName(forCountryName: server.name)
......@@ -45,12 +46,11 @@ class DedicatedIpRowViewCell: UITableViewCell, Restylable {
labelRegion.text = server.name
}
if let pingAddress = server.bestPingAddress().first {
labelIp.text = pingAddress.hostname
labelIp.accessibilityLabel = pingAddress.hostname
if let pingAddress = server.bestAddress() {
labelIp.text = pingAddress.ip
labelIp.accessibilityLabel = pingAddress.ip
}
}
// MARK: Restylable
......
......@@ -53,7 +53,7 @@ class DedicatedIpViewController: AutolayoutViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "Dedicated IP"
title = L10n.Dedicated.Ip.title
let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(viewHasRotated), name: UIDevice.orientationDidChangeNotification, object: nil)
......@@ -67,7 +67,7 @@ class DedicatedIpViewController: AutolayoutViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
styleNavigationBarWithTitle("Dedicated IP")
styleNavigationBarWithTitle(L10n.Dedicated.Ip.title)
}
override func viewWillDisappear(_ animated: Bool) {
......@@ -76,7 +76,7 @@ class DedicatedIpViewController: AutolayoutViewController {
}
@objc private func viewHasRotated() {
styleNavigationBarWithTitle("Dedicated IP")
styleNavigationBarWithTitle(L10n.Dedicated.Ip.title)
}
@objc private func reloadTableView() {
......@@ -107,7 +107,7 @@ class DedicatedIpViewController: AutolayoutViewController {
override func viewShouldRestyle() {
super.viewShouldRestyle()
styleNavigationBarWithTitle("Dedicated IP")
styleNavigationBarWithTitle(L10n.Dedicated.Ip.title)
if let viewContainer = viewContainer {
Theme.current.applyPrincipalBackground(view)
......@@ -147,17 +147,31 @@ extension DedicatedIpViewController: UITableViewDelegate, UITableViewDataSource
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let dipRegion = data[indexPath.row]
if let token = dipRegion.dipToken {
Client.providers.serverProvider.removeDIPToken(token)
NotificationCenter.default.post(name: .PIAThemeDidChange,
object: self,
userInfo: nil)
let alert = Macros.alert(nil, L10n.Dedicated.Ip.remove)
alert.addCancelActionWithTitle(L10n.Global.cancel, handler: {
self.reloadTableView()
})
alert.addActionWithTitle(L10n.Global.ok) {
self.confirmDelete(row: indexPath.row)
}
reloadTableView()
self.present(alert, animated: true, completion: nil)
}
}
private func confirmDelete(row: Int) {
let dipRegion = data[row]
if let token = dipRegion.dipToken {
Client.providers.serverProvider.removeDIPToken(token)
NotificationCenter.default.post(name: .PIAThemeDidChange,
object: self,
userInfo: nil)
}
reloadTableView()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
......
......@@ -57,7 +57,7 @@ class DedicatedRegionCell: UITableViewCell, Restylable {
}
labelIP.text = server.wireGuardAddressesForUDP?.first?.ip ?? ""
labelDedicatedIPTitle.text = "DEDICATED IP"
labelDedicatedIPTitle.text = L10n.Dedicated.Ip.title.uppercased()
iconSelected = isSelected
......@@ -107,9 +107,7 @@ class DedicatedRegionCell: UITableViewCell, Restylable {
self.isFavorite = !self.isFavorite
self.isFavorite ? self.server.favorite() : self.server.unfavorite()
self.animateFavoriteImage()
NotificationCenter.default.post(name: .PIAServerHasBeenUpdated,
object: self,
userInfo: nil)
Macros.postNotification(.PIAServerHasBeenUpdated)
}
private func animateFavoriteImage() {
......
......@@ -5,7 +5,7 @@
<key>usesDevelopmentClient</key>
<true/>
<key>customizesClientEnvironment</key>
<false/>
<true/>
<key>customizesWelcomePreset</key>
<false/>
<key>usesMockAccount</key>
......
{
"images" : [
{
"filename" : "alert.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "offline-icon.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -125,7 +125,7 @@ class MenuViewController: AutolayoutViewController {
if Client.configuration.featureFlags.contains(AppConstants.FeatureFlags.dedicatedIp),
!allItems[0].contains(.dedicatedIp) {
//allItems[0].insert(.dedicatedIp, at: 2)
allItems[0].insert(.dedicatedIp, at: 2)
}
Theme.current.applySideMenu()
......
//
// MessagesCommands.swift
// PIA VPN
//
// 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 UIKit
import PIALibrary
import PIAWireguard
import TunnelKit
protocol Command {
func execute()
}
class LinkCommand: Command {
private var payload: String
init(_ payload: String) {
self.payload = payload
}
func execute() {
if let url = URL(string: payload),
UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
}
class ViewCommand: Command {
private enum AvailableViews: String {
case settings = "settings"
case regions = "regions"
case account = "account"
case dip = "dip"
case about = "about"
}
private var payload: String
init(_ payload: String) {
self.payload = payload
if let view = AvailableViews(rawValue: payload) {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let rootNavVC = appDelegate.window?.rootViewController as? UINavigationController,
let dashboard = rootNavVC.viewControllers.first as? DashboardViewController {
switch view {
case .about:
dashboard.openAbout()
case .account:
dashboard.openAccount()
case .dip:
dashboard.openDedicatedIp()
case .regions:
dashboard.selectRegion(animated: true)
case .settings:
dashboard.openSettings()
}
}
}
}
func execute() {
}
}
class ActionCommand: Command {
private enum AvailableActions: String {
case killswitch = "killswitch"
case nmt = "nmt"
case ovpn = "ovpn"
case wg = "wg"
case ikev2 = "ikev2"
case geo = "geo"
}
private var payload: [String: Bool]
init(_ payload: [String: Bool]) {
self.payload = payload
self.payload.forEach({ key, value in
if let action = AvailableActions(rawValue: key) {
switch action {
case .killswitch:
enableKillSwitch(enable: value)
case .nmt:
enableNMT(enable: value)
case .ovpn:
if value {
activateOpenVPN()
}
case .wg:
if value {
activateWireGuard()
}
case .ikev2:
if value {
activateIKEv2()
}
case .geo:
enableGEOServers(enable: value)
}
}
})
Macros.postNotification(.PIASettingsHaveChanged)
}
func execute() {
}
private func activateWireGuard() {
let preferences = Client.preferences.editable()
guard let currentVPNConfiguration = preferences.vpnCustomConfiguration(for: PIAWGTunnelProfile.vpnType) as? PIAWireguardConfiguration ??
Client.preferences.defaults.vpnCustomConfiguration(for: PIAWGTunnelProfile.vpnType) as? PIAWireguardConfiguration else {
fatalError("No default VPN custom configuration provided for PIA Wireguard protocol")
}
preferences.setVPNCustomConfiguration(currentVPNConfiguration, for: PIAWGTunnelProfile.vpnType)
preferences.vpnType = PIAWGTunnelProfile.vpnType
preferences.commit()
}
private func activateOpenVPN() {
let preferences = Client.preferences.editable()
guard let currentVPNConfiguration = preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration ??
Client.preferences.defaults.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNTunnelProvider.Configuration else {
fatalError("No default VPN custom configuration provided for PIA OpenVPN protocol")
}
preferences.setVPNCustomConfiguration(currentVPNConfiguration, for: PIATunnelProfile.vpnType)
preferences.vpnType = PIATunnelProfile.vpnType
preferences.commit()
}
private func activateIKEv2() {
let preferences = Client.preferences.editable()