From e606892ff09b2fe41b9ca9561d4f47b000cf8ba8 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 10 Sep 2021 21:33:29 +0200 Subject: [PATCH] Added file hosting to nodes. --- nomadnet/Node.py | 29 ++++++++++++-- nomadnet/NomadNetworkApp.py | 7 ++++ nomadnet/ui/textui/Browser.py | 75 ++++++++++++++++++++++++++++++++--- 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/nomadnet/Node.py b/nomadnet/Node.py index 155d4d7..d4de6a9 100644 --- a/nomadnet/Node.py +++ b/nomadnet/Node.py @@ -44,7 +44,6 @@ class Node: for page in self.servedpages: request_path = "/page"+page.replace(self.app.pagespath, "") - self.destination.register_request_handler( request_path, response_generator = self.serve_page, @@ -55,6 +54,14 @@ class Node: self.servedfiles = [] self.scan_files(self.app.filespath) + for file in self.servedfiles: + request_path = "/file"+file.replace(self.app.filespath, "") + self.destination.register_request_handler( + request_path, + response_generator = self.serve_file, + allow = RNS.Destination.ALLOW_ALL + ) + def scan_pages(self, base_path): files = [file for file in os.listdir(base_path) if os.path.isfile(os.path.join(base_path, file)) and file[:1] != "."] directories = [file for file in os.listdir(base_path) if os.path.isdir(os.path.join(base_path, file)) and file[:1] != "."] @@ -76,10 +83,10 @@ class Node: self.scan_files(base_path+"/"+directory) def serve_page(self, path, data, request_id, remote_identity, requested_at): - RNS.log("Request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_VERBOSE) + RNS.log("Page request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_VERBOSE) file_path = path.replace("/page", self.app.pagespath, 1) try: - RNS.log("Serving file: "+file_path, RNS.LOG_VERBOSE) + RNS.log("Serving page: "+file_path, RNS.LOG_VERBOSE) fh = open(file_path, "rb") response_data = fh.read() fh.close() @@ -90,6 +97,22 @@ class Node: RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) return None + # TODO: Improve file handling, this will be slow for large files + def serve_file(self, path, data, request_id, remote_identity, requested_at): + RNS.log("File request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_VERBOSE) + file_path = path.replace("/file", self.app.filespath, 1) + file_name = path.replace("/file/", "", 1) + try: + RNS.log("Serving file: "+file_path, RNS.LOG_VERBOSE) + fh = open(file_path, "rb") + file_data = fh.read() + fh.close() + return [file_name, file_data] + + except Exception as e: + RNS.log("Error occurred while handling request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_ERROR) + RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) + return None def serve_default_index(self, path, data, request_id, remote_identity, requested_at): RNS.log("Serving default index for request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_VERBOSE) diff --git a/nomadnet/NomadNetworkApp.py b/nomadnet/NomadNetworkApp.py index 4b9dace..7be5ccf 100644 --- a/nomadnet/NomadNetworkApp.py +++ b/nomadnet/NomadNetworkApp.py @@ -54,6 +54,8 @@ class NomadNetworkApp: self.pagespath = self.configdir+"/storage/pages" self.filespath = self.configdir+"/storage/files" + self.downloads_path = os.path.expanduser("~/Downloads") + if not os.path.isdir(self.storagepath): os.makedirs(self.storagepath) @@ -254,6 +256,10 @@ class NomadNetworkApp: value = self.config["client"].as_bool(option) self.enable_client = value + if option == "downloads_path": + value = self.config["client"]["downloads_path"] + self.downloads_path = os.path.expanduser(value) + if option == "user_interface": value = value.lower() if value == "none": @@ -391,6 +397,7 @@ destination = file enable_client = yes user_interface = text +downloads_path = ~/Downloads [textui] diff --git a/nomadnet/ui/textui/Browser.py b/nomadnet/ui/textui/Browser.py index 6e2befe..fbab06e 100644 --- a/nomadnet/ui/textui/Browser.py +++ b/nomadnet/ui/textui/Browser.py @@ -1,4 +1,5 @@ import RNS +import os import time import urwid import nomadnet @@ -54,6 +55,7 @@ class Browser: self.response_progress = 0 self.response_size = None self.response_transfer_size = None + self.saved_file_name = None self.page_data = None self.displayed_page_data = None self.auth_identity = auth_identity @@ -248,9 +250,16 @@ class Browser: raise ValueError("Malformed URL") if destination_hash != None and path != None: - self.set_destination_hash(destination_hash) - self.set_path(path) - self.load_page() + if path.startswith("/file/"): + if destination_hash == self.destination_hash: + self.download_file(destination_hash, path) + else: + RNS.log("Cannot request file download from a node that is not currently connected.", RNS.LOG_ERROR) + RNS.log("The requested URL was: "+str(url), RNS.LOG_ERROR) + else: + self.set_destination_hash(destination_hash) + self.set_path(path) + self.load_page() def set_destination_hash(self, destination_hash): if len(destination_hash) == RNS.Identity.TRUNCATED_HASHLENGTH//8: @@ -268,6 +277,33 @@ class Browser: self.timeout = timeout + def download_file(self, destination_hash, path): + if self.link != None and self.link.destination.hash == self.destination_hash: + # Send the request + self.status = Browser.REQUESTING + self.response_progress = 0 + self.response_size = None + self.response_transfer_size = None + self.saved_file_name = None + + self.update_display() + receipt = self.link.request( + path, + data = None, + response_callback = self.file_received, + failed_callback = self.request_failed, + progress_callback = self.response_progressed + ) + + if receipt: + self.last_request_receipt = receipt + self.last_request_id = receipt.request_id + self.status = Browser.REQUEST_SENT + self.update_display() + else: + self.link.teardown() + + def load_page(self): load_thread = threading.Thread(target=self.__load) load_thread.setDaemon(True) @@ -278,7 +314,7 @@ class Browser: # If an established link exists, but it doesn't match the target # destination, we close and clear it. if self.link != None and self.link.destination.hash != self.destination_hash: - self.link.close() + self.link.teardown() self.link = None # If no link to the destination exists, we create one. @@ -327,6 +363,8 @@ class Browser: self.response_progress = 0 self.response_size = None self.response_transfer_size = None + self.saved_file_name = None + self.update_display() receipt = self.link.request( @@ -383,6 +421,30 @@ class Browser: except Exception as e: RNS.log("An error occurred while handling response. The contained exception was: "+str(e)) + + def file_received(self, request_receipt): + try: + file_name = request_receipt.response[0] + file_data = request_receipt.response[1] + file_destination = self.app.downloads_path+"/"+file_name + + counter = 0 + while os.path.isfile(file_destination): + counter += 1 + file_destination = self.app.downloads_path+"/"+file_name+"."+str(counter) + + fh = open(file_destination, "wb") + fh.write(file_data) + fh.close() + + self.saved_file_name = file_destination.replace(self.app.downloads_path+"/", "", 1) + self.status = Browser.DONE + self.response_progress = 0 + + self.update_display() + except Exception as e: + RNS.log("An error occurred while handling file response. The contained exception was: "+str(e)) + def request_failed(self, request_receipt=None): if request_receipt != None: @@ -453,7 +515,10 @@ class Browser: elif self.status == Browser.RECEIVING_RESPONSE: return "Receiving response..." elif self.status == Browser.DONE: - return "Done"+stats_string + if self.saved_file_name == None: + return "Done"+stats_string + else: + return "Saved "+str(self.saved_file_name)+stats_string elif self.status == Browser.DISCONECTED: return "Disconnected" else: