Commit 67767a60 authored by Jose Blaya's avatar Jose Blaya
Browse files

wip

parent 38bd1978
This diff is collapsed.
......@@ -16,6 +16,7 @@ private let log = SwiftyBeaver.self
@UIApplicationMain
class AppDelegate: NSObject, UIApplicationDelegate {
private enum ShortcutItem: String {
case connect
......@@ -25,15 +26,18 @@ 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 = []
configureHotspotHelper()
hotspotHelper = PIAHotspotHelper()
_ = hotspotHelper.configureHotspotHelper()
return true
}
......@@ -187,53 +191,4 @@ class AppDelegate: NSObject, UIApplicationDelegate {
}
}
//MARK: - NEHotspotHelper
private func configureHotspotHelper() {
let options: [String: NSObject] = [kNEHotspotHelperOptionDisplayName : L10n.Hotspothelper.Display.name as NSObject]
let queue: DispatchQueue = DispatchQueue(label: "com.privateinternetaccess.hotspot", attributes: DispatchQueue.Attributes.concurrent)
NEHotspotHelper.supportedNetworkInterfaces()
NEHotspotHelper.register(options: options,
queue: queue) { (cmd: NEHotspotHelperCommand) in
if !Client.providers.vpnProvider.isVPNConnected {
if cmd.commandType == .filterScanList {
var unsecuredList: [NEHotspotNetwork] = []
for element in cmd.networkList! {
if !element.isSecure {
element.setConfidence(.high)
unsecuredList.append(element)
}
}
let response = cmd.createResponse(NEHotspotHelperResult.success)
response.setNetworkList(unsecuredList)
response.deliver()
} else if cmd.commandType == .evaluate {
if let network = cmd.network {
if Client.preferences.shouldConnectWithUnsecuredNetworks && !network.isSecure {
network.setConfidence(.high)
//Connect the VPN
if !Client.providers.vpnProvider.isVPNConnected {
// this time delay seems to fix a strange issue of the VPN connecting from a fresh launch
Macros.dispatch(after: .milliseconds(200)) {
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()
}
}
}
}
}
}
}
//
// 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" : "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
......@@ -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>
......@@ -18,7 +22,5 @@
<array>
<string>$(AppIdentifierPrefix)group.com.privateinternetaccess</string>
</array>
<key>com.apple.developer.networking.HotspotHelper</key>
<true/>
</dict>
</plist>
//
// 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 options: [String: NSObject] = [kNEHotspotHelperOptionDisplayName : L10n.Hotspothelper.Display.name 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.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 {
if !Client.providers.vpnProvider.isVPNConnected {
if ((Client.preferences.shouldConnectWithUnsecuredNetworks && !network.isSecure) || Client.preferences.shouldConnectForAllNetworks ||
weakSelf.trustedNetworks().contains(network.ssid)) {
network.setConfidence(.high)
//Connect the VPN
if !Client.providers.vpnProvider.isVPNConnected {
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)
}
}
}
}
}
......@@ -40,6 +40,8 @@ enum Setting: Int {
case automaticReconnection
case trustedNetworks
case shouldConnectWithUnsecuredNetworks
case contentBlockerState
......@@ -129,7 +131,8 @@ class SettingsViewController: AutolayoutViewController {
],
.applicationSettings: [], // dynamic
.autoConnectSettings: [
.shouldConnectWithUnsecuredNetworks
.trustedNetworks,
.shouldConnectWithUnsecuredNetworks,
],
.contentBlocker: [
.contentBlockerState,
......@@ -158,6 +161,8 @@ class SettingsViewController: AutolayoutViewController {
private lazy var switchAutoJoinWiFi = UISwitch()
private lazy var switchAutoJoinAllNetworks = UISwitch()
private lazy var switchPersistent = UISwitch()
private lazy var switchMACE = UISwitch()
......@@ -223,6 +228,7 @@ class SettingsViewController: AutolayoutViewController {
}
switchPersistent.addTarget(self, action: #selector(togglePersistentConnection(_:)), for: .valueChanged)
switchAutoJoinWiFi.addTarget(self, action: #selector(toggleAutoconnectWithUnsecuredNetworks(_:)), for: .valueChanged)
switchAutoJoinAllNetworks.addTarget(self, action: #selector(toggleAutoconnectWithAllNetworks(_:)), for: .valueChanged)
switchMACE.addTarget(self, action: #selector(toggleMACE(_:)), for: .valueChanged)
// switchContentBlocker.isGrayed = true
switchContentBlocker.addTarget(self, action: #selector(showContentBlockerTutorial), for: .touchUpInside)
......@@ -275,6 +281,12 @@ class SettingsViewController: AutolayoutViewController {
reportUpdatedPreferences()
}
@objc private func toggleAutoconnectWithAllNetworks(_ sender: UISwitch) {
pendingPreferences.shouldConnectForAllNetworks = sender.isOn
redisplaySettings()
reportUpdatedPreferences()
}
@objc private func toggleMACE(_ sender: UISwitch) {
pendingPreferences.mace = sender.isOn
redisplaySettings()
......@@ -821,6 +833,11 @@ extension SettingsViewController: UITableViewDataSource, UITableViewDelegate {
case .resolveGoogleAdsDomain:
cell.textLabel?.text = "Resolve google-analytics.com"
cell.detailTextLabel?.text = nil
case .trustedNetworks:
cell.textLabel?.text = L10n.Settings.Hotspothelper.trustedNetworks
cell.detailTextLabel?.text = nil
}
Theme.current.applySolidLightBackground(cell)
......@@ -978,6 +995,9 @@ extension SettingsViewController: UITableViewDataSource, UITableViewDelegate {
case .resolveGoogleAdsDomain:
resolveGoogleAdsDomain()
case .trustedNetworks:
self.perform(segue: StoryboardSegue.Main.trustedNetworksSegueIdentifier)
default:
break
}
......
......@@ -272,6 +272,7 @@ enum Asset {
static let icon3dtSelectRegion = ImageAsset(name: "icon-3dt-select-region")
static let iconAbout = ImageAsset(name: "icon-about")
static let iconAccount = ImageAsset(name: "icon-account")
static let iconAdd = ImageAsset(name: "icon-add")
static let iconContact = ImageAsset(name: "icon-contact")
static let iconDownload = ImageAsset(name: "icon-download")
static let iconEye = ImageAsset(name: "icon-eye")
......@@ -286,6 +287,7 @@ enum Asset {
static let iconUnselectedWhite = ImageAsset(name: "icon-unselected-white")
static let iconUnselected = ImageAsset(name: "icon-unselected")
static let iconUpload = ImageAsset(name: "icon-upload")
static let iconWifi = ImageAsset(name: "icon-wifi")
static let imageContentBlocker = ImageAsset(name: "image-content-blocker")
static let imageRobot = ImageAsset(name: "image-robot")
static let imageRobotside = ImageAsset(name: "image-robotside")
......@@ -527,6 +529,7 @@ enum Asset {
icon3dtSelectRegion,
iconAbout,
iconAccount,
iconAdd,
iconContact,
iconDownload,
iconEye,
......@@ -541,6 +544,7 @@ enum Asset {
iconUnselectedWhite,
iconUnselected,
iconUpload,
iconWifi,
imageContentBlocker,
imageRobot,
imageRobotside,
......
......@@ -22,6 +22,7 @@ internal enum StoryboardSegue {
case selectRegionAnimatedSegueIdentifier = "SelectRegionAnimatedSegueIdentifier"
case selectRegionSegueIdentifier = "SelectRegionSegueIdentifier"
case settingsSegueIdentifier = "SettingsSegueIdentifier"
case trustedNetworksSegueIdentifier = "TrustedNetworksSegueIdentifier"
case unwindContentBlockerSegueIdentifier = "UnwindContentBlockerSegueIdentifier"
case unwindRegionsSegueIdentifier = "UnwindRegionsSegueIdentifier"
case unwindWalkthroughSegueIdentifier = "UnwindWalkthroughSegueIdentifier"
......
......@@ -444,6 +444,14 @@ internal enum L10n {
internal static let description = L10n.tr("Localizable", "settings.hotspothelper.description")
/// VPN WiFi Protection
internal static let title = L10n.tr("Localizable", "settings.hotspothelper.title")
/// Trusted networks
internal static let trustedNetworks = L10n.tr("Localizable", "settings.hotspothelper.trustedNetworks")
internal enum All {
/// Automatically connect for all WiFi networks.
internal static let description = L10n.tr("Localizable", "settings.hotspothelper.all.description")
/// Connect for all
internal static let title = L10n.tr("Localizable", "settings.hotspothelper.all.title")
}
}
internal enum Reset {
/// This will reset all of the above settings to default.
......@@ -463,6 +471,18 @@ internal enum L10n {
}
}
}
internal enum TrustedNetworks {
/// Trusted networks are networks to automatically join blablabla
internal static let message = L10n.tr("Localizable", "settings.trustedNetworks.message")
internal enum Sections {
/// Available networks
internal static let available = L10n.tr("Localizable", "settings.trustedNetworks.sections.available")
/// Current network
internal static let current = L10n.tr("Localizable", "settings.trustedNetworks.sections.current")
/// Trusted networks
internal static let trusted = L10n.tr("Localizable", "settings.trustedNetworks.sections.trusted")
}
}
}
internal enum Shortcuts {
......
//
// TrustedNetworksViewController.swift
// PIA VPN
//
// Created by Jose Antonio Blaya Garcia on 18/12/2018.
// Copyright © 2018 London Trust Media. All rights reserved.
//
import UIKit
import PIALibrary
class TrustedNetworksViewController: AutolayoutViewController {
@IBOutlet private weak var tableView: UITableView!
private var availableNetworks: [String] = []
private var trustedNetworks: [String] = []
private let currentNetwork: String? = nil
private var hotspotHelper: PIAHotspotHelper!
private lazy var switchAutoJoinAllNetworks = UISwitch()
private enum Sections: Int, EnumsBuilder {
case current = 0
case available
case trusted
case autoConnectAllNetworksSettings
}
private struct Cells {
static let network = "NetworkCell"
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = L10n.Settings.TrustedNetworks.Sections.trusted
self.hotspotHelper = PIAHotspotHelper(withDelegate: self)
self.switchAutoJoinAllNetworks.addTarget(self, action: #selector(toggleAutoconnectWithAllNetworks(_:)), for: .valueChanged)
configureTableView()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
filterAvailableNetworks()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: Restylable
override func viewShouldRestyle() {
super.viewShouldRestyle()
// XXX: for some reason, UITableView is not affected by appearance updates
if let viewContainer = viewContainer {
Theme.current.applyLightBackground(viewContainer)
}
Theme.current.applyLightBackground(tableView)
Theme.current.applyDividerToSeparator(tableView)
}
@objc private func toggleAutoconnectWithAllNetworks(_ sender: UISwitch) {