forked from core/mobile_nebula
Rework ios ipc (#28)
This commit is contained in:
parent
4cad646a7c
commit
4c28cc196e
|
@ -1,7 +1,7 @@
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import MobileNebula
|
import MobileNebula
|
||||||
import os.log
|
import os.log
|
||||||
import MMWormhole
|
import SwiftyJSON
|
||||||
|
|
||||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
private var networkMonitor: NWPathMonitor?
|
private var networkMonitor: NWPathMonitor?
|
||||||
|
@ -9,16 +9,32 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
private var site: Site?
|
private var site: Site?
|
||||||
private var _log = OSLog(subsystem: "net.defined.mobileNebula", category: "PacketTunnelProvider")
|
private var _log = OSLog(subsystem: "net.defined.mobileNebula", category: "PacketTunnelProvider")
|
||||||
private var wormhole = MMWormhole(applicationGroupIdentifier: "group.net.defined.mobileNebula", optionalDirectory: "ipc")
|
|
||||||
private var nebula: MobileNebulaNebula?
|
private var nebula: MobileNebulaNebula?
|
||||||
private var didSleep = false
|
private var didSleep = false
|
||||||
|
private var cachedRouteDescription: String?
|
||||||
|
|
||||||
private func log(_ message: StaticString, _ args: CVarArg...) {
|
// This is the system completionHandler, only set when we expect the UI to ask us to actually start so that errors can flow back to the UI
|
||||||
|
private var startCompleter: ((Error?) -> Void)?
|
||||||
|
|
||||||
|
private func log(_ message: StaticString, _ args: Any...) {
|
||||||
os_log(message, log: _log, args)
|
os_log(message, log: _log, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||||
NSKeyedUnarchiver.setClass(IPCRequest.classForKeyedUnarchiver(), forClassName: "Runner.IPCRequest")
|
// There is currently no way to get initialization errors back to the UI via completionHandler here
|
||||||
|
// `expectStart` is sent only via the UI which means we should wait for the real start command which has another completion handler the UI can intercept
|
||||||
|
// In the end we need to call this completionHandler to inform the system of our state
|
||||||
|
if options?["expectStart"] != nil {
|
||||||
|
startCompleter = completionHandler
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// VPN is being booted out of band of the UI. Use the system completion handler as there will be nothing to route initialization errors to but we still need to report
|
||||||
|
// success/fail by the presence of an error or nil
|
||||||
|
start(completionHandler: completionHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func start(completionHandler: @escaping (Error?) -> Void) {
|
||||||
let proto = self.protocolConfiguration as! NETunnelProviderProtocol
|
let proto = self.protocolConfiguration as! NETunnelProviderProtocol
|
||||||
var config: Data
|
var config: Data
|
||||||
var key: String
|
var key: String
|
||||||
|
@ -38,17 +54,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
do {
|
do {
|
||||||
key = try _site.getKey()
|
key = try _site.getKey()
|
||||||
} catch {
|
} catch {
|
||||||
wormhole.passMessageObject(IPCMessage(id: _site.id, type: "error", message: error.localizedDescription), identifier: "nebula")
|
|
||||||
return completionHandler(error)
|
return completionHandler(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
startNetworkMonitor()
|
|
||||||
|
|
||||||
let fileDescriptor = (self.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32) ?? -1
|
let fileDescriptor = (self.packetFlow.value(forKeyPath: "socket.fileDescriptor") as? Int32) ?? -1
|
||||||
if fileDescriptor < 0 {
|
if fileDescriptor < 0 {
|
||||||
let msg = IPCMessage(id: _site.id, type: "error", message: "Starting tunnel failed: Could not determine file descriptor")
|
return completionHandler("Starting tunnel failed: Could not determine file descriptor")
|
||||||
wormhole.passMessageObject(msg, identifier: "nebula")
|
|
||||||
return completionHandler(NSError())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ifnameSize = socklen_t(IFNAMSIZ)
|
var ifnameSize = socklen_t(IFNAMSIZ)
|
||||||
|
@ -66,9 +77,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
var err: NSError?
|
var err: NSError?
|
||||||
let ipNet = MobileNebulaParseCIDR(_site.cert!.cert.details.ips[0], &err)
|
let ipNet = MobileNebulaParseCIDR(_site.cert!.cert.details.ips[0], &err)
|
||||||
if (err != nil) {
|
if (err != nil) {
|
||||||
let msg = IPCMessage(id: _site.id, type: "error", message: err?.localizedDescription ?? "Unknown error from go MobileNebula.ParseCIDR - certificate")
|
return completionHandler(err!)
|
||||||
self.wormhole.passMessageObject(msg, identifier: "nebula")
|
|
||||||
return completionHandler(err)
|
|
||||||
}
|
}
|
||||||
tunnelNetworkSettings.ipv4Settings = NEIPv4Settings(addresses: [ipNet!.ip], subnetMasks: [ipNet!.maskCIDR])
|
tunnelNetworkSettings.ipv4Settings = NEIPv4Settings(addresses: [ipNet!.ip], subnetMasks: [ipNet!.maskCIDR])
|
||||||
var routes: [NEIPv4Route] = [NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR)]
|
var routes: [NEIPv4Route] = [NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR)]
|
||||||
|
@ -77,9 +86,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
_site.unsafeRoutes.forEach { unsafeRoute in
|
_site.unsafeRoutes.forEach { unsafeRoute in
|
||||||
let ipNet = MobileNebulaParseCIDR(unsafeRoute.route, &err)
|
let ipNet = MobileNebulaParseCIDR(unsafeRoute.route, &err)
|
||||||
if (err != nil) {
|
if (err != nil) {
|
||||||
let msg = IPCMessage(id: _site.id, type: "error", message: err?.localizedDescription ?? "Unknown error from go MobileNebula.ParseCIDR - unsafe routes")
|
return completionHandler(err!)
|
||||||
self.wormhole.passMessageObject(msg, identifier: "nebula")
|
|
||||||
return completionHandler(err)
|
|
||||||
}
|
}
|
||||||
routes.append(NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR))
|
routes.append(NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR))
|
||||||
}
|
}
|
||||||
|
@ -87,20 +94,17 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
tunnelNetworkSettings.ipv4Settings!.includedRoutes = routes
|
tunnelNetworkSettings.ipv4Settings!.includedRoutes = routes
|
||||||
tunnelNetworkSettings.mtu = _site.mtu as NSNumber
|
tunnelNetworkSettings.mtu = _site.mtu as NSNumber
|
||||||
|
|
||||||
wormhole.listenForMessage(withIdentifier: "app", listener: self.wormholeListener)
|
|
||||||
self.setTunnelNetworkSettings(tunnelNetworkSettings, completionHandler: {(error:Error?) in
|
self.setTunnelNetworkSettings(tunnelNetworkSettings, completionHandler: {(error:Error?) in
|
||||||
if (error != nil) {
|
if (error != nil) {
|
||||||
let msg = IPCMessage(id: _site.id, type: "error", message: error?.localizedDescription ?? "Unknown setTunnelNetworkSettings error")
|
return completionHandler(error!)
|
||||||
self.wormhole.passMessageObject(msg, identifier: "nebula")
|
|
||||||
return completionHandler(error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err: NSError?
|
var err: NSError?
|
||||||
self.nebula = MobileNebulaNewNebula(String(data: config, encoding: .utf8), key, self.site!.logFile, Int(fileDescriptor), &err)
|
self.nebula = MobileNebulaNewNebula(String(data: config, encoding: .utf8), key, self.site!.logFile, Int(fileDescriptor), &err)
|
||||||
|
self.startNetworkMonitor()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
let msg = IPCMessage(id: _site.id, type: "error", message: err?.localizedDescription ?? "Unknown error from go MobileNebula.Main")
|
return completionHandler(err!)
|
||||||
self.wormhole.passMessageObject(msg, identifier: "nebula")
|
|
||||||
return completionHandler(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.nebula!.start()
|
self.nebula!.start()
|
||||||
|
@ -132,21 +136,80 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pathUpdate(path: Network.NWPath) {
|
private func pathUpdate(path: Network.NWPath) {
|
||||||
//TODO: we can likely be smarter here and enumerate all the interfaces and their current addresses, only rebind if things changed
|
let routeDescription = collectAddresses(endpoints: path.gateways)
|
||||||
nebula?.rebind("network change")
|
if routeDescription != cachedRouteDescription {
|
||||||
|
// Don't bother to rebind if we don't have any gateways
|
||||||
|
if routeDescription != "" {
|
||||||
|
nebula?.rebind("network change to: \(routeDescription); from: \(cachedRouteDescription ?? "none")")
|
||||||
|
}
|
||||||
|
cachedRouteDescription = routeDescription
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func wormholeListener(msg: Any?) {
|
private func collectAddresses(endpoints: [Network.NWEndpoint]) -> String {
|
||||||
guard let call = msg as? IPCRequest else {
|
var str: [String] = []
|
||||||
|
endpoints.forEach{ endpoint in
|
||||||
|
switch endpoint {
|
||||||
|
case let .hostPort(.ipv6(host), port):
|
||||||
|
str.append("[\(host)]:\(port)")
|
||||||
|
case let .hostPort(.ipv4(host), port):
|
||||||
|
str.append("\(host):\(port)")
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.sorted().joined(separator: ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handleAppMessage(_ data: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||||
|
guard let call = try? JSONDecoder().decode(IPCRequest.self, from: data) else {
|
||||||
log("Failed to decode IPCRequest from network extension")
|
log("Failed to decode IPCRequest from network extension")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var error: Error?
|
var error: Error?
|
||||||
var data: Any?
|
var data: JSON?
|
||||||
|
|
||||||
|
// start command has special treatment due to needing to call two completers
|
||||||
|
if call.command == "start" {
|
||||||
|
self.start() { error in
|
||||||
|
// Notify the system of our start result
|
||||||
|
if self.startCompleter != nil {
|
||||||
|
if error == nil {
|
||||||
|
// Clean boot, no errors
|
||||||
|
self.startCompleter!(nil)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// We encountered an error, we can just pass NSError() here since ios throws it away
|
||||||
|
// But we will provide it in the event we can intercept the error without doing this workaround sometime in the future
|
||||||
|
self.startCompleter!(error!.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the UI if we have a completionHandler
|
||||||
|
if completionHandler != nil {
|
||||||
|
if error == nil {
|
||||||
|
// No response data, this is expected on a clean start
|
||||||
|
completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil)))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Error response has
|
||||||
|
completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .error, message: JSON(error!.localizedDescription))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if nebula == nil {
|
||||||
|
// Respond with an empty success message in the event a command comes in before we've truly started
|
||||||
|
log("Received command but do not have a nebula instance")
|
||||||
|
return completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil)))
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: try catch over all this
|
//TODO: try catch over all this
|
||||||
switch call.type {
|
switch call.command {
|
||||||
case "listHostmap": (data, error) = listHostmap(pending: false)
|
case "listHostmap": (data, error) = listHostmap(pending: false)
|
||||||
case "listPendingHostmap": (data, error) = listHostmap(pending: true)
|
case "listPendingHostmap": (data, error) = listHostmap(pending: true)
|
||||||
case "getHostInfo": (data, error) = getHostInfo(args: call.arguments!)
|
case "getHostInfo": (data, error) = getHostInfo(args: call.arguments!)
|
||||||
|
@ -154,37 +217,37 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
case "closeTunnel": (data, error) = closeTunnel(args: call.arguments!)
|
case "closeTunnel": (data, error) = closeTunnel(args: call.arguments!)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error = "Unknown IPC message type \(call.type)"
|
error = "Unknown IPC message type \(call.command)"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != nil) {
|
if (error != nil) {
|
||||||
self.wormhole.passMessageObject(IPCMessage(id: "", type: "error", message: error!.localizedDescription), identifier: call.callbackId)
|
completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .error, message: JSON(error?.localizedDescription ?? "Unknown error"))))
|
||||||
} else {
|
} else {
|
||||||
self.wormhole.passMessageObject(IPCMessage(id: "", type: "success", message: data), identifier: call.callbackId)
|
completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .success, message: data)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func listHostmap(pending: Bool) -> (String?, Error?) {
|
private func listHostmap(pending: Bool) -> (JSON?, Error?) {
|
||||||
var err: NSError?
|
var err: NSError?
|
||||||
let res = nebula!.listHostmap(pending, error: &err)
|
let res = nebula!.listHostmap(pending, error: &err)
|
||||||
return (res, err)
|
return (JSON(res), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getHostInfo(args: Dictionary<String, Any>) -> (String?, Error?) {
|
private func getHostInfo(args: JSON) -> (JSON?, Error?) {
|
||||||
var err: NSError?
|
var err: NSError?
|
||||||
let res = nebula!.getHostInfo(byVpnIp: args["vpnIp"] as? String, pending: args["pending"] as! Bool, error: &err)
|
let res = nebula!.getHostInfo(byVpnIp: args["vpnIp"].string, pending: args["pending"].boolValue, error: &err)
|
||||||
return (res, err)
|
return (JSON(res), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setRemoteForTunnel(args: Dictionary<String, Any>) -> (String?, Error?) {
|
private func setRemoteForTunnel(args: JSON) -> (JSON?, Error?) {
|
||||||
var err: NSError?
|
var err: NSError?
|
||||||
let res = nebula!.setRemoteForTunnel(args["vpnIp"] as? String, addr: args["addr"] as? String, error: &err)
|
let res = nebula!.setRemoteForTunnel(args["vpnIp"].string, addr: args["addr"].string, error: &err)
|
||||||
return (res, err)
|
return (JSON(res), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func closeTunnel(args: Dictionary<String, Any>) -> (Bool?, Error?) {
|
private func closeTunnel(args: JSON) -> (JSON?, Error?) {
|
||||||
let res = nebula!.closeTunnel(args["vpnIp"] as? String)
|
let res = nebula!.closeTunnel(args["vpnIp"].string)
|
||||||
return (res, nil)
|
return (JSON(res), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,58 +1,36 @@
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import MobileNebula
|
import MobileNebula
|
||||||
|
import SwiftyJSON
|
||||||
|
|
||||||
extension String: Error {}
|
extension String: Error {}
|
||||||
|
|
||||||
class IPCMessage: NSObject, NSCoding {
|
enum IPCResponseType: String, Codable {
|
||||||
var id: String
|
case error = "error"
|
||||||
var type: String
|
case success = "success"
|
||||||
var message: Any?
|
}
|
||||||
|
|
||||||
func encode(with aCoder: NSCoder) {
|
class IPCResponse: Codable {
|
||||||
aCoder.encode(id, forKey: "id")
|
var type: IPCResponseType
|
||||||
aCoder.encode(type, forKey: "type")
|
//TODO: change message to data?
|
||||||
aCoder.encode(message, forKey: "message")
|
var message: JSON?
|
||||||
}
|
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
init(type: IPCResponseType, message: JSON?) {
|
||||||
id = aDecoder.decodeObject(forKey: "id") as! String
|
|
||||||
type = aDecoder.decodeObject(forKey: "type") as! String
|
|
||||||
message = aDecoder.decodeObject(forKey: "message") as Any?
|
|
||||||
}
|
|
||||||
|
|
||||||
init(id: String, type: String, message: Any) {
|
|
||||||
self.id = id
|
|
||||||
self.type = type
|
self.type = type
|
||||||
self.message = message
|
self.message = message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IPCRequest: NSObject, NSCoding {
|
class IPCRequest: Codable {
|
||||||
var type: String
|
var command: String
|
||||||
var callbackId: String
|
var arguments: JSON?
|
||||||
var arguments: Dictionary<String, Any>?
|
|
||||||
|
|
||||||
func encode(with aCoder: NSCoder) {
|
init(command: String, arguments: JSON?) {
|
||||||
aCoder.encode(type, forKey: "type")
|
self.command = command
|
||||||
aCoder.encode(arguments, forKey: "arguments")
|
|
||||||
aCoder.encode(callbackId, forKey: "callbackId")
|
|
||||||
}
|
|
||||||
|
|
||||||
required init(coder aDecoder: NSCoder) {
|
|
||||||
callbackId = aDecoder.decodeObject(forKey: "callbackId") as! String
|
|
||||||
type = aDecoder.decodeObject(forKey: "type") as! String
|
|
||||||
arguments = aDecoder.decodeObject(forKey: "arguments") as? Dictionary<String, Any>
|
|
||||||
}
|
|
||||||
|
|
||||||
init(callbackId: String, type: String, arguments: Dictionary<String, Any>?) {
|
|
||||||
self.callbackId = callbackId
|
|
||||||
self.type = type
|
|
||||||
self.arguments = arguments
|
self.arguments = arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
init(callbackId: String, type: String) {
|
init(command: String) {
|
||||||
self.callbackId = callbackId
|
self.command = command
|
||||||
self.type = type
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +113,7 @@ let statusString: Dictionary<NEVPNStatus, String> = [
|
||||||
]
|
]
|
||||||
|
|
||||||
// Represents a site that was pulled out of the system configuration
|
// Represents a site that was pulled out of the system configuration
|
||||||
struct Site: Codable {
|
class Site: Codable {
|
||||||
// Stored in manager
|
// Stored in manager
|
||||||
var name: String
|
var name: String
|
||||||
var id: String
|
var id: String
|
||||||
|
@ -151,18 +129,17 @@ struct Site: Codable {
|
||||||
var cipher: String
|
var cipher: String
|
||||||
var sortKey: Int
|
var sortKey: Int
|
||||||
var logVerbosity: String
|
var logVerbosity: String
|
||||||
var connected: Bool?
|
var connected: Bool? //TODO: active is a better name
|
||||||
var status: String?
|
var status: String?
|
||||||
var logFile: String?
|
var logFile: String?
|
||||||
|
|
||||||
// A list of error encountered when trying to rehydrate a site from config
|
// A list of error encountered when trying to rehydrate a site from config
|
||||||
var errors: [String]
|
var errors: [String]
|
||||||
|
|
||||||
// We initialize to avoid an error with Codable, there is probably a better way since manager must be present for a Site but is not codable
|
var manager: NETunnelProviderManager?
|
||||||
var manager: NETunnelProviderManager = NETunnelProviderManager()
|
|
||||||
|
|
||||||
// Creates a new site from a vpn manager instance
|
// Creates a new site from a vpn manager instance
|
||||||
init(manager: NETunnelProviderManager) throws {
|
convenience init(manager: NETunnelProviderManager) throws {
|
||||||
//TODO: Throw an error and have Sites delete the site, notify the user instead of using !
|
//TODO: Throw an error and have Sites delete the site, notify the user instead of using !
|
||||||
let proto = manager.protocolConfiguration as! NETunnelProviderProtocol
|
let proto = manager.protocolConfiguration as! NETunnelProviderProtocol
|
||||||
try self.init(proto: proto)
|
try self.init(proto: proto)
|
||||||
|
@ -171,7 +148,7 @@ struct Site: Codable {
|
||||||
self.status = statusString[manager.connection.status]
|
self.status = statusString[manager.connection.status]
|
||||||
}
|
}
|
||||||
|
|
||||||
init(proto: NETunnelProviderProtocol) throws {
|
convenience init(proto: NETunnelProviderProtocol) throws {
|
||||||
let dict = proto.providerConfiguration
|
let dict = proto.providerConfiguration
|
||||||
let config = dict?["config"] as? Data ?? Data()
|
let config = dict?["config"] as? Data ?? Data()
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
|
|
|
@ -32,12 +32,12 @@ target 'Runner' do
|
||||||
use_modular_headers!
|
use_modular_headers!
|
||||||
|
|
||||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
pod 'MMWormhole', '~> 2.0.0'
|
pod 'SwiftyJSON', '~> 5.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
target 'NebulaNetworkExtension' do
|
target 'NebulaNetworkExtension' do
|
||||||
use_frameworks!
|
use_frameworks!
|
||||||
pod 'MMWormhole', '~> 2.0.0'
|
pod 'SwiftyJSON', '~> 5.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
|
|
|
@ -39,9 +39,6 @@ PODS:
|
||||||
- Flutter
|
- Flutter
|
||||||
- FLAnimatedImage (1.0.12)
|
- FLAnimatedImage (1.0.12)
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- MMWormhole (2.0.0):
|
|
||||||
- MMWormhole/Core (= 2.0.0)
|
|
||||||
- MMWormhole/Core (2.0.0)
|
|
||||||
- MTBBarcodeScanner (5.0.11)
|
- MTBBarcodeScanner (5.0.11)
|
||||||
- package_info (0.0.1):
|
- package_info (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
@ -54,6 +51,7 @@ PODS:
|
||||||
- FLAnimatedImage (>= 1.0.11)
|
- FLAnimatedImage (>= 1.0.11)
|
||||||
- SDWebImage/Core (~> 5.6)
|
- SDWebImage/Core (~> 5.6)
|
||||||
- SwiftProtobuf (1.8.0)
|
- SwiftProtobuf (1.8.0)
|
||||||
|
- SwiftyJSON (5.0.1)
|
||||||
- url_launcher (0.0.1):
|
- url_launcher (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
|
@ -61,9 +59,9 @@ DEPENDENCIES:
|
||||||
- barcode_scan (from `.symlinks/plugins/barcode_scan/ios`)
|
- barcode_scan (from `.symlinks/plugins/barcode_scan/ios`)
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- MMWormhole (~> 2.0.0)
|
|
||||||
- package_info (from `.symlinks/plugins/package_info/ios`)
|
- package_info (from `.symlinks/plugins/package_info/ios`)
|
||||||
- path_provider (from `.symlinks/plugins/path_provider/ios`)
|
- path_provider (from `.symlinks/plugins/path_provider/ios`)
|
||||||
|
- SwiftyJSON (~> 5.0)
|
||||||
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
|
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
|
@ -71,11 +69,11 @@ SPEC REPOS:
|
||||||
- DKImagePickerController
|
- DKImagePickerController
|
||||||
- DKPhotoGallery
|
- DKPhotoGallery
|
||||||
- FLAnimatedImage
|
- FLAnimatedImage
|
||||||
- MMWormhole
|
|
||||||
- MTBBarcodeScanner
|
- MTBBarcodeScanner
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SDWebImageFLPlugin
|
- SDWebImageFLPlugin
|
||||||
- SwiftProtobuf
|
- SwiftProtobuf
|
||||||
|
- SwiftyJSON
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
barcode_scan:
|
barcode_scan:
|
||||||
|
@ -98,15 +96,15 @@ SPEC CHECKSUMS:
|
||||||
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
|
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
|
||||||
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
|
FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
|
||||||
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
||||||
MMWormhole: 0cd3fd35a9118b2e2d762b499f54eeaace0be791
|
|
||||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||||
SDWebImage: 84000f962cbfa70c07f19d2234cbfcf5d779b5dc
|
SDWebImage: 84000f962cbfa70c07f19d2234cbfcf5d779b5dc
|
||||||
SDWebImageFLPlugin: 6c2295fb1242d44467c6c87dc5db6b0a13228fd8
|
SDWebImageFLPlugin: 6c2295fb1242d44467c6c87dc5db6b0a13228fd8
|
||||||
SwiftProtobuf: 2cbd9409689b7df170d82a92a33443c8e3e14a70
|
SwiftProtobuf: 2cbd9409689b7df170d82a92a33443c8e3e14a70
|
||||||
|
SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e
|
||||||
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
||||||
|
|
||||||
PODFILE CHECKSUM: e8d4fb1ed5b0713de2623a28dfae2585e15c0d00
|
PODFILE CHECKSUM: 87c61886589bcc4c3c709db9ee22a607d81c4861
|
||||||
|
|
||||||
COCOAPODS: 1.10.1
|
COCOAPODS: 1.10.1
|
||||||
|
|
|
@ -23,10 +23,10 @@
|
||||||
43AD63F424EB3802000FB47E /* Share.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AD63F324EB3802000FB47E /* Share.swift */; };
|
43AD63F424EB3802000FB47E /* Share.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AD63F324EB3802000FB47E /* Share.swift */; };
|
||||||
4CF2F06A02A63B862C9F6F03 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384887B4785D38431E800D3A /* Pods_Runner.framework */; };
|
4CF2F06A02A63B862C9F6F03 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384887B4785D38431E800D3A /* Pods_Runner.framework */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
78E28476711DF3A9D186C429 /* Pods_NebulaNetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8EFDD7248CAE56012FE2608C /* Pods_NebulaNetworkExtension.framework */; };
|
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
E91B9DAD4A83866D0AF1DAE1 /* Pods_NebulaNetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0A96949A0B117C4ACE752C /* Pods_NebulaNetworkExtension.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -64,11 +64,11 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
137DCAF9F91CD7AF6438A183 /* Pods-NebulaNetworkExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NebulaNetworkExtension.debug.xcconfig"; path = "Target Support Files/Pods-NebulaNetworkExtension/Pods-NebulaNetworkExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
384887B4785D38431E800D3A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
384887B4785D38431E800D3A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
41927814D2E140A347A01067 /* Pods-NebulaNetworkExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NebulaNetworkExtension.debug.xcconfig"; path = "Target Support Files/Pods-NebulaNetworkExtension/Pods-NebulaNetworkExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
437F72582469AAC500A0C4B9 /* Site.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Site.swift; sourceTree = "<group>"; };
|
437F72582469AAC500A0C4B9 /* Site.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Site.swift; sourceTree = "<group>"; };
|
||||||
437F725C2469AC5700A0C4B9 /* Keychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
|
437F725C2469AC5700A0C4B9 /* Keychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = "<group>"; };
|
||||||
43871C9A2444DD39004F9075 /* MobileNebula.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MobileNebula.framework; sourceTree = "<group>"; };
|
43871C9A2444DD39004F9075 /* MobileNebula.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = MobileNebula.framework; sourceTree = "<group>"; };
|
||||||
|
@ -84,12 +84,14 @@
|
||||||
43B66ECC245A146300B18C36 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
43B66ECC245A146300B18C36 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||||
43B828DA249C08DC00CA229C /* MMWormhole.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MMWormhole.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
43B828DA249C08DC00CA229C /* MMWormhole.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MMWormhole.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
43E9BBD0251450C5000BFB8C /* MMWormhole.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MMWormhole.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
43E9BBD0251450C5000BFB8C /* MMWormhole.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MMWormhole.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
53C42258A2092B55937DCF53 /* Pods-NebulaNetworkExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NebulaNetworkExtension.profile.xcconfig"; path = "Target Support Files/Pods-NebulaNetworkExtension/Pods-NebulaNetworkExtension.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
5C0A96949A0B117C4ACE752C /* Pods_NebulaNetworkExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NebulaNetworkExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
6E7A71D8C71BF965D042667D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
6E7A71D8C71BF965D042667D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
8E4961BE2F06B97C8C693530 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
8E4961BE2F06B97C8C693530 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
8EFDD7248CAE56012FE2608C /* Pods_NebulaNetworkExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NebulaNetworkExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
9169E2D0D49FAF5172A6E7B8 /* Pods-NebulaNetworkExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NebulaNetworkExtension.release.xcconfig"; path = "Target Support Files/Pods-NebulaNetworkExtension/Pods-NebulaNetworkExtension.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -98,8 +100,6 @@
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
C2D5198CF6975BF93E8A6F93 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
C2D5198CF6975BF93E8A6F93 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
E346A0DC829EBFB76D581AAD /* Pods-NebulaNetworkExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NebulaNetworkExtension.release.xcconfig"; path = "Target Support Files/Pods-NebulaNetworkExtension/Pods-NebulaNetworkExtension.release.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
FA7B03A7901388BC39329544 /* Pods-NebulaNetworkExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NebulaNetworkExtension.profile.xcconfig"; path = "Target Support Files/Pods-NebulaNetworkExtension/Pods-NebulaNetworkExtension.profile.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
files = (
|
files = (
|
||||||
43AA89622444DAA500EDC39C /* NetworkExtension.framework in Frameworks */,
|
43AA89622444DAA500EDC39C /* NetworkExtension.framework in Frameworks */,
|
||||||
43871C9B2444DD39004F9075 /* MobileNebula.framework in Frameworks */,
|
43871C9B2444DD39004F9075 /* MobileNebula.framework in Frameworks */,
|
||||||
78E28476711DF3A9D186C429 /* Pods_NebulaNetworkExtension.framework in Frameworks */,
|
E91B9DAD4A83866D0AF1DAE1 /* Pods_NebulaNetworkExtension.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -135,7 +135,7 @@
|
||||||
43B66ECA245A0C8400B18C36 /* CoreFoundation.framework */,
|
43B66ECA245A0C8400B18C36 /* CoreFoundation.framework */,
|
||||||
43AA894E2444D8BC00EDC39C /* NetworkExtension.framework */,
|
43AA894E2444D8BC00EDC39C /* NetworkExtension.framework */,
|
||||||
384887B4785D38431E800D3A /* Pods_Runner.framework */,
|
384887B4785D38431E800D3A /* Pods_Runner.framework */,
|
||||||
8EFDD7248CAE56012FE2608C /* Pods_NebulaNetworkExtension.framework */,
|
5C0A96949A0B117C4ACE752C /* Pods_NebulaNetworkExtension.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -217,9 +217,9 @@
|
||||||
C2D5198CF6975BF93E8A6F93 /* Pods-Runner.debug.xcconfig */,
|
C2D5198CF6975BF93E8A6F93 /* Pods-Runner.debug.xcconfig */,
|
||||||
6E7A71D8C71BF965D042667D /* Pods-Runner.release.xcconfig */,
|
6E7A71D8C71BF965D042667D /* Pods-Runner.release.xcconfig */,
|
||||||
8E4961BE2F06B97C8C693530 /* Pods-Runner.profile.xcconfig */,
|
8E4961BE2F06B97C8C693530 /* Pods-Runner.profile.xcconfig */,
|
||||||
137DCAF9F91CD7AF6438A183 /* Pods-NebulaNetworkExtension.debug.xcconfig */,
|
41927814D2E140A347A01067 /* Pods-NebulaNetworkExtension.debug.xcconfig */,
|
||||||
E346A0DC829EBFB76D581AAD /* Pods-NebulaNetworkExtension.release.xcconfig */,
|
9169E2D0D49FAF5172A6E7B8 /* Pods-NebulaNetworkExtension.release.xcconfig */,
|
||||||
FA7B03A7901388BC39329544 /* Pods-NebulaNetworkExtension.profile.xcconfig */,
|
53C42258A2092B55937DCF53 /* Pods-NebulaNetworkExtension.profile.xcconfig */,
|
||||||
);
|
);
|
||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -231,7 +231,7 @@
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 43AA895D2444DA6500EDC39C /* Build configuration list for PBXNativeTarget "NebulaNetworkExtension" */;
|
buildConfigurationList = 43AA895D2444DA6500EDC39C /* Build configuration list for PBXNativeTarget "NebulaNetworkExtension" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
D39D78EE128AD494ACEF8DC0 /* [CP] Check Pods Manifest.lock */,
|
2C0A52E24BC9F327251CBAD2 /* [CP] Check Pods Manifest.lock */,
|
||||||
43AA89632444DAD100EDC39C /* ShellScript */,
|
43AA89632444DAD100EDC39C /* ShellScript */,
|
||||||
43AA89502444DA6500EDC39C /* Sources */,
|
43AA89502444DA6500EDC39C /* Sources */,
|
||||||
43AA89512444DA6500EDC39C /* Frameworks */,
|
43AA89512444DA6500EDC39C /* Frameworks */,
|
||||||
|
@ -343,11 +343,11 @@
|
||||||
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
|
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
|
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/FLAnimatedImage/FLAnimatedImage.framework",
|
"${BUILT_PRODUCTS_DIR}/FLAnimatedImage/FLAnimatedImage.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/MMWormhole/MMWormhole.framework",
|
|
||||||
"${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework",
|
"${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/SDWebImageFLPlugin/SDWebImageFLPlugin.framework",
|
"${BUILT_PRODUCTS_DIR}/SDWebImageFLPlugin/SDWebImageFLPlugin.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework",
|
"${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework",
|
||||||
|
"${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/barcode_scan/barcode_scan.framework",
|
"${BUILT_PRODUCTS_DIR}/barcode_scan/barcode_scan.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
|
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/package_info/package_info.framework",
|
"${BUILT_PRODUCTS_DIR}/package_info/package_info.framework",
|
||||||
|
@ -359,11 +359,11 @@
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLAnimatedImage.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLAnimatedImage.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MMWormhole.framework",
|
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageFLPlugin.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImageFLPlugin.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework",
|
||||||
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework",
|
||||||
|
@ -375,6 +375,28 @@
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
|
2C0A52E24BC9F327251CBAD2 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-NebulaNetworkExtension-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -421,28 +443,6 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
|
||||||
};
|
};
|
||||||
D39D78EE128AD494ACEF8DC0 /* [CP] Check Pods Manifest.lock */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
|
||||||
"${PODS_ROOT}/Manifest.lock",
|
|
||||||
);
|
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
"$(DERIVED_FILE_DIR)/Pods-NebulaNetworkExtension-checkManifestLockResult.txt",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
FF0E0EB9A684F086443A8FBA /* [CP] Check Pods Manifest.lock */ = {
|
FF0E0EB9A684F086443A8FBA /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -579,7 +579,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_TEAM = 576H3XS7FP;
|
DEVELOPMENT_TEAM = 576H3XS7FP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -605,7 +605,7 @@
|
||||||
};
|
};
|
||||||
43AA895E2444DA6500EDC39C /* Debug */ = {
|
43AA895E2444DA6500EDC39C /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 137DCAF9F91CD7AF6438A183 /* Pods-NebulaNetworkExtension.debug.xcconfig */;
|
baseConfigurationReference = 41927814D2E140A347A01067 /* Pods-NebulaNetworkExtension.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
@ -614,7 +614,7 @@
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_TEAM = 576H3XS7FP;
|
DEVELOPMENT_TEAM = 576H3XS7FP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -641,7 +641,7 @@
|
||||||
};
|
};
|
||||||
43AA895F2444DA6500EDC39C /* Release */ = {
|
43AA895F2444DA6500EDC39C /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = E346A0DC829EBFB76D581AAD /* Pods-NebulaNetworkExtension.release.xcconfig */;
|
baseConfigurationReference = 9169E2D0D49FAF5172A6E7B8 /* Pods-NebulaNetworkExtension.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
@ -650,7 +650,7 @@
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_TEAM = 576H3XS7FP;
|
DEVELOPMENT_TEAM = 576H3XS7FP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -674,7 +674,7 @@
|
||||||
};
|
};
|
||||||
43AA89602444DA6500EDC39C /* Profile */ = {
|
43AA89602444DA6500EDC39C /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = FA7B03A7901388BC39329544 /* Pods-NebulaNetworkExtension.profile.xcconfig */;
|
baseConfigurationReference = 53C42258A2092B55937DCF53 /* Pods-NebulaNetworkExtension.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
@ -683,7 +683,7 @@
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = NebulaNetworkExtension/NebulaNetworkExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_TEAM = 576H3XS7FP;
|
DEVELOPMENT_TEAM = 576H3XS7FP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -819,7 +819,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_TEAM = 576H3XS7FP;
|
DEVELOPMENT_TEAM = 576H3XS7FP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -852,7 +852,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 11;
|
||||||
DEVELOPMENT_TEAM = 576H3XS7FP;
|
DEVELOPMENT_TEAM = 576H3XS7FP;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
|
|
@ -2,7 +2,7 @@ import UIKit
|
||||||
import Flutter
|
import Flutter
|
||||||
import MobileNebula
|
import MobileNebula
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import MMWormhole
|
import SwiftyJSON
|
||||||
|
|
||||||
enum ChannelName {
|
enum ChannelName {
|
||||||
static let vpn = "net.defined.mobileNebula/NebulaVpnService"
|
static let vpn = "net.defined.mobileNebula/NebulaVpnService"
|
||||||
|
@ -15,7 +15,6 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
private var sites: Sites?
|
private var sites: Sites?
|
||||||
private var wormhole = MMWormhole(applicationGroupIdentifier: "group.net.defined.mobileNebula", optionalDirectory: "ipc")
|
|
||||||
|
|
||||||
override func application(
|
override func application(
|
||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
|
@ -30,9 +29,6 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
|
||||||
sites = Sites(messenger: controller.binaryMessenger)
|
sites = Sites(messenger: controller.binaryMessenger)
|
||||||
let channel = FlutterMethodChannel(name: ChannelName.vpn, binaryMessenger: controller.binaryMessenger)
|
let channel = FlutterMethodChannel(name: ChannelName.vpn, binaryMessenger: controller.binaryMessenger)
|
||||||
|
|
||||||
NSKeyedUnarchiver.setClass(IPCMessage.classForKeyedUnarchiver(), forClassName: "NebulaNetworkExtension.IPCMessage")
|
|
||||||
wormhole.listenForMessage(withIdentifier: "nebula", listener: self.wormholeListener)
|
|
||||||
|
|
||||||
channel.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
|
channel.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
|
||||||
switch call.method {
|
switch call.method {
|
||||||
case "nebula.parseCerts": return self.nebulaParseCerts(call: call, result: result)
|
case "nebula.parseCerts": return self.nebulaParseCerts(call: call, result: result)
|
||||||
|
@ -45,11 +41,11 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
|
||||||
case "startSite": return self.startSite(call: call, result: result)
|
case "startSite": return self.startSite(call: call, result: result)
|
||||||
case "stopSite": return self.stopSite(call: call, result: result)
|
case "stopSite": return self.stopSite(call: call, result: result)
|
||||||
|
|
||||||
case "active.listHostmap": self.activeListHostmap(call: call, result: result)
|
case "active.listHostmap": self.vpnRequest(command: "listHostmap", arguments: call.arguments, result: result)
|
||||||
case "active.listPendingHostmap": self.activeListPendingHostmap(call: call, result: result)
|
case "active.listPendingHostmap": self.vpnRequest(command: "listPendingHostmap", arguments: call.arguments, result: result)
|
||||||
case "active.getHostInfo": self.activeGetHostInfo(call: call, result: result)
|
case "active.getHostInfo": self.vpnRequest(command: "getHostInfo", arguments: call.arguments, result: result)
|
||||||
case "active.setRemoteForTunnel": self.activeSetRemoteForTunnel(call: call, result: result)
|
case "active.setRemoteForTunnel": self.vpnRequest(command: "setRemoteForTunnel", arguments: call.arguments, result: result)
|
||||||
case "active.closeTunnel": self.activeCloseTunnel(call: call, result: result)
|
case "active.closeTunnel": self.vpnRequest(command: "closeTunnel", arguments: call.arguments, result: result)
|
||||||
|
|
||||||
case "share": Share.share(call: call, result: result)
|
case "share": Share.share(call: call, result: result)
|
||||||
case "shareFile": Share.shareFile(call: call, result: result)
|
case "shareFile": Share.shareFile(call: call, result: result)
|
||||||
|
@ -143,12 +139,14 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
|
||||||
func startSite(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
func startSite(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
guard let args = call.arguments as? Dictionary<String, String> else { return result(NoArgumentsError()) }
|
guard let args = call.arguments as? Dictionary<String, String> else { return result(NoArgumentsError()) }
|
||||||
guard let id = args["id"] else { return result(MissingArgumentError(message: "id is a required argument")) }
|
guard let id = args["id"] else { return result(MissingArgumentError(message: "id is a required argument")) }
|
||||||
|
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
let updater = self.sites?.getUpdater(id: id)
|
let updater = self.sites?.getUpdater(id: id)
|
||||||
updater?.update(connected: true)
|
updater?.update(connected: true)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
let manager = self.sites?.getSite(id: id)?.manager
|
let container = self.sites?.getContainer(id: id)
|
||||||
|
let manager = container?.site.manager
|
||||||
|
|
||||||
manager?.loadFromPreferences{ error in
|
manager?.loadFromPreferences{ error in
|
||||||
//TODO: Handle load error
|
//TODO: Handle load error
|
||||||
// This is silly but we need to enable the site each time to avoid situations where folks have multiple sites
|
// This is silly but we need to enable the site each time to avoid situations where folks have multiple sites
|
||||||
|
@ -158,12 +156,13 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
|
||||||
manager?.loadFromPreferences{ error in
|
manager?.loadFromPreferences{ error in
|
||||||
//TODO: Handle load error
|
//TODO: Handle load error
|
||||||
do {
|
do {
|
||||||
try manager?.connection.startVPNTunnel()
|
container?.updater.startFunc = {() -> Void in
|
||||||
|
return self.vpnRequest(command: "start", arguments: args, result: result)
|
||||||
|
}
|
||||||
|
try manager?.connection.startVPNTunnel(options: ["expectStart": NSNumber(1)])
|
||||||
} catch {
|
} catch {
|
||||||
return result(CallFailedError(message: "Could not start site", details: error.localizedDescription))
|
return result(CallFailedError(message: "Could not start site", details: error.localizedDescription))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result(nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,120 +187,46 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func activeListHostmap(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
func vpnRequest(command: String, arguments: Any?, result: @escaping FlutterResult) {
|
||||||
guard let args = call.arguments as? Dictionary<String, String> else { return result(NoArgumentsError()) }
|
guard let args = arguments as? Dictionary<String, Any> else { return result(NoArgumentsError()) }
|
||||||
guard let id = args["id"] else { return result(MissingArgumentError(message: "id is a required argument")) }
|
|
||||||
//TODO: match id for safety?
|
|
||||||
wormholeRequestWithCallback(type: "listHostmap", arguments: nil) { (data, err) -> () in
|
|
||||||
if (err != nil) {
|
|
||||||
return result(CallFailedError(message: err!.localizedDescription))
|
|
||||||
}
|
|
||||||
|
|
||||||
result(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func activeListPendingHostmap(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
||||||
guard let args = call.arguments as? Dictionary<String, String> else { return result(NoArgumentsError()) }
|
|
||||||
guard let id = args["id"] else { return result(MissingArgumentError(message: "id is a required argument")) }
|
|
||||||
//TODO: match id for safety?
|
|
||||||
wormholeRequestWithCallback(type: "listPendingHostmap", arguments: nil) { (data, err) -> () in
|
|
||||||
if (err != nil) {
|
|
||||||
return result(CallFailedError(message: err!.localizedDescription))
|
|
||||||
}
|
|
||||||
|
|
||||||
result(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func activeGetHostInfo(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
|
||||||
guard let args = call.arguments as? Dictionary<String, Any> else { return result(NoArgumentsError()) }
|
|
||||||
guard let id = args["id"] as? String else { return result(MissingArgumentError(message: "id is a required argument")) }
|
guard let id = args["id"] as? String else { return result(MissingArgumentError(message: "id is a required argument")) }
|
||||||
guard let vpnIp = args["vpnIp"] as? String else { return result(MissingArgumentError(message: "vpnIp is a required argument")) }
|
let container = sites?.getContainer(id: id)
|
||||||
let pending = args["pending"] as? Bool ?? false
|
|
||||||
|
|
||||||
//TODO: match id for safety?
|
if container == nil {
|
||||||
wormholeRequestWithCallback(type: "getHostInfo", arguments: ["vpnIp": vpnIp, "pending": pending]) { (data, err) -> () in
|
// No site for this id
|
||||||
if (err != nil) {
|
return result(nil)
|
||||||
return result(CallFailedError(message: err!.localizedDescription))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result(data)
|
if !(container!.site.connected ?? false) {
|
||||||
}
|
// Site isn't connected, no point in sending a command
|
||||||
|
return result(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func activeSetRemoteForTunnel(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
if let session = container!.site.manager?.connection as? NETunnelProviderSession {
|
||||||
guard let args = call.arguments as? Dictionary<String, String> else { return result(NoArgumentsError()) }
|
do {
|
||||||
guard let id = args["id"] else { return result(MissingArgumentError(message: "id is a required argument")) }
|
try session.sendProviderMessage(try JSONEncoder().encode(IPCRequest(command: command, arguments: JSON(args)))) { data in
|
||||||
guard let vpnIp = args["vpnIp"] else { return result(MissingArgumentError(message: "vpnIp is a required argument")) }
|
if data == nil {
|
||||||
guard let addr = args["addr"] else { return result(MissingArgumentError(message: "addr is a required argument")) }
|
return result(nil)
|
||||||
|
|
||||||
//TODO: match id for safety?
|
|
||||||
wormholeRequestWithCallback(type: "setRemoteForTunnel", arguments: ["vpnIp": vpnIp, "addr": addr]) { (data, err) -> () in
|
|
||||||
if (err != nil) {
|
|
||||||
return result(CallFailedError(message: err!.localizedDescription))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result(data)
|
//print(String(decoding: data!, as: UTF8.self))
|
||||||
}
|
guard let res = try? JSONDecoder().decode(IPCResponse.self, from: data!) else {
|
||||||
|
return result(CallFailedError(message: "Failed to decode response"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func activeCloseTunnel(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
if res.type == .success {
|
||||||
guard let args = call.arguments as? Dictionary<String, String> else { return result(NoArgumentsError()) }
|
return result(res.message?.object)
|
||||||
guard let id = args["id"] else { return result(MissingArgumentError(message: "id is a required argument")) }
|
|
||||||
guard let vpnIp = args["vpnIp"] else { return result(MissingArgumentError(message: "vpnIp is a required argument")) }
|
|
||||||
|
|
||||||
//TODO: match id for safety?
|
|
||||||
wormholeRequestWithCallback(type: "closeTunnel", arguments: ["vpnIp": vpnIp]) { (data, err) -> () in
|
|
||||||
if (err != nil) {
|
|
||||||
return result(CallFailedError(message: err!.localizedDescription))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result(data as? Bool ?? false)
|
return result(CallFailedError(message: res.message?.debugDescription ?? "Failed to convert error"))
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
return result(CallFailedError(message: error.localizedDescription))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
func wormholeListener(msg: Any?) {
|
//TODO: we have a site without a manager, things have gone weird. How to handle since this shouldn't happen?
|
||||||
guard let call = msg as? IPCMessage else {
|
result(nil)
|
||||||
print("Failed to decode IPCMessage from network extension")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch call.type {
|
|
||||||
case "error":
|
|
||||||
guard let updater = self.sites?.getUpdater(id: call.id) else {
|
|
||||||
return print("Could not find site to deliver error to \(call.id): \(String(describing: call.message))")
|
|
||||||
}
|
|
||||||
updater.setError(err: call.message as! String)
|
|
||||||
|
|
||||||
default:
|
|
||||||
print("Unknown IPC message type \(call.type)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func wormholeRequestWithCallback(type: String, arguments: Dictionary<String, Any>?, completion: @escaping (Any?, Error?) -> ()) {
|
|
||||||
let uuid = UUID().uuidString
|
|
||||||
|
|
||||||
wormhole.listenForMessage(withIdentifier: uuid) { msg -> () in
|
|
||||||
self.wormhole.stopListeningForMessage(withIdentifier: uuid)
|
|
||||||
|
|
||||||
guard let call = msg as? IPCMessage else {
|
|
||||||
completion("", "Failed to decode IPCMessage callback from network extension")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch call.type {
|
|
||||||
case "error":
|
|
||||||
completion("", call.message as? String ?? "Failed to convert error")
|
|
||||||
case "success":
|
|
||||||
completion(call.message, nil)
|
|
||||||
|
|
||||||
default:
|
|
||||||
completion("", "Unknown IPC message type \(call.type)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wormhole.passMessageObject(IPCRequest(callbackId: uuid, type: type, arguments: arguments), identifier: "app")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ class Sites {
|
||||||
try? fileManager.removeItem(at: sitePath)
|
try? fileManager.removeItem(at: sitePath)
|
||||||
#else
|
#else
|
||||||
_ = KeyChain.delete(key: site.site.id)
|
_ = KeyChain.delete(key: site.site.id)
|
||||||
site.site.manager.removeFromPreferences(completionHandler: callback)
|
site.site.manager!.removeFromPreferences(completionHandler: callback)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +105,10 @@ class Sites {
|
||||||
func getUpdater(id: String) -> SiteUpdater? {
|
func getUpdater(id: String) -> SiteUpdater? {
|
||||||
return self.sites[id]?.updater
|
return self.sites[id]?.updater
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getContainer(id: String) -> SiteContainer? {
|
||||||
|
return self.sites[id]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SiteUpdater: NSObject, FlutterStreamHandler {
|
class SiteUpdater: NSObject, FlutterStreamHandler {
|
||||||
|
@ -112,6 +116,7 @@ class SiteUpdater: NSObject, FlutterStreamHandler {
|
||||||
private var eventChannel: FlutterEventChannel;
|
private var eventChannel: FlutterEventChannel;
|
||||||
private var site: Site
|
private var site: Site
|
||||||
private var notification: Any?
|
private var notification: Any?
|
||||||
|
public var startFunc: (() -> Void)?
|
||||||
|
|
||||||
init(messenger: FlutterBinaryMessenger, site: Site) {
|
init(messenger: FlutterBinaryMessenger, site: Site) {
|
||||||
eventChannel = FlutterEventChannel(name: "net.defined.nebula/\(site.id)", binaryMessenger: messenger)
|
eventChannel = FlutterEventChannel(name: "net.defined.nebula/\(site.id)", binaryMessenger: messenger)
|
||||||
|
@ -120,13 +125,20 @@ class SiteUpdater: NSObject, FlutterStreamHandler {
|
||||||
eventChannel.setStreamHandler(self)
|
eventChannel.setStreamHandler(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// onListen is called when flutter code attaches an event listener
|
||||||
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
||||||
eventSink = events;
|
eventSink = events;
|
||||||
|
|
||||||
self.notification = NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: site.manager.connection , queue: nil) { _ in
|
self.notification = NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: site.manager!.connection , queue: nil) { n in
|
||||||
|
let connected = self.site.connected
|
||||||
|
self.site.status = statusString[self.site.manager!.connection.status]
|
||||||
|
self.site.connected = statusMap[self.site.manager!.connection.status]
|
||||||
|
|
||||||
self.site.status = statusString[self.site.manager.connection.status]
|
// Check to see if we just moved to connected and if we have a start function to call when that happens
|
||||||
self.site.connected = statusMap[self.site.manager.connection.status]
|
if self.site.connected! && connected != self.site.connected && self.startFunc != nil {
|
||||||
|
self.startFunc!()
|
||||||
|
self.startFunc = nil
|
||||||
|
}
|
||||||
|
|
||||||
let d: Dictionary<String, Any> = [
|
let d: Dictionary<String, Any> = [
|
||||||
"connected": self.site.connected!,
|
"connected": self.site.connected!,
|
||||||
|
@ -138,14 +150,7 @@ class SiteUpdater: NSObject, FlutterStreamHandler {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setError(err: String) {
|
/// onCancel is called when the flutter listener stops listening
|
||||||
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? {
|
func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||||
if (self.notification != nil) {
|
if (self.notification != nil) {
|
||||||
NotificationCenter.default.removeObserver(self.notification!)
|
NotificationCenter.default.removeObserver(self.notification!)
|
||||||
|
@ -153,6 +158,7 @@ class SiteUpdater: NSObject, FlutterStreamHandler {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// update is a way to send information to the flutter listener and generally should not be used directly
|
||||||
func update(connected: Bool) {
|
func update(connected: Bool) {
|
||||||
let d: Dictionary<String, Any> = [
|
let d: Dictionary<String, Any> = [
|
||||||
"connected": connected,
|
"connected": connected,
|
||||||
|
|
|
@ -185,8 +185,7 @@ class Site {
|
||||||
try {
|
try {
|
||||||
await platform.invokeMethod("startSite", <String, String>{"id": id});
|
await platform.invokeMethod("startSite", <String, String>{"id": id});
|
||||||
} on PlatformException catch (err) {
|
} on PlatformException catch (err) {
|
||||||
//TODO: fix this message
|
throw err.message ?? err.toString();
|
||||||
throw err.details ?? err.message ?? err.toString();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw err.toString();
|
throw err.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,6 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange = site.onChange().listen((_) {
|
onChange = site.onChange().listen((_) {
|
||||||
setState(() {});
|
|
||||||
if (lastState != site.connected) {
|
if (lastState != site.connected) {
|
||||||
//TODO: connected is set before the nebula object exists leading to a crash race, waiting for "Connected" status is a gross hack but keeps it alive
|
//TODO: connected is set before the nebula object exists leading to a crash race, waiting for "Connected" status is a gross hack but keeps it alive
|
||||||
if (site.status == 'Connected') {
|
if (site.status == 'Connected') {
|
||||||
|
@ -62,6 +61,8 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
|
||||||
pendingHosts = null;
|
pendingHosts = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setState(() {});
|
||||||
|
|
||||||
}, onError: (err) {
|
}, onError: (err) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
Utils.popError(context, "Error", err);
|
Utils.popError(context, "Error", err);
|
||||||
|
@ -88,7 +89,7 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
|
||||||
}),
|
}),
|
||||||
refreshController: refreshController,
|
refreshController: refreshController,
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
if (site.connected) {
|
if (site.connected && site.status == "Connected") {
|
||||||
await _listHostmap();
|
await _listHostmap();
|
||||||
}
|
}
|
||||||
refreshController.refreshCompleted();
|
refreshController.refreshCompleted();
|
||||||
|
|
Loading…
Reference in New Issue