Merge branch 'SebastianObi:main' into main

This commit is contained in:
Swissbandit 2023-04-29 11:46:22 +02:00 committed by GitHub
commit a36d8f9a9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 2747 additions and 444 deletions

View 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.

View file

@ -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