mirror of https://github.com/markqvist/LXMF.git
Compare commits
13 Commits
Author | SHA1 | Date |
---|---|---|
|
787cd069dc | |
|
c2207d1eb7 | |
|
a9622e3a33 | |
|
499fe4cc53 | |
|
37e99910ec | |
|
005d71707c | |
|
1bdcf6ad53 | |
|
e6021b8fed | |
|
326c0eed8f | |
|
336792c07a | |
|
570d2c6846 | |
|
1ef4665073 | |
|
d5540b927f |
|
@ -0,0 +1,3 @@
|
||||||
|
liberapay: Reticulum
|
||||||
|
ko_fi: markqvist
|
||||||
|
custom: "https://unsigned.io/donate"
|
16
LICENSE
16
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
Reticulum License
|
||||||
|
|
||||||
Copyright (c) 2020 Mark Qvist / unsigned.io
|
Copyright (c) 2020-2025 Mark Qvist
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -9,8 +9,16 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
- The Software shall not be used in any kind of system which includes amongst
|
||||||
copies or substantial portions of the Software.
|
its functions the ability to purposefully do harm to human beings.
|
||||||
|
|
||||||
|
- The Software shall not be used, directly or indirectly, in the creation of
|
||||||
|
an artificial intelligence, machine learning or language model training
|
||||||
|
dataset, including but not limited to any use that contributes to the
|
||||||
|
training or development of such a model or algorithm.
|
||||||
|
|
||||||
|
- The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import time
|
import time
|
||||||
|
import threading
|
||||||
import RNS
|
import RNS
|
||||||
import RNS.vendor.umsgpack as msgpack
|
import RNS.vendor.umsgpack as msgpack
|
||||||
|
|
||||||
|
@ -17,10 +18,11 @@ class LXMFDeliveryAnnounceHandler:
|
||||||
if lxmessage.method == LXMessage.DIRECT or lxmessage.method == LXMessage.OPPORTUNISTIC:
|
if lxmessage.method == LXMessage.DIRECT or lxmessage.method == LXMessage.OPPORTUNISTIC:
|
||||||
lxmessage.next_delivery_attempt = time.time()
|
lxmessage.next_delivery_attempt = time.time()
|
||||||
|
|
||||||
while self.lxmrouter.processing_outbound:
|
def outbound_trigger():
|
||||||
time.sleep(0.1)
|
while self.lxmrouter.processing_outbound: time.sleep(0.1)
|
||||||
|
self.lxmrouter.process_outbound()
|
||||||
|
|
||||||
self.lxmrouter.process_outbound()
|
threading.Thread(target=outbound_trigger, daemon=True).start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
stamp_cost = stamp_cost_from_app_data(app_data)
|
stamp_cost = stamp_cost_from_app_data(app_data)
|
||||||
|
@ -55,10 +57,8 @@ class LXMFPropagationAnnounceHandler:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if len(data) >= 3:
|
if len(data) >= 3:
|
||||||
try:
|
try: propagation_transfer_limit = float(data[2])
|
||||||
propagation_transfer_limit = float(data[2])
|
except: propagation_transfer_limit = None
|
||||||
except:
|
|
||||||
propagation_transfer_limit = None
|
|
||||||
|
|
||||||
if destination_hash in self.lxmrouter.static_peers:
|
if destination_hash in self.lxmrouter.static_peers:
|
||||||
self.lxmrouter.peer(destination_hash, node_timebase, propagation_transfer_limit, wanted_inbound_peers)
|
self.lxmrouter.peer(destination_hash, node_timebase, propagation_transfer_limit, wanted_inbound_peers)
|
||||||
|
|
|
@ -827,7 +827,7 @@ class LXMRouter:
|
||||||
closed_links = []
|
closed_links = []
|
||||||
for link_hash in self.direct_links:
|
for link_hash in self.direct_links:
|
||||||
link = self.direct_links[link_hash]
|
link = self.direct_links[link_hash]
|
||||||
inactive_time = link.inactive_for()
|
inactive_time = link.no_data_for()
|
||||||
|
|
||||||
if inactive_time > LXMRouter.LINK_MAX_INACTIVITY:
|
if inactive_time > LXMRouter.LINK_MAX_INACTIVITY:
|
||||||
link.teardown()
|
link.teardown()
|
||||||
|
@ -1618,7 +1618,7 @@ class LXMRouter:
|
||||||
### Message Routing & Delivery ########################
|
### Message Routing & Delivery ########################
|
||||||
#######################################################
|
#######################################################
|
||||||
|
|
||||||
def lxmf_delivery(self, lxmf_data, destination_type = None, phy_stats = None, ratchet_id = None, method = None, no_stamp_enforcement=False):
|
def lxmf_delivery(self, lxmf_data, destination_type = None, phy_stats = None, ratchet_id = None, method = None, no_stamp_enforcement=False, allow_duplicate=False):
|
||||||
try:
|
try:
|
||||||
message = LXMessage.unpack_from_bytes(lxmf_data)
|
message = LXMessage.unpack_from_bytes(lxmf_data)
|
||||||
if ratchet_id and not message.ratchet_id:
|
if ratchet_id and not message.ratchet_id:
|
||||||
|
@ -1685,7 +1685,7 @@ class LXMRouter:
|
||||||
RNS.log(str(self)+" ignored message from "+RNS.prettyhexrep(message.source_hash), RNS.LOG_DEBUG)
|
RNS.log(str(self)+" ignored message from "+RNS.prettyhexrep(message.source_hash), RNS.LOG_DEBUG)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.has_message(message.hash):
|
if not allow_duplicate and self.has_message(message.hash):
|
||||||
RNS.log(str(self)+" ignored already received message from "+RNS.prettyhexrep(message.source_hash), RNS.LOG_DEBUG)
|
RNS.log(str(self)+" ignored already received message from "+RNS.prettyhexrep(message.source_hash), RNS.LOG_DEBUG)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
@ -2107,7 +2107,7 @@ class LXMRouter:
|
||||||
if peer != from_peer:
|
if peer != from_peer:
|
||||||
peer.queue_unhandled_message(transient_id)
|
peer.queue_unhandled_message(transient_id)
|
||||||
|
|
||||||
def lxmf_propagation(self, lxmf_data, signal_local_delivery=None, signal_duplicate=None, is_paper_message=False, from_peer=None):
|
def lxmf_propagation(self, lxmf_data, signal_local_delivery=None, signal_duplicate=None, allow_duplicate=False, is_paper_message=False, from_peer=None):
|
||||||
no_stamp_enforcement = False
|
no_stamp_enforcement = False
|
||||||
if is_paper_message:
|
if is_paper_message:
|
||||||
no_stamp_enforcement = True
|
no_stamp_enforcement = True
|
||||||
|
@ -2116,7 +2116,7 @@ class LXMRouter:
|
||||||
if len(lxmf_data) >= LXMessage.LXMF_OVERHEAD:
|
if len(lxmf_data) >= LXMessage.LXMF_OVERHEAD:
|
||||||
transient_id = RNS.Identity.full_hash(lxmf_data)
|
transient_id = RNS.Identity.full_hash(lxmf_data)
|
||||||
|
|
||||||
if not transient_id in self.propagation_entries and not transient_id in self.locally_processed_transient_ids:
|
if (not transient_id in self.propagation_entries and not transient_id in self.locally_processed_transient_ids) or allow_duplicate == True:
|
||||||
received = time.time()
|
received = time.time()
|
||||||
destination_hash = lxmf_data[:LXMessage.DESTINATION_LENGTH]
|
destination_hash = lxmf_data[:LXMessage.DESTINATION_LENGTH]
|
||||||
|
|
||||||
|
@ -2128,7 +2128,7 @@ class LXMRouter:
|
||||||
decrypted_lxmf_data = delivery_destination.decrypt(encrypted_lxmf_data)
|
decrypted_lxmf_data = delivery_destination.decrypt(encrypted_lxmf_data)
|
||||||
if decrypted_lxmf_data != None:
|
if decrypted_lxmf_data != None:
|
||||||
delivery_data = lxmf_data[:LXMessage.DESTINATION_LENGTH]+decrypted_lxmf_data
|
delivery_data = lxmf_data[:LXMessage.DESTINATION_LENGTH]+decrypted_lxmf_data
|
||||||
self.lxmf_delivery(delivery_data, delivery_destination.type, ratchet_id=delivery_destination.latest_ratchet_id, method=LXMessage.PROPAGATED, no_stamp_enforcement=no_stamp_enforcement)
|
self.lxmf_delivery(delivery_data, delivery_destination.type, ratchet_id=delivery_destination.latest_ratchet_id, method=LXMessage.PROPAGATED, no_stamp_enforcement=no_stamp_enforcement, allow_duplicate=allow_duplicate)
|
||||||
self.locally_delivered_transient_ids[transient_id] = time.time()
|
self.locally_delivered_transient_ids[transient_id] = time.time()
|
||||||
|
|
||||||
if signal_local_delivery != None:
|
if signal_local_delivery != None:
|
||||||
|
@ -2166,7 +2166,7 @@ class LXMRouter:
|
||||||
RNS.trace_exception(e)
|
RNS.trace_exception(e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def ingest_lxm_uri(self, uri, signal_local_delivery=None, signal_duplicate=None):
|
def ingest_lxm_uri(self, uri, signal_local_delivery=None, signal_duplicate=None, allow_duplicate=False):
|
||||||
try:
|
try:
|
||||||
if not uri.lower().startswith(LXMessage.URI_SCHEMA+"://"):
|
if not uri.lower().startswith(LXMessage.URI_SCHEMA+"://"):
|
||||||
RNS.log("Cannot ingest LXM, invalid URI provided.", RNS.LOG_ERROR)
|
RNS.log("Cannot ingest LXM, invalid URI provided.", RNS.LOG_ERROR)
|
||||||
|
@ -2176,7 +2176,7 @@ class LXMRouter:
|
||||||
lxmf_data = base64.urlsafe_b64decode(uri.replace(LXMessage.URI_SCHEMA+"://", "").replace("/", "")+"==")
|
lxmf_data = base64.urlsafe_b64decode(uri.replace(LXMessage.URI_SCHEMA+"://", "").replace("/", "")+"==")
|
||||||
transient_id = RNS.Identity.full_hash(lxmf_data)
|
transient_id = RNS.Identity.full_hash(lxmf_data)
|
||||||
|
|
||||||
router_propagation_result = self.lxmf_propagation(lxmf_data, signal_local_delivery=signal_local_delivery, signal_duplicate=signal_duplicate, is_paper_message=True)
|
router_propagation_result = self.lxmf_propagation(lxmf_data, signal_local_delivery=signal_local_delivery, signal_duplicate=signal_duplicate, allow_duplicate=allow_duplicate, is_paper_message=True)
|
||||||
if router_propagation_result != False:
|
if router_propagation_result != False:
|
||||||
RNS.log("LXM with transient ID "+RNS.prettyhexrep(transient_id)+" was ingested.", RNS.LOG_DEBUG)
|
RNS.log("LXM with transient ID "+RNS.prettyhexrep(transient_id)+" was ingested.", RNS.LOG_DEBUG)
|
||||||
return router_propagation_result
|
return router_propagation_result
|
||||||
|
@ -2301,8 +2301,7 @@ class LXMRouter:
|
||||||
else:
|
else:
|
||||||
RNS.log("Outbound processing for "+str(lxmessage)+" to "+RNS.prettyhexrep(lxmessage.get_destination().hash), RNS.LOG_DEBUG)
|
RNS.log("Outbound processing for "+str(lxmessage)+" to "+RNS.prettyhexrep(lxmessage.get_destination().hash), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
if lxmessage.progress == None or lxmessage.progress < 0.01:
|
if lxmessage.progress == None or lxmessage.progress < 0.01: lxmessage.progress = 0.01
|
||||||
lxmessage.progress = 0.01
|
|
||||||
|
|
||||||
# Outbound handling for opportunistic messages
|
# Outbound handling for opportunistic messages
|
||||||
if lxmessage.method == LXMessage.OPPORTUNISTIC:
|
if lxmessage.method == LXMessage.OPPORTUNISTIC:
|
||||||
|
|
|
@ -529,14 +529,14 @@ def get_status(configdir = None, rnsconfigdir = None, verbosity = 0, quietness =
|
||||||
peered_outgoing += pm["outgoing"]
|
peered_outgoing += pm["outgoing"]
|
||||||
peered_rx_bytes += p["rx_bytes"]
|
peered_rx_bytes += p["rx_bytes"]
|
||||||
peered_tx_bytes += p["tx_bytes"]
|
peered_tx_bytes += p["tx_bytes"]
|
||||||
if p["alive"]:
|
|
||||||
available_peers += 1
|
if p["alive"]: available_peers += 1
|
||||||
else:
|
else: unreachable_peers += 1
|
||||||
unreachable_peers += 1
|
|
||||||
|
|
||||||
total_incoming = peered_incoming+s["unpeered_propagation_incoming"]+s["clients"]["client_propagation_messages_received"]
|
total_incoming = peered_incoming+s["unpeered_propagation_incoming"]+s["clients"]["client_propagation_messages_received"]
|
||||||
total_rx_bytes = peered_rx_bytes+s["unpeered_propagation_rx_bytes"]
|
total_rx_bytes = peered_rx_bytes+s["unpeered_propagation_rx_bytes"]
|
||||||
df = round(peered_outgoing/total_incoming, 2)
|
if total_incoming != 0: df = round(peered_outgoing/total_incoming, 2)
|
||||||
|
else: df = 0
|
||||||
|
|
||||||
dhs = RNS.prettyhexrep(s["destination_hash"]); uts = RNS.prettytime(s["uptime"])
|
dhs = RNS.prettyhexrep(s["destination_hash"]); uts = RNS.prettytime(s["uptime"])
|
||||||
print(f"\nLXMF Propagation Node running on {dhs}, uptime is {uts}")
|
print(f"\nLXMF Propagation Node running on {dhs}, uptime is {uts}")
|
||||||
|
@ -710,6 +710,25 @@ propagation_transfer_max_accepted_size = 256
|
||||||
|
|
||||||
# prioritise_destinations = 41d20c727598a3fbbdf9106133a3a0ed, d924b81822ca24e68e2effea99bcb8cf
|
# prioritise_destinations = 41d20c727598a3fbbdf9106133a3a0ed, d924b81822ca24e68e2effea99bcb8cf
|
||||||
|
|
||||||
|
# You can configure the maximum number of other
|
||||||
|
# propagation nodes that this node will peer
|
||||||
|
# with automatically. The default is 50.
|
||||||
|
|
||||||
|
# max_peers = 25
|
||||||
|
|
||||||
|
# You can configure a list of static propagation
|
||||||
|
# node peers, that this node will always be
|
||||||
|
# peered with, by specifying a list of
|
||||||
|
# destination hashes.
|
||||||
|
|
||||||
|
# static_peers = e17f833c4ddf8890dd3a79a6fea8161d, 5a2d0029b6e5ec87020abaea0d746da4
|
||||||
|
|
||||||
|
# You can configure the propagation node to
|
||||||
|
# only accept incoming propagation messages
|
||||||
|
# from configured static peers.
|
||||||
|
|
||||||
|
# from_static_only = True
|
||||||
|
|
||||||
# By default, any destination is allowed to
|
# By default, any destination is allowed to
|
||||||
# connect and download messages, but you can
|
# connect and download messages, but you can
|
||||||
# optionally restrict this. If you enable
|
# optionally restrict this. If you enable
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "0.6.2"
|
__version__ = "0.7.1"
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
qrcode==7.4.2
|
qrcode>=7.4.2
|
||||||
rns==0.7.8
|
rns>=0.9.1
|
||||||
setuptools==70.0.0
|
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -15,9 +15,10 @@ setuptools.setup(
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
url="https://github.com/markqvist/lxmf",
|
url="https://github.com/markqvist/lxmf",
|
||||||
packages=["LXMF", "LXMF.Utilities"],
|
packages=["LXMF", "LXMF.Utilities"],
|
||||||
|
license="Reticulum License",
|
||||||
|
license_files = ("LICENSE"),
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"License :: OSI Approved :: MIT License",
|
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
],
|
],
|
||||||
entry_points= {
|
entry_points= {
|
||||||
|
@ -25,6 +26,6 @@ setuptools.setup(
|
||||||
'lxmd=LXMF.Utilities.lxmd:main',
|
'lxmd=LXMF.Utilities.lxmd:main',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
install_requires=['rns>=0.9.1'],
|
install_requires=["rns>=0.9.5"],
|
||||||
python_requires='>=3.7',
|
python_requires=">=3.7",
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue