mirror of
https://github.com/markqvist/NomadNet.git
synced 2026-10-25 18:40:13 -04:00
Improved string sanitization, added dynamic timeout calculations, improved state persistence, fixed .allowed not being respected for downloadable files, by Zenith
This commit is contained in:
parent
3eb410afdd
commit
54491d8f2b
14 changed files with 139 additions and 99 deletions
|
|
@ -873,7 +873,7 @@ class ConversationMessage:
|
|||
name = str(name) if name is not None else ""
|
||||
except Exception:
|
||||
name = ""
|
||||
name = re.sub(r"[\x00-\x1f\x7f]", "", name)
|
||||
name = re.sub(r"[\x00-\x1f\x7f-\x9f]", "", name)
|
||||
parts = re.split(r"[/\\]", name)
|
||||
name = parts[-1] if parts else ""
|
||||
if ":" in name:
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import RNS.vendor.umsgpack as msgpack
|
|||
|
||||
class Node:
|
||||
JOB_INTERVAL = 5
|
||||
SETTINGS_FLUSH_INTERVAL = 60
|
||||
START_ANNOUNCE_DELAY = 6
|
||||
|
||||
def __init__(self, app):
|
||||
|
|
@ -24,6 +25,8 @@ class Node:
|
|||
self.file_refresh_interval = self.app.file_refresh_interval
|
||||
self.job_interval = Node.JOB_INTERVAL
|
||||
self.should_run_jobs = True
|
||||
self.settings_dirty = False
|
||||
self.last_settings_flush = time.time()
|
||||
self.app_data = None
|
||||
self.name = self.app.node_name
|
||||
|
||||
|
|
@ -100,6 +103,7 @@ class Node:
|
|||
directories = [file for file in os.listdir(base_path) if os.path.isdir(os.path.join(base_path, file)) and file[:1] != "."]
|
||||
|
||||
for file in files:
|
||||
if not file.endswith(".allowed"):
|
||||
self.servedfiles.append(base_path+"/"+file)
|
||||
|
||||
for directory in directories:
|
||||
|
|
@ -109,51 +113,13 @@ class Node:
|
|||
RNS.log("Page request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_VERBOSE)
|
||||
try:
|
||||
self.app.peer_settings["served_page_requests"] += 1
|
||||
self.app.save_peer_settings()
|
||||
self.settings_dirty = True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could not increase served page request count", RNS.LOG_ERROR)
|
||||
|
||||
file_path = path.replace("/page", self.app.pagespath, 1)
|
||||
|
||||
allowed_path = file_path+".allowed"
|
||||
request_allowed = False
|
||||
|
||||
if os.path.isfile(allowed_path):
|
||||
allowed_list = []
|
||||
|
||||
try:
|
||||
if os.access(allowed_path, os.X_OK):
|
||||
allowed_result = subprocess.run([allowed_path], stdout=subprocess.PIPE)
|
||||
allowed_input = allowed_result.stdout
|
||||
|
||||
else:
|
||||
with open(allowed_path, "rb") as fh:
|
||||
allowed_input = fh.read()
|
||||
|
||||
allowed_hash_strs = allowed_input.splitlines()
|
||||
|
||||
for hash_str in allowed_hash_strs:
|
||||
if len(hash_str) == RNS.Identity.TRUNCATED_HASHLENGTH//8*2:
|
||||
try:
|
||||
allowed_hash = bytes.fromhex(hash_str.decode("utf-8"))
|
||||
allowed_list.append(allowed_hash)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could not decode RNS Identity hash from: "+str(hash_str), RNS.LOG_DEBUG)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while fetching list of allowed identities for request: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
if hasattr(remote_identity, "hash") and remote_identity.hash in allowed_list:
|
||||
request_allowed = True
|
||||
else:
|
||||
request_allowed = False
|
||||
RNS.log("Denying request, remote identity was not in list of allowed identities", RNS.LOG_VERBOSE)
|
||||
|
||||
else:
|
||||
request_allowed = True
|
||||
request_allowed = self.request_allowed(file_path, remote_identity)
|
||||
|
||||
try:
|
||||
if request_allowed:
|
||||
|
|
@ -187,18 +153,52 @@ class Node:
|
|||
RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
return None
|
||||
|
||||
def request_allowed(self, file_path, remote_identity):
|
||||
allowed_path = file_path+".allowed"
|
||||
if not os.path.isfile(allowed_path):
|
||||
return True
|
||||
|
||||
allowed_list = []
|
||||
try:
|
||||
if os.access(allowed_path, os.X_OK):
|
||||
allowed_input = subprocess.run([allowed_path], stdout=subprocess.PIPE).stdout
|
||||
else:
|
||||
with open(allowed_path, "rb") as fh:
|
||||
allowed_input = fh.read()
|
||||
|
||||
for hash_str in allowed_input.splitlines():
|
||||
if len(hash_str) == RNS.Identity.TRUNCATED_HASHLENGTH//8*2:
|
||||
try:
|
||||
allowed_list.append(bytes.fromhex(hash_str.decode("utf-8")))
|
||||
except Exception as e:
|
||||
RNS.log("Could not decode RNS Identity hash from: "+str(hash_str), RNS.LOG_DEBUG)
|
||||
RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while fetching list of allowed identities for request: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
if hasattr(remote_identity, "hash") and remote_identity.hash in allowed_list:
|
||||
return True
|
||||
|
||||
RNS.log("Denying request, remote identity was not in list of allowed identities", RNS.LOG_VERBOSE)
|
||||
return False
|
||||
|
||||
# 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)
|
||||
try:
|
||||
self.app.peer_settings["served_file_requests"] += 1
|
||||
self.app.save_peer_settings()
|
||||
self.settings_dirty = True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could not increase served file request count", RNS.LOG_ERROR)
|
||||
|
||||
file_path = path.replace("/file", self.app.filespath, 1)
|
||||
file_name = path.replace("/file/", "", 1)
|
||||
if not self.request_allowed(file_path, remote_identity):
|
||||
RNS.log("Request denied", RNS.LOG_VERBOSE)
|
||||
return DEFAULT_NOTALLOWED.encode("utf-8")
|
||||
|
||||
try:
|
||||
RNS.log("Serving file: "+file_path, RNS.LOG_VERBOSE)
|
||||
return [open(file_path, "rb"), {"name": file_name.encode("utf-8")}]
|
||||
|
|
@ -236,13 +236,19 @@ class Node:
|
|||
self.register_files()
|
||||
self.last_file_refresh = time.time()
|
||||
|
||||
if self.settings_dirty and now > self.last_settings_flush + Node.SETTINGS_FLUSH_INTERVAL:
|
||||
self.settings_dirty = False
|
||||
self.last_settings_flush = now
|
||||
try: self.app.save_peer_settings()
|
||||
except Exception as e: RNS.log("Could not save peer settings: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
time.sleep(self.job_interval)
|
||||
|
||||
def peer_connected(self, link):
|
||||
RNS.log("Peer connected to "+str(self.destination), RNS.LOG_VERBOSE)
|
||||
try:
|
||||
self.app.peer_settings["node_connects"] += 1
|
||||
self.app.save_peer_settings()
|
||||
self.settings_dirty = True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Could not increase node connection count", RNS.LOG_ERROR)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import threading
|
|||
import traceback
|
||||
import subprocess
|
||||
import contextlib
|
||||
import tempfile
|
||||
|
||||
import RNS
|
||||
import LXMF
|
||||
|
|
@ -129,6 +130,7 @@ class NomadNetworkApp:
|
|||
self.firstrun = False
|
||||
self.should_run_jobs = True
|
||||
self.job_interval = 5
|
||||
self.peer_settings_lock = threading.Lock()
|
||||
self.defer_jobs = 90
|
||||
self.page_refresh_interval = 0
|
||||
self.file_refresh_interval = 0
|
||||
|
|
@ -249,11 +251,13 @@ class NomadNetworkApp:
|
|||
RNS.log("The contained exception was: %s" % (str(e)), RNS.LOG_ERROR)
|
||||
nomadnet.panic()
|
||||
|
||||
if os.path.isfile(self.peersettingspath):
|
||||
self.peer_settings = None
|
||||
self.peer_settings_notice = None
|
||||
for path in (self.peersettingspath, self.peersettingspath+".bak"):
|
||||
if self.peer_settings != None or not os.path.isfile(path): continue
|
||||
try:
|
||||
file = open(self.peersettingspath, "rb")
|
||||
with open(path, "rb") as file:
|
||||
self.peer_settings = msgpack.unpackb(file.read())
|
||||
file.close()
|
||||
|
||||
if not "node_last_announce" in self.peer_settings:
|
||||
self.peer_settings["node_last_announce"] = None
|
||||
|
|
@ -274,17 +278,21 @@ class NomadNetworkApp:
|
|||
self.peer_settings["served_file_requests"] = 0
|
||||
|
||||
self.peer_settings["announce_interval"] = self.announce_interval
|
||||
if path != self.peersettingspath:
|
||||
self.peer_settings_notice = "Peer settings were restored from the last backup.\nCheck your display name and propagation node."
|
||||
|
||||
except Exception as e:
|
||||
RNS.logdest = RNS.LOG_STDOUT
|
||||
RNS.log(f"Could not load local peer settings from {self.peersettingspath}", RNS.LOG_ERROR)
|
||||
RNS.log(f"Could not load local peer settings from {path}", RNS.LOG_ERROR)
|
||||
RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR)
|
||||
RNS.log(f"This likely means that the peer settings file has become corrupt.", RNS.LOG_ERROR)
|
||||
RNS.log(f"You can try deleting the file at {self.peersettingspath} and restarting nomadnet.", RNS.LOG_ERROR)
|
||||
nomadnet.panic()
|
||||
else:
|
||||
RNS.log(f"Moving the corrupt file to {path}.corrupt", RNS.LOG_ERROR)
|
||||
try: os.replace(path, path+".corrupt")
|
||||
except Exception: pass
|
||||
self.peer_settings = None
|
||||
self.peer_settings_notice = "Peer settings were corrupt and have been reset.\nCheck your display name and propagation node."
|
||||
|
||||
if self.peer_settings == None:
|
||||
try:
|
||||
RNS.log("No peer settings file found, creating new...")
|
||||
RNS.log("Creating new peer settings file...")
|
||||
self.peer_settings = {
|
||||
"display_name": "Anonymous Peer",
|
||||
"announce_interval": self.announce_interval,
|
||||
|
|
@ -650,9 +658,16 @@ class NomadNetworkApp:
|
|||
return self.message_router.get_outbound_propagation_node()
|
||||
|
||||
def save_peer_settings(self):
|
||||
tmp_path = f"{self.peersettingspath}.tmp"
|
||||
with open(tmp_path, "wb") as file: file.write(msgpack.packb(self.peer_settings))
|
||||
with self.peer_settings_lock:
|
||||
fd, tmp_path = tempfile.mkstemp(dir=os.path.dirname(self.peersettingspath), prefix=".peersettings.")
|
||||
try:
|
||||
with os.fdopen(fd, "wb") as file: file.write(msgpack.packb(self.peer_settings))
|
||||
if os.path.isfile(self.peersettingspath): os.replace(self.peersettingspath, self.peersettingspath+".bak")
|
||||
os.replace(tmp_path, self.peersettingspath)
|
||||
except Exception:
|
||||
try: os.unlink(tmp_path)
|
||||
except Exception: pass
|
||||
raise
|
||||
|
||||
def lxmf_delivery(self, message):
|
||||
time_string = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(message.timestamp))
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from collections import deque
|
|||
import RNS
|
||||
|
||||
from nomadnet.vendor import cbor
|
||||
from nomadnet.util import strip_modifiers
|
||||
|
||||
|
||||
HISTORY_DIR_NAME = "rrc_history"
|
||||
|
|
@ -192,7 +193,7 @@ class RRCMessage:
|
|||
self.room = room
|
||||
self.src = src
|
||||
self.nick = nick
|
||||
self.text = text
|
||||
self.text = strip_modifiers(text)
|
||||
self.ts = ts
|
||||
self.mention = False
|
||||
|
||||
|
|
@ -355,10 +356,11 @@ class RRCHub:
|
|||
|
||||
def _connect_worker(self):
|
||||
try:
|
||||
timeout_s = 20.0
|
||||
path_timeout = self.manager.app.rns.get_medium_path_timeout()
|
||||
timeout_s = max(20.0, path_timeout)
|
||||
if not RNS.Transport.has_path(self.hub_hash):
|
||||
RNS.Transport.request_path(self.hub_hash)
|
||||
deadline = time.monotonic() + min(5.0, timeout_s)
|
||||
deadline = time.monotonic() + max(5.0, path_timeout)
|
||||
while time.monotonic() < deadline:
|
||||
if RNS.Transport.has_path(self.hub_hash):
|
||||
break
|
||||
|
|
@ -429,7 +431,7 @@ class RRCHub:
|
|||
except Exception as e:
|
||||
self._log("HELLO send failed: "+str(e), RNS.LOG_ERROR)
|
||||
attempts += 1
|
||||
self._stop_hello.wait(timeout=3.0)
|
||||
self._stop_hello.wait(timeout=max(3.0, (cur_link.rtt or 0)*cur_link.traffic_timeout_factor))
|
||||
if not self.welcomed and not self._stop_hello.is_set():
|
||||
self._set_status(RRCHub.STATUS_FAILED, "WELCOME timeout")
|
||||
try:
|
||||
|
|
@ -847,7 +849,7 @@ class RRCHub:
|
|||
parsed = _parse_room_list_notice(text)
|
||||
if parsed is not None:
|
||||
with self._lock:
|
||||
self.available_rooms = parsed
|
||||
self.available_rooms = {strip_modifiers(name): strip_modifiers(topic) for name, topic in parsed.items()}
|
||||
silent = self._silent_list_pending > 0
|
||||
if silent:
|
||||
self._silent_list_pending -= 1
|
||||
|
|
@ -925,7 +927,7 @@ class RRCHub:
|
|||
if isinstance(body, dict):
|
||||
hub_name = body.get(B_WELCOME_HUB)
|
||||
if isinstance(hub_name, str):
|
||||
self.hub_name = hub_name
|
||||
self.hub_name = strip_modifiers(hub_name)
|
||||
ver = body.get(B_WELCOME_VER)
|
||||
if isinstance(ver, str):
|
||||
self.hub_version = ver
|
||||
|
|
@ -1135,7 +1137,7 @@ class RRCHub:
|
|||
room_n = room.strip().lower() if isinstance(room, str) else None
|
||||
if room_n is None and isinstance(body, str) and body.strip():
|
||||
with self._lock:
|
||||
self.motd = body
|
||||
self.motd = strip_modifiers(body)
|
||||
self.manager._notify_change(self)
|
||||
msg = RRCMessage(
|
||||
"notice",
|
||||
|
|
@ -1273,7 +1275,7 @@ class RRCHub:
|
|||
return
|
||||
if kind == RES_KIND_MOTD:
|
||||
with self._lock:
|
||||
self.motd = text
|
||||
self.motd = strip_modifiers(text)
|
||||
self.manager._notify_change(self)
|
||||
elif self._process_notice_text(text):
|
||||
return
|
||||
|
|
|
|||
|
|
@ -279,6 +279,7 @@ class TextUI:
|
|||
self.set_colormode(colormode)
|
||||
|
||||
self.main_display.start()
|
||||
if intro_timeout <= 0 and self.app.peer_settings_notice: self.main_display.show_notice(self.app.peer_settings_notice)
|
||||
self.loop.run()
|
||||
|
||||
def set_colormode(self, colormode):
|
||||
|
|
@ -297,3 +298,4 @@ class TextUI:
|
|||
|
||||
def display_main(self, loop, user_data):
|
||||
self.loop.widget = self.main_display.widget
|
||||
if self.app.peer_settings_notice: self.main_display.show_notice(self.app.peer_settings_notice)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import threading
|
|||
from threading import Lock
|
||||
from .MicronParser import markup_to_attrmaps, make_style, default_state
|
||||
from nomadnet.Directory import DirectoryEntry
|
||||
from nomadnet.Conversation import ConversationMessage
|
||||
from nomadnet.vendor.Scrollable import *
|
||||
from nomadnet.util import strip_modifiers
|
||||
from nomadnet.util import sanitize_name
|
||||
|
|
@ -825,10 +826,9 @@ class Browser:
|
|||
partial["pr_throttle"] = time.time()+15
|
||||
RNS.log(f"Requesting path for partial: {partial_destination_hash} / {path}", RNS.LOG_EXTREME)
|
||||
RNS.Transport.request_path(partial_destination_hash)
|
||||
pr_time = time.time()+RNS.Transport.first_hop_timeout(partial_destination_hash)
|
||||
pr_time = time.time()+self.path_timeout(partial_destination_hash)
|
||||
while not RNS.Transport.has_path(partial_destination_hash):
|
||||
now = time.time()
|
||||
if now > pr_time+self.timeout: return
|
||||
if time.time() > pr_time: return
|
||||
time.sleep(0.25)
|
||||
|
||||
for pid in self.page_partials:
|
||||
|
|
@ -853,7 +853,7 @@ class Browser:
|
|||
partial["link"] = None
|
||||
|
||||
partial["link"] = RNS.Link(destination, established_callback = established, closed_callback = closed)
|
||||
timeout = time.time()+self.timeout
|
||||
timeout = time.time()+max(self.timeout, partial["link"].establishment_timeout)
|
||||
while partial["link"].status != RNS.Link.ACTIVE and time.time() < timeout: time.sleep(0.1)
|
||||
|
||||
if partial["link"] and partial["link"].status == RNS.Link.ACTIVE and partial["request_id"] == None:
|
||||
|
|
@ -1065,6 +1065,9 @@ class Browser:
|
|||
def set_timeout(self, timeout):
|
||||
self.timeout = timeout
|
||||
|
||||
def path_timeout(self, destination_hash):
|
||||
return max(self.timeout + self.app.rns.get_first_hop_timeout(destination_hash), self.app.rns.get_medium_path_timeout())
|
||||
|
||||
def download_local_file(self, path):
|
||||
try:
|
||||
file_path = self.app.filespath+path.replace("/file", "", 1)
|
||||
|
|
@ -1110,10 +1113,9 @@ class Browser:
|
|||
self.status = Browser.PATH_REQUESTED
|
||||
self.update_display()
|
||||
|
||||
pr_time = time.time()+RNS.Transport.first_hop_timeout(self.destination_hash)
|
||||
pr_time = time.time()+self.path_timeout(self.destination_hash)
|
||||
while not RNS.Transport.has_path(self.destination_hash):
|
||||
now = time.time()
|
||||
if now > pr_time+self.timeout:
|
||||
if time.time() > pr_time:
|
||||
self.request_timeout()
|
||||
return
|
||||
|
||||
|
|
@ -1493,10 +1495,9 @@ class Browser:
|
|||
self.status = Browser.PATH_REQUESTED
|
||||
self.update_display()
|
||||
|
||||
pr_time = time.time()+RNS.Transport.first_hop_timeout(self.destination_hash)
|
||||
pr_time = time.time()+self.path_timeout(self.destination_hash)
|
||||
while not RNS.Transport.has_path(self.destination_hash):
|
||||
now = time.time()
|
||||
if now > pr_time+self.timeout:
|
||||
if time.time() > pr_time:
|
||||
self.request_timeout()
|
||||
return
|
||||
|
||||
|
|
@ -1738,9 +1739,13 @@ class Browser:
|
|||
|
||||
def file_received(self, request_receipt):
|
||||
try:
|
||||
if type(request_receipt.response) == io.BufferedReader:
|
||||
if isinstance(request_receipt.response, bytes):
|
||||
markup = request_receipt.response.decode("utf-8")
|
||||
self.attr_maps = markup_to_attrmaps(strip_modifiers(markup), url_delegate=self, fg_color=self.page_foreground_color, bg_color=self.page_background_color)
|
||||
|
||||
elif type(request_receipt.response) == io.BufferedReader:
|
||||
if request_receipt.metadata != None:
|
||||
file_name = os.path.basename(request_receipt.metadata["name"].decode("utf-8"))
|
||||
file_name = ConversationMessage.safe_attachment_name(request_receipt.metadata["name"])
|
||||
file_handle = request_receipt.response
|
||||
file_destination = self.app.downloads_path+"/"+file_name
|
||||
|
||||
|
|
@ -1759,7 +1764,7 @@ class Browser:
|
|||
else:
|
||||
file_name = request_receipt.response[0]
|
||||
file_data = request_receipt.response[1]
|
||||
file_destination_name = os.path.basename(file_name)
|
||||
file_destination_name = ConversationMessage.safe_attachment_name(file_name)
|
||||
file_destination = self.app.downloads_path+"/"+file_destination_name
|
||||
|
||||
counter = 0
|
||||
|
|
|
|||
|
|
@ -2809,7 +2809,7 @@ class LXMessageWidget(urwid.WidgetWrap):
|
|||
title_string = g["warning"]+" "+g["arrow_l"]+" "+message.get_signature_description() + "\n " + title_string
|
||||
|
||||
if message.get_title() != "":
|
||||
title_string += " | " + message.get_title()
|
||||
title_string += " | " + strip_modifiers(message.get_title())
|
||||
|
||||
inbound_untrusted = False
|
||||
if not is_outbound and msg_source_hash is not None:
|
||||
|
|
|
|||
|
|
@ -953,18 +953,18 @@ class PageEditorDisplay():
|
|||
common, restricted, total = self._folder_permissions(path)
|
||||
content = common or ""
|
||||
if not total:
|
||||
state = "This folder contains no pages."
|
||||
state = "This folder contains no files."
|
||||
elif not restricted:
|
||||
state = "All %d pages here are public." % total
|
||||
state = "All %d files here are public." % total
|
||||
elif common is not None:
|
||||
state = "All %d pages here share these entries." % total
|
||||
state = "All %d files here share these entries." % total
|
||||
else:
|
||||
state = "Entries differ across pages (%d of %d restricted)." % (restricted, total)
|
||||
state = "Entries differ across files (%d of %d restricted)." % (restricted, total)
|
||||
note = ("Allowed identity hashes per line.\n"
|
||||
+ state + "\nSaving applies these entries to every page in this folder and its subfolders.")
|
||||
+ state + "\nSaving applies these entries to every file in this folder and its subfolders.")
|
||||
else:
|
||||
content = self._read_allowed(path)
|
||||
note = "Allowed identity hashes per line. Empty = public.\nApplies to this page."
|
||||
note = "Allowed identity hashes per line. Empty = public.\nApplies to this page or file."
|
||||
|
||||
title = "Permissions: " + os.path.basename(path) + ("/" if is_dir else "")
|
||||
self.show_dialog(PermissionsDialog(title, note, content,
|
||||
|
|
|
|||
|
|
@ -498,9 +498,9 @@ By default, you can find the examples in `!~/.nomadnetwork/examples`!. If you bu
|
|||
|
||||
>>Authenticating Users
|
||||
|
||||
Sometimes, you don't want everyone to be able to view certain pages or execute certain scripts. In such cases, you can use `*authentication`* to control who gets to run certain requests.
|
||||
Sometimes, you don't want everyone to be able to view certain pages, download certain files or execute certain scripts. In such cases, you can use `*authentication`* to control who gets to run certain requests.
|
||||
|
||||
To enable authentication for any page, simply add a new file to your pages directory with ".allowed" added to the file-name of the page. If your page is named "secret_page.mu", just add a file named "secret_page.mu.allowed".
|
||||
To enable authentication for any page or file, simply add a new file next to it with ".allowed" added to its file-name. If your page is named "secret_page.mu", just add a file named "secret_page.mu.allowed". The same works for files in your files directory, and the ".allowed" file itself is never served.
|
||||
|
||||
For each user allowed to access the page, add a line to this file, containing the hash of that users primary identity. Users can find their own identity hash in the `![ Network ]`! part of the program, under `!Local Peer Info`!. If you want to allow access for three different users, your file would look like this:
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import itertools
|
|||
import mmap
|
||||
import urwid
|
||||
import nomadnet
|
||||
from nomadnet.util import strip_modifiers
|
||||
|
||||
|
||||
class LogDisplayShortcuts():
|
||||
|
|
@ -68,7 +69,7 @@ class LogTerminal(urwid.WidgetWrap):
|
|||
class LogTail(urwid.WidgetWrap):
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.log_tail = urwid.Text(tail(self.app.logfilepath, 50))
|
||||
self.log_tail = urwid.Text(strip_modifiers(tail(self.app.logfilepath, 50)))
|
||||
self.log = urwid.Scrollable(self.log_tail)
|
||||
self.log.set_scrollpos(-1)
|
||||
self.log_scrollbar = urwid.ScrollBar(self.log)
|
||||
|
|
|
|||
|
|
@ -249,6 +249,12 @@ class MainDisplay():
|
|||
valign="middle", height=8, min_width=20,
|
||||
)
|
||||
|
||||
def show_notice(self, text):
|
||||
def close(button=None): self.app.ui.loop.widget = self.frame
|
||||
pile = urwid.Pile([urwid.Text(text, align="center"), urwid.Divider(), urwid.Button("OK", on_press=close)])
|
||||
dialog = QuitDialogLineBox(urwid.Filler(pile, urwid.TOP), title="Notice", on_cancel=close)
|
||||
self.app.ui.loop.widget = urwid.Overlay(dialog, self.frame, align="center", width=64, valign="middle", height=9, min_width=20)
|
||||
|
||||
def do_quit(self, sender=None):
|
||||
logterm_pid = None
|
||||
if True or RNS.vendor.platformutils.is_android():
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from urwid.util import is_mouse_press
|
|||
from urwid.text_layout import calc_coords
|
||||
from .ReadlineEdit import ReadlineEdit
|
||||
from RNS.Utilities.rngit.util import MarkdownToMicron
|
||||
from nomadnet.util import STRIP_CONTROL_RE
|
||||
|
||||
DEFAULT_FG_DARK = "ddd"
|
||||
DEFAULT_FG_LIGHT = "222"
|
||||
|
|
@ -101,6 +102,7 @@ def ensure_selected_styles():
|
|||
else: SELECTED_STYLES = STYLES_LIGHT
|
||||
|
||||
def markup_to_attrmaps(markup, url_delegate = None, fg_color=None, bg_color=None, link_class=None, anchors=None):
|
||||
markup = STRIP_CONTROL_RE.sub("", markup)
|
||||
global LINK_CLASS
|
||||
if link_class: LINK_CLASS = link_class
|
||||
else: LINK_CLASS = LinkableText
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ class ProbeView:
|
|||
|
||||
def _timeout(self):
|
||||
try:
|
||||
return DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(self._dest_hash)
|
||||
return max(DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(self._dest_hash), self.app.rns.get_medium_path_timeout())
|
||||
except Exception:
|
||||
return DEFAULT_PROBE_TIMEOUT
|
||||
|
||||
|
|
@ -639,7 +639,7 @@ class PathView:
|
|||
|
||||
def _timeout(self):
|
||||
try:
|
||||
return DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(self._dest_hash)
|
||||
return max(DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(self._dest_hash), self.app.rns.get_medium_path_timeout())
|
||||
except Exception:
|
||||
return DEFAULT_PROBE_TIMEOUT
|
||||
|
||||
|
|
@ -830,7 +830,7 @@ class IdentityView:
|
|||
self.lookup_button.original_widget.set_label("Looking up…")
|
||||
self._log("Identity unknown, requesting from the network …")
|
||||
try:
|
||||
self._deadline = time.time() + DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(dest_hash)
|
||||
self._deadline = time.time() + max(DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(dest_hash), self.app.rns.get_medium_path_timeout())
|
||||
except Exception:
|
||||
self._deadline = time.time() + DEFAULT_PROBE_TIMEOUT
|
||||
self.app.ui.loop.set_alarm_in(0.25, self._await_identity)
|
||||
|
|
@ -975,7 +975,7 @@ class SendFileView:
|
|||
|
||||
def _timeout(self):
|
||||
try:
|
||||
return DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(self._dest_hash)
|
||||
return max(DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(self._dest_hash), self.app.rns.get_medium_path_timeout())
|
||||
except Exception:
|
||||
return DEFAULT_PROBE_TIMEOUT
|
||||
|
||||
|
|
@ -1040,7 +1040,7 @@ class SendFileView:
|
|||
self._log("Could not establish link: %s" % str(e), "error_text")
|
||||
self._finish()
|
||||
return
|
||||
self._link_deadline = time.time() + self._timeout()
|
||||
self._link_deadline = time.time() + max(self._timeout(), self._link.establishment_timeout)
|
||||
self.app.ui.loop.set_alarm_in(0.5, self._check_link)
|
||||
|
||||
def _check_link(self, loop, user_data):
|
||||
|
|
@ -1402,7 +1402,7 @@ class SpeedTestView:
|
|||
return
|
||||
self._incoming_link = link
|
||||
link.set_remote_identified_callback(self._on_remote_identified)
|
||||
self.app.ui.loop.set_alarm_in(8, lambda l, u: self._incoming_timeout(link))
|
||||
self.app.ui.loop.set_alarm_in(max(8, self.app.rns.get_medium_path_timeout()), lambda l, u: self._incoming_timeout(link))
|
||||
|
||||
def _incoming_timeout(self, link):
|
||||
if self._recv_link is link:
|
||||
|
|
@ -1565,7 +1565,7 @@ class SpeedTestView:
|
|||
|
||||
def _timeout(self):
|
||||
try:
|
||||
return DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(self._dest_hash)
|
||||
return max(DEFAULT_PROBE_TIMEOUT + self.app.rns.get_first_hop_timeout(self._dest_hash), self.app.rns.get_medium_path_timeout())
|
||||
except Exception:
|
||||
return DEFAULT_PROBE_TIMEOUT
|
||||
|
||||
|
|
@ -1594,7 +1594,7 @@ class SpeedTestView:
|
|||
self._log("Could not establish link: %s" % str(e), "error_text")
|
||||
self._finish()
|
||||
return
|
||||
self._link_deadline = time.time() + self._timeout()
|
||||
self._link_deadline = time.time() + max(self._timeout(), self._client_link.establishment_timeout)
|
||||
self.app.ui.loop.set_alarm_in(0.5, self._check_link)
|
||||
|
||||
def _check_link(self, loop, user_data):
|
||||
|
|
|
|||
|
|
@ -121,8 +121,9 @@ def strip_modifiers(text):
|
|||
stripped = re.sub(r'[\U0001F3FB-\U0001F3FF]', '', stripped, flags=re.UNICODE)
|
||||
stripped = re.sub(r'[\u200D\u200C]', '', stripped)
|
||||
stripped = re.sub(r'\r\n?', '\n', stripped)
|
||||
stripped = STRIP_CONTROL_RE.sub('', stripped)
|
||||
|
||||
return stripped.strip().replace("\x00", "")
|
||||
return stripped.strip()
|
||||
|
||||
def sanitize_name(name):
|
||||
if name is None: return None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue