Compare commits

...

10 commits

Author SHA1 Message Date
Mark Qvist
fab12ad9bf
Updated build scripts 2026-06-01 14:26:47 +02:00
Mark Qvist
a29c4a0e17
Updated version 2026-06-01 00:03:39 +02:00
Mark Qvist
20864133a3
Fixed typo 2026-05-31 22:10:48 +02:00
Mark Qvist
c877efaec1
Updated version 2026-05-29 09:58:06 +02:00
Mark Qvist
11b2480223
Updated versions 2026-05-29 09:57:51 +02:00
Jeremy O'Brien
5be161cb1e
Prevent message write race among different processes touching the same message 2026-05-28 08:44:22 -04:00
Mark Qvist
bf924c739c Cleanup 2026-05-27 12:28:58 +02:00
Mark Qvist
044f3d2879 Updated versions 2026-05-26 23:32:02 +02:00
Mark Qvist
312e0a8ded Updated version 2026-05-26 13:18:33 +02:00
Mark Qvist
575fb7d77d Prevent LXM persist race in write_to_directory when messages change state within very short time spans 2026-05-26 13:17:46 +02:00
6 changed files with 35 additions and 42 deletions

View file

@ -101,20 +101,6 @@ RENDERER_MICRON = 0x01
RENDERER_MARKDOWN = 0x02
RENDERER_BBCODE = 0x03
############################################################
# To be finalized in 1.0.0. A workdoc with open interaction
# through rngit is available for comments and nuancing on:
#
# a8d24177d946de4f1f0a0fe1af9a1338:/page/work.mu`g=reticulum|r=lxmf
#
# Clients that have implemented different reply, reaction
# or comment mechanisms can choose to transitionally parse
# their own specific formats, but are recommended to attempt
# parsing the structure and format defined herein first,
# and fall back to their client-specific structure second.
# Reaction dict indicies are integers to preserve bandwidth.
#
# Clients choose how to handle reaction content, if at all.
# While reactions are typically a single unicode emoji or
# similar, the exact implementation and sanitization is
@ -123,8 +109,6 @@ RENDERER_BBCODE = 0x03
REACTION_TO = 0x00 # Bytes, full LXMessage.hash
REACTION_CONTENT = 0x01 # Bytes, the reaction content in UTF-8 encoding
# Comment dict indicies are integers to preserve bandwidth.
#
# Clients choose how to handle messages intended as comments
# for other message, if at all. The actual comment content
# is carried as the normal LXM content, meaning clients that
@ -133,8 +117,6 @@ REACTION_CONTENT = 0x01 # Bytes, the reaction content in UTF-8 encoding
# with the following keys:
COMMENT_FOR = 0x00 # Bytes, full LXMessage.hash
# Continuation dict indicies are integers to preserve bandwidth.
#
# Clients choose how to handle messages that continue earlier
# messages, if at all. The actual continuation content is
# carried as the normal LXM content, meaning clients that
@ -142,7 +124,6 @@ COMMENT_FOR = 0x00 # Bytes, full LXMessage.hash
# When using the FIELD_CONTINUATION field, the contents is a
# dict with the following keys:
CONTINUATION_OF = 0x00 # Bytes, full LXMessage.hash
############################################################
# Optional propagation node metadata fields. These
# fields may be highly unstable in allocation and
@ -188,8 +169,7 @@ def display_name_from_app_data(app_data=None):
return None
# Original announce format
else:
return app_data.decode("utf-8")
else: return app_data.decode("utf-8")
def stamp_cost_from_app_data(app_data=None):
if app_data == None or app_data == b"": return None
@ -238,8 +218,8 @@ def pn_stamp_cost_from_app_data(app_data=None):
if pn_announce_data_is_valid(app_data):
data = msgpack.unpackb(app_data)
return data[5][0]
else:
return None
else: return None
def pn_announce_data_is_valid(data):
try:

View file

@ -404,7 +404,7 @@ class LXMPeer:
if response == LXMPeer.ERROR_NO_IDENTITY:
if self.link != None:
RNS.log("Remote peer indicated that no identification was received, retrying...", RNS.LOG_VERBOSE)
self.link.identify()
self.link.identify(self.router.identity)
self.state = LXMPeer.LINK_READY
self.sync()
return

View file

@ -8,6 +8,7 @@ import multiprocessing
import LXMF.LXStamper as LXStamper
from .LXMF import APP_NAME, compression_support_from_app_data
from threading import Lock
class LXMessage:
@ -184,6 +185,7 @@ class LXMessage:
self.__delivery_destination = None
self.__delivery_callback = None
self.__pn_encrypted_data = None
self.__persist_lock = Lock()
self.failed_callback = None
self.deferred_stamp_generating = False
@ -672,25 +674,26 @@ class LXMessage:
def write_to_directory(self, directory_path):
file_name = RNS.hexrep(self.hash, delimit=False)
file_path = directory_path+"/"+file_name
tmp_path = file_path+".tmp."+str(os.getpid() or time.time())
tmp_path = file_path+".tmp."+str(os.getpid() or time.time())+"."+RNS.hexrep(os.urandom(8), delimit=False)
try:
with open(tmp_path, "wb") as file:
file.write(self.packed_container())
file.flush()
try: os.fsync(file.fileno())
except OSError as e: RNS.log(f"Error while waiting for persist fsync for {self}: {e}", RNS.LOG_WARNING)
os.replace(tmp_path, file_path)
return file_path
except Exception as e:
with self.__persist_lock:
try:
if os.path.exists(tmp_path): os.unlink(tmp_path)
except Exception as e: RNS.log(f"Error while cleaning temporary file {tmp_path} for {self}: {e}", RNS.LOG_ERROR)
with open(tmp_path, "wb") as file:
file.write(self.packed_container())
file.flush()
try: os.fsync(file.fileno())
except OSError as e: RNS.log(f"Error while waiting for persist fsync for {self}: {e}", RNS.LOG_WARNING)
RNS.log(f"Error while writing LXMF message to file \"{file_path}\". The contained exception was: {e}", RNS.LOG_ERROR)
return None
os.replace(tmp_path, file_path)
return file_path
except Exception as e:
try:
if os.path.exists(tmp_path): os.unlink(tmp_path)
except Exception as e: RNS.log(f"Error while cleaning temporary file {tmp_path} for {self}: {e}", RNS.LOG_ERROR)
RNS.log(f"Error while writing LXMF message to file \"{file_path}\". The contained exception was: {e}", RNS.LOG_ERROR)
return None
def as_uri(self, finalise=True):
if not self.packed:

View file

@ -1 +1 @@
__version__ = "0.9.9"
__version__ = "1.0.1"

View file

@ -26,5 +26,10 @@ build_spkg: remove_symlinks build_sdist create_symlinks
release: remove_symlinks build_wheel build_spkg create_symlinks
upload:
@echo Ready to publish release over Reticulum
@read VOID
rngit release rns://7649a50d84610232d1416b41d2896aff/reticulum/lxmf create $$(python setup.py --getversion):dist --name lxmf
upload-pip:
@echo Uploading to PyPi...
twine upload dist/*.whl dist/*.tar.gz

View file

@ -1,3 +1,4 @@
import sys
import setuptools
with open("README.md", "r") as fh:
@ -5,6 +6,10 @@ with open("README.md", "r") as fh:
exec(open("LXMF/_version.py", "r").read())
if "--getversion" in sys.argv:
print(__version__, end="")
exit(0)
setuptools.setup(
name="lxmf",
version=__version__,
@ -26,6 +31,6 @@ setuptools.setup(
'lxmd=LXMF.Utilities.lxmd:main',
]
},
install_requires=["rns>=1.3.0"],
install_requires=["rns>=1.3.5"],
python_requires=">=3.7",
)