diff --git a/main.py b/main.py index c4994db..90dd1ab 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,9 @@ import RNS import LXMF import time +from kivy.logger import Logger, LOG_LEVELS +Logger.setLevel(LOG_LEVELS["error"]) + from sideband.core import SidebandCore from kivymd.app import MDApp @@ -478,6 +481,69 @@ class SidebandApp(MDApp): self.open_conversations(direction="right") + ### Connectivity screen + ###################################### + def connectivity_action(self, sender=None): + def save_connectivity(sender=None, event=None): + RNS.log("Save connectivity") + self.sideband.config["connect_local"] = self.root.ids.connectivity_use_local.active + self.sideband.config["connect_local_groupid"] = self.root.ids.connectivity_local_groupid.text + self.sideband.config["connect_local_ifac_netname"] = self.root.ids.connectivity_local_ifac_netname.text + self.sideband.config["connect_local_ifac_passphrase"] = self.root.ids.connectivity_local_ifac_passphrase.text + self.sideband.config["connect_tcp"] = self.root.ids.connectivity_use_tcp.active + self.sideband.config["connect_tcp_host"] = self.root.ids.connectivity_tcp_host.text + self.sideband.config["connect_tcp_port"] = self.root.ids.connectivity_tcp_port.text + self.sideband.config["connect_tcp_ifac_netname"] = self.root.ids.connectivity_tcp_ifac_netname.text + self.sideband.config["connect_tcp_ifac_passphrase"] = self.root.ids.connectivity_tcp_ifac_passphrase.text + self.sideband.config["connect_i2p"] = self.root.ids.connectivity_use_i2p.active + self.sideband.config["connect_i2p_b32"] = self.root.ids.connectivity_i2p_b32.text + self.sideband.config["connect_i2p_ifac_netname"] = self.root.ids.connectivity_i2p_ifac_netname.text + self.sideband.config["connect_i2p_ifac_passphrase"] = self.root.ids.connectivity_i2p_ifac_passphrase.text + self.sideband.save_configuration() + + info = "By default, sideband will try to discover and connect to any available Reticulum network via active WiFi and/or Ethernet interfaces. If any Reticulum Transport Instances are found, Sideband will use these to connect to wider Reticulum networks. You can disable this behaviour if you don't want it.\n\n" + info += "You can connect also connect to a network via a remote or local Reticulum instance using TCP or I2P. [b]Please Note![/b] Connecting via I2P requires that you already have I2P running on your device, and the SAM API enabled.\n\n" + info += "For changes to connectivity to take effect, you must shut down and restart Sideband." + self.root.ids.connectivity_info.text = info + + self.root.ids.connectivity_use_local.active = self.sideband.config["connect_local"] + self.root.ids.connectivity_local_groupid.text = self.sideband.config["connect_local_groupid"] + self.root.ids.connectivity_local_ifac_netname.text = self.sideband.config["connect_local_ifac_netname"] + self.root.ids.connectivity_local_ifac_passphrase.text = self.sideband.config["connect_local_ifac_passphrase"] + + self.root.ids.connectivity_use_tcp.active = self.sideband.config["connect_tcp"] + self.root.ids.connectivity_tcp_host.text = self.sideband.config["connect_tcp_host"] + self.root.ids.connectivity_tcp_port.text = self.sideband.config["connect_tcp_port"] + self.root.ids.connectivity_tcp_ifac_netname.text = self.sideband.config["connect_tcp_ifac_netname"] + self.root.ids.connectivity_tcp_ifac_passphrase.text = self.sideband.config["connect_tcp_ifac_passphrase"] + + self.root.ids.connectivity_use_i2p.active = self.sideband.config["connect_i2p"] + self.root.ids.connectivity_i2p_b32.text = self.sideband.config["connect_i2p_b32"] + self.root.ids.connectivity_i2p_ifac_netname.text = self.sideband.config["connect_i2p_ifac_netname"] + self.root.ids.connectivity_i2p_ifac_passphrase.text = self.sideband.config["connect_i2p_ifac_passphrase"] + + self.root.ids.connectivity_use_local.bind(active=save_connectivity) + self.root.ids.connectivity_local_groupid.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_local_ifac_netname.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_local_ifac_passphrase.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_use_tcp.bind(active=save_connectivity) + self.root.ids.connectivity_tcp_host.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_tcp_port.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_tcp_ifac_netname.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_tcp_ifac_passphrase.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_use_i2p.bind(active=save_connectivity) + self.root.ids.connectivity_i2p_b32.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_i2p_ifac_netname.bind(on_text_validate=save_connectivity) + self.root.ids.connectivity_i2p_ifac_passphrase.bind(on_text_validate=save_connectivity) + + self.root.ids.screen_manager.transition.direction = "left" + self.root.ids.screen_manager.current = "connectivity_screen" + self.root.ids.nav_drawer.set_state("closed") + + + def close_connectivity_action(self, sender=None): + self.open_conversations(direction="right") + ### Announce Stream screen ###################################### def announces_action(self, sender=None): @@ -531,20 +597,6 @@ class SidebandApp(MDApp): self.root.ids.screen_manager.current = "map_screen" self.root.ids.nav_drawer.set_state("closed") - def connectivity_action(self, sender=None): - def link_exec(sender=None, event=None): - RNS.log("Click") - import webbrowser - webbrowser.open("https://unsigned.io/sideband") - - info = "The [b]Connectivity[/b] feature will allow you use LoRa and radio interfaces directly on Android over USB or Bluetooth, through a simple and user-friendly setup process. It will also let you view advanced connectivity stats and options.\n\nThis feature is not yet implemented in Sideband.\n\nWant it faster? Go to [u][ref=link]https://unsigned.io/sideband[/ref][/u] to support the project." - self.root.ids.connectivity_info.text = info - self.root.ids.connectivity_info.bind(on_ref_press=link_exec) - - self.root.ids.screen_manager.transition.direction = "left" - self.root.ids.screen_manager.current = "connectivity_screen" - self.root.ids.nav_drawer.set_state("closed") - def broadcasts_action(self, sender=None): def link_exec(sender=None, event=None): RNS.log("Click") diff --git a/sideband/core.py b/sideband/core.py index 3caadd1..1cfecee 100644 --- a/sideband/core.py +++ b/sideband/core.py @@ -72,7 +72,8 @@ class SidebandCore(): self.app_dir = plyer.storagepath.get_application_dir() self.rns_configdir = None - if RNS.vendor.platformutils.get_platform() == "android": + # TODO: Reset + if RNS.vendor.platformutils.get_platform() == "android" or True: self.app_dir = self.app_dir+"/io.unsigned.sideband/files/" self.rns_configdir = self.app_dir+"/app_storage/reticulum" @@ -97,7 +98,8 @@ class SidebandCore(): # Initialise Reticulum configuration - if RNS.vendor.platformutils.get_platform() == "android": + # TODO: Reset + if RNS.vendor.platformutils.get_platform() == "android" or True: try: self.rns_configdir = self.app_dir+"/app_storage/reticulum" if not os.path.isdir(self.rns_configdir): @@ -132,6 +134,7 @@ class SidebandCore(): self.identity.to_file(self.identity_path) self.config = {} + # Settings self.config["display_name"] = "Anonymous Peer" self.config["start_announce"] = False self.config["propagation_by_default"] = False @@ -142,6 +145,21 @@ class SidebandCore(): self.config["lxmf_sync_max"] = 3 self.config["last_lxmf_propagation_node"] = None self.config["nn_home_node"] = None + # Connectivity + self.config["connect_local"] = True + self.config["connect_local_groupid"] = "" + self.config["connect_local_ifac_netname"] = "" + self.config["connect_local_ifac_passphrase"] = "" + self.config["connect_tcp"] = False + self.config["connect_tcp_host"] = "sideband.connect.reticulum.network" + self.config["connect_tcp_port"] = "7822" + self.config["connect_tcp_ifac_netname"] = "" + self.config["connect_tcp_ifac_passphrase"] = "" + self.config["connect_i2p"] = False + self.config["connect_i2p_b32"] = "mrwqlsioq4hoo2lmeeud7dkfscnm7yxak7dmiyvsrnpfag3z5tsq.b32.i2p" + self.config["connect_i2p_ifac_netname"] = "" + self.config["connect_i2p_ifac_passphrase"] = "" + self.__save_config() if not os.path.isfile(self.db_path): @@ -152,7 +170,7 @@ class SidebandCore(): RNS.log("Loading Sideband identity...") self.identity = RNS.Identity.from_file(self.identity_path) - RNS.log("Loading Sideband configuration...") + RNS.log("Loading Sideband configuration... "+str(self.config_path)) config_file = open(self.config_path, "rb") self.config = msgpack.unpackb(config_file.read()) config_file.close() @@ -684,6 +702,118 @@ class SidebandCore(): self.reticulum = RNS.Reticulum(configdir=self.rns_configdir) RNS.log("Reticulum started, activating LXMF...") + if RNS.vendor.platformutils.get_platform() == "android" or True: + if not self.reticulum.is_connected_to_shared_instance: + RNS.log("Running as master or standalone instance, adding interfaces") + + self.interface_local = None + self.interface_tcp = None + self.interface_i2p = None + + if self.config["connect_local"]: + try: + RNS.log("Adding Auto Interface...") + if self.config["connect_local_groupid"] == "": + group_id = None + else: + group_id = self.config["connect_local_groupid"] + + if self.config["connect_local_ifac_netname"] == "": + ifac_netname = None + else: + ifac_netname = self.config["connect_local_ifac_netname"] + + if self.config["connect_local_ifac_passphrase"] == "": + ifac_netkey = None + else: + ifac_netkey = self.config["connect_local_ifac_passphrase"] + + autointerface = RNS.Interfaces.AutoInterface.AutoInterface( + RNS.Transport, + name = "AutoInterface", + group_id = group_id + ) + autointerface.OUT = True + self.reticulum._add_interface(autointerface,ifac_netname=ifac_netname,ifac_netkey=ifac_netkey) + self.interface_local = autointerface + + except Exception as e: + RNS.log("Error while adding AutoInterface. The contained exception was: "+str(e)) + self.interface_local = None + + if self.config["connect_tcp"]: + try: + RNS.log("Adding TCP Interface...") + + if self.config["connect_tcp_host"] != "": + tcp_host = self.config["connect_tcp_host"] + tcp_port = int(self.config["connect_tcp_port"]) + + if tcp_port > 0 and tcp_port <= 65536: + if self.config["connect_tcp_ifac_netname"] == "": + ifac_netname = None + else: + ifac_netname = self.config["connect_tcp_ifac_netname"] + + if self.config["connect_tcp_ifac_passphrase"] == "": + ifac_netkey = None + else: + ifac_netkey = self.config["connect_tcp_ifac_passphrase"] + + tcpinterface = RNS.Interfaces.TCPInterface.TCPClientInterface( + RNS.Transport, + "TCPClientInterface", + tcp_host, + tcp_port, + kiss_framing = False, + i2p_tunneled = False + ) + + tcpinterface.OUT = True + self.reticulum._add_interface(tcpinterface,ifac_netname=ifac_netname,ifac_netkey=ifac_netkey) + self.interface_tcp = tcpinterface + + except Exception as e: + RNS.log("Error while adding TCP Interface. The contained exception was: "+str(e)) + self.interface_tcp = None + + if self.config["connect_i2p"]: + try: + RNS.log("Adding I2P Interface...") + + if self.config["connect_i2p_b32"].endswith(".b32.i2p"): + + if self.config["connect_i2p_ifac_netname"] == "": + ifac_netname = None + else: + ifac_netname = self.config["connect_i2p_ifac_netname"] + + if self.config["connect_i2p_ifac_passphrase"] == "": + ifac_netkey = None + else: + ifac_netkey = self.config["connect_i2p_ifac_passphrase"] + + i2pinterface = I2PInterface.I2PInterface( + RNS.Transport, + "I2PInterface", + RNS.Reticulum.storagepath, + self.config["connect_i2p_b32"], + connectable = False, + ) + + i2pinterface.OUT = True + self.reticulum._add_interface(i2pinterface,ifac_netname=ifac_netname,ifac_netkey=ifac_netkey) + self.interface_i2p = i2pinterface + + + except Exception as e: + RNS.log("Error while adding I2P Interface. The contained exception was: "+str(e)) + self.interface_i2p = None + + + + RNS.log(str(RNS.Transport.interfaces)) + self.message_router = LXMF.LXMRouter(identity = self.identity, storagepath = self.lxmf_storage, autopeer = True) self.message_router.register_delivery_callback(self.lxmf_delivery) @@ -879,10 +1009,7 @@ instance_control_port = 37429 panic_on_interface_error = No [logging] -loglevel = 3 +# TODO: Reset to 3 +loglevel = 6 -[interfaces] - [[Default Interface]] - type = AutoInterface - interface_enabled = True """.encode("utf-8") diff --git a/ui/layouts.py b/ui/layouts.py index 4fa4307..e89871b 100644 --- a/ui/layouts.py +++ b/ui/layouts.py @@ -136,16 +136,28 @@ MDNavigationLayout: pos_hint: {"top": 1} left_action_items: [['menu', lambda x: nav_drawer.set_state("open")]] + right_action_items: + [ + ['close', lambda x: root.ids.screen_manager.app.close_connectivity_action(self)], + ] ScrollView: - id:connectivity_scrollview + id: connectivity_scrollview MDBoxLayout: orientation: "vertical" spacing: "24dp" size_hint_y: None height: self.minimum_height - padding: dp(64) + padding: dp(16) + + MDLabel: + text: "" + font_style: "H6" + + MDLabel: + text: "Configuring Connectivity" + font_style: "H6" MDLabel: id: connectivity_info @@ -154,6 +166,146 @@ MDNavigationLayout: size_hint_y: None text_size: self.width, None height: self.texture_size[1] + + + MDBoxLayout: + orientation: "horizontal" + # spacing: "24dp" + size_hint_y: None + height: dp(48) + + MDLabel: + text: "Connect via local WiFi/Ethernet" + font_style: "H6" + + MDSwitch: + id: connectivity_use_local + active: False + + MDLabel: + text: "" + font_size: dp(16) + + MDTextField: + id: connectivity_local_groupid + hint_text: "Optional WiFi/Ethernet Group ID" + text: "" + max_text_length: 128 + font_size: dp(24) + + MDTextField: + id: connectivity_local_ifac_netname + hint_text: "Optional IFAC network name" + text: "" + font_size: dp(24) + + MDTextField: + id: connectivity_local_ifac_passphrase + hint_text: "Optional IFAC passphrase" + text: "" + font_size: dp(24) + + + + MDBoxLayout: + orientation: "horizontal" + # spacing: "24dp" + size_hint_y: None + height: dp(48) + + MDLabel: + text: "Connect via TCP" + font_style: "H6" + + MDSwitch: + id: connectivity_use_tcp + active: False + + + MDTextField: + id: connectivity_tcp_host + hint_text: "TCP Host" + text: "" + font_size: dp(24) + + MDTextField: + id: connectivity_tcp_port + hint_text: "TCP Port" + text: "" + font_size: dp(24) + + MDTextField: + id: connectivity_tcp_ifac_netname + hint_text: "Optional IFAC network name" + text: "" + font_size: dp(24) + + MDTextField: + id: connectivity_tcp_ifac_passphrase + hint_text: "Optional IFAC passphrase" + text: "" + font_size: dp(24) + + + + MDBoxLayout: + orientation: "horizontal" + # spacing: "24dp" + size_hint_y: None + height: dp(48) + + MDLabel: + text: "Connect via I2P" + font_style: "H6" + + MDSwitch: + id: connectivity_use_i2p + active: False + + + MDTextField: + id: connectivity_i2p_b32 + hint_text: "I2P B32" + text: "" + font_size: dp(24) + + MDTextField: + id: connectivity_i2p_ifac_netname + hint_text: "Optional IFAC network name" + text: "" + font_size: dp(24) + + MDTextField: + id: connectivity_i2p_ifac_passphrase + hint_text: "Optional IFAC passphrase" + text: "" + font_size: dp(24) + + + + MDBoxLayout: + orientation: "horizontal" + # spacing: "24dp" + size_hint_y: None + height: dp(48) + + MDLabel: + text: "Connect via RNode" + font_style: "H6" + disabled: True + + MDSwitch: + id: connectivity_use_rnode + active: False + disabled: True + + MDTextField: + id: connectivity_rnode_cid + hint_text: "RNode Pairing ID" + text: "" + font_size: dp(24) + disabled: True + MDScreen: