mirror of
https://github.com/markqvist/LXMF-Tools.git
synced 2025-05-19 06:20:42 -04:00
Merge branch 'SebastianObi:main' into main
This commit is contained in:
commit
a36d8f9a9d
25 changed files with 2747 additions and 444 deletions
4
lxmf_terminal/Examples/README.md
Normal file
4
lxmf_terminal/Examples/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Examples
|
||||
This folder contains sample configurations for different application environments or scenarios.
|
||||
|
||||
Copy these files accordingly to the appropriate configuration folder. Then customize the content of these files to your needs.
|
|
@ -193,9 +193,10 @@ class lxmf_connection:
|
|||
message_notification_callback = None
|
||||
message_notification_success_callback = None
|
||||
message_notification_failed_callback = None
|
||||
config_set_callback = None
|
||||
|
||||
|
||||
def __init__(self, storage_path=None, identity_file="identity", identity=None, destination_name="lxmf", destination_type="delivery", display_name="", announce_data=None, send_delay=0, desired_method="direct", propagation_node=None, try_propagation_on_fail=False, announce_startup=False, announce_startup_delay=0, announce_periodic=False, announce_periodic_interval=360, sync_startup=False, sync_startup_delay=0, sync_limit=8, sync_periodic=False, sync_periodic_interval=360):
|
||||
def __init__(self, storage_path=None, identity_file="identity", identity=None, destination_name="lxmf", destination_type="delivery", display_name="", announce_data=None, announce_hidden=False, send_delay=0, desired_method="direct", propagation_node=None, propagation_node_auto=False, propagation_node_active=None, try_propagation_on_fail=False, announce_startup=False, announce_startup_delay=0, announce_periodic=False, announce_periodic_interval=360, sync_startup=False, sync_startup_delay=0, sync_limit=8, sync_periodic=False, sync_periodic_interval=360):
|
||||
self.storage_path = storage_path
|
||||
|
||||
self.identity_file = identity_file
|
||||
|
@ -208,6 +209,7 @@ class lxmf_connection:
|
|||
|
||||
self.display_name = display_name
|
||||
self.announce_data = announce_data
|
||||
self.announce_hidden = announce_hidden
|
||||
|
||||
self.send_delay = int(send_delay)
|
||||
|
||||
|
@ -216,6 +218,8 @@ class lxmf_connection:
|
|||
else:
|
||||
self.desired_method_direct = True
|
||||
self.propagation_node = propagation_node
|
||||
self.propagation_node_auto = propagation_node_auto
|
||||
self.propagation_node_active = propagation_node_active
|
||||
self.try_propagation_on_fail = try_propagation_on_fail
|
||||
|
||||
self.announce_startup = announce_startup
|
||||
|
@ -230,6 +234,10 @@ class lxmf_connection:
|
|||
self.sync_periodic = sync_periodic
|
||||
self.sync_periodic_interval = int(sync_periodic_interval)
|
||||
|
||||
if not self.storage_path:
|
||||
log("LXMF - No storage_path parameter", LOG_ERROR)
|
||||
return
|
||||
|
||||
if not os.path.isdir(self.storage_path):
|
||||
os.makedirs(self.storage_path)
|
||||
log("LXMF - Storage path was created", LOG_NOTICE)
|
||||
|
@ -284,10 +292,18 @@ class lxmf_connection:
|
|||
|
||||
self.destination.set_link_established_callback(self.client_connected)
|
||||
|
||||
self.autoselect_propagation_node()
|
||||
if self.propagation_node_auto:
|
||||
self.propagation_callback = lxmf_connection_propagation(self, "lxmf.propagation")
|
||||
RNS.Transport.register_announce_handler(self.propagation_callback)
|
||||
if self.propagation_node_active:
|
||||
self.propagation_node_set(self.propagation_node_active)
|
||||
elif self.propagation_node:
|
||||
self.propagation_node_set(self.propagation_node)
|
||||
else:
|
||||
self.propagation_node_set(self.propagation_node)
|
||||
|
||||
if self.announce_startup or self.announce_periodic:
|
||||
self.announce(True)
|
||||
self.announce(initial=True)
|
||||
|
||||
if self.sync_startup or self.sync_periodic:
|
||||
self.sync(True)
|
||||
|
@ -314,6 +330,10 @@ class lxmf_connection:
|
|||
self.message_notification_failed_callback = handler_function
|
||||
|
||||
|
||||
def register_config_set_callback(self, handler_function):
|
||||
self.config_set_callback = handler_function
|
||||
|
||||
|
||||
def destination_hash(self):
|
||||
return self.destination.hash
|
||||
|
||||
|
@ -357,7 +377,7 @@ class lxmf_connection:
|
|||
return ""
|
||||
|
||||
|
||||
def send(self, destination, content="", title="", fields=None, timestamp=None, app_data=""):
|
||||
def send(self, destination, content="", title="", fields=None, timestamp=None, app_data="", destination_name=None, destination_type=None):
|
||||
if type(destination) is not bytes:
|
||||
if len(destination) == ((RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2)+2:
|
||||
destination = destination[1:-1]
|
||||
|
@ -372,8 +392,13 @@ class lxmf_connection:
|
|||
log("LXMF - Destination is invalid", LOG_ERROR)
|
||||
return
|
||||
|
||||
if destination_name == None:
|
||||
destination_name = self.destination_name
|
||||
if destination_type == None:
|
||||
destination_type = self.destination_type
|
||||
|
||||
destination_identity = RNS.Identity.recall(destination)
|
||||
destination = RNS.Destination(destination_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, self.destination_name, self.destination_type)
|
||||
destination = RNS.Destination(destination_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, destination_name, destination_type)
|
||||
self.send_message(destination, self.destination, content, title, fields, timestamp, app_data)
|
||||
|
||||
|
||||
|
@ -442,7 +467,7 @@ class lxmf_connection:
|
|||
message.desired_method_str = "propagated"
|
||||
|
||||
|
||||
def announce(self, initial=False):
|
||||
def announce(self, app_data=None, attached_interface=None, initial=False):
|
||||
announce_timer = None
|
||||
|
||||
if self.announce_periodic and self.announce_periodic_interval > 0:
|
||||
|
@ -459,26 +484,29 @@ class lxmf_connection:
|
|||
announce_timer.daemon = True
|
||||
announce_timer.start()
|
||||
else:
|
||||
self.announce_now()
|
||||
self.announce_now(app_data=app_data, attached_interface=attached_interface)
|
||||
return
|
||||
|
||||
self.announce_now()
|
||||
self.announce_now(app_data=app_data, attached_interface=attached_interface)
|
||||
|
||||
|
||||
def announce_now(self, app_data=None):
|
||||
if app_data:
|
||||
def announce_now(self, app_data=None, attached_interface=None):
|
||||
if self.announce_hidden:
|
||||
self.destination.announce("".encode("utf-8"), attached_interface=attached_interface)
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()) +" (Hidden)", LOG_DEBUG)
|
||||
elif app_data != None:
|
||||
if isinstance(app_data, str):
|
||||
self.destination.announce(app_data.encode("utf-8"))
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()) +":" + announce_data, LOG_DEBUG)
|
||||
self.destination.announce(app_data.encode("utf-8"), attached_interface=attached_interface)
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()) +":" + app_data, LOG_DEBUG)
|
||||
else:
|
||||
self.destination.announce(app_data)
|
||||
self.destination.announce(app_data, attached_interface=attached_interface)
|
||||
log("LMF - Announced: " + RNS.prettyhexrep(self.destination_hash()), LOG_DEBUG)
|
||||
elif self.announce_data:
|
||||
if isinstance(self.announce_data, str):
|
||||
self.destination.announce(self.announce_data.encode("utf-8"))
|
||||
self.destination.announce(self.announce_data.encode("utf-8"), attached_interface=attached_interface)
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()) +":" + self.announce_data, LOG_DEBUG)
|
||||
else:
|
||||
self.destination.announce(self.announce_data)
|
||||
self.destination.announce(self.announce_data, attached_interface=attached_interface)
|
||||
log("LXMF - Announced: " + RNS.prettyhexrep(self.destination_hash()), LOG_DEBUG)
|
||||
else:
|
||||
self.destination.announce()
|
||||
|
@ -520,24 +548,50 @@ class lxmf_connection:
|
|||
return False
|
||||
|
||||
|
||||
def autoselect_propagation_node(self):
|
||||
if self.propagation_node is not None:
|
||||
if len(self.propagation_node) != ((RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2):
|
||||
log("LXMF - Propagation node length is invalid", LOG_ERROR)
|
||||
else:
|
||||
try:
|
||||
propagation_hash = bytes.fromhex(self.propagation_node)
|
||||
except Exception as e:
|
||||
log("LXMF - Propagation node is invalid", LOG_ERROR)
|
||||
return
|
||||
def propagation_node_set(self, dest_str):
|
||||
if not dest_str:
|
||||
return False
|
||||
|
||||
node_identity = RNS.Identity.recall(propagation_hash)
|
||||
if node_identity != None:
|
||||
log("LXMF - Propagation node: " + RNS.prettyhexrep(propagation_hash), LOG_INFO)
|
||||
propagation_hash = RNS.Destination.hash_from_name_and_identity("lxmf.propagation", node_identity)
|
||||
self.message_router.set_outbound_propagation_node(propagation_hash)
|
||||
else:
|
||||
log("LXMF - Propagation node identity not known", LOG_ERROR)
|
||||
if len(dest_str) != ((RNS.Reticulum.TRUNCATED_HASHLENGTH//8)*2):
|
||||
log("LXMF - Propagation node length is invalid", LOG_ERROR)
|
||||
return False
|
||||
|
||||
try:
|
||||
dest_hash = bytes.fromhex(dest_str)
|
||||
except Exception as e:
|
||||
log("LXMF - Propagation node is invalid", LOG_ERROR)
|
||||
return False
|
||||
|
||||
node_identity = RNS.Identity.recall(dest_hash)
|
||||
if node_identity != None:
|
||||
log("LXMF - Propagation node: " + RNS.prettyhexrep(dest_hash), LOG_INFO)
|
||||
dest_hash = RNS.Destination.hash_from_name_and_identity("lxmf.propagation", node_identity)
|
||||
self.message_router.set_outbound_propagation_node(dest_hash)
|
||||
self.propagation_node_active = dest_str
|
||||
return True
|
||||
else:
|
||||
log("LXMF - Propagation node identity not known", LOG_ERROR)
|
||||
return False
|
||||
|
||||
|
||||
def propagation_node_update(self, dest_str):
|
||||
if self.propagation_node_hash_str() != dest_str:
|
||||
if self.propagation_node_set(dest_str) and self.config_set_callback is not None:
|
||||
self.config_set_callback("propagation_node_active", dest_str)
|
||||
|
||||
|
||||
def propagation_node_hash(self):
|
||||
try:
|
||||
return bytes.fromhex(self.propagation_node_active)
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def propagation_node_hash_str(self):
|
||||
if self.propagation_node_active:
|
||||
return self.propagation_node_active
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def client_connected(self, link):
|
||||
|
@ -625,6 +679,43 @@ class lxmf_connection:
|
|||
log("- App Data: " + message.app_data, LOG_DEBUG)
|
||||
|
||||
|
||||
|
||||
|
||||
class lxmf_connection_propagation():
|
||||
def __init__(self, owner, aspect_filter=None):
|
||||
self.owner = owner
|
||||
self.aspect_filter = aspect_filter
|
||||
|
||||
EMITTED_DELTA_GRACE = 300
|
||||
EMITTED_DELTA_IGNORE = 10
|
||||
|
||||
def received_announce(self, destination_hash, announced_identity, app_data):
|
||||
if app_data == None:
|
||||
return
|
||||
|
||||
if len(app_data) == 0:
|
||||
return
|
||||
|
||||
try:
|
||||
unpacked = umsgpack.unpackb(app_data)
|
||||
node_active = unpacked[0]
|
||||
emitted = unpacked[1]
|
||||
hop_count = RNS.Transport.hops_to(destination_hash)
|
||||
age = time.time() - emitted
|
||||
if age < 0:
|
||||
if age < -1*PropDetector.EMITTED_DELTA_GRACE:
|
||||
return
|
||||
log("LXMF - Received an propagation node announce from "+RNS.prettyhexrep(destination_hash)+": "+str(age)+" seconds ago, "+str(hop_count)+" hops away", LOG_INFO)
|
||||
if self.owner.propagation_node_active == None:
|
||||
self.owner.propagation_node_update(RNS.hexrep(destination_hash, False))
|
||||
else:
|
||||
prev_hop_count = RNS.Transport.hops_to(self.owner.propagation_node_hash())
|
||||
if hop_count <= prev_hop_count:
|
||||
self.owner.propagation_node_update(RNS.hexrep(destination_hash, False))
|
||||
except:
|
||||
return
|
||||
|
||||
|
||||
##############################################################################################################
|
||||
# LXMF Functions
|
||||
|
||||
|
@ -839,6 +930,36 @@ def config_getoption(config, section, key, default=False, lng_key=""):
|
|||
|
||||
|
||||
|
||||
#### Config - Set #####
|
||||
def config_set(key=None, value=""):
|
||||
global PATH
|
||||
|
||||
try:
|
||||
file = PATH + "/config.cfg.owr"
|
||||
if os.path.isfile(file):
|
||||
fh = open(file,'r')
|
||||
data = fh.read()
|
||||
fh.close()
|
||||
data = re.sub(r'^#?'+key+'( +)?=( +)?(\w+)?', key+" = "+value, data, count=1, flags=re.MULTILINE)
|
||||
fh = open(file,'w')
|
||||
fh.write(data)
|
||||
fh.close()
|
||||
|
||||
file = PATH + "/config.cfg"
|
||||
if os.path.isfile(file):
|
||||
fh = open(file,'r')
|
||||
data = fh.read()
|
||||
fh.close()
|
||||
data = re.sub(r'^#?'+key+'( +)?=( +)?(\w+)?', key+" = "+value, data, count=1, flags=re.MULTILINE)
|
||||
fh = open(file,'w')
|
||||
fh.write(data)
|
||||
fh.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
#### Config - Read #####
|
||||
def config_read(file=None, file_override=None):
|
||||
global CONFIG
|
||||
|
@ -932,15 +1053,15 @@ def config_default(file=None, file_override=None):
|
|||
# Value convert
|
||||
|
||||
|
||||
def val_to_bool(val):
|
||||
def val_to_bool(val, fallback_true=True, fallback_false=False):
|
||||
if val == "on" or val == "On" or val == "true" or val == "True" or val == "yes" or val == "Yes" or val == "1" or val == "open" or val == "opened" or val == "up":
|
||||
return True
|
||||
elif val == "off" or val == "Off" or val == "false" or val == "False" or val == "no" or val == "No" or val == "0" or val == "close" or val == "closed" or val == "down":
|
||||
return False
|
||||
elif val != "":
|
||||
return True
|
||||
return fallback_true
|
||||
else:
|
||||
return False
|
||||
return fallback_false
|
||||
|
||||
|
||||
##############################################################################################################
|
||||
|
@ -1106,6 +1227,11 @@ def setup(path=None, path_rns=None, path_log=None, loglevel=None, service=False)
|
|||
else:
|
||||
config_propagation_node = None
|
||||
|
||||
if CONFIG.has_option("lxmf", "propagation_node_active"):
|
||||
config_propagation_node_active = CONFIG["lxmf"]["propagation_node_active"]
|
||||
else:
|
||||
config_propagation_node_active = None
|
||||
|
||||
if path is None:
|
||||
path = PATH
|
||||
|
||||
|
@ -1114,9 +1240,12 @@ def setup(path=None, path_rns=None, path_log=None, loglevel=None, service=False)
|
|||
destination_name=CONFIG["lxmf"]["destination_name"],
|
||||
destination_type=CONFIG["lxmf"]["destination_type"],
|
||||
display_name=CONFIG["lxmf"]["display_name"],
|
||||
announce_hidden=CONFIG["lxmf"].getboolean("announce_hidden"),
|
||||
send_delay=CONFIG["lxmf"]["send_delay"],
|
||||
desired_method=CONFIG["lxmf"]["desired_method"],
|
||||
propagation_node=config_propagation_node,
|
||||
propagation_node_auto=CONFIG["lxmf"].getboolean("propagation_node_auto"),
|
||||
propagation_node_active=config_propagation_node_active,
|
||||
try_propagation_on_fail=CONFIG["lxmf"].getboolean("try_propagation_on_fail"),
|
||||
announce_startup=CONFIG["lxmf"].getboolean("announce_startup"),
|
||||
announce_startup_delay=CONFIG["lxmf"]["announce_startup_delay"],
|
||||
|
@ -1130,6 +1259,7 @@ def setup(path=None, path_rns=None, path_log=None, loglevel=None, service=False)
|
|||
|
||||
LXMF_CONNECTION.register_announce_callback(lxmf_announce_callback)
|
||||
LXMF_CONNECTION.register_message_received_callback(lxmf_message_received_callback)
|
||||
LXMF_CONNECTION.register_config_set_callback(config_set)
|
||||
|
||||
log("LXMF - Connected", LOG_DEBUG)
|
||||
|
||||
|
@ -1228,7 +1358,13 @@ display_name = CMD
|
|||
desired_method = direct #direct/propagated
|
||||
|
||||
# Propagation node address/hash.
|
||||
#propagation_node =
|
||||
propagation_node =
|
||||
|
||||
# Set propagation node automatically.
|
||||
propagation_node_auto = True
|
||||
|
||||
# Current propagation node (Automatically set by the software).
|
||||
propagation_node_active =
|
||||
|
||||
# Try to deliver a message via the LXMF propagation network,
|
||||
# if a direct delivery to the recipient is not possible.
|
||||
|
@ -1244,6 +1380,10 @@ announce_startup_delay = 0 #Seconds
|
|||
announce_periodic = No
|
||||
announce_periodic_interval = 360 #Minutes
|
||||
|
||||
# The announce is hidden for client applications
|
||||
# but is still used for the routing tables.
|
||||
announce_hidden = No
|
||||
|
||||
# Some waiting time after message send
|
||||
# for LXMF/Reticulum processing.
|
||||
send_delay = 0 #Seconds
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue