Commit 038fe2ff authored by Jose Blaya's avatar Jose Blaya
Browse files

- Merge VPN Network Protection into PIAX

- Update VPN WiFi Protection view controller to PIAX
parent 7fba12cd
......@@ -80,23 +80,6 @@ qa_deploy:
tags:
- ios
qa_notify:
stage: notify
variables:
FL_SLACK_USERNAME: "HockeyApp"
GITLAB_CHANGELOG_ISSUE_FORMAT: "- #%{id}: <%{url}|%{desc}>"
dependencies:
- qa_deploy
script:
- bundle exec fastlane qa_notify
when:
manual
only:
- develop
- /^release.*$/
tags:
- ios
beta_manual_archive:
stage: archive
variables:
......@@ -134,6 +117,51 @@ beta_manual_deploy:
tags:
- ios
manual_qa_archive:
stage: archive
variables:
MATCH_TYPE: "adhoc"
GYM_SCHEME: "PIA VPN dev"
GYM_EXPORT_METHOD: "ad-hoc"
script:
- bundle exec fastlane create_archive
when:
manual
artifacts:
paths:
- "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.*"
expire_in: 1 week
only:
- branches
tags:
- ios
manual_qa_deploy:
stage: deploy
variables:
IPA_OUTPUT_PATH: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa"
FL_HOCKEY_IPA: "$STAGE_ARTIFACTS_PATH/$STAGE_ARCHIVE_NAME.ipa"
FL_HOCKEY_COMMIT_SHA: $CI_COMMIT_SHA
FL_HOCKEY_BUILD_SERVER_URL: "$CI_PROJECT_URL/-/jobs/$CI_JOB_ID"
FL_HOCKEY_REPOSITORY_URL: $CI_PROJECT_URL
FL_HOCKEY_NOTIFY: "false"
FL_HOCKEY_STRATEGY: "replace"
script:
- bundle exec fastlane qa_deploy
when:
manual
environment:
name: "hockey"
url: $HOCKEY_URL
artifacts:
paths:
- $SERIALIZED_ARCHIVE_JSON
expire_in: 1 week
only:
- branches
tags:
- ios
#beta_archive:
# stage: archive
# variables:
......
# Summary
<!-- Brief description of issue. -->
<!-- Remove any empty/irrelevant sections before submitting -->
### Reproduced on
<!-- One of the following: -->
- MR !102 (e98004bc) <!-- merge request ID + optional commit -->
- branch-name (e98004bc) <!-- work branch + optional commit -->
- master (e98004bc) <!-- master + optional commit -->
- build 01234 <!-- master builds only -->
- v0.8.6 <!-- released build -->
### Steps to reproduce
1. ...
2. ...
### What is the current bug behavior?
(What actually happens)
<!--(What actually happens)-->
### What is the expected correct behavior?
(What you should see instead)
<!--(What you should see instead)-->
### Relevant logs and/or screenshots
### Possible fixes suggested remediation
### Possible fixes and suggested remediation
### Assignees and labels
(delete as applicable)
~bug ~confirmed ~regression ~suggestion
~bug
<!--(delete as applicable)-->
~confirmed ~regression ~suggestion
~blocker ~major ~minor
<!-- Device Type-->
~tablet ~mobile ~tv
<!-- OS Version-->
~ios9 ~ios10 ~ios11
<!-- Milestone-->
%@ms
/milestone
/cc @Dev /assign @QA
* [ ] Patched
......
This diff is collapsed.
......@@ -10,11 +10,13 @@ import UIKit
import PIALibrary
import SideMenu
import SwiftyBeaver
import NetworkExtension
private let log = SwiftyBeaver.self
@UIApplicationMain
class AppDelegate: NSObject, UIApplicationDelegate {
private enum ShortcutItem: String {
case connect
......@@ -24,14 +26,19 @@ class AppDelegate: NSObject, UIApplicationDelegate {
}
var window: UIWindow?
private var hotspotHelper: PIAHotspotHelper!
deinit {
NotificationCenter.default.removeObserver(self)
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
Bootstrapper.shared.bootstrap()
application.shortcutItems = []
hotspotHelper = PIAHotspotHelper()
_ = hotspotHelper.configureHotspotHelper()
return true
}
......@@ -183,4 +190,5 @@ class AppDelegate: NSObject, UIApplicationDelegate {
mainVC.selectRegion(animated: true)
}
}
}
//
// EnumsBuilder.swift
// PIA VPN
//
// Created by Jose Antonio Blaya Garcia on 18/12/2018.
// Copyright © 2018 London Trust Media. All rights reserved.
//
import Foundation
import Foundation
/// Usage example:
/// fileprivate enum Cells: Int, EnumsBuilder {
/// case updateAmount = 0
/// case totalAmount = 1
/// var identifier: String {
/// switch self {
/// case .updateAmount: return "updateAmountCell"
/// case .totalAmount: return "totalAmountCell"
/// }
/// }
/// }
///
/// Cells.objectIdentifyBy(index: indexPath.row).identifier // eg. "totalAmountCell"
protocol EnumsBuilder {}
extension EnumsBuilder where Self: RawRepresentable, Self.RawValue == Int {
static func objectIdentifyBy(index: Int) -> Self {
guard let object = Self(rawValue: index) else {
fatalError("Object identifier not implemented")
}
return object
}
static func countCases() -> Int {
var numCases = 0
while Self(rawValue: numCases) != nil {
numCases += 1
}
return numCases
}
}
extension EnumsBuilder where Self: RawRepresentable, Self.RawValue == String {
static func objectIdentifyBy(name: String) -> Self {
guard let object = Self(rawValue: name) else {
fatalError("Object identifier not implemented")
}
return object
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "groupCopy2.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "rectangleCopy3.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "icons8-wi-fi-filled-50.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icons8-wi-fi-filled-52.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "icons8-wi-fi-filled-51.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
......@@ -52,6 +52,10 @@
<string>Roboto-Regular.ttf</string>
<string>Roboto-Thin.ttf</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>network-authentication</string>
</array>
<key>UILaunchStoryboardName</key>
<string>Launch Screen</string>
<key>UIMainStoryboardFile</key>
......
......@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.networking.HotspotHelper</key>
<true/>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
......@@ -10,6 +12,8 @@
<array>
<string>allow-vpn</string>
</array>
<key>com.apple.developer.networking.wifi-info</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.privateinternetaccess</string>
......
//
// PIAHotspotHelper.swift
// PIA VPN
//
// Created by Jose Antonio Blaya Garcia on 18/12/2018.
// Copyright © 2018 London Trust Media. All rights reserved.
//
import Foundation
import NetworkExtension
import PIALibrary
import SwiftyBeaver
private let log = SwiftyBeaver.self
public protocol PIAHotspotHelperDelegate: class {
/**
Refreshes the available WiFi networks.
- Parameter callback: Returns a refreshed list of available SSIDs as `String`.
*/
func refreshAvailableNetworks(_ networks: [String]?)
}
class PIAHotspotHelper {
private var delegate: PIAHotspotHelperDelegate?
init(withDelegate delegate: PIAHotspotHelperDelegate? = nil) {
self.delegate = delegate
}
/**
Returns the current WiFi SSID.
- Returns: SSID as `String`.
*/
public func currentWiFiNetwork() -> String? {
return UIDevice.current.WiFiSSID
}
/**
Configures the HotspotHelper API to perform actions depending of the command.
- Returns: true if correctly configured.
*/
public func configureHotspotHelper() -> Bool {
let hotspotDisplayName = Client.preferences.useWiFiProtection ?
L10n.Hotspothelper.Display.Protected.name :
L10n.Hotspothelper.Display.name
let options: [String: NSObject] = [kNEHotspotHelperOptionDisplayName : hotspotDisplayName as NSObject]
let queue: DispatchQueue = DispatchQueue(label: "com.privateinternetaccess.hotspot", attributes: DispatchQueue.Attributes.concurrent)
NEHotspotHelper.supportedNetworkInterfaces()
return NEHotspotHelper.register(options: options,
queue: queue) { [weak self] (cmd: NEHotspotHelperCommand) in
if let weakSelf = self {
if cmd.commandType == .filterScanList {
log.info("filtering ssid list")
var availableList: [String] = []
var unsecuredList: [NEHotspotNetwork] = []
for element in cmd.networkList! {
if !element.ssid.isEmpty,
!availableList.contains(element.ssid) {
availableList.append(element.ssid)
}
if !element.isSecure {
element.setConfidence(.high)
unsecuredList.append(element)
}
}
weakSelf.saveCurrentNetworkList(availableNetworks: availableList)
let response = cmd.createResponse(NEHotspotHelperResult.success)
if !Client.providers.vpnProvider.isVPNConnected {
response.setNetworkList(unsecuredList)
log.info("present PIA message for unprotected networks")
}
response.deliver()
} else if cmd.commandType == .evaluate {
if let network = cmd.network,
Client.preferences.useWiFiProtection {
if !Client.providers.vpnProvider.isVPNConnected {
if Client.preferences.shouldConnectForAllNetworks ||
!weakSelf.trustedNetworks().contains(network.ssid) {
network.setConfidence(.high)
Macros.dispatch(after: .milliseconds(200)) {
log.info("connecting VPN because network "+network.ssid+"has passed the filter")
Client.providers.vpnProvider.connect(nil)
}
}
let response = cmd.createResponse(.success)
response.setNetwork(network)
response.deliver()
} else {
let response = cmd.createResponse(.failure)
response.setNetwork(network)
response.deliver()
}
}
}
}
}
}
private func saveCurrentNetworkList(availableNetworks: [String]) {
let preferences = Client.preferences.editable()
preferences.availableNetworks = availableNetworks
preferences.commit()
}
/**
List of available networks.
- Returns: Array of available SSID.
*/
public func retrieveCurrentNetworkList() -> [String] {
var availableNetworks = Client.preferences.availableNetworks
if let ssid = UIDevice.current.WiFiSSID,
!availableNetworks.contains(ssid) {
availableNetworks.append(ssid)
}
return availableNetworks
}
/**
List of trusted networks.
- Returns: Array of trusted SSID.
*/
public func trustedNetworks() -> [String] {
return Client.preferences.trustedNetworks
}
/**
Saves the WiFi network.
- Parameter ssid: SSID as `String`.
*/
public func saveTrustedNetwork(_ ssid: String) {
var trustedNetworks = Client.preferences.trustedNetworks
if !trustedNetworks.contains(ssid) {
trustedNetworks.append(ssid)
}
let preferences = Client.preferences.editable()
preferences.trustedNetworks = trustedNetworks
preferences.commit()
}
/**
Removes the WiFi networks.
- Parameter ssid: SSID as `String`.
*/
public func removeTrustedNetwork(_ ssid: String) {
let trustedNetworks = Client.preferences.trustedNetworks
let newTrustedNetworks = trustedNetworks.filter { $0 != ssid }
let preferences = Client.preferences.editable()
preferences.trustedNetworks = newTrustedNetworks
preferences.commit()
}
/**
Remove all elements from the trusted network list.
*/
public func clearTrustedNetworkList() {
var trustedNetworks = Client.preferences.trustedNetworks
trustedNetworks.removeAll()
let preferences = Client.preferences.editable()
preferences.trustedNetworks = trustedNetworks
preferences.commit()
}
private func tryToConnectVPNForAllNetworks() {
if Client.preferences.shouldConnectForAllNetworks {
//Connect the VPN
if !Client.providers.vpnProvider.isVPNConnected {
Macros.dispatch(after: .milliseconds(200)) {
Client.providers.vpnProvider.connect(nil)
}
}
}
}
}
......@@ -39,7 +39,11 @@ enum Setting: Int {
case encryptionHandshake
case automaticReconnection
case trustCellularData
case trustedNetworks
case contentBlockerState
case contentBlockerRefreshRules
......@@ -89,7 +93,11 @@ class SettingsViewController: AutolayoutViewController {
case encryption
case applicationSettings
case cellularData
case autoConnectSettings
case contentBlocker
case applicationInformation
......@@ -103,6 +111,8 @@ class SettingsViewController: AutolayoutViewController {
.connection,
.encryption,
.applicationSettings,
.cellularData,
.autoConnectSettings,
.applicationInformation,
.reset,
.contentBlocker
......@@ -123,6 +133,12 @@ class SettingsViewController: AutolayoutViewController {
.encryptionHandshake
],
.applicationSettings: [], // dynamic
.cellularData: [
.trustCellularData
],
.autoConnectSettings: [
.trustedNetworks
],
.contentBlocker: [
.contentBlockerState,
.contentBlockerRefreshRules
......@@ -149,14 +165,18 @@ class SettingsViewController: AutolayoutViewController {
@IBOutlet private weak var tableView: UITableView!
private lazy var switchAutoJoinWiFi = UISwitch()
private lazy var switchPersistent = UISwitch()
private lazy var switchMACE = UISwitch()
private lazy var switchContentBlocker = FakeSwitch()
private lazy var switchDarkMode = UISwitch()
private lazy var switchCellularData = UISwitch()
private lazy var imvSelectedOption = UIImageView(image: Asset.accessorySelected.image)
private var isContentBlockerEnabled = false
......@@ -216,6 +236,7 @@ class SettingsViewController: AutolayoutViewController {
// switchContentBlocker.isGrayed = true
switchContentBlocker.addTarget(self, action: #selector(showContentBlockerTutorial), for: .touchUpInside)
switchDarkMode.addTarget(self, action: #selector(toggleDarkMode(_:)), for: .valueChanged)
switchCellularData.addTarget(self, action: #selector(toggleCellularData(_:)), for: .valueChanged)
redisplaySettings()
NotificationCenter.default.addObserver(self, selector: #selector(refreshContentBlockerState), name: .UIApplicationDidBecomeActive, object: nil)
......@@ -270,6 +291,12 @@ class SettingsViewController: AutolayoutViewController {
@objc private func viewHasRotated() {
styleNavigationBarWithTitle(L10n.Menu.Item.settings)
}
@objc private func toggleCellularData(_ sender: UISwitch) {
pendingPreferences.trustCellularData = sender.isOn
redisplaySettings()
reportUpdatedPreferences()
}
// XXX: no need to bufferize app preferences
@objc private func toggleDarkMode(_ sender: UISwitch) {
......@@ -393,6 +420,8 @@ class SettingsViewController: AutolayoutViewController {
action.execute { (error) in
self.pendingVPNAction = nil
Client.providers.vpnProvider.updatePreferences(nil)
if shouldReconnect && !isDisconnected {
Client.providers.vpnProvider.reconnect(after: nil) { (error) in
completionHandler()
......@@ -463,6 +492,11 @@ class SettingsViewController: AutolayoutViewController {
private func commitPreferences() {
AppPreferences.shared.piaSocketType = pendingOpenVPNSocketType
//Update with values from Trusted Network Settings
pendingPreferences.trustedNetworks = Client.preferences.trustedNetworks