diff --git a/essentials/Constants.py b/essentials/Constants.py index 30224e6..4eb9da7 100644 --- a/essentials/Constants.py +++ b/essentials/Constants.py @@ -6,8 +6,10 @@ import os @dataclass(frozen=True) class Constants: - TOR_BOOTSTRAP_TIMEOUT: Final[int] = int(os.environ.get('TOR_BOOTSTRAP_TIMEOUT', '90')) + TOR_BOOTSTRAP_TIMEOUT: Final[int] = int( + os.environ.get("TOR_BOOTSTRAP_TIMEOUT", "90") + ) - TOR_CONTROL_SOCKET_FILENAME: Final[str] = 'tor.sock' - TOR_PROCESS_IDENTIFIER_FILENAME: Final[str] = 'tor.pid' - TOR_INSTANCE_LOCK_FILENAME: Final[str] = 'lock' + TOR_CONTROL_SOCKET_FILENAME: Final[str] = "tor.sock" + TOR_PROCESS_IDENTIFIER_FILENAME: Final[str] = "tor.pid" + TOR_INSTANCE_LOCK_FILENAME: Final[str] = "lock" diff --git a/essentials/Errors.py b/essentials/Errors.py index 5aa8ca6..11ca3d7 100644 --- a/essentials/Errors.py +++ b/essentials/Errors.py @@ -5,5 +5,6 @@ class CommandNotFoundError(OSError): self.subject = subject super().__init__(f"Command '{subject}' could not be found.") + class TorServiceInitializationError(Exception): pass diff --git a/essentials/models/Event.py b/essentials/models/Event.py index 5e254cf..b764646 100644 --- a/essentials/models/Event.py +++ b/essentials/models/Event.py @@ -1,6 +1,6 @@ class Event: - def __init__(self, subject = None, meta = None): + def __init__(self, subject=None, meta=None): self.subject = subject self.meta = meta or {} diff --git a/essentials/modules/TorModule.py b/essentials/modules/TorModule.py index b0b92c2..a7f2e0d 100644 --- a/essentials/modules/TorModule.py +++ b/essentials/modules/TorModule.py @@ -2,6 +2,7 @@ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FutureTimeout from essentials.Constants import Constants from essentials.Errors import TorServiceInitializationError from essentials.observers.ConnectionObserver import ConnectionObserver + from pathlib import Path from typing import Optional import os @@ -20,30 +21,40 @@ class TorModule: self.state_home = state_home self.bootstrap_timeout = Constants.TOR_BOOTSTRAP_TIMEOUT - self.control_socket_path = f'{self.state_home}/{Constants.TOR_CONTROL_SOCKET_FILENAME}' - self.process_identifier_path = f'{self.state_home}/{Constants.TOR_PROCESS_IDENTIFIER_FILENAME}' - self.instance_lock_path = f'{self.state_home}/{Constants.TOR_INSTANCE_LOCK_FILENAME}' + self.control_socket_path = ( + f"{self.state_home}/{Constants.TOR_CONTROL_SOCKET_FILENAME}" + ) + self.process_identifier_path = ( + f"{self.state_home}/{Constants.TOR_PROCESS_IDENTIFIER_FILENAME}" + ) + self.instance_lock_path = ( + f"{self.state_home}/{Constants.TOR_INSTANCE_LOCK_FILENAME}" + ) def start_service(self, connection_observer: Optional[ConnectionObserver] = None): + print("started tor service") + Path(self.state_home).mkdir(mode=0o700, parents=True, exist_ok=True) self.stop_service() - if connection_observer is not None: - connection_observer.notify('tor_bootstrapping') + # if connection_observer is not None: + # connection_observer.notify("tor_bootstrapping") with ThreadPoolExecutor(max_workers=1) as executor: future = executor.submit( stem.process.launch_tor_with_config, config={ - 'DataDirectory': self.state_home, - 'ControlSocket': self.control_socket_path, - 'PIDFile': self.process_identifier_path, - 'SocksPort': '0' + "DataDirectory": self.state_home, + "ControlSocket": self.control_socket_path, + "PIDFile": self.process_identifier_path, + "SocksPort": "0", }, - init_msg_handler=lambda contents: TorModule.__on_initialization_message(contents, connection_observer) + init_msg_handler=lambda contents: TorModule.__on_initialization_message( + contents, connection_observer + ), ) try: @@ -52,23 +63,28 @@ class TorModule: except FutureTimeoutError: self.stop_service() - raise TorServiceInitializationError('The dedicated Tor service could not be initialized.') - - if connection_observer is not None: - connection_observer.notify('tor_bootstrapped') + raise TorServiceInitializationError( + "The dedicated Tor service could not be initialized." + ) + # if connection_observer is not None: + # connection_observer.notify("tor_bootstrapped") + print("tor bootstrapped") try: - controller = stem.control.Controller.from_socket_file(self.control_socket_path) + controller = stem.control.Controller.from_socket_file( + self.control_socket_path + ) controller.authenticate() except (FileNotFoundError, stem.SocketError, TypeError, IndexError): self.stop_service() - raise TorServiceInitializationError('The dedicated Tor service could not be initialized.') + raise TorServiceInitializationError( + "The dedicated Tor service could not be initialized." + ) def stop_service(self): - control_socket_file = Path(self.control_socket_path) process_identifier_file = Path(self.process_identifier_path) instance_lock_file = Path(self.instance_lock_path) @@ -88,6 +104,8 @@ class TorModule: except ProcessLookupError: pass + try: + process = psutil.Process(process_identifier) if process.is_running(): @@ -100,54 +118,87 @@ class TorModule: process_identifier_file.unlink(missing_ok=True) instance_lock_file.unlink(missing_ok=True) - def create_session(self, port_number: int, connection_observer: Optional[ConnectionObserver] = None): + def create_session( + self, port_number: int, connection_observer: Optional[ConnectionObserver] = None + ): + print("created a session") try: - controller = stem.control.Controller.from_socket_file(self.control_socket_path) + controller = stem.control.Controller.from_socket_file( + self.control_socket_path + ) controller.authenticate() except (FileNotFoundError, stem.SocketError, TypeError, IndexError): self.start_service(connection_observer=connection_observer) - controller = stem.control.Controller.from_socket_file(self.control_socket_path) + controller = stem.control.Controller.from_socket_file( + self.control_socket_path + ) controller.authenticate() - socks_port_numbers = [str(port_number) for port_number in controller.get_ports('socks')] + socks_port_numbers = [ + str(port_number) for port_number in controller.get_ports("socks") + ] socks_port_numbers.append(str(port_number)) - controller.set_conf('SocksPort', socks_port_numbers) + controller.set_conf("SocksPort", socks_port_numbers) def destroy_session(self, port_number: int): + print("destroying session") try: - controller = stem.control.Controller.from_socket_file(self.control_socket_path) + controller = stem.control.Controller.from_socket_file( + self.control_socket_path + ) controller.authenticate() - socks_port_numbers = [str(port_number) for port_number in controller.get_ports('socks')] + socks_port_numbers = [ + str(port_number) for port_number in controller.get_ports("socks") + ] if len(socks_port_numbers) > 1: - socks_port_numbers = [socks_port_number for socks_port_number in socks_port_numbers if socks_port_number != port_number] - controller.set_conf('SocksPort', socks_port_numbers) + socks_port_numbers = [ + socks_port_number + for socks_port_number in socks_port_numbers + if socks_port_number != port_number + ] + controller.set_conf("SocksPort", socks_port_numbers) else: - controller.set_conf('SocksPort', '0') + controller.set_conf("SocksPort", "0") except (FileNotFoundError, stem.SocketError, TypeError, IndexError): pass @staticmethod - def __on_initialization_message(contents, connection_observer: Optional[ConnectionObserver] = None): - + def __on_initialization_message( + contents, connection_observer: Optional[ConnectionObserver] = None + ): if connection_observer is not None: - if 'Bootstrapped ' in contents: + if "Bootstrapped " in contents: - progress = (m := re.search(r' (\d{1,3})% ', contents)) and int(m.group(1)) + # original version, + # progress = (m := re.search(r" (\d{1,3})% ", contents)) and int( + # m.group(1) + # ) + + # # Search for the pattern in the contents + match = re.search(r" (\d{1,3})% ", contents) + + # # Check if we found a match + if match: + # If we found it, get the number and convert to integer + progress = int(match.group(1)) + else: + # If we didn't find it, use a default value + progress = 0 + + progress_string = f"Tor Bootstrap {progress}%" + connection_observer.notify("tor_bootstrap_progressing", progress_string) - connection_observer.notify('tor_bootstrap_progressing', None, dict( - progress=progress - )) diff --git a/essentials/observers/BaseObserver.py b/essentials/observers/BaseObserver.py index 04cc137..3396920 100644 --- a/essentials/observers/BaseObserver.py +++ b/essentials/observers/BaseObserver.py @@ -5,16 +5,16 @@ class BaseObserver: def subscribe(self, topic, callback): - callbacks = getattr(self, f'on_{topic}', None) + callbacks = getattr(self, f"on_{topic}", None) if callbacks is None: return callbacks.append(callback) - def notify(self, topic, subject = None, meta = None): + def notify(self, topic, subject=None, meta=None): - callbacks = getattr(self, f'on_{topic}', None) + callbacks = getattr(self, f"on_{topic}", None) if callbacks is None: return diff --git a/essentials/services/ConnectionService.py b/essentials/services/ConnectionService.py index 0bdf0b4..2b2016b 100644 --- a/essentials/services/ConnectionService.py +++ b/essentials/services/ConnectionService.py @@ -7,7 +7,7 @@ class ConnectionService: def get_random_available_port_number(): socket_instance = socket.socket() - socket_instance.bind(('', 0)) + socket_instance.bind(("", 0)) port_number = socket_instance.getsockname()[1] socket_instance.close() diff --git a/pyproject.toml b/pyproject.toml index 1756dbd..bc80b5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sp-essentials" -version = "1.0.0" +version = "1.1.0" authors = [ { name = "Simplified Privacy" }, ]