forked from core/mobile_nebula
141 lines
5.4 KiB
Swift
141 lines
5.4 KiB
Swift
|
import NetworkExtension
|
||
|
|
||
|
class SiteList {
|
||
|
private var sites = [String: Site]()
|
||
|
|
||
|
/// Gets the root directory that can be used to share files between the UI and VPN process. Does ensure the directory exists
|
||
|
static func getRootDir() throws -> URL {
|
||
|
let fileManager = FileManager.default
|
||
|
let rootDir = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.net.defined.mobileNebula")!
|
||
|
|
||
|
if (!fileManager.fileExists(atPath: rootDir.absoluteString)) {
|
||
|
try fileManager.createDirectory(at: rootDir, withIntermediateDirectories: true)
|
||
|
}
|
||
|
|
||
|
return rootDir
|
||
|
}
|
||
|
|
||
|
/// Gets the directory where all sites live, $rootDir/sites. Does ensure the directory exists
|
||
|
static func getSitesDir() throws -> URL {
|
||
|
let fileManager = FileManager.default
|
||
|
let sitesDir = try getRootDir().appendingPathComponent("sites", isDirectory: true)
|
||
|
if (!fileManager.fileExists(atPath: sitesDir.absoluteString)) {
|
||
|
try fileManager.createDirectory(at: sitesDir, withIntermediateDirectories: true)
|
||
|
}
|
||
|
return sitesDir
|
||
|
}
|
||
|
|
||
|
/// Gets the directory where a single site would live, $rootDir/sites/$siteID
|
||
|
static func getSiteDir(id: String, create: Bool = false) throws -> URL {
|
||
|
let fileManager = FileManager.default
|
||
|
let siteDir = try getSitesDir().appendingPathComponent(id, isDirectory: true)
|
||
|
if (create && !fileManager.fileExists(atPath: siteDir.absoluteString)) {
|
||
|
try fileManager.createDirectory(at: siteDir, withIntermediateDirectories: true)
|
||
|
}
|
||
|
return siteDir
|
||
|
}
|
||
|
|
||
|
/// Gets the file that represents the site configuration, $rootDir/sites/$siteID/config.json
|
||
|
static func getSiteConfigFile(id: String, createDir: Bool) throws -> URL {
|
||
|
return try getSiteDir(id: id, create: createDir).appendingPathComponent("config", isDirectory: false).appendingPathExtension("json")
|
||
|
}
|
||
|
|
||
|
/// Gets the file that represents the site log output, $rootDir/sites/$siteID/log
|
||
|
static func getSiteLogFile(id: String, createDir: Bool) throws -> URL {
|
||
|
return try getSiteDir(id: id, create: createDir).appendingPathComponent("logs", isDirectory: false)
|
||
|
}
|
||
|
|
||
|
init(completion: @escaping ([String: Site]?, Error?) -> ()) {
|
||
|
#if targetEnvironment(simulator)
|
||
|
SiteList.loadAllFromFS { sites, err in
|
||
|
if sites != nil {
|
||
|
self.sites = sites!
|
||
|
}
|
||
|
completion(sites, err)
|
||
|
}
|
||
|
#else
|
||
|
SiteList.loadAllFromNETPM { sites, err in
|
||
|
if sites != nil {
|
||
|
self.sites = sites!
|
||
|
}
|
||
|
completion(sites, err)
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
private static func loadAllFromFS(completion: @escaping ([String: Site]?, Error?) -> ()) {
|
||
|
let fileManager = FileManager.default
|
||
|
var siteDirs: [URL]
|
||
|
var sites = [String: Site]()
|
||
|
|
||
|
do {
|
||
|
siteDirs = try fileManager.contentsOfDirectory(at: getSitesDir(), includingPropertiesForKeys: nil)
|
||
|
|
||
|
} catch {
|
||
|
completion(nil, error)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
siteDirs.forEach { path in
|
||
|
do {
|
||
|
let site = try Site(path: path.appendingPathComponent("config").appendingPathExtension("json"))
|
||
|
sites[site.id] = site
|
||
|
|
||
|
} catch {
|
||
|
print(error)
|
||
|
try? fileManager.removeItem(at: path)
|
||
|
print("Deleted non conforming site \(path)")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
completion(sites, nil)
|
||
|
}
|
||
|
|
||
|
private static func loadAllFromNETPM(completion: @escaping ([String: Site]?, Error?) -> ()) {
|
||
|
var sites = [String: Site]()
|
||
|
|
||
|
// dispatchGroup is used to ensure we have migrated all sites before returning them
|
||
|
// If there are no sites to migrate, there are never any entrants
|
||
|
let dispatchGroup = DispatchGroup()
|
||
|
|
||
|
NETunnelProviderManager.loadAllFromPreferences() { newManagers, err in
|
||
|
if (err != nil) {
|
||
|
return completion(nil, err)
|
||
|
}
|
||
|
|
||
|
newManagers?.forEach { manager in
|
||
|
do {
|
||
|
let site = try Site(manager: manager)
|
||
|
if site.needsToMigrateToFS {
|
||
|
dispatchGroup.enter()
|
||
|
site.incomingSite?.save(manager: manager) { error in
|
||
|
if error != nil {
|
||
|
print("Error while migrating site to fs: \(error!.localizedDescription)")
|
||
|
}
|
||
|
|
||
|
print("Migraded site to fs: \(site.name)")
|
||
|
site.needsToMigrateToFS = false
|
||
|
dispatchGroup.leave()
|
||
|
}
|
||
|
}
|
||
|
sites[site.id] = site
|
||
|
|
||
|
} catch {
|
||
|
//TODO: notify the user about this
|
||
|
print("Deleted non conforming site \(manager) \(error)")
|
||
|
manager.removeFromPreferences()
|
||
|
//TODO: delete from disk, we need to try and discover the site id though
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dispatchGroup.notify(queue: .main) {
|
||
|
completion(sites, nil)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func getSites() -> [String: Site] {
|
||
|
return sites
|
||
|
}
|
||
|
}
|