From 90f2a8424312358cc7adba001d186f166593169d Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 11 Aug 2020 20:15:23 +0200 Subject: [PATCH] Work on bundles --- RNS/Bundle.py | 112 ++++++++++++++++++++++++++++++++++++++++++++++- RNS/Identity.py | 7 +-- RNS/Reticulum.py | 10 +++-- 3 files changed, 121 insertions(+), 8 deletions(-) diff --git a/RNS/Bundle.py b/RNS/Bundle.py index bf56383..76e51ff 100644 --- a/RNS/Bundle.py +++ b/RNS/Bundle.py @@ -1,7 +1,117 @@ import RNS +import time +import os +import os.path +from .vendor import umsgpack as umsgpack class Bundle: - pass + NO_CUSTODY = 0x00; + TAKING_CUSTODY = 0x01; + FULL_CUSTODY = 0x02; + + CHUNK_SIZE = RNS.Resource.MAX_EFFICIENT_SIZE / 4 + + def __init__(self, destination = None, data = None, filepath = None, advertised_id = None): + self.destination = destination + self.state = None + self.data_file = None + self.meta_file = None + self.id = None + self.storagepath = None + self.size = None + self.chunks = 0 + self.heartbeat = time.time() + self.resources = [] + + try: + if data != None or filepath != None: + + if filepath == None and data != None: + try: + self.id = RNS.Identity.fullHash(data) + self.storagepath = Reticulum.bundlepath+"/"+self.id.hex() + self.datapath = self.storagepath+"/data" + self.metadatapath = self.storagepath+"/metadata" + + if not os.path.isdir(self.storagepath): + os.makedirs(self.storagepath) + else: + RNS.log("Warning, bundle already exists in storage location, recreating", RNS.LOG_DEBUG) + + self.data_file = open(self.datapath, "wb") + self.data_file.write(data) + self.data_file.close() + except Exception as e: + RNS.log("Error while initialising bundle from data, the contained exception was:", RNS.LOG_ERROR) + RNS.log(str(e)) + + self.state = Bundle.FULL_CUSTODY + + elif data == None and filepath != None: + try: + input_file = open(filepath, "rb") + self.id = RNS.Identity.fullHash(input_file.read()) + input_file.seek(0) + + self.storagepath = RNS.Reticulum.bundlepath+"/"+self.id.hex() + self.datapath = self.storagepath+"/data" + self.metadatapath = self.storagepath+"/metadata" + + if not os.path.isdir(self.storagepath): + os.makedirs(self.storagepath) + else: + RNS.log("Warning, bundle already exists in storage location, recreating", RNS.LOG_DEBUG) + + self.data_file = open(self.datapath, "wb") + self.data_file.write(input_file.read()) + self.data_file.close() + input_file.close() + + except Exception as e: + RNS.log("Error while reading input file for bundle, the contained exception was:", RNS.LOG_ERROR) + RNS.log(str(e)) + + self.state = Bundle.FULL_CUSTODY + + else: + raise ValueError("Bundle cannot be created from data and file path at the same time") + + elif advertised_id != None: + # Incoming bundle transfer + self.state = Bundle.TAKING_CUSTODY + else: + raise ValueError("No source of data specified for bundle initialisation") + + # Prepare file handles and metadata + self.size = os.stat(self.datapath).st_size + if self.size < 1: + raise IOError("Bundle data is empty") + + self.chunks = ((self.size-1)//Bundle.CHUNK_SIZE)+1 + self.data_file = open(self.datapath, "rb") + self.flush_metadata() + + except Exception as e: + RNS.log("Error while initialising bundle. The contained exception was:", RNS.LOG_ERROR) + RNS.log(str(e), RNS.LOG_ERROR) + raise e + + def flush_metadata(self): + try: + metadata = { + "destination": self.destination, + "heartbeat": self.heartbeat, + "size": self.size, + "chunks": self.chunks, + "state": self.state} + + self.meta_file = open(self.metadatapath, "wb") + self.meta_file.write(umsgpack.packb(metadata)) + self.meta_file.close() + + except Exception as e: + RNS.log("Error while flushing metadata for bundle "+RNS.prettyhexrep(self.id), RNS.LOG_ERROR) + RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) class BundleAdvertisement: pass \ No newline at end of file diff --git a/RNS/Identity.py b/RNS/Identity.py index a7e0058..34b0ee9 100644 --- a/RNS/Identity.py +++ b/RNS/Identity.py @@ -79,10 +79,11 @@ class Identity: @staticmethod def truncatedHash(data): - digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) - digest.update(data) + # TODO: Remove + # digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) + # digest.update(data) - return digest.finalize()[:(Identity.TRUNCATED_HASHLENGTH//8)] + return Identity.fullHash(data)[:(Identity.TRUNCATED_HASHLENGTH//8)] @staticmethod def getRandomHash(): diff --git a/RNS/Reticulum.py b/RNS/Reticulum.py index 4a84456..e64dfa9 100755 --- a/RNS/Reticulum.py +++ b/RNS/Reticulum.py @@ -9,8 +9,6 @@ import os.path import os import RNS -#import traceback - class Reticulum: MTU = 500 HEADER_MAXSIZE = 23 @@ -33,9 +31,10 @@ class Reticulum: if configdir != None: Reticulum.configdir = configdir - Reticulum.configpath = Reticulum.configdir+"/config" + Reticulum.configpath = Reticulum.configdir+"/config" Reticulum.storagepath = Reticulum.configdir+"/storage" - Reticulum.cachepath = Reticulum.configdir+"/storage/cache" + Reticulum.cachepath = Reticulum.configdir+"/storage/cache" + Reticulum.bundlepath = Reticulum.configdir+"/storage/bundles" Reticulum.__allow_unencrypted = False Reticulum.__transport_enabled = False @@ -54,6 +53,9 @@ class Reticulum: if not os.path.isdir(Reticulum.cachepath): os.makedirs(Reticulum.cachepath) + if not os.path.isdir(Reticulum.bundlepath): + os.makedirs(Reticulum.bundlepath) + if os.path.isfile(self.configpath): try: self.config = ConfigObj(self.configpath)