from core.Constants import Constants from core.controllers.SessionStateController import SessionStateController from core.models.session.Application import Application from core.models.session.ApplicationVersion import ApplicationVersion from core.models.session.SessionProfile import SessionProfile from core.models.session.SessionState import SessionState from core.observers.ProfileObserver import ProfileObserver from core.services.WebServiceApiService import WebServiceApiService from pathlib import Path from typing import Optional import os import re import shutil import stat import subprocess class ApplicationController: @staticmethod def get(code: str): return Application.find(code) @staticmethod def launch(version: ApplicationVersion, profile: SessionProfile, port_number: int = None, profile_observer: Optional[ProfileObserver] = None): persistent_state_path = f'{profile.get_data_path()}/persistent-state' if not os.path.isdir(persistent_state_path) or len(os.listdir(persistent_state_path)) == 0: shutil.copytree(f'{version.installation_path}/resources/initial-state', persistent_state_path) display = ApplicationController._find_unused_display() base_initialization_file_template = open(f'/{Constants.SP_DATA_HOME}/.init.ptpl', 'r').read() base_initialization_file_contents = base_initialization_file_template.format(display=display, time_zone=profile.location.time_zone, sp_data_home=Constants.SP_DATA_HOME) application_initialization_file_template = open(f'/{version.installation_path}/.init.ptpl', 'r').read() application_initialization_file_contents = application_initialization_file_template.format(application_version_home=version.installation_path, port_number=port_number or -1, home=Constants.HOME, profile_data_path=profile.get_data_path(), config_home=Constants.CONFIG_HOME, application_version_number=version.version_number) session_state = SessionStateController.get_or_new(profile.id) SessionStateController.update_or_create(session_state) initialization_file_contents = base_initialization_file_contents + application_initialization_file_contents initialization_file_path = f'{session_state.get_state_path()}/.init' Path(initialization_file_path).touch(exist_ok=True, mode=0o600 | stat.S_IEXEC) open(initialization_file_path, 'w').write(initialization_file_contents) fork_process_id = os.fork() if not fork_process_id: process = subprocess.Popen(('xinit', initialization_file_path, '--', '/usr/bin/Xephyr', '-ac', '-title', f'Simplified Privacy - {profile.name or "Unnamed Profile"}', '-screen', profile.resolution, '-no-host-grab', display), stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) session_state = SessionState(session_state.id, session_state.network_port_numbers, [process.pid]) SessionStateController.update_or_create(session_state) os.waitpid(process.pid, 0) from core.controllers.ProfileController import ProfileController ProfileController.disable(profile, False, profile_observer=profile_observer) @staticmethod def fetch(proxies: Optional[dict] = None): applications = WebServiceApiService.get_applications(proxies) supported_applications = [] for application in applications: if application.code in ('firefox', 'brave', 'chromium'): supported_applications.append(application) Application.truncate() Application.save_many(applications) @staticmethod def _find_unused_display(): file_names = os.listdir('/tmp/.X11-unix') active_displays = [] for file_name in file_names: match_object = re.search(r'X(\d+)', file_name) if match_object: detected_display = int(match_object.group(1)) if 170 <= detected_display <= 198: active_displays.append(detected_display) if len(active_displays) > 0: unused_display = sorted(active_displays)[-1] + 1 return ':' + str(unused_display) else: return ':170'