mirror of
https://github.com/yggdrasil-network/yggdrasil-ios.git
synced 2025-04-28 06:05:09 +03:00
Sanitised
This commit is contained in:
commit
52ca049b50
39 changed files with 3283 additions and 0 deletions
312
Yggdrasil Network iOS/View Controllers/PeersViewController.swift
Normal file
312
Yggdrasil Network iOS/View Controllers/PeersViewController.swift
Normal file
|
@ -0,0 +1,312 @@
|
|||
//
|
||||
// PeersViewController.swift
|
||||
// YggdrasilNetwork
|
||||
//
|
||||
// Created by Neil Alexander on 07/01/2019.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import NetworkExtension
|
||||
import CoreTelephony
|
||||
|
||||
class PeersViewController: UITableViewController {
|
||||
var app = UIApplication.shared.delegate as! AppDelegate
|
||||
var config: [String: Any]? = nil
|
||||
|
||||
@IBOutlet var peerTable: UITableView!
|
||||
@IBOutlet weak var addButtonItem: UIBarButtonItem!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
if let proto = self.app.vpnManager.protocolConfiguration as? NETunnelProviderProtocol {
|
||||
config = proto.providerConfiguration ?? nil
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItems = [
|
||||
self.editButtonItem,
|
||||
self.addButtonItem
|
||||
]
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.onYggdrasilPeersUpdated), name: NSNotification.Name.YggdrasilPeersUpdated, object: nil)
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.YggdrasilPeersUpdated, object: nil)
|
||||
}
|
||||
|
||||
@objc func onYggdrasilPeersUpdated(notification: NSNotification) {
|
||||
peerTable.reloadData()
|
||||
}
|
||||
|
||||
// MARK: - Table view data source
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 3
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch section {
|
||||
case 0: return app.yggdrasilSwitchPeers.count
|
||||
case 1:
|
||||
if let config = self.app.yggdrasilConfig {
|
||||
if let peers = config.get("Peers") as? [String] {
|
||||
return peers.count
|
||||
}
|
||||
}
|
||||
return 0
|
||||
case 2:
|
||||
if UIDevice.current.hasCellularCapabilites {
|
||||
return 3
|
||||
}
|
||||
return 2
|
||||
default: return 0
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "discoveredPeerPrototype", for: indexPath)
|
||||
let peers = app.yggdrasilSwitchPeers.sorted { (a, b) -> Bool in
|
||||
return (a["Port"] as! Int) < (b["Port"] as! Int)
|
||||
}
|
||||
|
||||
if indexPath.row < peers.count {
|
||||
let value = peers[indexPath.row]
|
||||
let proto = value["Protocol"] as? String ?? "tcp"
|
||||
let sent = value["BytesSent"] as? Double ?? 0
|
||||
let recvd = value["BytesRecvd"] as? Double ?? 0
|
||||
let rx = self.format(bytes: sent)
|
||||
let tx = self.format(bytes: recvd)
|
||||
|
||||
cell.textLabel?.text = "\(value["Endpoint"] ?? "unknown")"
|
||||
cell.detailTextLabel?.text = "\(proto.uppercased()) peer on port \(value["Port"] ?? "unknown"), sent \(tx), received \(rx)"
|
||||
}
|
||||
return cell
|
||||
case 1:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "configuredPeerPrototype", for: indexPath)
|
||||
if let config = self.app.yggdrasilConfig {
|
||||
if let peers = config.get("Peers") as? [String] {
|
||||
cell.textLabel?.text = peers[indexPath.last!]
|
||||
} else {
|
||||
cell.textLabel?.text = "(unknown)"
|
||||
}
|
||||
}
|
||||
return cell
|
||||
case 2:
|
||||
switch indexPath.last {
|
||||
case 0:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "togglePrototype", for: indexPath) as! ToggleTableViewCell
|
||||
cell.isUserInteractionEnabled = true
|
||||
cell.label?.text = "Search for multicast peers"
|
||||
cell.label?.isEnabled = true
|
||||
cell.toggle?.addTarget(self, action: #selector(toggledMulticast), for: .valueChanged)
|
||||
cell.toggle?.isEnabled = true
|
||||
if let config = self.app.yggdrasilConfig {
|
||||
let interfaces = config.get("MulticastInterfaces") as? [String] ?? []
|
||||
cell.toggle?.isOn = interfaces.contains("en*")
|
||||
}
|
||||
return cell
|
||||
case 1:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "togglePrototype", for: indexPath) as! ToggleTableViewCell
|
||||
cell.isUserInteractionEnabled = false
|
||||
cell.label?.text = "Search for nearby iOS peers"
|
||||
cell.label?.isEnabled = false
|
||||
cell.toggle?.addTarget(self, action: #selector(toggledAWDL), for: .valueChanged)
|
||||
cell.toggle?.setOn(false, animated: false)
|
||||
cell.toggle?.isEnabled = false
|
||||
/*if let config = self.app.yggdrasilConfig {
|
||||
let interfaces = config.get("MulticastInterfaces") as? [String] ?? []
|
||||
cell.toggle?.isOn = interfaces.contains("awdl0")
|
||||
}*/
|
||||
return cell
|
||||
case 2:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "menuPrototype", for: indexPath)
|
||||
cell.isUserInteractionEnabled = true
|
||||
cell.textLabel?.text = "Device settings"
|
||||
cell.textLabel?.isEnabled = true
|
||||
return cell
|
||||
default:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "menuPrototype", for: indexPath)
|
||||
cell.isUserInteractionEnabled = false
|
||||
cell.textLabel?.text = "Unknown"
|
||||
cell.textLabel?.isEnabled = true
|
||||
return cell
|
||||
}
|
||||
default:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "configuredPeerPrototype", for: indexPath)
|
||||
cell.textLabel?.text = "(unknown)"
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
func format(bytes: Double) -> String {
|
||||
guard bytes > 0 else {
|
||||
return "0 bytes"
|
||||
}
|
||||
|
||||
// Adapted from http://stackoverflow.com/a/18650828
|
||||
let suffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
||||
let k: Double = 1000
|
||||
let i = floor(log(bytes) / log(k))
|
||||
|
||||
// Format number with thousands separator and everything below 1 GB with no decimal places.
|
||||
let numberFormatter = NumberFormatter()
|
||||
numberFormatter.maximumFractionDigits = i < 3 ? 0 : 1
|
||||
numberFormatter.numberStyle = .decimal
|
||||
|
||||
let numberString = numberFormatter.string(from: NSNumber(value: bytes / pow(k, i))) ?? "Unknown"
|
||||
let suffix = suffixes[Int(i)]
|
||||
return "\(numberString) \(suffix)"
|
||||
}
|
||||
|
||||
@objc func toggledMulticast(_ sender: UISwitch) {
|
||||
if let config = self.app.yggdrasilConfig {
|
||||
var interfaces = config.get("MulticastInterfaces") as! [String]
|
||||
if sender.isOn {
|
||||
interfaces.append("en*")
|
||||
} else {
|
||||
interfaces.removeAll(where: { $0 == "en*" })
|
||||
}
|
||||
config.set("MulticastInterfaces", to: interfaces as [String])
|
||||
try? config.save(to: &app.vpnManager)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func toggledAWDL(_ sender: UISwitch) {
|
||||
if let config = self.app.yggdrasilConfig {
|
||||
var interfaces = config.get("MulticastInterfaces") as! [String]
|
||||
if sender.isOn {
|
||||
interfaces.append("awdl0")
|
||||
} else {
|
||||
interfaces.removeAll(where: { $0 == "awdl0" })
|
||||
}
|
||||
config.set("MulticastInterfaces", to: interfaces as [String])
|
||||
try? config.save(to: &app.vpnManager)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
switch section {
|
||||
case 0:
|
||||
if self.app.yggdrasilPeers.count > 0 {
|
||||
return "Connected Peers"
|
||||
}
|
||||
return "No peers currently connected"
|
||||
case 1:
|
||||
if let config = self.app.yggdrasilConfig {
|
||||
if let peers = config.get("Peers") as? [String] {
|
||||
if peers.count > 0 {
|
||||
return "Configured Peers"
|
||||
}
|
||||
}
|
||||
}
|
||||
return "No peers currently configured"
|
||||
case 2:
|
||||
return "Peer Connectivity"
|
||||
default: return "(Unknown)"
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
|
||||
switch section {
|
||||
case 1:
|
||||
return "Yggdrasil will automatically attempt to connect to configured peers when started."
|
||||
case 2:
|
||||
var str = "Multicast peers will be discovered on the same Wi-Fi network or via USB."
|
||||
if UIDevice.current.hasCellularCapabilites {
|
||||
str += " Data charges may apply when using mobile data. You can prevent mobile data usage in the device settings."
|
||||
}
|
||||
return str
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
return indexPath.first == 1
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
|
||||
switch indexPath.first {
|
||||
case 0:
|
||||
return [UITableViewRowAction(style: UITableViewRowAction.Style.default, title: "Disconnect", handler: { (action, index) in
|
||||
|
||||
})]
|
||||
case 1:
|
||||
return [UITableViewRowAction(style: UITableViewRowAction.Style.normal, title: "Remove", handler: { (action, index) in
|
||||
print(action, index)
|
||||
if let config = self.app.yggdrasilConfig {
|
||||
config.remove(index: index.last!, from: "Peers")
|
||||
do {
|
||||
try config.save(to: &self.app.vpnManager)
|
||||
tableView.reloadSections(IndexSet(integer: 1), with: UITableView.RowAnimation.automatic)
|
||||
} catch {
|
||||
let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
|
||||
self.parent?.present(alert, animated: true, completion: nil)
|
||||
print("Error removing: \(error)")
|
||||
}
|
||||
}
|
||||
})]
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch indexPath.first {
|
||||
case 2:
|
||||
if let last = indexPath.last, last == 2 {
|
||||
UIApplication.shared.open(NSURL(string:UIApplication.openSettingsURLString)! as URL, options: [:]) { (result) in
|
||||
NSLog("Result " + result.description)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
@IBAction func addNewPeerButtonPressed(_ sender: UIBarButtonItem) {
|
||||
let alert = UIAlertController(title: "Add Configured Peer", message: """
|
||||
Enter the full URI of the peer to add. Yggdrasil will automatically connect to this peer when started.
|
||||
""", preferredStyle: UIAlertController.Style.alert)
|
||||
let action = UIAlertAction(title: "Add", style: .default) { (alertAction) in
|
||||
let textField = alert.textFields![0] as UITextField
|
||||
if let text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
|
||||
if let config = self.app.yggdrasilConfig {
|
||||
if let peers = config.get("Peers") as? [String], !peers.contains(text) {
|
||||
config.add(text, in: "Peers")
|
||||
do {
|
||||
try config.save(to: &self.app.vpnManager)
|
||||
if let index = config.get("Peers") as? [String] {
|
||||
self.peerTable.insertRows(at: [IndexPath(indexes: [1, index.count-1])], with: .automatic)
|
||||
self.peerTable.reloadSections(IndexSet(integer: 1), with: UITableView.RowAnimation.automatic)
|
||||
}
|
||||
} catch {
|
||||
let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
|
||||
self.parent?.present(alert, animated: true, completion: nil)
|
||||
print("Add error: \(error)")
|
||||
}
|
||||
} else {
|
||||
let alert = UIAlertController(title: "Error", message: "Peer already exists", preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
|
||||
self.parent?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let cancel = UIAlertAction(title: "Cancel", style: .cancel)
|
||||
alert.addTextField { (textField) in
|
||||
textField.placeholder = "tcp://hostname:port"
|
||||
}
|
||||
alert.addAction(action)
|
||||
alert.addAction(cancel)
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue