// // TrustedNetworksUI.swift // Passepartout // // Created by Davide De Rosa on 6/21/18. // Copyright (c) 2021 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn // // This file is part of Passepartout. // // Passepartout is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Passepartout is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Passepartout. If not, see . // import Foundation public protocol TrustedNetworksUIDelegate: class { func trustedNetworksCouldDisconnect(_: TrustedNetworksUI) -> Bool func trustedNetworksShouldConfirmDisconnection(_: TrustedNetworksUI, triggeredAt rowIndex: Int, completionHandler: @escaping () -> Void) func trustedNetworks(_: TrustedNetworksUI, shouldInsertWifiAt rowIndex: Int) func trustedNetworks(_: TrustedNetworksUI, shouldReloadWifiAt rowIndex: Int, isTrusted: Bool) func trustedNetworks(_: TrustedNetworksUI, shouldDeleteWifiAt rowIndex: Int) func trustedNetworksShouldReinstall(_: TrustedNetworksUI) } public class TrustedNetworksUI { public enum RowType { case trustsMobile case trustedWiFi case addCurrentWiFi } public private(set) var trustedWifis: [String: Bool] public private(set) var sortedWifis: [String] #if os(iOS) private let hasMobileNetwork: Bool public private(set) var trustsMobileNetwork: Bool public private(set) var rows: [RowType] #endif public weak var delegate: TrustedNetworksUIDelegate? public init() { trustedWifis = [:] sortedWifis = [] #if os(iOS) hasMobileNetwork = Utils.hasCellularData() trustsMobileNetwork = false rows = [] #endif } public func load(from trustedNetworks: TrustedNetworks) { trustedWifis = trustedNetworks.includedWiFis sortedWifis = trustedWifis.keys.sorted() #if os(iOS) trustsMobileNetwork = trustedNetworks.includesMobile rows.removeAll() if hasMobileNetwork { rows.append(.trustsMobile) } for _ in sortedWifis { rows.append(.trustedWiFi) } rows.append(.addCurrentWiFi) #endif } #if os(iOS) public func setMobile(_ isTrusted: Bool) { let completionHandler: () -> Void = { self.trustsMobileNetwork = isTrusted self.delegate?.trustedNetworksShouldReinstall(self) } guard !(isTrusted && mightDisconnect()) else { delegate?.trustedNetworksShouldConfirmDisconnection(self, triggeredAt: 0, completionHandler: completionHandler) return } completionHandler() } #endif public func wifi(at rowIndex: Int) -> (String, Bool) { let index = indexForWifi(at: rowIndex) let wifiName = sortedWifis[index] let isTrusted = trustedWifis[wifiName] ?? false return (wifiName, isTrusted) } public func addCurrentWifi() -> Bool { guard let currentWifi = Utils.currentWifiNetworkName() else { return false } addWifi(currentWifi) return true } public func addWifi(_ wifiToAdd: String) { var index = 0 var isDuplicate = false for wifi in sortedWifis { guard wifiToAdd != wifi else { isDuplicate = true break } guard wifiToAdd > wifi else { break } index += 1 } guard !(trustedWifis[wifiToAdd] ?? false) else { return } let isTrusted = false let rowIndex = rowIndexForWifi(at: index) trustedWifis[wifiToAdd] = isTrusted if !isDuplicate { sortedWifis.insert(wifiToAdd, at: index) #if os(iOS) rows.insert(.trustedWiFi, at: rowIndex) #endif delegate?.trustedNetworks(self, shouldInsertWifiAt: rowIndex) } else { delegate?.trustedNetworks(self, shouldReloadWifiAt: rowIndex, isTrusted: isTrusted) } delegate?.trustedNetworksShouldReinstall(self) } public func removeWifi(at rowIndex: Int) { let index = indexForWifi(at: rowIndex) let removedWifi = sortedWifis.remove(at: index) trustedWifis.removeValue(forKey: removedWifi) #if os(iOS) rows.remove(at: rowIndex) #endif delegate?.trustedNetworks(self, shouldDeleteWifiAt: rowIndex) delegate?.trustedNetworksShouldReinstall(self) } public func enableWifi(at rowIndex: Int) { let index = indexForWifi(at: rowIndex) let wifi = sortedWifis[index] let completionHandler: () -> Void = { self.trustedWifis[wifi] = true self.delegate?.trustedNetworks(self, shouldReloadWifiAt: rowIndex, isTrusted: true) self.delegate?.trustedNetworksShouldReinstall(self) } guard !mightDisconnect() else { delegate?.trustedNetworksShouldConfirmDisconnection(self, triggeredAt: rowIndex, completionHandler: completionHandler) return } completionHandler() } public func disableWifi(at rowIndex: Int) { let index = indexForWifi(at: rowIndex) let wifi = sortedWifis[index] trustedWifis[wifi] = false delegate?.trustedNetworks(self, shouldReloadWifiAt: rowIndex, isTrusted: false) delegate?.trustedNetworksShouldReinstall(self) } public func isTrusted(wifi: String) -> Bool { return trustedWifis[wifi] ?? false } private func indexForWifi(at rowIndex: Int) -> Int { #if os(iOS) return hasMobileNetwork ? (rowIndex - 1) : rowIndex #else return rowIndex #endif } private func rowIndexForWifi(at index: Int) -> Int { #if os(iOS) return index + (hasMobileNetwork ? 1 : 0) #else return index #endif } private func mightDisconnect() -> Bool { return delegate?.trustedNetworksCouldDisconnect(self) ?? false } }