2022-04-07 21:03:53 +02:00
import RNS
import LXMF
import threading
import plyer
import os . path
import time
import sqlite3
import RNS . vendor . umsgpack as msgpack
2022-10-16 00:25:07 +02:00
import RNS . Interfaces . Interface as Interface
2022-04-07 21:03:53 +02:00
2022-10-29 16:39:54 +02:00
from . res import sideband_fb_data
2022-10-02 01:14:29 +02:00
if RNS . vendor . platformutils . get_platform ( ) == " android " :
from jnius import autoclass , cast
2022-04-07 21:03:53 +02:00
class PropagationNodeDetector ( ) :
EMITTED_DELTA_GRACE = 300
EMITTED_DELTA_IGNORE = 10
aspect_filter = " lxmf.propagation "
def received_announce ( self , destination_hash , announced_identity , app_data ) :
try :
2022-11-23 18:57:53 +01:00
if len ( app_data ) > 0 :
unpacked = msgpack . unpackb ( app_data )
node_active = unpacked [ 0 ]
emitted = unpacked [ 1 ]
hops = RNS . Transport . hops_to ( destination_hash )
age = time . time ( ) - emitted
if age < 0 :
RNS . log ( " Warning, propagation node announce emitted in the future, possible timing inconsistency or tampering attempt. " )
if age < - 1 * PropagationNodeDetector . EMITTED_DELTA_GRACE :
raise ValueError ( " Announce timestamp too far in the future, discarding it " )
if age > - 1 * PropagationNodeDetector . EMITTED_DELTA_IGNORE :
# age = 0
pass
RNS . log ( " Detected active propagation node " + RNS . prettyhexrep ( destination_hash ) + " emission " + str ( age ) + " seconds ago, " + str ( hops ) + " hops away " )
self . owner . log_announce ( destination_hash , RNS . prettyhexrep ( destination_hash ) . encode ( " utf-8 " ) , dest_type = PropagationNodeDetector . aspect_filter )
if self . owner . config [ " lxmf_propagation_node " ] == None :
if self . owner . active_propagation_node == None :
2022-04-07 21:03:53 +02:00
self . owner . set_active_propagation_node ( destination_hash )
else :
2022-11-23 18:57:53 +01:00
prev_hops = RNS . Transport . hops_to ( self . owner . active_propagation_node )
if hops < = prev_hops :
self . owner . set_active_propagation_node ( destination_hash )
else :
pass
else :
pass
2022-04-07 21:03:53 +02:00
except Exception as e :
RNS . log ( " Error while processing received propagation node announce: " + str ( e ) )
def __init__ ( self , owner ) :
self . owner = owner
self . owner_app = owner . owner_app
class SidebandCore ( ) :
CONV_P2P = 0x01
CONV_GROUP = 0x02
CONV_BROADCAST = 0x03
2022-10-07 17:58:54 +02:00
MAX_ANNOUNCES = 24
2022-04-07 21:03:53 +02:00
2022-10-08 22:47:08 +02:00
SERVICE_JOB_INTERVAL = 1
PERIODIC_JOBS_INTERVAL = 60
PERIODIC_SYNC_RETRY = 360
2022-10-02 01:14:29 +02:00
2022-04-07 21:03:53 +02:00
aspect_filter = " lxmf.delivery "
def received_announce ( self , destination_hash , announced_identity , app_data ) :
# Add the announce to the directory announce
# stream logger
2022-07-06 17:02:29 +02:00
self . log_announce ( destination_hash , app_data , dest_type = SidebandCore . aspect_filter )
2022-04-07 21:03:53 +02:00
2022-10-03 18:29:54 +02:00
def __init__ ( self , owner_app , is_service = False , is_client = False , android_app_dir = None , verbose = False ) :
2022-10-01 11:18:14 +02:00
self . is_service = is_service
self . is_client = is_client
2022-11-23 18:57:53 +01:00
self . db = None
2022-10-01 11:18:14 +02:00
if not self . is_service and not self . is_client :
self . is_standalone = True
else :
self . is_standalone = False
2022-10-03 18:29:54 +02:00
self . log_verbose = verbose
2022-04-07 21:03:53 +02:00
self . owner_app = owner_app
self . reticulum = None
2022-10-01 11:18:14 +02:00
self . app_dir = plyer . storagepath . get_home_dir ( ) + " /.config/sideband "
2022-10-03 21:09:05 +02:00
if self . app_dir . startswith ( " file:// " ) :
self . app_dir = self . app_dir . replace ( " file:// " , " " )
2022-04-07 21:03:53 +02:00
self . rns_configdir = None
2022-10-03 21:09:05 +02:00
if RNS . vendor . platformutils . is_android ( ) :
2022-10-01 11:18:14 +02:00
self . app_dir = android_app_dir + " /io.unsigned.sideband/files/ "
2022-04-07 21:03:53 +02:00
self . rns_configdir = self . app_dir + " /app_storage/reticulum "
2022-10-02 01:14:29 +02:00
self . asset_dir = self . app_dir + " /app/assets "
2022-10-03 21:09:05 +02:00
elif RNS . vendor . platformutils . is_darwin ( ) :
2022-10-03 19:24:46 +02:00
core_path = os . path . abspath ( __file__ )
self . asset_dir = core_path . replace ( " /sideband/core.py " , " /assets " )
2022-10-19 22:25:20 +02:00
elif RNS . vendor . platformutils . get_platform ( ) == " linux " :
core_path = os . path . abspath ( __file__ )
self . asset_dir = core_path . replace ( " /sideband/core.py " , " /assets " )
2022-10-02 01:14:29 +02:00
else :
self . asset_dir = plyer . storagepath . get_application_dir ( ) + " /sbapp/assets "
self . icon = self . asset_dir + " /icon.png "
2022-10-03 18:49:53 +02:00
self . icon_48 = self . asset_dir + " /icon_48.png "
self . icon_32 = self . asset_dir + " /icon_32.png "
2022-10-03 22:36:44 +02:00
self . icon_macos = self . asset_dir + " /icon_macos.png "
2022-10-02 01:14:29 +02:00
self . notification_icon = self . asset_dir + " /notification_icon.png "
2022-04-07 21:03:53 +02:00
if not os . path . isdir ( self . app_dir + " /app_storage " ) :
os . makedirs ( self . app_dir + " /app_storage " )
self . config_path = self . app_dir + " /app_storage/sideband_config "
self . identity_path = self . app_dir + " /app_storage/primary_identity "
self . db_path = self . app_dir + " /app_storage/sideband.db "
self . lxmf_storage = self . app_dir + " /app_storage/ "
2022-11-22 14:25:56 +01:00
self . tmp_dir = self . app_dir + " /app_storage/tmp "
2022-11-22 19:47:13 +01:00
self . exports_dir = self . app_dir + " /exports "
2022-10-02 01:14:29 +02:00
2022-09-17 22:55:27 +02:00
self . first_run = True
2022-11-23 11:28:02 +01:00
self . saving_configuration = False
2022-09-17 22:55:27 +02:00
2022-11-23 18:57:53 +01:00
self . getstate_cache = { }
2022-04-07 21:03:53 +02:00
try :
if not os . path . isfile ( self . config_path ) :
self . __init_config ( )
else :
self . __load_config ( )
2022-09-17 22:55:27 +02:00
self . first_run = False
2022-11-22 14:25:56 +01:00
if not os . path . isdir ( self . tmp_dir ) :
os . makedirs ( self . tmp_dir )
else :
self . clear_tmp_dir ( )
2022-11-22 19:47:13 +01:00
if os . path . isdir ( self . exports_dir ) :
self . clear_exports_dir ( )
2022-04-07 21:03:53 +02:00
except Exception as e :
RNS . log ( " Error while configuring Sideband: " + str ( e ) , RNS . LOG_ERROR )
# Initialise Reticulum configuration
2022-07-06 12:20:41 +02:00
if RNS . vendor . platformutils . get_platform ( ) == " android " :
2022-04-07 21:03:53 +02:00
try :
self . rns_configdir = self . app_dir + " /app_storage/reticulum "
if not os . path . isdir ( self . rns_configdir ) :
os . makedirs ( self . rns_configdir )
RNS . log ( " Configuring Reticulum instance... " )
2022-10-15 19:12:39 +02:00
if self . config [ " connect_transport " ] :
RNS . log ( " Enabling Reticulum Transport " )
generated_config = rns_config . replace ( " TRANSPORT_IS_ENABLED " , " Yes " )
else :
RNS . log ( " Not enabling Reticulum Transport " )
generated_config = rns_config . replace ( " TRANSPORT_IS_ENABLED " , " No " )
2022-04-07 21:03:53 +02:00
config_file = open ( self . rns_configdir + " /config " , " wb " )
2022-10-15 19:12:39 +02:00
config_file . write ( generated_config . encode ( " utf-8 " ) )
2022-04-07 21:03:53 +02:00
config_file . close ( )
except Exception as e :
RNS . log ( " Error while configuring Reticulum instance: " + str ( e ) , RNS . LOG_ERROR )
else :
pass
self . active_propagation_node = None
self . propagation_detector = PropagationNodeDetector ( self )
RNS . Transport . register_announce_handler ( self )
RNS . Transport . register_announce_handler ( self . propagation_detector )
2022-09-14 22:09:58 +02:00
2022-11-22 14:25:56 +01:00
def clear_tmp_dir ( self ) :
if os . path . isdir ( self . tmp_dir ) :
for file in os . listdir ( self . tmp_dir ) :
fpath = self . tmp_dir + " / " + file
os . unlink ( fpath )
2022-11-22 19:47:13 +01:00
def clear_exports_dir ( self ) :
if os . path . isdir ( self . exports_dir ) :
for file in os . listdir ( self . exports_dir ) :
fpath = self . exports_dir + " / " + file
RNS . log ( " Clearing " + str ( fpath ) )
os . unlink ( fpath )
2022-04-07 21:03:53 +02:00
def __init_config ( self ) :
RNS . log ( " Creating new Sideband configuration... " )
if os . path . isfile ( self . identity_path ) :
self . identity = RNS . Identity . from_file ( self . identity_path )
else :
self . identity = RNS . Identity ( )
self . identity . to_file ( self . identity_path )
self . config = { }
2022-07-06 12:19:05 +02:00
# Settings
2022-04-07 21:03:53 +02:00
self . config [ " display_name " ] = " Anonymous Peer "
2022-10-15 20:22:45 +02:00
self . config [ " notifications_on " ] = True
2022-10-03 23:09:02 +02:00
self . config [ " dark_ui " ] = False
2022-04-07 21:03:53 +02:00
self . config [ " start_announce " ] = False
self . config [ " propagation_by_default " ] = False
self . config [ " home_node_as_broadcast_repeater " ] = False
self . config [ " send_telemetry_to_home_node " ] = False
self . config [ " lxmf_propagation_node " ] = None
self . config [ " lxmf_sync_limit " ] = None
self . config [ " lxmf_sync_max " ] = 3
2022-10-08 21:12:36 +02:00
self . config [ " lxmf_periodic_sync " ] = False
self . config [ " lxmf_sync_interval " ] = 43200
2022-04-07 21:03:53 +02:00
self . config [ " last_lxmf_propagation_node " ] = None
self . config [ " nn_home_node " ] = None
2022-11-22 14:25:56 +01:00
self . config [ " print_command " ] = " lp "
2022-07-06 12:19:05 +02:00
# Connectivity
2022-10-15 19:12:39 +02:00
self . config [ " connect_transport " ] = False
2022-07-06 12:19:05 +02:00
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
2022-11-03 19:52:27 +01:00
self . config [ " connect_i2p_b32 " ] = " pmlm3l5rpympihoy2o5ago43kluei2jjjzsalcuiuylbve3mwi2a.b32.i2p "
2022-07-06 12:19:05 +02:00
self . config [ " connect_i2p_ifac_netname " ] = " "
self . config [ " connect_i2p_ifac_passphrase " ] = " "
2022-10-12 16:38:59 +02:00
self . config [ " connect_rnode " ] = False
self . config [ " connect_rnode_ifac_netname " ] = " "
self . config [ " connect_rnode_ifac_passphrase " ] = " "
2022-10-15 11:11:34 +02:00
self . config [ " connect_serial " ] = False
self . config [ " connect_serial_ifac_netname " ] = " "
self . config [ " connect_serial_ifac_passphrase " ] = " "
self . config [ " connect_modem " ] = False
self . config [ " connect_modem_ifac_netname " ] = " "
self . config [ " connect_modem_ifac_passphrase " ] = " "
2022-10-18 15:06:25 +02:00
self . config [ " connect_ifmode_local " ] = " full "
self . config [ " connect_ifmode_tcp " ] = " full "
self . config [ " connect_ifmode_i2p " ] = " full "
self . config [ " connect_ifmode_rnode " ] = " full "
self . config [ " connect_ifmode_modem " ] = " full "
self . config [ " connect_ifmode_serial " ] = " full "
self . config [ " connect_ifmode_bluetooth " ] = " full "
2022-10-13 18:44:54 +02:00
# Hardware
self . config [ " hw_rnode_frequency " ] = None
self . config [ " hw_rnode_modulation " ] = " LoRa "
self . config [ " hw_rnode_bandwidth " ] = 62500
self . config [ " hw_rnode_spreading_factor " ] = 8
self . config [ " hw_rnode_coding_rate " ] = 6
self . config [ " hw_rnode_tx_power " ] = 0
self . config [ " hw_rnode_beaconinterval " ] = None
self . config [ " hw_rnode_beacondata " ] = None
2022-11-03 20:22:16 +01:00
self . config [ " hw_rnode_bt_device " ] = None
2022-10-15 14:55:47 +02:00
self . config [ " hw_modem_baudrate " ] = 57600
self . config [ " hw_modem_databits " ] = 8
self . config [ " hw_modem_stopbits " ] = 1
self . config [ " hw_modem_parity " ] = " none "
2022-10-13 18:44:54 +02:00
self . config [ " hw_modem_preamble " ] = 150
self . config [ " hw_modem_tail " ] = 20
self . config [ " hw_modem_persistence " ] = 220
self . config [ " hw_modem_slottime " ] = 20
2022-10-15 14:55:47 +02:00
self . config [ " hw_modem_beaconinterval " ] = None
self . config [ " hw_modem_beacondata " ] = None
2022-10-13 18:44:54 +02:00
self . config [ " hw_serial_baudrate " ] = 57600
self . config [ " hw_serial_databits " ] = 8
self . config [ " hw_serial_stopbits " ] = 1
self . config [ " hw_serial_parity " ] = " none "
2022-07-06 12:19:05 +02:00
2022-04-07 21:03:53 +02:00
if not os . path . isfile ( self . db_path ) :
self . __db_init ( )
2022-10-03 00:47:27 +02:00
else :
self . _db_initstate ( )
self . _db_initpersistent ( )
self . __save_config ( )
2022-04-07 21:03:53 +02:00
2022-09-17 17:12:25 +02:00
def should_persist_data ( self ) :
if self . reticulum != None :
self . reticulum . _should_persist_data ( )
self . save_configuration ( )
2022-04-07 21:03:53 +02:00
def __load_config ( self ) :
2022-10-02 12:45:06 +02:00
RNS . log ( " Loading Sideband identity... " , RNS . LOG_DEBUG )
2022-04-07 21:03:53 +02:00
self . identity = RNS . Identity . from_file ( self . identity_path )
2022-10-02 12:45:06 +02:00
RNS . log ( " Loading Sideband configuration... " + str ( self . config_path ) , RNS . LOG_DEBUG )
2022-04-07 21:03:53 +02:00
config_file = open ( self . config_path , " rb " )
self . config = msgpack . unpackb ( config_file . read ( ) )
config_file . close ( )
2022-10-02 14:03:56 +02:00
# Migration actions from earlier config formats
if not " dark_ui " in self . config :
self . config [ " dark_ui " ] = True
2022-10-08 21:12:36 +02:00
if not " lxmf_periodic_sync " in self . config :
self . config [ " lxmf_periodic_sync " ] = False
if not " lxmf_sync_interval " in self . config :
self . config [ " lxmf_sync_interval " ] = 43200
2022-10-09 00:30:58 +02:00
if not " notifications_on " in self . config :
self . config [ " notifications_on " ] = True
2022-11-22 14:25:56 +01:00
if not " print_command " in self . config :
self . config [ " print_command " ] = " lp "
2022-10-18 15:06:25 +02:00
2022-10-15 19:12:39 +02:00
if not " connect_transport " in self . config :
self . config [ " connect_transport " ] = False
2022-10-12 16:38:59 +02:00
if not " connect_rnode " in self . config :
self . config [ " connect_rnode " ] = False
if not " connect_rnode_ifac_netname " in self . config :
self . config [ " connect_rnode_ifac_netname " ] = " "
if not " connect_rnode_ifac_passphrase " in self . config :
self . config [ " connect_rnode_ifac_passphrase " ] = " "
2022-10-15 11:11:34 +02:00
if not " connect_serial " in self . config :
self . config [ " connect_serial " ] = False
if not " connect_serial_ifac_netname " in self . config :
self . config [ " connect_serial_ifac_netname " ] = " "
if not " connect_serial_ifac_passphrase " in self . config :
self . config [ " connect_serial_ifac_passphrase " ] = " "
if not " connect_modem " in self . config :
self . config [ " connect_modem " ] = False
if not " connect_modem_ifac_netname " in self . config :
self . config [ " connect_modem_ifac_netname " ] = " "
if not " connect_modem_ifac_passphrase " in self . config :
self . config [ " connect_modem_ifac_passphrase " ] = " "
2022-10-18 15:06:25 +02:00
if not " connect_ifmode_local " in self . config :
self . config [ " connect_ifmode_local " ] = " full "
if not " connect_ifmode_tcp " in self . config :
self . config [ " connect_ifmode_tcp " ] = " full "
if not " connect_ifmode_i2p " in self . config :
self . config [ " connect_ifmode_i2p " ] = " full "
if not " connect_ifmode_rnode " in self . config :
self . config [ " connect_ifmode_rnode " ] = " full "
if not " connect_ifmode_modem " in self . config :
self . config [ " connect_ifmode_modem " ] = " full "
if not " connect_ifmode_serial " in self . config :
self . config [ " connect_ifmode_serial " ] = " full "
if not " connect_ifmode_bluetooth " in self . config :
self . config [ " connect_ifmode_bluetooth " ] = " full "
2022-10-13 18:44:54 +02:00
if not " hw_rnode_frequency " in self . config :
self . config [ " hw_rnode_frequency " ] = None
if not " hw_rnode_modulation " in self . config :
self . config [ " hw_rnode_modulation " ] = " LoRa "
if not " hw_rnode_bandwidth " in self . config :
self . config [ " hw_rnode_bandwidth " ] = 62500
if not " hw_rnode_spreading_factor " in self . config :
self . config [ " hw_rnode_spreading_factor " ] = 8
if not " hw_rnode_coding_rate " in self . config :
self . config [ " hw_rnode_coding_rate " ] = 6
if not " hw_rnode_tx_power " in self . config :
self . config [ " hw_rnode_tx_power " ] = 0
if not " hw_rnode_beaconinterval " in self . config :
self . config [ " hw_rnode_beaconinterval " ] = None
if not " hw_rnode_beacondata " in self . config :
self . config [ " hw_rnode_beacondata " ] = None
2022-11-02 22:29:13 +01:00
if not " hw_rnode_bluetooth " in self . config :
self . config [ " hw_rnode_bluetooth " ] = False
2022-11-03 20:22:16 +01:00
if not " hw_rnode_bt_device " in self . config :
self . config [ " hw_rnode_bt_device " ] = None
2022-10-13 18:44:54 +02:00
2022-10-15 14:55:47 +02:00
if not " hw_modem_baudrate " in self . config :
self . config [ " hw_modem_baudrate " ] = 115200
if not " hw_modem_databits " in self . config :
self . config [ " hw_modem_databits " ] = 8
if not " hw_modem_stopbits " in self . config :
self . config [ " hw_modem_stopbits " ] = 1
if not " hw_modem_parity " in self . config :
self . config [ " hw_modem_parity " ] = " none "
2022-10-13 18:44:54 +02:00
if not " hw_modem_preamble " in self . config :
self . config [ " hw_modem_preamble " ] = 150
if not " hw_modem_tail " in self . config :
self . config [ " hw_modem_tail " ] = 20
if not " hw_modem_persistence " in self . config :
self . config [ " hw_modem_persistence " ] = 220
if not " hw_modem_slottime " in self . config :
self . config [ " hw_modem_slottime " ] = 20
2022-10-15 14:55:47 +02:00
if not " hw_modem_beaconinterval " in self . config :
self . config [ " hw_modem_beaconinterval " ] = None
if not " hw_modem_beacondata " in self . config :
self . config [ " hw_modem_beacondata " ] = None
2022-10-13 18:44:54 +02:00
if not " hw_serial_baudrate " in self . config :
self . config [ " hw_serial_baudrate " ] = 57600
if not " hw_serial_databits " in self . config :
self . config [ " hw_serial_databits " ] = 8
if not " hw_serial_stopbits " in self . config :
self . config [ " hw_serial_stopbits " ] = 1
if not " hw_serial_parity " in self . config :
self . config [ " hw_serial_parity " ] = " none "
2022-10-02 14:03:56 +02:00
# Make sure we have a database
2022-04-07 21:03:53 +02:00
if not os . path . isfile ( self . db_path ) :
self . __db_init ( )
2022-10-02 01:14:29 +02:00
else :
self . _db_initstate ( )
self . _db_initpersistent ( )
2022-11-23 18:57:53 +01:00
self . __db_indices ( )
2022-04-07 21:03:53 +02:00
2022-10-02 12:45:06 +02:00
def __reload_config ( self ) :
RNS . log ( " Reloading Sideband configuration... " + str ( self . config_path ) , RNS . LOG_DEBUG )
config_file = open ( self . config_path , " rb " )
self . config = msgpack . unpackb ( config_file . read ( ) )
config_file . close ( )
self . update_active_lxmf_propagation_node ( )
2022-04-07 21:03:53 +02:00
def __save_config ( self ) :
2022-10-02 12:45:06 +02:00
RNS . log ( " Saving Sideband configuration... " , RNS . LOG_DEBUG )
2022-11-23 11:28:02 +01:00
def save_function ( ) :
while self . saving_configuration :
time . sleep ( 0.15 )
try :
self . saving_configuration = True
config_file = open ( self . config_path , " wb " )
config_file . write ( msgpack . packb ( self . config ) )
config_file . close ( )
self . saving_configuration = False
except Exception as e :
self . saving_configuration = False
RNS . log ( " Error while saving Sideband configuration: " + str ( e ) , RNS . LOG_ERROR )
threading . Thread ( target = save_function , daemon = True ) . start ( )
2022-04-07 21:03:53 +02:00
2022-10-02 12:45:06 +02:00
if self . is_client :
self . setstate ( " wants.settings_reload " , True )
def reload_configuration ( self ) :
self . __reload_config ( )
2022-04-07 21:03:53 +02:00
def save_configuration ( self ) :
self . __save_config ( )
def set_active_propagation_node ( self , dest ) :
if dest == None :
RNS . log ( " No active propagation node configured " )
else :
try :
self . active_propagation_node = dest
self . config [ " last_lxmf_propagation_node " ] = dest
self . message_router . set_outbound_propagation_node ( dest )
2022-10-29 16:39:54 +02:00
if not self . is_service :
self . owner_app . root . ids . settings_propagation_node_address . text = RNS . hexrep ( dest , delimit = False )
2022-04-07 21:03:53 +02:00
RNS . log ( " Active propagation node set to: " + RNS . prettyhexrep ( dest ) )
self . __save_config ( )
except Exception as e :
RNS . log ( " Error while setting LXMF propagation node: " + str ( e ) , RNS . LOG_ERROR )
2022-10-02 01:14:29 +02:00
def notify ( self , title , content , group = None , context_id = None ) :
2022-10-09 00:30:58 +02:00
if self . config [ " notifications_on " ] :
2022-10-02 01:14:29 +02:00
if RNS . vendor . platformutils . get_platform ( ) == " android " :
if self . getpersistent ( " permissions.notifications " ) :
notifications_permitted = True
else :
notifications_permitted = False
else :
notifications_permitted = True
if notifications_permitted :
if RNS . vendor . platformutils . get_platform ( ) == " android " :
if self . is_service :
self . owner_service . android_notification ( title , content , group = group , context_id = context_id )
else :
plyer . notification . notify ( title , content , notification_icon = self . notification_icon , context_override = None )
else :
plyer . notification . notify ( title , content , app_icon = self . icon_32 )
2022-07-06 17:02:29 +02:00
def log_announce ( self , dest , app_data , dest_type ) :
2022-04-07 21:03:53 +02:00
try :
2022-07-06 17:02:29 +02:00
RNS . log ( " Received " + str ( dest_type ) + " announce for " + RNS . prettyhexrep ( dest ) + " with data: " + app_data . decode ( " utf-8 " ) )
self . _db_save_announce ( dest , app_data , dest_type )
2022-10-02 01:14:29 +02:00
self . setstate ( " app.flags.new_announces " , True )
2022-04-07 21:03:53 +02:00
except Exception as e :
RNS . log ( " Exception while decoding LXMF destination announce data: " + str ( e ) )
def list_conversations ( self ) :
result = self . _db_conversations ( )
if result != None :
return result
else :
return [ ]
def list_announces ( self ) :
result = self . _db_announces ( )
if result != None :
return result
else :
return [ ]
def has_conversation ( self , context_dest ) :
existing_conv = self . _db_conversation ( context_dest )
if existing_conv != None :
return True
else :
return False
def is_trusted ( self , context_dest ) :
try :
existing_conv = self . _db_conversation ( context_dest )
if existing_conv != None :
if existing_conv [ " trust " ] == 1 :
return True
else :
return False
else :
return False
except Exception as e :
RNS . log ( " Error while checking trust for " + RNS . prettyhexrep ( context_dest ) + " : " + str ( e ) , RNS . LOG_ERROR )
return False
def raw_display_name ( self , context_dest ) :
try :
existing_conv = self . _db_conversation ( context_dest )
if existing_conv != None :
if existing_conv [ " name " ] != None and existing_conv [ " name " ] != " " :
return existing_conv [ " name " ]
else :
return " "
else :
return " "
except Exception as e :
RNS . log ( " Error while getting peer name: " + str ( e ) , RNS . LOG_ERROR )
return " "
def peer_display_name ( self , context_dest ) :
try :
existing_conv = self . _db_conversation ( context_dest )
if existing_conv != None :
if existing_conv [ " name " ] != None and existing_conv [ " name " ] != " " :
if existing_conv [ " trust " ] == 1 :
return existing_conv [ " name " ]
else :
return existing_conv [ " name " ] + " " + RNS . prettyhexrep ( context_dest )
else :
app_data = RNS . Identity . recall_app_data ( context_dest )
if app_data != None :
if existing_conv [ " trust " ] == 1 :
return app_data . decode ( " utf-8 " )
else :
return app_data . decode ( " utf-8 " ) + " " + RNS . prettyhexrep ( context_dest )
else :
return RNS . prettyhexrep ( context_dest )
else :
app_data = RNS . Identity . recall_app_data ( context_dest )
if app_data != None :
return app_data . decode ( " utf-8 " ) + " " + RNS . prettyhexrep ( context_dest )
else :
return RNS . prettyhexrep ( context_dest )
except Exception as e :
2022-07-06 17:02:29 +02:00
RNS . log ( " Could not decode a valid peer name from data: " + str ( e ) , RNS . LOG_DEBUG )
2022-04-07 21:03:53 +02:00
return RNS . prettyhexrep ( context_dest )
def clear_conversation ( self , context_dest ) :
self . _db_clear_conversation ( context_dest )
2022-10-08 19:55:46 +02:00
def delete_announce ( self , context_dest ) :
self . _db_delete_announce ( context_dest )
2022-04-07 21:03:53 +02:00
def delete_conversation ( self , context_dest ) :
self . _db_clear_conversation ( context_dest )
self . _db_delete_conversation ( context_dest )
def delete_message ( self , message_hash ) :
self . _db_delete_message ( message_hash )
def read_conversation ( self , context_dest ) :
self . _db_conversation_set_unread ( context_dest , False )
def unread_conversation ( self , context_dest ) :
self . _db_conversation_set_unread ( context_dest , True )
def trusted_conversation ( self , context_dest ) :
self . _db_conversation_set_trusted ( context_dest , True )
def untrusted_conversation ( self , context_dest ) :
self . _db_conversation_set_trusted ( context_dest , False )
def named_conversation ( self , name , context_dest ) :
self . _db_conversation_set_name ( context_dest , name )
2022-11-23 13:28:26 +01:00
def count_messages ( self , context_dest ) :
result = self . _db_message_count ( context_dest )
if result != None :
return result
else :
return None
def list_messages ( self , context_dest , after = None , before = None , limit = None ) :
result = self . _db_messages ( context_dest , after , before , limit )
2022-04-07 21:03:53 +02:00
if result != None :
return result
else :
return [ ]
2022-10-02 01:31:47 +02:00
def service_available ( self ) :
service_heartbeat = self . getstate ( " service.heartbeat " )
if not service_heartbeat :
return False
else :
try :
if time . time ( ) - service_heartbeat > 2.5 :
return False
else :
return True
except :
return False
2022-10-02 01:14:29 +02:00
def gui_foreground ( self ) :
return self . _db_getstate ( " app.foreground " )
def gui_display ( self ) :
return self . _db_getstate ( " app.displaying " )
def gui_conversation ( self ) :
return self . _db_getstate ( " app.active_conversation " )
def setstate ( self , prop , val ) :
2022-11-23 18:57:53 +01:00
self . getstate_cache [ prop ] = val
2022-10-02 01:14:29 +02:00
self . _db_setstate ( prop , val )
2022-11-23 18:57:53 +01:00
# def cb():
# self._db_setstate(prop, val)
# threading.Thread(target=cb, daemon=True).start()
def getstate ( self , prop , allow_cache = False ) :
if not RNS . vendor . platformutils . is_android ( ) :
return self . _db_getstate ( prop )
else :
db_timeout = 0.060
cached_value = None
has_cached_value = False
if prop in self . getstate_cache :
cached_value = self . getstate_cache [ prop ]
has_cached_value = True
if not allow_cache or not has_cached_value :
self . getstate_cache [ prop ] = self . _db_getstate ( prop )
return self . getstate_cache [ prop ]
else :
get_thread_running = True
def get_job ( ) :
self . getstate_cache [ prop ] = self . _db_getstate ( prop )
get_thread_running = False
get_thread = threading . Thread ( target = get_job , daemon = True )
get_thread . timeout = time . time ( ) + db_timeout
get_thread . start ( )
while get_thread . is_alive ( ) and time . time ( ) < get_thread . timeout :
time . sleep ( 0.01 )
if get_thread . is_alive ( ) :
return self . getstate_cache [ prop ]
else :
return self . getstate_cache [ prop ]
2022-10-02 01:14:29 +02:00
def setpersistent ( self , prop , val ) :
self . _db_setpersistent ( prop , val )
2022-11-23 18:57:53 +01:00
# def cb():
# self._db_setpersistent(prop, val)
# threading.Thread(target=cb, daemon=True).start()
2022-10-02 01:14:29 +02:00
def getpersistent ( self , prop ) :
return self . _db_getpersistent ( prop )
2022-04-07 21:03:53 +02:00
def __event_conversations_changed ( self ) :
pass
def __event_conversation_changed ( self , context_dest ) :
pass
2022-11-23 18:57:53 +01:00
def __db_connect ( self ) :
if self . db == None :
self . db = sqlite3 . connect ( self . db_path , check_same_thread = False )
return self . db
2022-04-07 21:03:53 +02:00
def __db_init ( self ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
dbc . execute ( " DROP TABLE IF EXISTS lxm " )
dbc . execute ( " CREATE TABLE lxm (lxm_hash BLOB PRIMARY KEY, dest BLOB, source BLOB, title BLOB, tx_ts INTEGER, rx_ts INTEGER, state INTEGER, method INTEGER, t_encrypted INTEGER, t_encryption INTEGER, data BLOB) " )
dbc . execute ( " DROP TABLE IF EXISTS conv " )
dbc . execute ( " CREATE TABLE conv (dest_context BLOB PRIMARY KEY, last_tx INTEGER, last_rx INTEGER, unread INTEGER, type INTEGER, trust INTEGER, name BLOB, data BLOB) " )
dbc . execute ( " DROP TABLE IF EXISTS announce " )
2022-07-06 17:02:29 +02:00
dbc . execute ( " CREATE TABLE announce (id PRIMARY KEY, received INTEGER, source BLOB, data BLOB, dest_type BLOB) " )
2022-04-07 21:03:53 +02:00
2022-10-02 01:14:29 +02:00
dbc . execute ( " DROP TABLE IF EXISTS state " )
dbc . execute ( " CREATE TABLE state (property BLOB PRIMARY KEY, value BLOB) " )
dbc . execute ( " DROP TABLE IF EXISTS persistent " )
dbc . execute ( " CREATE TABLE persistent (property BLOB PRIMARY KEY, value BLOB) " )
db . commit ( )
2022-11-23 18:57:53 +01:00
def __db_indices ( self ) :
db = self . __db_connect ( )
dbc = db . cursor ( )
dbc . execute ( " CREATE UNIQUE INDEX IF NOT EXISTS idx_persistent_property ON persistent(property) " )
dbc . execute ( " CREATE UNIQUE INDEX IF NOT EXISTS idx_state_property ON state(property) " )
dbc . execute ( " CREATE UNIQUE INDEX IF NOT EXISTS idx_conv_dest_context ON conv(dest_context) " )
db . commit ( )
2022-10-02 01:14:29 +02:00
def _db_initstate ( self ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-10-02 01:14:29 +02:00
dbc = db . cursor ( )
dbc . execute ( " DROP TABLE IF EXISTS state " )
dbc . execute ( " CREATE TABLE state (property BLOB PRIMARY KEY, value BLOB) " )
2022-11-23 18:57:53 +01:00
db . commit ( )
2022-10-02 01:14:29 +02:00
self . _db_setstate ( " database_ready " , True )
def _db_getstate ( self , prop ) :
2022-11-23 18:57:53 +01:00
try :
db = self . __db_connect ( )
dbc = db . cursor ( )
2022-10-02 01:14:29 +02:00
2022-11-23 18:57:53 +01:00
query = " select * from state where property=:uprop "
dbc . execute ( query , { " uprop " : prop . encode ( " utf-8 " ) } )
result = dbc . fetchall ( )
if len ( result ) < 1 :
2022-10-02 01:14:29 +02:00
return None
2022-11-23 18:57:53 +01:00
else :
try :
entry = result [ 0 ]
val = msgpack . unpackb ( entry [ 1 ] )
return val
except Exception as e :
RNS . log ( " Could not unpack state value from database for property \" " + str ( prop ) + " \" . The contained exception was: " + str ( e ) , RNS . LOG_ERROR )
return None
except Exception as e :
RNS . log ( " An error occurred during getstate database operation: " + str ( e ) , RNS . LOG_ERROR )
self . db = None
2022-10-02 01:14:29 +02:00
def _db_setstate ( self , prop , val ) :
2022-11-23 18:57:53 +01:00
try :
uprop = prop . encode ( " utf-8 " )
bval = msgpack . packb ( val )
2022-10-02 01:14:29 +02:00
2022-11-23 18:57:53 +01:00
if self . _db_getstate ( prop ) == None :
try :
db = self . __db_connect ( )
dbc = db . cursor ( )
query = " INSERT INTO state (property, value) values (?, ?) "
data = ( uprop , bval )
dbc . execute ( query , data )
db . commit ( )
2022-10-02 01:14:29 +02:00
2022-11-23 18:57:53 +01:00
except Exception as e :
RNS . log ( " Error while setting state property " + str ( prop ) + " in DB: " + str ( e ) , RNS . LOG_ERROR )
RNS . log ( " Retrying as update query... " , RNS . LOG_ERROR )
db = self . __db_connect ( )
dbc = db . cursor ( )
query = " UPDATE state set value=:bval where property=:uprop; "
dbc . execute ( query , { " bval " : bval , " uprop " : uprop } )
db . commit ( )
else :
db = self . __db_connect ( )
dbc = db . cursor ( )
query = " UPDATE state set value=:bval where property=:uprop; "
dbc . execute ( query , { " bval " : bval , " uprop " : uprop } )
db . commit ( )
except Exception as e :
RNS . log ( " An error occurred during setstate database operation: " + str ( e ) , RNS . LOG_ERROR )
self . db = None
2022-10-02 01:14:29 +02:00
def _db_initpersistent ( self ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-10-02 01:14:29 +02:00
dbc = db . cursor ( )
dbc . execute ( " CREATE TABLE IF NOT EXISTS persistent (property BLOB PRIMARY KEY, value BLOB) " )
2022-11-23 18:57:53 +01:00
db . commit ( )
2022-10-02 01:14:29 +02:00
def _db_getpersistent ( self , prop ) :
2022-11-23 18:57:53 +01:00
try :
db = self . __db_connect ( )
dbc = db . cursor ( )
query = " select * from persistent where property=:uprop "
dbc . execute ( query , { " uprop " : prop . encode ( " utf-8 " ) } )
result = dbc . fetchall ( )
if len ( result ) < 1 :
2022-10-02 01:14:29 +02:00
return None
2022-11-23 18:57:53 +01:00
else :
try :
entry = result [ 0 ]
val = msgpack . unpackb ( entry [ 1 ] )
if val == None :
query = " delete from persistent where (property=:uprop); "
dbc . execute ( query , { " uprop " : prop . encode ( " utf-8 " ) } )
db . commit ( )
return val
except Exception as e :
RNS . log ( " Could not unpack persistent value from database for property \" " + str ( prop ) + " \" . The contained exception was: " + str ( e ) , RNS . LOG_ERROR )
return None
except Exception as e :
RNS . log ( " An error occurred during persistent getstate database operation: " + str ( e ) , RNS . LOG_ERROR )
self . db = None
2022-10-02 01:14:29 +02:00
def _db_setpersistent ( self , prop , val ) :
2022-11-23 18:57:53 +01:00
try :
db = self . __db_connect ( )
dbc = db . cursor ( )
uprop = prop . encode ( " utf-8 " )
bval = msgpack . packb ( val )
2022-10-02 01:14:29 +02:00
2022-11-23 18:57:53 +01:00
if self . _db_getpersistent ( prop ) == None :
try :
query = " INSERT INTO persistent (property, value) values (?, ?) "
data = ( uprop , bval )
dbc . execute ( query , data )
db . commit ( )
except Exception as e :
RNS . log ( " Error while setting persistent state property " + str ( prop ) + " in DB: " + str ( e ) , RNS . LOG_ERROR )
RNS . log ( " Retrying as update query... " )
query = " UPDATE state set value=:bval where property=:uprop; "
dbc . execute ( query , { " bval " : bval , " uprop " : uprop } )
db . commit ( )
2022-10-02 01:14:29 +02:00
2022-11-23 18:57:53 +01:00
else :
query = " UPDATE persistent set value=:bval where property=:uprop; "
dbc . execute ( query , { " bval " : bval , " uprop " : uprop } )
db . commit ( )
except Exception as e :
RNS . log ( " An error occurred during persistent setstate database operation: " + str ( e ) , RNS . LOG_ERROR )
self . db = None
2022-04-07 21:03:53 +02:00
def _db_conversation_set_unread ( self , context_dest , unread ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " UPDATE conv set unread = ? where dest_context = ? "
data = ( unread , context_dest )
dbc . execute ( query , data )
result = dbc . fetchall ( )
db . commit ( )
def _db_conversation_set_trusted ( self , context_dest , trusted ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " UPDATE conv set trust = ? where dest_context = ? "
data = ( trusted , context_dest )
dbc . execute ( query , data )
result = dbc . fetchall ( )
db . commit ( )
def _db_conversation_set_name ( self , context_dest , name ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " UPDATE conv set name=:name_data where dest_context=:ctx; "
dbc . execute ( query , { " ctx " : context_dest , " name_data " : name . encode ( " utf-8 " ) } )
result = dbc . fetchall ( )
db . commit ( )
def _db_conversations ( self ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
dbc . execute ( " select * from conv " )
result = dbc . fetchall ( )
if len ( result ) < 1 :
return None
else :
convs = [ ]
for entry in result :
conv = {
" dest " : entry [ 0 ] ,
" unread " : entry [ 3 ] ,
}
convs . append ( conv )
return convs
def _db_announces ( self ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
dbc . execute ( " select * from announce order by received desc " )
result = dbc . fetchall ( )
if len ( result ) < 1 :
return None
else :
announces = [ ]
2022-10-06 16:07:03 +02:00
added_dests = [ ]
2022-04-07 21:03:53 +02:00
for entry in result :
try :
2022-10-06 16:07:03 +02:00
if not entry [ 2 ] in added_dests :
announce = {
" dest " : entry [ 2 ] ,
" data " : entry [ 3 ] . decode ( " utf-8 " ) ,
" time " : entry [ 1 ] ,
" type " : entry [ 4 ]
}
added_dests . append ( entry [ 2 ] )
announces . append ( announce )
2022-04-07 21:03:53 +02:00
except Exception as e :
RNS . log ( " Exception while fetching announce from DB: " + str ( e ) , RNS . LOG_ERROR )
2022-10-06 16:07:03 +02:00
announces . reverse ( )
2022-04-07 21:03:53 +02:00
return announces
def _db_conversation ( self , context_dest ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " select * from conv where dest_context=:ctx "
dbc . execute ( query , { " ctx " : context_dest } )
result = dbc . fetchall ( )
if len ( result ) < 1 :
return None
else :
c = result [ 0 ]
conv = { }
conv [ " dest " ] = c [ 0 ]
conv [ " last_tx " ] = c [ 1 ]
conv [ " last_rx " ] = c [ 2 ]
conv [ " unread " ] = c [ 3 ]
conv [ " type " ] = c [ 4 ]
conv [ " trust " ] = c [ 5 ]
conv [ " name " ] = c [ 6 ] . decode ( " utf-8 " )
conv [ " data " ] = msgpack . unpackb ( c [ 7 ] )
return conv
def _db_clear_conversation ( self , context_dest ) :
2022-10-08 19:55:46 +02:00
RNS . log ( " Clearing conversation with " + RNS . prettyhexrep ( context_dest ) , RNS . LOG_DEBUG )
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " delete from lxm where (dest=:ctx_dst or source=:ctx_dst); "
dbc . execute ( query , { " ctx_dst " : context_dest } )
db . commit ( )
def _db_delete_conversation ( self , context_dest ) :
2022-10-08 19:55:46 +02:00
RNS . log ( " Deleting conversation with " + RNS . prettyhexrep ( context_dest ) , RNS . LOG_DEBUG )
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " delete from conv where (dest_context=:ctx_dst); "
dbc . execute ( query , { " ctx_dst " : context_dest } )
db . commit ( )
2022-10-08 19:55:46 +02:00
def _db_delete_announce ( self , context_dest ) :
RNS . log ( " Deleting announce with " + RNS . prettyhexrep ( context_dest ) , RNS . LOG_DEBUG )
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-10-08 19:55:46 +02:00
dbc = db . cursor ( )
query = " delete from announce where (source=:ctx_dst); "
dbc . execute ( query , { " ctx_dst " : context_dest } )
db . commit ( )
2022-04-07 21:03:53 +02:00
def _db_create_conversation ( self , context_dest , name = None , trust = False ) :
2022-10-08 19:55:46 +02:00
RNS . log ( " Creating conversation for " + RNS . prettyhexrep ( context_dest ) , RNS . LOG_DEBUG )
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
def_name = " " . encode ( " utf-8 " )
query = " INSERT INTO conv (dest_context, last_tx, last_rx, unread, type, trust, name, data) values (?, ?, ?, ?, ?, ?, ?, ?) "
data = ( context_dest , 0 , 0 , 0 , SidebandCore . CONV_P2P , 0 , def_name , msgpack . packb ( None ) )
dbc . execute ( query , data )
db . commit ( )
if trust :
self . _db_conversation_set_trusted ( context_dest , True )
if name != None and name != " " :
self . _db_conversation_set_name ( context_dest , name )
self . __event_conversations_changed ( )
def _db_delete_message ( self , msg_hash ) :
RNS . log ( " Deleting message " + RNS . prettyhexrep ( msg_hash ) )
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " delete from lxm where (lxm_hash=:mhash); "
dbc . execute ( query , { " mhash " : msg_hash } )
db . commit ( )
def _db_clean_messages ( self ) :
RNS . log ( " Purging stale messages... " + str ( self . db_path ) )
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " delete from lxm where (state=:outbound_state or state=:sending_state); "
dbc . execute ( query , { " outbound_state " : LXMF . LXMessage . OUTBOUND , " sending_state " : LXMF . LXMessage . SENDING } )
db . commit ( )
def _db_message_set_state ( self , lxm_hash , state ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " UPDATE lxm set state = ? where lxm_hash = ? "
data = ( state , lxm_hash )
dbc . execute ( query , data )
db . commit ( )
result = dbc . fetchall ( )
def _db_message_set_method ( self , lxm_hash , method ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " UPDATE lxm set method = ? where lxm_hash = ? "
data = ( method , lxm_hash )
dbc . execute ( query , data )
db . commit ( )
result = dbc . fetchall ( )
def message ( self , msg_hash ) :
return self . _db_message ( msg_hash )
def _db_message ( self , msg_hash ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
query = " select * from lxm where lxm_hash=:mhash "
dbc . execute ( query , { " mhash " : msg_hash } )
result = dbc . fetchall ( )
if len ( result ) < 1 :
return None
else :
entry = result [ 0 ]
2022-11-22 14:25:56 +01:00
lxm_method = entry [ 7 ]
if lxm_method == LXMF . LXMessage . PAPER :
lxm_data = msgpack . unpackb ( entry [ 10 ] )
packed_lxm = lxm_data [ 0 ]
paper_packed_lxm = lxm_data [ 1 ]
else :
packed_lxm = entry [ 10 ]
lxm = LXMF . LXMessage . unpack_from_bytes ( packed_lxm , original_method = lxm_method )
if lxm . desired_method == LXMF . LXMessage . PAPER :
lxm . paper_packed = paper_packed_lxm
2022-04-07 21:03:53 +02:00
message = {
" hash " : lxm . hash ,
" dest " : lxm . destination_hash ,
" source " : lxm . source_hash ,
" title " : lxm . title ,
" content " : lxm . content ,
" received " : entry [ 5 ] ,
" sent " : lxm . timestamp ,
" state " : entry [ 6 ] ,
" method " : entry [ 7 ] ,
" lxm " : lxm
}
return message
2022-11-23 13:28:26 +01:00
def _db_message_count ( self , context_dest ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
2022-11-23 13:28:26 +01:00
query = " select count(*) from lxm where dest=:context_dest or source=:context_dest "
dbc . execute ( query , { " context_dest " : context_dest } )
result = dbc . fetchall ( )
if len ( result ) < 1 :
return None
2022-04-07 21:03:53 +02:00
else :
2022-11-23 13:28:26 +01:00
return result [ 0 ] [ 0 ]
def _db_messages ( self , context_dest , after = None , before = None , limit = None ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-11-23 13:28:26 +01:00
dbc = db . cursor ( )
if after != None and before == None :
2022-04-07 21:03:53 +02:00
query = " select * from lxm where (dest=:context_dest or source=:context_dest) and rx_ts>:after_ts "
dbc . execute ( query , { " context_dest " : context_dest , " after_ts " : after } )
2022-11-23 13:28:26 +01:00
elif after == None and before != None :
query = " select * from lxm where (dest=:context_dest or source=:context_dest) and rx_ts<:before_ts "
dbc . execute ( query , { " context_dest " : context_dest , " before_ts " : before } )
elif after != None and before != None :
query = " select * from lxm where (dest=:context_dest or source=:context_dest) and rx_ts<:before_ts and rx_ts>:after_ts "
dbc . execute ( query , { " context_dest " : context_dest , " before_ts " : before , " after_ts " : after } )
else :
query = " select * from lxm where dest=:context_dest or source=:context_dest "
dbc . execute ( query , { " context_dest " : context_dest } )
2022-04-07 21:03:53 +02:00
result = dbc . fetchall ( )
if len ( result ) < 1 :
return None
else :
messages = [ ]
for entry in result :
2022-11-22 14:25:56 +01:00
lxm_method = entry [ 7 ]
if lxm_method == LXMF . LXMessage . PAPER :
lxm_data = msgpack . unpackb ( entry [ 10 ] )
packed_lxm = lxm_data [ 0 ]
paper_packed_lxm = lxm_data [ 1 ]
else :
packed_lxm = entry [ 10 ]
lxm = LXMF . LXMessage . unpack_from_bytes ( packed_lxm , original_method = lxm_method )
if lxm . desired_method == LXMF . LXMessage . PAPER :
lxm . paper_packed = paper_packed_lxm
2022-04-07 21:03:53 +02:00
message = {
" hash " : lxm . hash ,
" dest " : lxm . destination_hash ,
" source " : lxm . source_hash ,
" title " : lxm . title ,
" content " : lxm . content ,
" received " : entry [ 5 ] ,
" sent " : lxm . timestamp ,
" state " : entry [ 6 ] ,
" method " : entry [ 7 ] ,
" lxm " : lxm
}
2022-11-23 13:28:26 +01:00
messages . append ( message )
if len ( messages ) > limit :
messages = messages [ - limit : ]
2022-04-07 21:03:53 +02:00
return messages
def _db_save_lxm ( self , lxm , context_dest ) :
state = lxm . state
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
if not lxm . packed :
lxm . pack ( )
2022-11-22 14:25:56 +01:00
if lxm . method == LXMF . LXMessage . PAPER :
packed_lxm = msgpack . packb ( [ lxm . packed , lxm . paper_packed ] )
else :
packed_lxm = lxm . packed
2022-04-07 21:03:53 +02:00
query = " INSERT INTO lxm (lxm_hash, dest, source, title, tx_ts, rx_ts, state, method, t_encrypted, t_encryption, data) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
data = (
lxm . hash ,
lxm . destination_hash ,
lxm . source_hash ,
lxm . title ,
lxm . timestamp ,
time . time ( ) ,
state ,
lxm . method ,
lxm . transport_encrypted ,
lxm . transport_encryption ,
2022-11-22 14:25:56 +01:00
packed_lxm ,
2022-04-07 21:03:53 +02:00
)
dbc . execute ( query , data )
db . commit ( )
self . __event_conversation_changed ( context_dest )
2022-07-06 17:02:29 +02:00
def _db_save_announce ( self , destination_hash , app_data , dest_type = " lxmf.delivery " ) :
2022-11-23 18:57:53 +01:00
db = self . __db_connect ( )
2022-04-07 21:03:53 +02:00
dbc = db . cursor ( )
2022-10-08 23:34:24 +02:00
query = " delete from announce where id is NULL or id not in (select id from announce order by received desc limit " + str ( self . MAX_ANNOUNCES ) + " ) "
dbc . execute ( query )
2022-10-06 16:07:03 +02:00
query = " delete from announce where (source=:source); "
dbc . execute ( query , { " source " : destination_hash } )
2022-10-08 23:34:24 +02:00
now = time . time ( )
hash_material = str ( time ) . encode ( " utf-8 " ) + destination_hash + app_data + dest_type . encode ( " utf-8 " )
announce_hash = RNS . Identity . full_hash ( hash_material )
query = " INSERT INTO announce (id, received, source, data, dest_type) values (?, ?, ?, ?, ?) "
2022-04-07 21:03:53 +02:00
data = (
2022-10-08 23:34:24 +02:00
announce_hash ,
now ,
2022-04-07 21:03:53 +02:00
destination_hash ,
app_data ,
2022-07-06 17:02:29 +02:00
dest_type ,
2022-04-07 21:03:53 +02:00
)
dbc . execute ( query , data )
2022-10-08 23:34:24 +02:00
db . commit ( )
2022-04-07 21:03:53 +02:00
def lxmf_announce ( self ) :
2022-10-02 01:14:29 +02:00
if self . is_standalone or self . is_service :
self . lxmf_destination . announce ( )
self . setstate ( " wants.announce " , False )
else :
self . setstate ( " wants.announce " , True )
2022-04-07 21:03:53 +02:00
def is_known ( self , dest_hash ) :
try :
source_identity = RNS . Identity . recall ( dest_hash )
if source_identity :
return True
else :
return False
except Exception as e :
return False
def request_key ( self , dest_hash ) :
try :
RNS . Transport . request_path ( dest_hash )
return True
except Exception as e :
RNS . log ( " Error while querying for key: " + str ( e ) , RNS . LOG_ERROR )
return False
2022-10-02 01:14:29 +02:00
def _service_jobs ( self ) :
if self . is_service :
2022-10-12 18:55:08 +02:00
last_usb_discovery = time . time ( )
2022-10-02 01:14:29 +02:00
while True :
2022-10-08 22:47:08 +02:00
time . sleep ( SidebandCore . SERVICE_JOB_INTERVAL )
2022-10-12 18:55:08 +02:00
now = time . time ( )
2022-10-02 01:14:29 +02:00
if self . getstate ( " wants.announce " ) :
self . lxmf_announce ( )
2022-11-02 22:29:13 +01:00
if self . getstate ( " wants.bt_on " ) :
self . setstate ( " wants.bt_on " , False )
2022-11-23 21:19:31 +01:00
self . owner_app . discover_usb_devices ( )
2022-11-02 22:29:13 +01:00
self . setstate ( " executing.bt_on " , True )
if self . interface_rnode != None :
self . interface_rnode . enable_bluetooth ( )
else :
if hasattr ( self . owner_app , " usb_devices " ) and self . owner_app . usb_devices != None :
if len ( self . owner_app . usb_devices ) > 0 :
target_port = self . owner_app . usb_devices [ 0 ] [ " port " ]
RNS . Interfaces . Android . RNodeInterface . RNodeInterface . bluetooth_control ( port = target_port , enable_bluetooth = True )
2022-11-23 21:19:31 +01:00
else :
RNS . log ( " Could not execute RNode Bluetooth control command, no USB devices available " , RNS . LOG_ERROR )
2022-11-02 22:29:13 +01:00
self . setstate ( " executing.bt_on " , False )
if self . getstate ( " wants.bt_off " ) :
self . setstate ( " wants.bt_off " , False )
2022-11-23 21:19:31 +01:00
self . owner_app . discover_usb_devices ( )
2022-11-02 22:29:13 +01:00
self . setstate ( " executing.bt_off " , True )
if self . interface_rnode != None :
self . interface_rnode . disable_bluetooth ( )
else :
if hasattr ( self . owner_app , " usb_devices " ) and self . owner_app . usb_devices != None :
if len ( self . owner_app . usb_devices ) > 0 :
target_port = self . owner_app . usb_devices [ 0 ] [ " port " ]
RNS . Interfaces . Android . RNodeInterface . RNodeInterface . bluetooth_control ( port = target_port , disable_bluetooth = True )
2022-11-23 21:19:31 +01:00
else :
RNS . log ( " Could not execute RNode Bluetooth control command, no USB devices available " , RNS . LOG_ERROR )
2022-11-02 22:29:13 +01:00
self . setstate ( " executing.bt_off " , False )
if self . getstate ( " wants.bt_pair " ) :
self . setstate ( " wants.bt_pair " , False )
2022-11-23 21:19:31 +01:00
self . owner_app . discover_usb_devices ( )
2022-11-02 22:29:13 +01:00
self . setstate ( " executing.bt_pair " , True )
if self . interface_rnode != None :
self . interface_rnode . bluetooth_pair ( )
else :
if hasattr ( self . owner_app , " usb_devices " ) and self . owner_app . usb_devices != None :
if len ( self . owner_app . usb_devices ) > 0 :
target_port = self . owner_app . usb_devices [ 0 ] [ " port " ]
RNS . Interfaces . Android . RNodeInterface . RNodeInterface . bluetooth_control ( port = target_port , pairing_mode = True )
2022-11-23 21:19:31 +01:00
else :
RNS . log ( " Could not execute RNode Bluetooth control command, no USB devices available " , RNS . LOG_ERROR )
2022-11-02 22:29:13 +01:00
self . setstate ( " executing.bt_pair " , False )
2022-10-12 18:55:08 +02:00
if ( now - last_usb_discovery > 5 ) :
if self . interface_rnode != None and not self . interface_rnode . online :
self . owner_app . discover_usb_devices ( )
last_usb_discovery = time . time ( )
if hasattr ( self . owner_app , " usb_devices " ) and self . owner_app . usb_devices != None :
if len ( self . owner_app . usb_devices ) > 0 :
target_device = self . owner_app . usb_devices [ 0 ]
if self . interface_rnode . port != target_device [ " port " ] :
RNS . log ( " Updating RNode device to " + str ( target_device ) )
self . interface_rnode . port = target_device [ " port " ]
2022-10-15 13:34:04 +02:00
if self . interface_serial != None and not self . interface_serial . online :
self . owner_app . discover_usb_devices ( )
last_usb_discovery = time . time ( )
if hasattr ( self . owner_app , " usb_devices " ) and self . owner_app . usb_devices != None :
if len ( self . owner_app . usb_devices ) > 0 :
target_device = self . owner_app . usb_devices [ 0 ]
if self . interface_serial . port != target_device [ " port " ] :
RNS . log ( " Updating serial device to " + str ( target_device ) )
self . interface_serial . port = target_device [ " port " ]
if self . interface_modem != None and not self . interface_modem . online :
self . owner_app . discover_usb_devices ( )
last_usb_discovery = time . time ( )
if hasattr ( self . owner_app , " usb_devices " ) and self . owner_app . usb_devices != None :
if len ( self . owner_app . usb_devices ) > 0 :
target_device = self . owner_app . usb_devices [ 0 ]
if self . interface_modem . port != target_device [ " port " ] :
RNS . log ( " Updating modem device to " + str ( target_device ) )
self . interface_modem . port = target_device [ " port " ]
2022-10-08 22:47:08 +02:00
def _periodic_jobs ( self ) :
if self . is_service or self . is_standalone :
while True :
time . sleep ( SidebandCore . PERIODIC_JOBS_INTERVAL )
if self . config [ " lxmf_periodic_sync " ] == True :
if self . getpersistent ( " lxmf.lastsync " ) == None :
self . setpersistent ( " lxmf.lastsync " , time . time ( ) )
else :
now = time . time ( )
syncinterval = self . config [ " lxmf_sync_interval " ]
lastsync = self . getpersistent ( " lxmf.lastsync " )
nextsync = lastsync + syncinterval
RNS . log ( " Last sync was " + RNS . prettytime ( now - lastsync ) + " ago " , RNS . LOG_DEBUG )
RNS . log ( " Next sync is " + ( " in " + RNS . prettytime ( nextsync - now ) if nextsync - now > 0 else " now " ) , RNS . LOG_DEBUG )
if now > nextsync :
if self . request_lxmf_sync ( ) :
RNS . log ( " Scheduled LXMF sync succeeded " , RNS . LOG_DEBUG )
self . setpersistent ( " lxmf.lastsync " , time . time ( ) )
self . setpersistent ( " lxmf.syncretrying " , False )
else :
if not self . getpersistent ( " lxmf.syncretrying " ) :
RNS . log ( " Scheduled LXMF sync failed, retrying in " + RNS . prettytime ( SidebandCore . PERIODIC_SYNC_RETRY ) , RNS . LOG_DEBUG )
self . setpersistent ( " lxmf.lastsync " , lastsync + SidebandCore . PERIODIC_SYNC_RETRY )
self . setpersistent ( " lxmf.syncretrying " , True )
else :
RNS . log ( " Retry of scheduled LXMF sync failed too, waiting until next scheduled sync to try again " , RNS . LOG_DEBUG )
self . setpersistent ( " lxmf.lastsync " , time . time ( ) )
self . setpersistent ( " lxmf.syncretrying " , False )
2022-04-07 21:03:53 +02:00
def __start_jobs_deferred ( self ) :
if self . config [ " start_announce " ] :
2022-10-02 01:14:29 +02:00
self . lxmf_announce ( )
2022-04-07 21:03:53 +02:00
2022-10-02 01:14:29 +02:00
if self . is_service :
self . service_thread = threading . Thread ( target = self . _service_jobs , daemon = True )
self . service_thread . start ( )
2022-10-08 22:47:08 +02:00
if self . is_standalone or self . is_service :
self . periodic_thread = threading . Thread ( target = self . _periodic_jobs , daemon = True )
self . periodic_thread . start ( )
2022-10-02 01:14:29 +02:00
2022-04-07 21:03:53 +02:00
def __start_jobs_immediate ( self ) :
2022-10-13 21:22:16 +02:00
if self . log_verbose :
2022-10-03 18:29:54 +02:00
selected_level = 7
else :
selected_level = 2
2022-10-12 16:38:59 +02:00
self . setstate ( " init.loadingstate " , " Substantiating Reticulum " )
2022-10-03 18:29:54 +02:00
self . reticulum = RNS . Reticulum ( configdir = self . rns_configdir , loglevel = selected_level )
2022-04-07 21:03:53 +02:00
2022-07-06 12:20:41 +02:00
if RNS . vendor . platformutils . get_platform ( ) == " android " :
2022-07-06 12:19:05 +02:00
if not self . reticulum . is_connected_to_shared_instance :
RNS . log ( " Running as master or standalone instance, adding interfaces " )
2022-10-15 11:42:34 +02:00
self . interface_local = None
self . interface_tcp = None
self . interface_i2p = None
self . interface_rnode = None
self . interface_modem = None
self . interface_serial = None
2022-07-06 12:19:05 +02:00
if self . config [ " connect_local " ] :
2022-10-12 16:38:59 +02:00
self . setstate ( " init.loadingstate " , " Discovering Topography " )
2022-07-06 12:19:05 +02:00
try :
2022-11-02 22:29:13 +01:00
RNS . log ( " Adding Auto Interface... " , RNS . LOG_DEBUG )
2022-07-06 12:19:05 +02:00
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
2022-10-16 00:25:07 +02:00
if RNS . Reticulum . transport_enabled ( ) :
2022-10-18 15:44:36 +02:00
if_mode = Interface . Interface . MODE_FULL
if self . config [ " connect_ifmode_local " ] == " gateway " :
if_mode = Interface . Interface . MODE_GATEWAY
elif self . config [ " connect_ifmode_local " ] == " access point " :
if_mode = Interface . Interface . MODE_ACCESS_POINT
elif self . config [ " connect_ifmode_local " ] == " roaming " :
if_mode = Interface . Interface . MODE_ROAMING
elif self . config [ " connect_ifmode_local " ] == " boundary " :
if_mode = Interface . Interface . MODE_BOUNDARY
2022-10-16 00:25:07 +02:00
else :
if_mode = None
self . reticulum . _add_interface ( autointerface , mode = if_mode , ifac_netname = ifac_netname , ifac_netkey = ifac_netkey )
2022-07-06 12:19:05 +02:00
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 " ] :
2022-10-12 16:38:59 +02:00
self . setstate ( " init.loadingstate " , " Connecting TCP Tunnel " )
2022-07-06 12:19:05 +02:00
try :
2022-11-02 22:29:13 +01:00
RNS . log ( " Adding TCP Interface... " , RNS . LOG_DEBUG )
2022-07-06 12:19:05 +02:00
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
2022-10-16 00:25:07 +02:00
if RNS . Reticulum . transport_enabled ( ) :
2022-10-18 15:44:36 +02:00
if_mode = Interface . Interface . MODE_FULL
if self . config [ " connect_ifmode_tcp " ] == " gateway " :
if_mode = Interface . Interface . MODE_GATEWAY
elif self . config [ " connect_ifmode_tcp " ] == " access point " :
if_mode = Interface . Interface . MODE_ACCESS_POINT
elif self . config [ " connect_ifmode_tcp " ] == " roaming " :
if_mode = Interface . Interface . MODE_ROAMING
elif self . config [ " connect_ifmode_tcp " ] == " boundary " :
if_mode = Interface . Interface . MODE_BOUNDARY
2022-10-16 00:25:07 +02:00
else :
if_mode = None
self . reticulum . _add_interface ( tcpinterface , mode = if_mode , ifac_netname = ifac_netname , ifac_netkey = ifac_netkey )
2022-07-06 12:19:05 +02:00
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 " ] :
2022-10-12 16:38:59 +02:00
self . setstate ( " init.loadingstate " , " Opening I2P Endpoints " )
2022-07-06 12:19:05 +02:00
try :
2022-11-02 22:29:13 +01:00
RNS . log ( " Adding I2P Interface... " , RNS . LOG_DEBUG )
2022-07-06 12:19:05 +02:00
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 " ]
2022-07-06 13:07:11 +02:00
i2pinterface = RNS . Interfaces . I2PInterface . I2PInterface (
2022-07-06 12:19:05 +02:00
RNS . Transport ,
" I2PInterface " ,
RNS . Reticulum . storagepath ,
2022-07-06 13:07:11 +02:00
[ self . config [ " connect_i2p_b32 " ] ] ,
2022-07-06 12:19:05 +02:00
connectable = False ,
)
i2pinterface . OUT = True
2022-10-16 00:25:07 +02:00
if RNS . Reticulum . transport_enabled ( ) :
2022-10-18 15:44:36 +02:00
if_mode = Interface . Interface . MODE_FULL
if self . config [ " connect_ifmode_i2p " ] == " gateway " :
if_mode = Interface . Interface . MODE_GATEWAY
elif self . config [ " connect_ifmode_i2p " ] == " access point " :
if_mode = Interface . Interface . MODE_ACCESS_POINT
elif self . config [ " connect_ifmode_i2p " ] == " roaming " :
if_mode = Interface . Interface . MODE_ROAMING
elif self . config [ " connect_ifmode_i2p " ] == " boundary " :
if_mode = Interface . Interface . MODE_BOUNDARY
2022-10-16 00:25:07 +02:00
else :
if_mode = None
self . reticulum . _add_interface ( i2pinterface , mode = if_mode , ifac_netname = ifac_netname , ifac_netkey = ifac_netkey )
2022-07-06 16:08:53 +02:00
for si in RNS . Transport . interfaces :
if type ( si ) == RNS . Interfaces . I2PInterface . I2PInterfacePeer :
self . interface_i2p = si
2022-07-06 12:19:05 +02:00
except Exception as e :
RNS . log ( " Error while adding I2P Interface. The contained exception was: " + str ( e ) )
self . interface_i2p = None
2022-10-12 16:38:59 +02:00
if self . config [ " connect_rnode " ] :
self . setstate ( " init.loadingstate " , " Starting RNode " )
try :
2022-11-02 22:29:13 +01:00
RNS . log ( " Adding RNode Interface... " , RNS . LOG_DEBUG )
2022-10-12 16:38:59 +02:00
target_device = None
if len ( self . owner_app . usb_devices ) > 0 :
# TODO: Add more intelligent selection here
target_device = self . owner_app . usb_devices [ 0 ]
2022-11-02 22:29:13 +01:00
if target_device or self . config [ " hw_rnode_bluetooth " ] :
if target_device != None :
target_port = target_device [ " port " ]
else :
target_port = None
2022-11-23 20:42:56 +01:00
bt_device_name = None
rnode_allow_bluetooth = False
2022-11-02 22:29:13 +01:00
if self . getpersistent ( " permissions.bluetooth " ) :
if self . config [ " hw_rnode_bluetooth " ] :
2022-11-23 20:47:38 +01:00
RNS . log ( " Allowing RNode bluetooth " , RNS . LOG_DEBUG )
2022-11-02 22:29:13 +01:00
rnode_allow_bluetooth = True
2022-11-03 20:22:16 +01:00
if self . config [ " hw_rnode_bt_device " ] != None :
bt_device_name = self . config [ " hw_rnode_bt_device " ]
2022-11-23 20:42:56 +01:00
2022-11-02 22:29:13 +01:00
else :
2022-11-23 20:47:38 +01:00
RNS . log ( " Disallowing RNode bluetooth since config is disabled " , RNS . LOG_DEBUG )
2022-11-02 22:29:13 +01:00
rnode_allow_bluetooth = False
else :
2022-11-23 20:47:38 +01:00
RNS . log ( " Disallowing RNode bluetooth due to missing permission " , RNS . LOG_DEBUG )
2022-11-23 20:42:56 +01:00
rnode_allow_bluetooth = False
2022-11-02 22:29:13 +01:00
2022-10-12 16:38:59 +02:00
if self . config [ " connect_rnode_ifac_netname " ] == " " :
ifac_netname = None
else :
ifac_netname = self . config [ " connect_rnode_ifac_netname " ]
if self . config [ " connect_rnode_ifac_passphrase " ] == " " :
ifac_netkey = None
else :
ifac_netkey = self . config [ " connect_rnode_ifac_passphrase " ]
rnodeinterface = RNS . Interfaces . Android . RNodeInterface . RNodeInterface (
RNS . Transport ,
" RNodeInterface " ,
2022-11-02 22:29:13 +01:00
target_port ,
2022-10-13 18:44:54 +02:00
frequency = self . config [ " hw_rnode_frequency " ] ,
bandwidth = self . config [ " hw_rnode_bandwidth " ] ,
txpower = self . config [ " hw_rnode_tx_power " ] ,
sf = self . config [ " hw_rnode_spreading_factor " ] ,
cr = self . config [ " hw_rnode_coding_rate " ] ,
2022-10-12 16:38:59 +02:00
flow_control = None ,
2022-10-13 18:44:54 +02:00
id_interval = self . config [ " hw_rnode_beaconinterval " ] ,
id_callsign = self . config [ " hw_rnode_beacondata " ] ,
2022-11-02 22:29:13 +01:00
allow_bluetooth = rnode_allow_bluetooth ,
2022-11-03 20:22:16 +01:00
target_device_name = bt_device_name ,
2022-10-12 16:38:59 +02:00
)
rnodeinterface . OUT = True
2022-10-16 00:25:07 +02:00
if RNS . Reticulum . transport_enabled ( ) :
if_mode = Interface . Interface . MODE_FULL
2022-10-18 15:44:36 +02:00
if self . config [ " connect_ifmode_rnode " ] == " gateway " :
if_mode = Interface . Interface . MODE_GATEWAY
elif self . config [ " connect_ifmode_rnode " ] == " access point " :
if_mode = Interface . Interface . MODE_ACCESS_POINT
elif self . config [ " connect_ifmode_rnode " ] == " roaming " :
if_mode = Interface . Interface . MODE_ROAMING
elif self . config [ " connect_ifmode_rnode " ] == " boundary " :
if_mode = Interface . Interface . MODE_BOUNDARY
else :
if_mode = None
2022-10-16 00:25:07 +02:00
self . reticulum . _add_interface ( rnodeinterface , mode = if_mode , ifac_netname = ifac_netname , ifac_netkey = ifac_netkey )
2022-10-12 16:38:59 +02:00
self . interface_rnode = rnodeinterface
2022-10-29 16:39:54 +02:00
if rnodeinterface != None :
if len ( rnodeinterface . hw_errors ) > 0 :
self . setpersistent ( " startup.errors.rnode " , rnodeinterface . hw_errors [ 0 ] )
if self . interface_rnode . online :
self . interface_rnode . display_image ( sideband_fb_data )
self . interface_rnode . enable_external_framebuffer ( )
2022-10-12 16:38:59 +02:00
except Exception as e :
RNS . log ( " Error while adding RNode Interface. The contained exception was: " + str ( e ) )
self . interface_rnode = None
2022-10-15 11:42:34 +02:00
elif self . config [ " connect_serial " ] :
self . setstate ( " init.loadingstate " , " Starting Serial Interface " )
try :
2022-11-02 22:29:13 +01:00
RNS . log ( " Adding Serial Interface... " , RNS . LOG_DEBUG )
2022-10-15 11:42:34 +02:00
target_device = None
if len ( self . owner_app . usb_devices ) > 0 :
# TODO: Add more intelligent selection here
target_device = self . owner_app . usb_devices [ 0 ]
if target_device :
if self . config [ " connect_serial_ifac_netname " ] == " " :
ifac_netname = None
else :
ifac_netname = self . config [ " connect_serial_ifac_netname " ]
if self . config [ " connect_serial_ifac_passphrase " ] == " " :
ifac_netkey = None
else :
ifac_netkey = self . config [ " connect_serial_ifac_passphrase " ]
serialinterface = RNS . Interfaces . Android . SerialInterface . SerialInterface (
2022-10-15 14:55:47 +02:00
RNS . Transport ,
" SerialInterface " ,
target_device [ " port " ] ,
self . config [ " hw_serial_baudrate " ] ,
self . config [ " hw_serial_databits " ] ,
self . config [ " hw_serial_parity " ] ,
self . config [ " hw_serial_stopbits " ] ,
)
2022-10-15 11:42:34 +02:00
serialinterface . OUT = True
2022-10-16 00:25:07 +02:00
if RNS . Reticulum . transport_enabled ( ) :
if_mode = Interface . Interface . MODE_FULL
2022-10-18 15:44:36 +02:00
if self . config [ " connect_ifmode_serial " ] == " gateway " :
if_mode = Interface . Interface . MODE_GATEWAY
elif self . config [ " connect_ifmode_serial " ] == " access point " :
if_mode = Interface . Interface . MODE_ACCESS_POINT
elif self . config [ " connect_ifmode_serial " ] == " roaming " :
if_mode = Interface . Interface . MODE_ROAMING
elif self . config [ " connect_ifmode_serial " ] == " boundary " :
if_mode = Interface . Interface . MODE_BOUNDARY
else :
if_mode = None
2022-10-16 00:25:07 +02:00
self . reticulum . _add_interface ( serialinterface , mode = if_mode , ifac_netname = ifac_netname , ifac_netkey = ifac_netkey )
2022-10-15 11:42:34 +02:00
self . interface_serial = serialinterface
except Exception as e :
RNS . log ( " Error while adding Serial Interface. The contained exception was: " + str ( e ) )
self . interface_serial = None
2022-10-15 14:55:47 +02:00
elif self . config [ " connect_modem " ] :
self . setstate ( " init.loadingstate " , " Starting Radio Modem " )
try :
2022-11-02 22:29:13 +01:00
RNS . log ( " Adding Modem Interface... " , RNS . LOG_DEBUG )
2022-10-15 14:55:47 +02:00
target_device = None
if len ( self . owner_app . usb_devices ) > 0 :
# TODO: Add more intelligent selection here
target_device = self . owner_app . usb_devices [ 0 ]
if target_device :
if self . config [ " connect_modem_ifac_netname " ] == " " :
ifac_netname = None
else :
ifac_netname = self . config [ " connect_modem_ifac_netname " ]
if self . config [ " connect_modem_ifac_passphrase " ] == " " :
ifac_netkey = None
else :
ifac_netkey = self . config [ " connect_modem_ifac_passphrase " ]
modeminterface = RNS . Interfaces . Android . KISSInterface . KISSInterface (
RNS . Transport ,
" ModemInterface " ,
target_device [ " port " ] ,
self . config [ " hw_modem_baudrate " ] ,
self . config [ " hw_modem_databits " ] ,
self . config [ " hw_modem_parity " ] ,
self . config [ " hw_modem_stopbits " ] ,
self . config [ " hw_modem_preamble " ] ,
self . config [ " hw_modem_tail " ] ,
self . config [ " hw_modem_persistence " ] ,
self . config [ " hw_modem_slottime " ] ,
False , # flow control
self . config [ " hw_modem_beaconinterval " ] ,
self . config [ " hw_modem_beacondata " ] ,
)
modeminterface . OUT = True
2022-10-16 00:25:07 +02:00
if RNS . Reticulum . transport_enabled ( ) :
if_mode = Interface . Interface . MODE_FULL
2022-10-18 15:44:36 +02:00
if self . config [ " connect_ifmode_modem " ] == " gateway " :
if_mode = Interface . Interface . MODE_GATEWAY
elif self . config [ " connect_ifmode_modem " ] == " access point " :
if_mode = Interface . Interface . MODE_ACCESS_POINT
elif self . config [ " connect_ifmode_modem " ] == " roaming " :
if_mode = Interface . Interface . MODE_ROAMING
elif self . config [ " connect_ifmode_modem " ] == " boundary " :
if_mode = Interface . Interface . MODE_BOUNDARY
else :
if_mode = None
2022-10-16 00:25:07 +02:00
self . reticulum . _add_interface ( modeminterface , mode = if_mode , ifac_netname = ifac_netname , ifac_netkey = ifac_netkey )
2022-10-15 14:55:47 +02:00
self . interface_modem = modeminterface
except Exception as e :
RNS . log ( " Error while adding Modem Interface. The contained exception was: " + str ( e ) )
self . interface_modem = None
2022-10-02 01:14:29 +02:00
RNS . log ( " Reticulum started, activating LXMF... " )
2022-10-12 16:38:59 +02:00
self . setstate ( " init.loadingstate " , " Activating LXMF Router " )
2022-10-02 01:14:29 +02:00
self . message_router = LXMF . LXMRouter ( identity = self . identity , storagepath = self . lxmf_storage , autopeer = True )
self . message_router . register_delivery_callback ( self . lxmf_delivery )
2022-04-07 21:03:53 +02:00
2022-10-02 01:14:29 +02:00
self . lxmf_destination = self . message_router . register_delivery_identity ( self . identity , display_name = self . config [ " display_name " ] )
self . lxmf_destination . set_default_app_data ( self . get_display_name_bytes )
2022-10-01 11:18:14 +02:00
2022-10-02 01:14:29 +02:00
self . rns_dir = RNS . Reticulum . configdir
2022-04-07 21:03:53 +02:00
2022-10-02 12:45:06 +02:00
self . update_active_lxmf_propagation_node ( )
def update_active_lxmf_propagation_node ( self ) :
2022-07-05 15:22:11 +02:00
if self . config [ " lxmf_propagation_node " ] != None and self . config [ " lxmf_propagation_node " ] != " " :
self . set_active_propagation_node ( self . config [ " lxmf_propagation_node " ] )
else :
if self . config [ " last_lxmf_propagation_node " ] != None and self . config [ " last_lxmf_propagation_node " ] != " " :
self . set_active_propagation_node ( self . config [ " last_lxmf_propagation_node " ] )
else :
self . set_active_propagation_node ( None )
2022-04-07 21:03:53 +02:00
def message_notification ( self , message ) :
if message . state == LXMF . LXMessage . FAILED and hasattr ( message , " try_propagation_on_fail " ) and message . try_propagation_on_fail :
RNS . log ( " Direct delivery of " + str ( message ) + " failed. Retrying as propagated message. " , RNS . LOG_VERBOSE )
message . try_propagation_on_fail = None
message . delivery_attempts = 0
del message . next_delivery_attempt
message . packed = None
message . desired_method = LXMF . LXMessage . PROPAGATED
self . _db_message_set_method ( message . hash , LXMF . LXMessage . PROPAGATED )
self . message_router . handle_outbound ( message )
else :
self . lxm_ingest ( message , originator = True )
2022-11-22 14:25:56 +01:00
def paper_message ( self , content , destination_hash ) :
try :
if content == " " :
raise ValueError ( " Message content cannot be empty " )
dest_identity = RNS . Identity . recall ( destination_hash )
dest = RNS . Destination ( dest_identity , RNS . Destination . OUT , RNS . Destination . SINGLE , " lxmf " , " delivery " )
source = self . lxmf_destination
desired_method = LXMF . LXMessage . PAPER
lxm = LXMF . LXMessage ( dest , source , content , title = " " , desired_method = desired_method )
self . lxm_ingest ( lxm , originator = True )
return True
except Exception as e :
RNS . log ( " Error while creating paper message: " + str ( e ) , RNS . LOG_ERROR )
return False
2022-04-07 21:03:53 +02:00
def send_message ( self , content , destination_hash , propagation ) :
try :
if content == " " :
raise ValueError ( " Message content cannot be empty " )
dest_identity = RNS . Identity . recall ( destination_hash )
dest = RNS . Destination ( dest_identity , RNS . Destination . OUT , RNS . Destination . SINGLE , " lxmf " , " delivery " )
source = self . lxmf_destination
if propagation :
desired_method = LXMF . LXMessage . PROPAGATED
else :
desired_method = LXMF . LXMessage . DIRECT
lxm = LXMF . LXMessage ( dest , source , content , title = " " , desired_method = desired_method )
lxm . register_delivery_callback ( self . message_notification )
lxm . register_failed_callback ( self . message_notification )
if self . message_router . get_outbound_propagation_node ( ) != None :
lxm . try_propagation_on_fail = True
self . message_router . handle_outbound ( lxm )
self . lxm_ingest ( lxm , originator = True )
return True
except Exception as e :
RNS . log ( " Error while sending message: " + str ( e ) , RNS . LOG_ERROR )
return False
def new_conversation ( self , dest_str , name = " " , trusted = False ) :
if len ( dest_str ) != RNS . Reticulum . TRUNCATED_HASHLENGTH / / 8 * 2 :
return False
try :
addr_b = bytes . fromhex ( dest_str )
self . _db_create_conversation ( addr_b , name , trusted )
except Exception as e :
RNS . log ( " Error while creating conversation: " + str ( e ) , RNS . LOG_ERROR )
return False
return True
def create_conversation ( self , context_dest , name = None , trusted = False ) :
try :
self . _db_create_conversation ( context_dest , name , trusted )
except Exception as e :
RNS . log ( " Error while creating conversation: " + str ( e ) , RNS . LOG_ERROR )
return False
return True
2022-11-22 14:25:56 +01:00
def lxm_ingest_uri ( self , uri ) :
local_delivery_signal = " local_delivery_occurred "
duplicate_signal = " duplicate_lxm "
ingest_result = self . message_router . ingest_lxm_uri (
uri ,
signal_local_delivery = local_delivery_signal ,
signal_duplicate = duplicate_signal
)
if ingest_result == False :
response = " The URI contained no decodable messages "
elif ingest_result == local_delivery_signal :
response = " Message was decoded, decrypted successfully, and added to your conversation list. "
elif ingest_result == duplicate_signal :
response = " The decoded message has already been processed by the LXMF Router, and will not be ingested again. "
else :
# TODO: Add message to sneakernet queues
response = " The decoded message was not addressed to your LXMF address, and has been discarded. "
self . setstate ( " lxm_uri_ingest.result " , response )
2022-04-07 21:03:53 +02:00
def lxm_ingest ( self , message , originator = False ) :
2022-09-17 17:12:25 +02:00
should_notify = False
is_trusted = False
2022-04-07 21:03:53 +02:00
if originator :
context_dest = message . destination_hash
else :
context_dest = message . source_hash
2022-09-17 17:12:25 +02:00
is_trusted = self . is_trusted ( context_dest )
2022-04-07 21:03:53 +02:00
if self . _db_message ( message . hash ) :
2022-07-05 15:22:11 +02:00
RNS . log ( " Message exists, setting state to: " + str ( message . state ) , RNS . LOG_DEBUG )
2022-04-07 21:03:53 +02:00
self . _db_message_set_state ( message . hash , message . state )
else :
2022-07-05 15:22:11 +02:00
RNS . log ( " Message does not exist, saving " , RNS . LOG_DEBUG )
2022-04-07 21:03:53 +02:00
self . _db_save_lxm ( message , context_dest )
2022-10-02 12:15:37 +02:00
if is_trusted :
should_notify = True
2022-04-07 21:03:53 +02:00
if self . _db_conversation ( context_dest ) == None :
self . _db_create_conversation ( context_dest )
2022-10-02 01:14:29 +02:00
self . setstate ( " app.flags.new_conversations " , True )
2022-04-07 21:03:53 +02:00
2022-10-02 01:14:29 +02:00
if self . gui_display ( ) == " messages_screen " :
if self . gui_conversation ( ) != context_dest :
2022-04-07 21:03:53 +02:00
self . unread_conversation ( context_dest )
2022-10-02 01:14:29 +02:00
self . setstate ( " app.flags.unread_conversations " , True )
2022-09-17 17:12:25 +02:00
else :
2022-10-02 01:14:29 +02:00
if self . gui_foreground ( ) :
2022-09-17 17:12:25 +02:00
should_notify = False
2022-04-07 21:03:53 +02:00
else :
self . unread_conversation ( context_dest )
2022-10-02 01:14:29 +02:00
self . setstate ( " app.flags.unread_conversations " , True )
2022-04-07 21:03:53 +02:00
2022-10-07 01:24:16 +02:00
if RNS . vendor . platformutils . is_android ( ) :
if self . gui_display ( ) == " conversations_screen " and self . gui_foreground ( ) :
should_notify = False
2022-10-02 12:15:37 +02:00
2022-10-02 01:14:29 +02:00
if self . is_client :
should_notify = False
2022-04-07 21:03:53 +02:00
2022-09-17 17:12:25 +02:00
if should_notify :
nlen = 128
text = message . content . decode ( " utf-8 " )
notification_content = text [ : nlen ]
if len ( text ) > nlen :
text + = " ... "
2022-10-02 01:14:29 +02:00
self . notify ( title = self . peer_display_name ( context_dest ) , content = notification_content , group = " LXM " , context_id = RNS . hexrep ( context_dest , delimit = False ) )
2022-09-17 17:12:25 +02:00
2022-04-07 21:03:53 +02:00
def start ( self ) :
self . _db_clean_messages ( )
self . __start_jobs_immediate ( )
thread = threading . Thread ( target = self . __start_jobs_deferred )
thread . setDaemon ( True )
thread . start ( )
2022-10-02 01:14:29 +02:00
self . _db_setstate ( " core.started " , True )
2022-04-07 21:03:53 +02:00
RNS . log ( " Sideband Core " + str ( self ) + " started " )
def request_lxmf_sync ( self , limit = None ) :
if self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_IDLE or self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_COMPLETE :
self . message_router . request_messages_from_propagation_node ( self . identity , max_messages = limit )
RNS . log ( " LXMF message sync requested from propagation node " + RNS . prettyhexrep ( self . message_router . get_outbound_propagation_node ( ) ) + " for " + str ( self . identity ) )
return True
else :
return False
def cancel_lxmf_sync ( self ) :
if self . message_router . propagation_transfer_state != LXMF . LXMRouter . PR_IDLE :
self . message_router . cancel_propagation_node_requests ( )
def get_sync_progress ( self ) :
return self . message_router . propagation_transfer_progress
def lxmf_delivery ( self , message ) :
time_string = time . strftime ( " % Y- % m- %d % H: % M: % S " , time . localtime ( message . timestamp ) )
signature_string = " Signature is invalid, reason undetermined "
if message . signature_validated :
signature_string = " Validated "
else :
if message . unverified_reason == LXMF . LXMessage . SIGNATURE_INVALID :
signature_string = " Invalid signature "
if message . unverified_reason == LXMF . LXMessage . SOURCE_UNKNOWN :
signature_string = " Cannot verify, source is unknown "
RNS . log ( " LXMF delivery " + str ( time_string ) + " . " + str ( signature_string ) + " . " )
try :
self . lxm_ingest ( message )
except Exception as e :
RNS . log ( " Error while ingesting LXMF message " + RNS . prettyhexrep ( message . hash ) + " to database: " + str ( e ) )
def get_display_name_bytes ( self ) :
return self . config [ " display_name " ] . encode ( " utf-8 " )
def get_sync_status ( self ) :
if self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_IDLE :
return " Idle "
elif self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_PATH_REQUESTED :
return " Path requested "
elif self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_LINK_ESTABLISHING :
return " Establishing link "
elif self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_LINK_ESTABLISHED :
return " Link established "
elif self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_REQUEST_SENT :
return " Sync request sent "
elif self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_RECEIVING :
return " Receiving messages "
elif self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_RESPONSE_RECEIVED :
return " Messages received "
elif self . message_router . propagation_transfer_state == LXMF . LXMRouter . PR_COMPLETE :
new_msgs = self . message_router . propagation_transfer_last_result
if new_msgs == 0 :
return " Done, no new messages "
else :
return " Downloaded " + str ( new_msgs ) + " new messages "
else :
return " Unknown "
2022-10-29 16:39:54 +02:00
def cleanup ( self ) :
if RNS . vendor . platformutils . get_platform ( ) == " android " :
if not self . reticulum . is_connected_to_shared_instance :
RNS . Transport . detach_interfaces ( )
2022-04-07 21:03:53 +02:00
rns_config = """
[ reticulum ]
2022-10-15 19:12:39 +02:00
enable_transport = TRANSPORT_IS_ENABLED
2022-04-07 21:03:53 +02:00
share_instance = Yes
shared_instance_port = 37428
instance_control_port = 37429
panic_on_interface_error = No
[ logging ]
2022-07-06 12:20:41 +02:00
loglevel = 3
2022-04-07 21:03:53 +02:00
2022-10-16 00:25:07 +02:00
"""