mobile_nebula/ios/Runner/Sites.swift

164 lines
5.6 KiB
Swift
Raw Normal View History

2020-07-27 20:43:58 +00:00
import NetworkExtension
import MobileNebula
class SiteContainer {
var site: Site
var updater: SiteUpdater
init(site: Site, updater: SiteUpdater) {
self.site = site
self.updater = updater
}
}
class Sites {
private var sites = [String: SiteContainer]()
private var messenger: FlutterBinaryMessenger?
init(messenger: FlutterBinaryMessenger?) {
self.messenger = messenger
}
func loadSites(completion: @escaping ([String: Site]?, Error?) -> ()) {
#if targetEnvironment(simulator)
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("sites")
var configPaths: [URL]
do {
if (!fileManager.fileExists(atPath: documentsURL.absoluteString)) {
try fileManager.createDirectory(at: documentsURL, withIntermediateDirectories: true)
}
configPaths = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil)
} catch {
return completion(nil, error)
}
configPaths.forEach { path in
do {
let config = try Data(contentsOf: path)
let decoder = JSONDecoder()
let incoming = try decoder.decode(IncomingSite.self, from: config)
let site = try Site(incoming: incoming)
let updater = SiteUpdater(messenger: self.messenger!, site: site)
self.sites[site.id] = SiteContainer(site: site, updater: updater)
} catch {
print(error)
// try? fileManager.removeItem(at: path)
print("Deleted non conforming site \(path)")
}
}
let justSites = self.sites.mapValues {
return $0.site
}
completion(justSites, nil)
#else
NETunnelProviderManager.loadAllFromPreferences() { newManagers, err in
if (err != nil) {
return completion(nil, err)
}
newManagers?.forEach { manager in
do {
let site = try Site(manager: manager)
// Load the private key to make sure we can
_ = try site.getKey()
let updater = SiteUpdater(messenger: self.messenger!, site: site)
self.sites[site.id] = SiteContainer(site: site, updater: updater)
} catch {
//TODO: notify the user about this
print("Deleted non conforming site \(manager) \(error)")
manager.removeFromPreferences()
}
}
let justSites = self.sites.mapValues {
return $0.site
}
completion(justSites, nil)
}
#endif
}
func deleteSite(id: String, callback: @escaping (Error?) -> ()) {
if let site = self.sites.removeValue(forKey: id) {
#if targetEnvironment(simulator)
let fileManager = FileManager.default
let sitePath = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("sites").appendingPathComponent(site.site.id)
try? fileManager.removeItem(at: sitePath)
#else
_ = KeyChain.delete(key: site.site.id)
site.site.manager.removeFromPreferences(completionHandler: callback)
#endif
}
// Nothing to remove
callback(nil)
}
func getSite(id: String) -> Site? {
return self.sites[id]?.site
}
func getUpdater(id: String) -> SiteUpdater? {
return self.sites[id]?.updater
}
}
class SiteUpdater: NSObject, FlutterStreamHandler {
private var eventSink: FlutterEventSink?;
private var eventChannel: FlutterEventChannel;
private var site: Site
private var notification: Any?
init(messenger: FlutterBinaryMessenger, site: Site) {
eventChannel = FlutterEventChannel(name: "net.defined.nebula/\(site.id)", binaryMessenger: messenger)
self.site = site
super.init()
eventChannel.setStreamHandler(self)
}
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
eventSink = events;
self.notification = NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: site.manager.connection , queue: nil) { _ in
self.site.status = statusString[self.site.manager.connection.status]
self.site.connected = statusMap[self.site.manager.connection.status]
let d: Dictionary<String, Any> = [
"connected": self.site.connected!,
"status": self.site.status!,
]
self.eventSink?(d)
}
return nil
}
func setError(err: String) {
let d: Dictionary<String, Any> = [
"connected": self.site.connected!,
"status": self.site.status!,
]
self.eventSink?(FlutterError(code: "", message: err, details: d))
}
func onCancel(withArguments arguments: Any?) -> FlutterError? {
if (self.notification != nil) {
NotificationCenter.default.removeObserver(self.notification!)
}
return nil
}
func update(connected: Bool) {
let d: Dictionary<String, Any> = [
"connected": connected,
"status": connected ? "Connected" : "Disconnected",
]
self.eventSink?(d)
}
}