validate and deduplicate relay config input in qt gui
Adds validation and deduplication of the relay urls entered in the QT settings dialog. This is supposed to prevent malformed or duplicated relay entries. Also resets the relays to the default value if no (valid) url is entered. This prevents the user from getting stuck without relays (otherwise the user would have to research for relay urls manually if they don't know any).
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
# SOFTWARE.
|
||||
|
||||
import ast
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Dict
|
||||
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtWidgets import (QComboBox, QTabWidget, QDialog, QSpinBox, QCheckBox, QLabel,
|
||||
@@ -32,7 +32,7 @@ from PyQt6.QtWidgets import (QComboBox, QTabWidget, QDialog, QSpinBox, QCheckB
|
||||
|
||||
from electrum.i18n import _, languages
|
||||
from electrum import util
|
||||
from electrum.util import base_units_list, event_listener
|
||||
from electrum.util import base_units_list, event_listener, is_valid_websocket_url
|
||||
|
||||
from electrum.gui import messages
|
||||
|
||||
@@ -181,7 +181,16 @@ class SettingsDialog(QDialog, QtEventListener):
|
||||
self.nostr_relays_e = QLineEdit(nostr_relays)
|
||||
|
||||
def on_nostr_edit():
|
||||
self.config.NOSTR_RELAYS = str(self.nostr_relays_e.text())
|
||||
relays: Dict[str, None] = dict() # dicts keep insertion order
|
||||
for url in self.nostr_relays_e.text().split(','):
|
||||
url = url.strip()
|
||||
if url and is_valid_websocket_url(url):
|
||||
relays[url] = None
|
||||
if relays.keys():
|
||||
self.config.NOSTR_RELAYS = ",".join(relays.keys())
|
||||
else: # if no valid relays are given, assign default relays from config
|
||||
self.config.NOSTR_RELAYS = None
|
||||
self.nostr_relays_e.setText(self.config.NOSTR_RELAYS)
|
||||
self.nostr_relays_e.editingFinished.connect(on_nostr_edit)
|
||||
|
||||
msat_cb = checkbox_from_configvar(self.config.cv.BTC_AMOUNTS_PREC_POST_SAT)
|
||||
|
||||
@@ -31,14 +31,13 @@ from typing import (NamedTuple, Union, TYPE_CHECKING, Tuple, Optional, Callable,
|
||||
from datetime import datetime, timezone, timedelta
|
||||
import decimal
|
||||
from decimal import Decimal
|
||||
import urllib
|
||||
from urllib.parse import urlparse
|
||||
import threading
|
||||
import hmac
|
||||
import hashlib
|
||||
import stat
|
||||
import locale
|
||||
import asyncio
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import builtins
|
||||
import json
|
||||
import time
|
||||
@@ -699,6 +698,27 @@ def is_valid_email(s):
|
||||
regexp = r"[^@]+@[^@]+\.[^@]+"
|
||||
return re.match(regexp, s) is not None
|
||||
|
||||
def is_valid_websocket_url(url: str) -> bool:
|
||||
"""
|
||||
uses this django url validation regex:
|
||||
https://github.com/django/django/blob/2c6906a0c4673a7685817156576724aba13ad893/django/core/validators.py#L45C1-L52C43
|
||||
Note: this is not perfect, urls and their parsing can get very complex (see recent django code).
|
||||
however its sufficient for catching weird user input in the gui dialog
|
||||
"""
|
||||
# stores the compiled regex in the function object itself to avoid recompiling it every call
|
||||
if not hasattr(is_valid_websocket_url, "regex"):
|
||||
is_valid_websocket_url.regex = re.compile(
|
||||
r'^(?:ws|wss)://' # ws:// or wss://
|
||||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...
|
||||
r'localhost|' # localhost...
|
||||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4
|
||||
r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6
|
||||
r'(?::\d+)?' # optional port
|
||||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||
try:
|
||||
return re.match(is_valid_websocket_url.regex, url) is not None
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def is_hash256_str(text: Any) -> bool:
|
||||
if not isinstance(text, str): return False
|
||||
|
||||
Reference in New Issue
Block a user