forked from Support/sp-hydra-veil-gui
1285 lines
56 KiB
Python
Executable file
1285 lines
56 KiB
Python
Executable file
import functools
|
|
import os
|
|
import threading
|
|
|
|
from PyQt6 import QtCore, QtGui
|
|
from PyQt6.QtCore import Qt, QSize
|
|
from PyQt6.QtGui import QIcon, QPainter, QPixmap
|
|
from PyQt6.QtWidgets import (
|
|
QApplication, QButtonGroup, QFrame, QHBoxLayout, QLabel, QLineEdit,
|
|
QMessageBox, QPushButton, QScrollArea, QTextEdit, QVBoxLayout, QWidget,
|
|
)
|
|
|
|
from core.controllers.ConfigurationController import ConfigurationController
|
|
from core.controllers.ProfileController import ProfileController
|
|
from core.controllers.tickets.UseTicketController import (
|
|
do_we_use_a_random_ticket,
|
|
get_unused_tickets,
|
|
)
|
|
from core.models.session.SessionProfile import SessionProfile
|
|
from core.models.system.SystemProfile import SystemProfile
|
|
|
|
from gui.v2.infrastructure.setup_observers import ticket_observer
|
|
from gui.v2.actions.profile_order import normalize_profile_order
|
|
from gui.v2.ui.pages.Page import Page
|
|
from gui.v2.ui.pages.location_verification_page import LocationVerificationPage
|
|
from gui.v2.ui.styles.styles import SCROLLBAR_CYAN_QSS
|
|
from gui.v2.ui.popups.confirmation_popup import ConfirmationPopup
|
|
from gui.v2.ui.popups.endpoint_verification_popup import EndpointVerificationPopup
|
|
from gui.v2.ui.popups.ticket_data_loss_popup import TicketDataLossPopup
|
|
from gui.v2.workers.worker import Worker
|
|
from gui.v2.workers.worker_thread import WorkerThread
|
|
|
|
|
|
class MenuPage(Page):
|
|
def __init__(self, page_stack=None, main_window=None, parent=None):
|
|
super().__init__("Menu", page_stack, main_window, parent)
|
|
self.main_window = main_window
|
|
self.connection_manager = main_window.connection_manager
|
|
self.is_tor_mode = main_window.is_tor_mode
|
|
self.is_animating = main_window.is_animating
|
|
self.animation_step = main_window.animation_step
|
|
self.btn_path = main_window.btn_path
|
|
self.page_stack = page_stack
|
|
self.profile_info = {}
|
|
self.additional_labels = []
|
|
self.update_status = main_window
|
|
self.title.setGeometry(400, 40, 380, 30)
|
|
self.title.setText("Load Profile")
|
|
|
|
self.scroll_area = QScrollArea(self)
|
|
self.scroll_area.setGeometry(420, 80, 370, 350)
|
|
self.scroll_area.setWidgetResizable(True)
|
|
self.scroll_area.setHorizontalScrollBarPolicy(
|
|
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
self.scroll_area.setVerticalScrollBarPolicy(
|
|
Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
|
self.scroll_area.setStyleSheet(SCROLLBAR_CYAN_QSS)
|
|
|
|
self.scroll_widget = QWidget()
|
|
self.scroll_widget.setStyleSheet("background-color: transparent;")
|
|
self.scroll_area.setWidget(self.scroll_widget)
|
|
|
|
self.not_connected_profile = os.path.join(
|
|
self.btn_path, "button_profile.png")
|
|
self.connected_profile = os.path.join(
|
|
self.btn_path, "button_session_profile.png")
|
|
self.system_wide_connected_profile = os.path.join(
|
|
self.btn_path, "button_system_profile.png")
|
|
self.connected_tor_profile = os.path.join(
|
|
self.btn_path, "button_session_tor.png")
|
|
self.worker = None
|
|
self.IsSystem: int = 0
|
|
self.button_states = {}
|
|
self.is_system_connected = False
|
|
self.profile_button_map = {}
|
|
self.font_style = f"font-family: '{main_window.open_sans_family}';" if main_window.open_sans_family else ""
|
|
|
|
self.create_interface_elements()
|
|
|
|
def showEvent(self, event):
|
|
super().showEvent(event)
|
|
if hasattr(self, 'buttons'):
|
|
for button in self.buttons:
|
|
parent = button.parent()
|
|
if parent:
|
|
verification_icons = parent.findChildren(QPushButton)
|
|
for icon in verification_icons:
|
|
if icon.geometry().width() == 20 and icon.geometry().height() == 20:
|
|
icon.setEnabled(
|
|
self.connection_manager.is_synced())
|
|
|
|
def on_update_check_finished(self):
|
|
reply = QMessageBox.question(self, 'Update Available',
|
|
'An update is available. Would you like to download it?',
|
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
|
if reply == QMessageBox.StandardButton.Yes:
|
|
self.worker = WorkerThread('DOWNLOAD_UPDATE')
|
|
self.worker.start()
|
|
else:
|
|
self.update_status.update_status('Update canceled')
|
|
|
|
def create_interface_elements(self):
|
|
for icon_name, function, position in [
|
|
("create_profile", self.create_prof, (410, 440, 110, 60)),
|
|
("edit_profile", self.edit_prof, (540, 440, 110, 60)),
|
|
("launch", self.launch, (0, 440, 185, 63)),
|
|
("disconnect", self.disconnect, (200, 433, 185, 73)),
|
|
("disconnect_system_wide", self.disconnect, (200, 433, 185, 73)),
|
|
("just", self.connect, (200, 433, 185, 73)),
|
|
("just_session", self.connect, (200, 433, 185, 73)),
|
|
("settings", self.settings_gui, (670, 440, 110, 60)),
|
|
]:
|
|
boton = QPushButton(self)
|
|
boton.setGeometry(QtCore.QRect(*position))
|
|
boton.setObjectName("create_edit_settings_buttons")
|
|
icon = QtGui.QIcon(os.path.join(self.btn_path, f"{icon_name}.png"))
|
|
boton.setIcon(icon)
|
|
boton.setIconSize(boton.size())
|
|
boton.clicked.connect(functools.partial(function))
|
|
boton.setEnabled(False)
|
|
if icon_name == "disconnect":
|
|
boton.setEnabled(True)
|
|
boton.setVisible(False)
|
|
self.disconnect_button = boton
|
|
if icon_name == "launch":
|
|
self.boton_launch = boton
|
|
self.boton_launch.setVisible(False)
|
|
if icon_name == "create_profile":
|
|
self.boton_create = boton
|
|
self.boton_create.setEnabled(True)
|
|
if icon_name == "edit_profile":
|
|
self.boton_edit = boton
|
|
if icon_name == "just":
|
|
boton.setVisible(False)
|
|
self.boton_just = boton
|
|
self.connect_button = boton
|
|
if icon_name == "just_session":
|
|
self.boton_just_session = boton
|
|
self.connect_button = boton
|
|
if icon_name == "disconnect_system_wide":
|
|
boton.setEnabled(True)
|
|
boton.setVisible(False)
|
|
self.disconnect_system_wide_button = boton
|
|
if icon_name == "settings":
|
|
self.settings_button = boton
|
|
self.settings_button.setEnabled(True)
|
|
|
|
self.create()
|
|
|
|
def _make_confirmation_popup(self, message, button_text='Proceed'):
|
|
popup = ConfirmationPopup(
|
|
self, message=message, action_button_text=button_text, cancel_button_text="Cancel")
|
|
popup.setWindowModality(Qt.WindowModality.ApplicationModal)
|
|
return popup
|
|
|
|
def _show_disconnect_confirmation(self, connected_profiles, button_text='Disable', message=None):
|
|
self.popup = self._make_confirmation_popup(message, button_text)
|
|
|
|
def on_result(result):
|
|
if result:
|
|
self.update_status.update_status('Disabling profiles...')
|
|
self.worker_thread = WorkerThread(
|
|
'DISABLE_ALL_PROFILES', profile_data=connected_profiles)
|
|
self.worker_thread.text_output.connect(
|
|
lambda text: self.update_status.update_status(text))
|
|
self.worker_thread.start()
|
|
|
|
self.popup.finished.connect(on_result)
|
|
self.popup.show()
|
|
|
|
def delete_profile(self):
|
|
self.popup = ConfirmationPopup(self, message="Are you sure you want to\ndelete this profile?",
|
|
action_button_text="Delete", cancel_button_text="Cancel")
|
|
self.popup.setWindowModality(Qt.WindowModality.ApplicationModal)
|
|
self.popup.finished.connect(
|
|
lambda result: self.on_popup_finished(result))
|
|
self.popup.show()
|
|
|
|
def on_popup_finished(self, result):
|
|
if result:
|
|
profile_id = self.reverse_id
|
|
self.update_status.update_status(f'Deleting profile...')
|
|
self.worker_thread = WorkerThread(
|
|
'DESTROY_PROFILE', profile_data={'id': profile_id})
|
|
self.worker_thread.text_output.connect(self.delete_status_update)
|
|
self.worker_thread.start()
|
|
else:
|
|
pass
|
|
|
|
def delete_status_update(self, text):
|
|
self.update_status.update_status(text)
|
|
if not text.startswith('Could not delete'):
|
|
self.clear_profile_details()
|
|
self.eliminacion()
|
|
|
|
def show_disconnect_button(self, toggle, profile_type, target_button):
|
|
try:
|
|
if target_button.isChecked():
|
|
if profile_type == 1:
|
|
self.disconnect_button.setVisible(toggle)
|
|
self.boton_just_session.setVisible(not toggle)
|
|
else:
|
|
self.disconnect_system_wide_button.setVisible(toggle)
|
|
self.boton_just.setVisible(not toggle)
|
|
except RuntimeError:
|
|
pass
|
|
|
|
def match_core_profiles(self, profiles_dict):
|
|
new_dict = {}
|
|
|
|
profile_ids = normalize_profile_order(
|
|
getattr(self.update_status, 'gui_config_file', None),
|
|
profiles_dict.keys())
|
|
|
|
for idx in profile_ids:
|
|
try:
|
|
profile = profiles_dict[idx]
|
|
new_profile = {}
|
|
|
|
protocol = profile.connection.code
|
|
|
|
print(f"DEBUG: the type is = {type(profile.location)}, value = {profile.location}")
|
|
|
|
if isinstance(profile.location, dict):
|
|
# Fake/missing data — show placeholders from data,
|
|
country_code = profile.location.get("country_code", "na")
|
|
code = profile.location.get("code", "na")
|
|
location = f'{country_code}_{code}'
|
|
else:
|
|
# Proper Location object
|
|
location = f'{profile.location.country_code}_{profile.location.code}'
|
|
except:
|
|
import sys
|
|
print(f"idx: {idx}")
|
|
sys.exit("STOPPING TO READ PRINTS FOR IDX. this failed on getting the location data inside match_core_profiles")
|
|
|
|
new_profile['location'] = location
|
|
|
|
if protocol == 'wireguard':
|
|
new_profile['protocol'] = 'wireguard'
|
|
else:
|
|
new_profile['protocol'] = 'hidetor'
|
|
|
|
connection_type = 'browser-only' if isinstance(
|
|
profile, SessionProfile) else 'system-wide'
|
|
|
|
if protocol == 'wireguard':
|
|
new_profile['connection'] = connection_type
|
|
elif protocol == 'tor':
|
|
new_profile['connection'] = protocol
|
|
else:
|
|
new_profile['connection'] = 'just proxy'
|
|
|
|
if isinstance(profile, SessionProfile):
|
|
browser = profile.application_version.application_code
|
|
else:
|
|
browser = 'unknown'
|
|
if browser != 'unknown':
|
|
new_profile['browser'] = browser
|
|
new_profile['browser_version'] = profile.application_version.version_number
|
|
new_profile['browser_supported'] = profile.application_version.supported
|
|
else:
|
|
new_profile['browser'] = 'unknown browser'
|
|
new_profile['browser_supported'] = False
|
|
|
|
resolution = profile.resolution if hasattr(
|
|
profile, 'resolution') else 'None'
|
|
new_profile['dimentions'] = resolution
|
|
|
|
new_profile['name'] = profile.name
|
|
|
|
|
|
new_dict[f'Profile_{idx}'] = new_profile
|
|
|
|
return new_dict
|
|
|
|
def create(self):
|
|
self.boton_just.setEnabled(False)
|
|
self.boton_just_session.setEnabled(False)
|
|
self.boton_edit.setEnabled(False)
|
|
if hasattr(self, 'verification_button'):
|
|
self.verification_button.setEnabled(False)
|
|
self.profiles_data = self.match_core_profiles(
|
|
ProfileController.get_all())
|
|
|
|
self.number_of_profiles = len(self.profiles_data)
|
|
self.profile_info = dict(self.profiles_data)
|
|
|
|
self.button_group = QButtonGroup(self)
|
|
self.buttons = []
|
|
|
|
for profile_name, profile_value in self.profiles_data.items():
|
|
self.create_profile_widget(profile_name, profile_value)
|
|
|
|
self.update_scroll_widget_size()
|
|
|
|
def refresh_profiles_data(self):
|
|
self.profiles_data = self.match_core_profiles(
|
|
ProfileController.get_all())
|
|
self.number_of_profiles = len(self.profiles_data)
|
|
self.profile_info = dict(self.profiles_data)
|
|
for profile_name, profile_value in self.profiles_data.items():
|
|
self.create_profile_widget(profile_name, profile_value)
|
|
self.update_scroll_widget_size()
|
|
|
|
def refresh_menu_buttons(self):
|
|
profiles = ProfileController.get_all()
|
|
self.button_states.clear()
|
|
self.connection_manager._connected_profiles.clear()
|
|
self.IsSystem = 0
|
|
for profile_id, profile in profiles.items():
|
|
if ProfileController.is_enabled(profile):
|
|
self.connection_manager.add_connected_profile(profile_id)
|
|
if isinstance(profile, SessionProfile):
|
|
if profile.connection and profile.connection.code == 'tor':
|
|
self.button_states[profile_id] = self.connected_tor_profile
|
|
else:
|
|
self.button_states[profile_id] = self.connected_profile
|
|
elif isinstance(profile, SystemProfile):
|
|
self.button_states[profile_id] = self.system_wide_connected_profile
|
|
self.IsSystem += 1
|
|
|
|
if self.IsSystem > 0:
|
|
self.update_status.update_image(
|
|
appearance_value='background_connected')
|
|
else:
|
|
self.update_status.update_image()
|
|
|
|
for widget in self.scroll_widget.findChildren(QLabel):
|
|
widget.setParent(None)
|
|
|
|
for widget in self.scroll_widget.findChildren(QPushButton):
|
|
widget.setParent(None)
|
|
|
|
self.profile_button_map.clear()
|
|
self.button_group = QButtonGroup(self)
|
|
self.buttons = []
|
|
|
|
self.create()
|
|
|
|
def update_scroll_widget_size(self):
|
|
if self.number_of_profiles == 0:
|
|
self.scroll_widget.setFixedSize(370, 0)
|
|
return
|
|
|
|
rows = (self.number_of_profiles + 1) // 2
|
|
height = rows * 120
|
|
self.scroll_widget.setFixedSize(370, height)
|
|
|
|
def clear_profile_details(self):
|
|
for label in self.additional_labels:
|
|
label.deleteLater()
|
|
self.additional_labels.clear()
|
|
self.selected_profiles.clear()
|
|
self.selected_wireguard.clear()
|
|
self.selected_residential.clear()
|
|
self.update_status.current_profile_id = None
|
|
self.current_profile_location = None
|
|
self.reverse_id = None
|
|
self.boton_edit.setEnabled(False)
|
|
self.boton_launch.setEnabled(False)
|
|
self.boton_just.setVisible(False)
|
|
self.boton_just_session.setVisible(False)
|
|
self.disconnect_button.setVisible(False)
|
|
self.disconnect_system_wide_button.setVisible(False)
|
|
if hasattr(self, 'button_group'):
|
|
self.button_group.setExclusive(False)
|
|
for button in getattr(self, 'buttons', []):
|
|
button.setChecked(False)
|
|
self.button_group.setExclusive(True)
|
|
|
|
def create_profile_widget(self, key, value):
|
|
profile_id = int(key.split('_')[1])
|
|
|
|
parent_label = self.create_parent_label(profile_id)
|
|
button = self.create_profile_button(parent_label, profile_id, key)
|
|
|
|
self.profile_button_map[profile_id] = button
|
|
|
|
self.create_profile_name_label(parent_label, value["name"])
|
|
self.create_profile_icons(parent_label, value)
|
|
|
|
def create_parent_label(self, profile_id):
|
|
parent_label = QLabel(self.scroll_widget)
|
|
profile_list = list(self.profiles_data.keys())
|
|
index = profile_list.index(f'Profile_{profile_id}')
|
|
row, column = divmod(index, 2)
|
|
|
|
parent_label.setGeometry(column * 179, row * 120, 175, 100)
|
|
parent_label.setVisible(True)
|
|
return parent_label
|
|
|
|
def create_profile_button(self, parent_label, profile_id, key):
|
|
button = QPushButton(parent_label)
|
|
button.setGeometry(0, 0, 175, 60)
|
|
button.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: transparent;
|
|
border: none;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: rgba(200, 200, 200, 50);
|
|
}
|
|
QPushButton:checked {
|
|
background-color: rgba(200, 200, 200, 80);
|
|
}
|
|
""")
|
|
|
|
icon = QIcon(self.button_states.get(
|
|
profile_id, self.not_connected_profile))
|
|
button.setIcon(icon)
|
|
button.setIconSize(QtCore.QSize(175, 60))
|
|
|
|
button.show()
|
|
button.setCheckable(True)
|
|
self.button_group.addButton(button)
|
|
self.buttons.append(button)
|
|
|
|
button.clicked.connect(
|
|
lambda checked, pid=profile_id: self.print_profile_details(f"Profile_{pid}"))
|
|
|
|
return button
|
|
|
|
def create_profile_name_label(self, parent_label, name):
|
|
child_label = QLabel(parent_label)
|
|
child_label.setGeometry(0, 65, 175, 30)
|
|
child_label.setText(name)
|
|
child_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
child_label.setAttribute(
|
|
Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
|
child_label.show()
|
|
|
|
def create_profile_icons(self, parent_label, value):
|
|
connection_type = value.get('connection', '')
|
|
if connection_type in ['tor', 'just proxy']:
|
|
self.create_tor_proxy_icons(parent_label, value, connection_type)
|
|
else:
|
|
self.create_other_icons(parent_label, value, connection_type)
|
|
|
|
def create_tor_proxy_icons(self, parent_label, value, connection_type):
|
|
for j, label_name in enumerate(['protocol', 'location', 'browser']):
|
|
child_label = QLabel(parent_label)
|
|
child_label.setObjectName(f"label_{j}")
|
|
child_label.setGeometry(3 + j * 60, 5, 50, 50)
|
|
child_label.setObjectName("label_profiles")
|
|
child_label.setAttribute(
|
|
Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
|
child_label.setStyleSheet(
|
|
"background-color: rgba(128, 128, 128, 0.5); border-radius: 25px;")
|
|
image_path = self.get_icon_path(label_name, value, connection_type)
|
|
pixmap = QPixmap(image_path)
|
|
if pixmap.isNull() and label_name == 'location':
|
|
fallback_path = os.path.join(
|
|
self.btn_path, "default_location_mini.png")
|
|
pixmap = QPixmap(fallback_path)
|
|
if pixmap.isNull():
|
|
pixmap = QPixmap(50, 50)
|
|
pixmap.fill(QtGui.QColor(200, 200, 200))
|
|
if pixmap.isNull() and label_name == 'browser':
|
|
fallback_path = os.path.join(
|
|
self.btn_path, "default_browser_mini.png")
|
|
pixmap = QPixmap(fallback_path)
|
|
if pixmap.isNull():
|
|
pixmap = QPixmap(50, 50)
|
|
pixmap.fill(QtGui.QColor(200, 200, 200))
|
|
child_label.setPixmap(pixmap)
|
|
child_label.show()
|
|
|
|
def create_other_icons(self, parent_label, value, connection_type):
|
|
for j, label_name in enumerate(['protocol', 'location', 'browser']):
|
|
child_label = QLabel(parent_label)
|
|
child_label.setObjectName(f"label_{j}")
|
|
child_label.setGeometry(3 + j * 60, 5, 50, 50)
|
|
child_label.setObjectName("label_profiles")
|
|
child_label.setAttribute(
|
|
Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
|
child_label.setStyleSheet(
|
|
"background-color: rgba(128, 128, 128, 0.5); border-radius: 25px;")
|
|
|
|
image_path = self.get_icon_path(label_name, value, connection_type)
|
|
pixmap = QPixmap(image_path)
|
|
if pixmap.isNull() and label_name == 'location':
|
|
fallback_path = os.path.join(
|
|
self.btn_path, "default_location_mini.png")
|
|
pixmap = QPixmap(fallback_path)
|
|
if pixmap.isNull():
|
|
pixmap = QPixmap(50, 50)
|
|
pixmap.fill(QtGui.QColor(200, 200, 200))
|
|
if pixmap.isNull() and label_name == 'browser':
|
|
fallback_path = os.path.join(
|
|
self.btn_path, "default_browser_mini.png")
|
|
pixmap = QPixmap(fallback_path)
|
|
if pixmap.isNull():
|
|
pixmap = QPixmap(50, 50)
|
|
pixmap.fill(QtGui.QColor(200, 200, 200))
|
|
|
|
if label_name == 'browser' and not value.get('browser_supported', True) and value.get('browser') != 'unknown browser':
|
|
warning_pixmap = QPixmap(
|
|
os.path.join(self.btn_path, 'warning.png'))
|
|
painter = QPainter(pixmap)
|
|
painter.drawPixmap(pixmap.width() - 22,
|
|
pixmap.height() - 21, warning_pixmap)
|
|
painter.end()
|
|
|
|
child_label.setPixmap(pixmap)
|
|
child_label.show()
|
|
|
|
def get_icon_path(self, label_name, value, connection_type):
|
|
if label_name == 'protocol':
|
|
if connection_type == 'tor':
|
|
return os.path.join(self.btn_path, "toricon_mini.png")
|
|
elif connection_type == 'just proxy':
|
|
return os.path.join(self.btn_path, "just proxy_mini.png")
|
|
|
|
return os.path.join(self.btn_path, "wireguard_mini.png")
|
|
elif label_name == 'browser':
|
|
if connection_type == 'system-wide':
|
|
return os.path.join(self.btn_path, "wireguard_system_wide.png")
|
|
else:
|
|
return os.path.join(self.btn_path, f"{value[label_name]} latest_mini.png")
|
|
|
|
return os.path.join(self.btn_path, f"icon_mini_{value[label_name]}.png")
|
|
|
|
def truncate_key(self, text, max_length=50):
|
|
if not text or text == "N/A" or len(text) <= max_length:
|
|
return text
|
|
start_len = max_length // 2 - 2
|
|
end_len = max_length // 2 - 2
|
|
return text[:start_len] + "....." + text[-end_len:]
|
|
|
|
def create_verification_widget(self, profile_obj):
|
|
verification_widget = QWidget(self)
|
|
verification_widget.setGeometry(0, 90, 400, 300)
|
|
verification_widget.setStyleSheet("background: transparent;")
|
|
|
|
verification_layout = QVBoxLayout(verification_widget)
|
|
verification_layout.setContentsMargins(20, 20, 20, 20)
|
|
verification_layout.setSpacing(15)
|
|
|
|
operator = None
|
|
if profile_obj.location and profile_obj.location.operator:
|
|
operator = profile_obj.location.operator
|
|
|
|
info_items = [
|
|
("Operator Name", "operator_name"),
|
|
("Nostr Key", "nostr_public_key"),
|
|
("HydraVeil Key", "hydraveil_public_key"),
|
|
]
|
|
|
|
for label, key in info_items:
|
|
container = QWidget()
|
|
container.setStyleSheet("background: transparent;")
|
|
container_layout = QHBoxLayout(container)
|
|
container_layout.setContentsMargins(0, 0, 0, 0)
|
|
container_layout.setSpacing(10)
|
|
|
|
label_widget = QLabel(label + ":")
|
|
label_widget.setStyleSheet(
|
|
f"color: white; font-size: 12px; {self.font_style}")
|
|
label_widget.setMinimumWidth(120)
|
|
container_layout.addWidget(label_widget)
|
|
|
|
value_widget = QLineEdit()
|
|
value_widget.setReadOnly(True)
|
|
|
|
if operator:
|
|
if key == "operator_name":
|
|
value = operator.name or "N/A"
|
|
elif key == "nostr_public_key":
|
|
value = operator.nostr_public_key or "N/A"
|
|
value = self.truncate_key(
|
|
value) if value != "N/A" else value
|
|
elif key == "hydraveil_public_key":
|
|
value = operator.public_key or "N/A"
|
|
value = self.truncate_key(
|
|
value) if value != "N/A" else value
|
|
else:
|
|
value = "N/A"
|
|
else:
|
|
value = "N/A"
|
|
|
|
value_widget.setText(value)
|
|
value_widget.setCursorPosition(0)
|
|
value_widget.setStyleSheet(f"""
|
|
QLineEdit {{
|
|
color: white;
|
|
background: transparent;
|
|
border: none;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
|
padding: 5px 0px;
|
|
font-size: 12px;
|
|
{self.font_style}
|
|
}}
|
|
""")
|
|
container_layout.addWidget(value_widget, 1)
|
|
|
|
checkmark_widget = QLabel("✓")
|
|
checkmark_widget.setStyleSheet(
|
|
f"color: #4CAF50; font-size: 20px; font-weight: bold; {self.font_style}")
|
|
checkmark_widget.setFixedSize(20, 20)
|
|
checkmark_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
if value and value != "N/A":
|
|
checkmark_widget.show()
|
|
else:
|
|
checkmark_widget.hide()
|
|
container_layout.addWidget(checkmark_widget)
|
|
|
|
copy_button = QPushButton()
|
|
copy_button.setIcon(
|
|
QIcon(os.path.join(self.btn_path, "paste_button.png")))
|
|
copy_button.setIconSize(QSize(24, 24))
|
|
copy_button.setFixedSize(30, 30)
|
|
copy_button.setStyleSheet(f"""
|
|
QPushButton {{
|
|
background: transparent;
|
|
border: none;
|
|
}}
|
|
QPushButton:hover {{
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 4px;
|
|
}}
|
|
""")
|
|
|
|
if operator:
|
|
if key == "operator_name":
|
|
full_value = operator.name or "N/A"
|
|
elif key == "nostr_public_key":
|
|
full_value = operator.nostr_public_key or "N/A"
|
|
elif key == "hydraveil_public_key":
|
|
full_value = operator.public_key or "N/A"
|
|
else:
|
|
full_value = "N/A"
|
|
else:
|
|
full_value = "N/A"
|
|
|
|
copy_button.clicked.connect(
|
|
lambda checked=False, val=full_value: self.copy_verification_value(val))
|
|
container_layout.addWidget(copy_button)
|
|
|
|
verification_layout.addWidget(container)
|
|
|
|
verification_widget.show()
|
|
return verification_widget
|
|
|
|
def copy_verification_value(self, value):
|
|
if value and value != "N/A":
|
|
clipboard = QApplication.clipboard()
|
|
clipboard.setText(value)
|
|
self.update_status.update_status(
|
|
"Verification value copied to clipboard")
|
|
|
|
def print_profile_details(self, profile_name):
|
|
self.reverse_id = int(profile_name.split('_')[1])
|
|
|
|
target_button = self.profile_button_map.get(self.reverse_id)
|
|
if target_button:
|
|
self.connection_manager.set_profile_button_objects(
|
|
self.reverse_id, target_button)
|
|
else:
|
|
print(f"No button found for profile {self.reverse_id}")
|
|
is_connected = self.connection_manager.is_profile_connected(
|
|
int(self.reverse_id))
|
|
if is_connected:
|
|
self.boton_edit.setEnabled(False)
|
|
else:
|
|
self.boton_edit.setEnabled(True)
|
|
|
|
profile = self.profile_info.get(profile_name)
|
|
self.boton_launch.setEnabled(True)
|
|
self.boton_just.setEnabled(True)
|
|
self.boton_just_session.setEnabled(True)
|
|
self.boton_create.setEnabled(True)
|
|
if hasattr(self, 'buttons'):
|
|
for button in self.buttons:
|
|
parent = button.parent()
|
|
if parent:
|
|
verification_icons = parent.findChildren(QPushButton)
|
|
for icon in verification_icons:
|
|
if icon.geometry().width() == 20 and icon.geometry().height() == 20:
|
|
icon.setEnabled(
|
|
self.connection_manager.is_synced())
|
|
|
|
for label in self.additional_labels:
|
|
label.deleteLater()
|
|
|
|
self.additional_labels.clear()
|
|
|
|
self.change_connect_button()
|
|
|
|
if profile:
|
|
self.update_status.current_profile_id = self.reverse_id
|
|
profile_number = profile_name.split("_")[1]
|
|
name = profile.get("name", "")
|
|
protocol = profile.get("protocol", "")
|
|
location = profile.get("location", "")
|
|
connection = profile.get("connection", "")
|
|
country_garaje = profile.get("country_garaje", "")
|
|
|
|
profile_obj = ProfileController.get(self.reverse_id)
|
|
is_profile_enabled = self.connection_manager.is_profile_connected(
|
|
self.reverse_id)
|
|
show_verification_widget = False
|
|
label_principal = None
|
|
label_tor = None
|
|
text_color = "white"
|
|
operator_name = profile_obj.location.operator.name if profile_obj.location and profile_obj.location.operator else ""
|
|
|
|
if protocol.lower() == "wireguard" and is_profile_enabled and profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard':
|
|
verification_widget = self.create_verification_widget(
|
|
profile_obj)
|
|
self.additional_labels.append(verification_widget)
|
|
show_verification_widget = True
|
|
label_principal = None
|
|
|
|
elif operator_name != 'Simplified Privacy' and protocol.lower() == "wireguard":
|
|
if profile_obj.is_session_profile():
|
|
text_color = "black"
|
|
label_background = QLabel(self)
|
|
label_background.setGeometry(0, 60, 410, 354)
|
|
pixmap_bg = QPixmap(os.path.join(
|
|
self.btn_path, "browser only.png"))
|
|
label_background.setPixmap(pixmap_bg)
|
|
label_background.setScaledContents(True)
|
|
label_background.show()
|
|
label_background.lower()
|
|
self.additional_labels.append(label_background)
|
|
|
|
l_img = QLabel(self)
|
|
l_img.setGeometry(20, 125, 100, 100)
|
|
pixmap_loc = QPixmap(os.path.join(
|
|
self.btn_path, f"icon_{location}.png"))
|
|
l_img.setPixmap(pixmap_loc)
|
|
l_img.setScaledContents(True)
|
|
l_img.show()
|
|
self.additional_labels.append(l_img)
|
|
l_name = f"{profile_obj.location.country_name}, {profile_obj.location.name}" if profile_obj.location else ""
|
|
o_name = profile_obj.location.operator.name if profile_obj.location and profile_obj.location.operator else ""
|
|
n_key = profile_obj.location.operator.nostr_public_key if profile_obj.location and profile_obj.location.operator else ""
|
|
|
|
info_txt = QTextEdit(self)
|
|
info_txt.setGeometry(130, 110, 260, 250)
|
|
info_txt.setReadOnly(True)
|
|
info_txt.setFrameStyle(QFrame.Shape.NoFrame)
|
|
info_txt.setStyleSheet(
|
|
f"background: transparent; border: none; color: {text_color}; font-size: 17px;")
|
|
info_txt.setVerticalScrollBarPolicy(
|
|
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
info_txt.setHorizontalScrollBarPolicy(
|
|
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
info_txt.setWordWrapMode(
|
|
QtGui.QTextOption.WrapMode.WrapAtWordBoundaryOrAnywhere)
|
|
info_txt.setText(
|
|
f"Location: {l_name}\n\nOperator: {o_name}")
|
|
info_txt.show()
|
|
self.additional_labels.append(info_txt)
|
|
|
|
nostr_txt = QTextEdit(self)
|
|
nostr_txt.setGeometry(20, 260, 370, 80)
|
|
nostr_txt.setReadOnly(True)
|
|
nostr_txt.setFrameStyle(QFrame.Shape.NoFrame)
|
|
nostr_txt.setStyleSheet(
|
|
f"background: transparent; border: none; color: {text_color}; font-size: 17px;")
|
|
nostr_txt.setVerticalScrollBarPolicy(
|
|
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
nostr_txt.setHorizontalScrollBarPolicy(
|
|
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
nostr_txt.setWordWrapMode(
|
|
QtGui.QTextOption.WrapMode.WrapAnywhere)
|
|
nostr_txt.setText(f"Nostr: {n_key}")
|
|
nostr_txt.show()
|
|
self.additional_labels.append(nostr_txt)
|
|
|
|
elif protocol.lower() in ["wireguard", "open", "residential", "hidetor"]:
|
|
label_principal = QLabel(self)
|
|
label_principal.setGeometry(0, 90, 400, 300)
|
|
pixmap = QPixmap(os.path.join(
|
|
self.btn_path, f"{protocol}_{location}.png"))
|
|
label_principal.setPixmap(pixmap)
|
|
label_principal.setScaledContents(True)
|
|
label_principal.show()
|
|
self.additional_labels.append(label_principal)
|
|
|
|
if protocol.lower() in ["wireguard", "open", "residential", "hidetor"]:
|
|
|
|
if protocol.lower() == "wireguard" and ConfigurationController.get_endpoint_verification_enabled():
|
|
if is_profile_enabled and profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard':
|
|
verified_icon = QLabel(self)
|
|
verified_icon.setGeometry(20, 50, 100, 80)
|
|
verified_pixmap = QPixmap(os.path.join(
|
|
self.btn_path, "verified_profile.png"))
|
|
verified_icon.setPixmap(verified_pixmap)
|
|
verified_icon.setScaledContents(True)
|
|
verified_icon.show()
|
|
self.additional_labels.append(verified_icon)
|
|
|
|
if protocol.lower() == "hidetor" and not show_verification_widget:
|
|
|
|
if label_principal:
|
|
label_principal.setGeometry(0, 80, 400, 300)
|
|
label_background = QLabel(self)
|
|
label_background.setGeometry(0, 60, 410, 354)
|
|
if connection == 'just proxy':
|
|
image_path = os.path.join(
|
|
self.btn_path, f"icon_{location}.png")
|
|
else:
|
|
image_path = os.path.join(
|
|
self.btn_path, f"hdtor_{location}.png")
|
|
pixmap = QPixmap(image_path)
|
|
label_background.setPixmap(pixmap)
|
|
label_background.show()
|
|
label_background.setScaledContents(True)
|
|
label_background.lower()
|
|
self.additional_labels.append(label_background)
|
|
|
|
if protocol.lower() == "wireguard" and connection.lower() == "browser-only" and not show_verification_widget:
|
|
|
|
if label_principal:
|
|
label_principal.setGeometry(0, 80, 400, 300)
|
|
|
|
label_background = QLabel(self)
|
|
label_background.setGeometry(0, 60, 410, 354)
|
|
is_supported = profile.get('browser_supported', False)
|
|
image_name = "browser only.png" if is_supported else "unsupported_browser_only.png"
|
|
pixmap = QPixmap(os.path.join(self.btn_path, image_name))
|
|
label_background.setPixmap(pixmap)
|
|
label_background.show()
|
|
label_background.setScaledContents(True)
|
|
label_background.lower()
|
|
self.additional_labels.append(label_background)
|
|
|
|
if protocol.lower() == "residential" and connection.lower() == "tor":
|
|
|
|
label_background = QLabel(self)
|
|
label_background.setGeometry(0, 60, 410, 354)
|
|
pixmap = QPixmap(os.path.join(
|
|
self.btn_path, "browser_tor.png"))
|
|
label_background.setPixmap(pixmap)
|
|
label_background.show()
|
|
label_background.setScaledContents(True)
|
|
self.additional_labels.append(label_background)
|
|
|
|
label_garaje = QLabel(self)
|
|
label_garaje.setGeometry(5, 110, 400, 300)
|
|
pixmap = QPixmap(os.path.join(
|
|
self.btn_path, f"{country_garaje} garaje.png"))
|
|
label_garaje.setPixmap(pixmap)
|
|
label_garaje.show()
|
|
label_garaje.setScaledContents(True)
|
|
self.additional_labels.append(label_garaje)
|
|
|
|
label_tor = QLabel(self)
|
|
label_tor.setGeometry(330, 300, 75, 113)
|
|
|
|
pixmap = QPixmap(os.path.join(self.btn_path, "tor 86x130.png"))
|
|
if label_tor:
|
|
label_tor.setPixmap(pixmap)
|
|
label_tor.show()
|
|
label_tor.setScaledContents(True)
|
|
self.additional_labels.append(label_tor)
|
|
if protocol.lower() == "residential" and connection.lower() == "just proxy":
|
|
|
|
label_background = QLabel(self)
|
|
label_background.setGeometry(0, 60, 410, 354)
|
|
pixmap = QPixmap(os.path.join(
|
|
self.btn_path, "browser_just_proxy.png"))
|
|
label_background.setPixmap(pixmap)
|
|
label_background.show()
|
|
label_background.setScaledContents(True)
|
|
self.additional_labels.append(label_background)
|
|
|
|
label_garaje = QLabel(self)
|
|
label_garaje.setGeometry(5, 110, 400, 300)
|
|
pixmap = QPixmap(os.path.join(
|
|
self.btn_path, f"{country_garaje} garaje.png"))
|
|
label_garaje.setPixmap(pixmap)
|
|
label_garaje.show()
|
|
label_garaje.setScaledContents(True)
|
|
self.additional_labels.append(label_garaje)
|
|
|
|
editar_profile = {"profile_number": profile_number,
|
|
"name": name, "protocol": protocol}
|
|
self.add_selected_profile(editar_profile)
|
|
|
|
self.current_profile_location = location
|
|
|
|
def show_profile_verification(self, profile_id):
|
|
profile = ProfileController.get(profile_id)
|
|
if not profile or not profile.location:
|
|
return
|
|
|
|
location_key = f'{profile.location.country_code}_{profile.location.code}'
|
|
location_info = self.connection_manager.get_location_info(location_key)
|
|
if not location_info:
|
|
return
|
|
|
|
location_icon_name = None
|
|
btn_path = self.btn_path
|
|
|
|
for icon_file in os.listdir(btn_path):
|
|
if icon_file.startswith('button_') and icon_file.endswith('.png'):
|
|
icon_name = icon_file.replace(
|
|
'button_', '').replace('.png', '')
|
|
test_loc = self.connection_manager.get_location_info(icon_name)
|
|
if test_loc and test_loc.country_code == location_info.country_code and test_loc.code == location_info.code:
|
|
location_icon_name = icon_name
|
|
break
|
|
|
|
if location_icon_name:
|
|
verification_page = LocationVerificationPage(
|
|
self.page_stack, self.custom_window, location_icon_name, self)
|
|
self.page_stack.addWidget(verification_page)
|
|
self.page_stack.setCurrentIndex(
|
|
self.page_stack.indexOf(verification_page))
|
|
|
|
def launch(self):
|
|
pass
|
|
|
|
def connect(self):
|
|
self.boton_just.setEnabled(False)
|
|
self.boton_just_session.setEnabled(False)
|
|
|
|
profile = ProfileController.get(int(self.reverse_id))
|
|
is_tor = self.update_status.get_current_connection() == 'tor'
|
|
if profile.connection.code == 'tor' and not profile.application_version.installed and not is_tor:
|
|
message = f'You are using a Tor profile, but the associated browser is not installed. If you want the browser to be downloaded with Tor, you must switch to a Tor connection. Otherwise, proceed with a clearweb connection.'
|
|
|
|
else:
|
|
self.get_billing_code_by_id()
|
|
return
|
|
|
|
self.popup = self._make_confirmation_popup(message, button_text='Proceed')
|
|
self.popup.finished.connect(
|
|
lambda result: self._handle_popup_result(result))
|
|
self.popup.show()
|
|
|
|
def _handle_popup_result(self, result, change_screen=False):
|
|
if result:
|
|
if change_screen:
|
|
self._route_to_billing_entry()
|
|
else:
|
|
self.get_billing_code_by_id()
|
|
else:
|
|
self.popup.close()
|
|
|
|
def _route_to_billing_entry(self):
|
|
try:
|
|
unused = get_unused_tickets(ticket_observer)
|
|
random_setting, _ = do_we_use_a_random_ticket(ticket_observer)
|
|
except Exception:
|
|
unused = {'valid': False}
|
|
random_setting = None
|
|
|
|
has_tickets = isinstance(unused, dict) and unused.get('valid') and len(unused.get('data', [])) > 0
|
|
random_off = random_setting is None
|
|
|
|
if has_tickets and random_off:
|
|
chooser = self.custom_window.navigator.get_cached("ticket_or_billing_choice")
|
|
if chooser:
|
|
chooser.populate()
|
|
self.page_stack.setCurrentWidget(chooser)
|
|
return
|
|
|
|
id_page = self.custom_window.navigator.get_cached("id")
|
|
if id_page:
|
|
self.page_stack.setCurrentWidget(id_page)
|
|
else:
|
|
self.custom_window.navigator.navigate("id")
|
|
|
|
def disconnect(self):
|
|
self.disconnect_button.setEnabled(False)
|
|
self.disconnect_system_wide_button.setEnabled(False)
|
|
self.update_status.update_status('Disconnecting...')
|
|
profile_data = {
|
|
'id': int(self.reverse_id)
|
|
}
|
|
profile = ProfileController.get(int(self.reverse_id))
|
|
is_session_profile = isinstance(profile, SessionProfile)
|
|
if not is_session_profile:
|
|
connected_profiles = self.connection_manager.get_connected_profiles()
|
|
if len(connected_profiles) > 1:
|
|
message = f'Profile{"" if len(connected_profiles) == 1 else "s"} {", ".join(str(profile_id) for profile_id in connected_profiles)} are still connected.\nAll connected session profiles will be disconnected. \nDo you want to proceed?'
|
|
self._show_disconnect_confirmation(
|
|
connected_profiles, button_text='Disable', message=message)
|
|
self.disconnect_button.setEnabled(True)
|
|
self.disconnect_system_wide_button.setEnabled(True)
|
|
return
|
|
self.worker_thread = WorkerThread(
|
|
'DISABLE_PROFILE', profile_data=profile_data)
|
|
self.worker_thread.finished.connect(self.on_disconnect_done)
|
|
self.worker_thread.start()
|
|
|
|
def on_disconnect_done(self):
|
|
self.disconnect_button.setEnabled(True)
|
|
self.disconnect_system_wide_button.setEnabled(True)
|
|
pass
|
|
|
|
def _config_flag(self, section, key, default=False):
|
|
try:
|
|
config = self.update_status._load_gui_config()
|
|
if config and section in config:
|
|
return config[section].get(key, default)
|
|
except Exception:
|
|
pass
|
|
return default
|
|
|
|
def create_prof(self):
|
|
fast_mode = self._config_flag("registrations", "fast_registration_enabled")
|
|
if fast_mode:
|
|
if not self.connection_manager.is_synced():
|
|
self.update_status.update_status('Syncing in progress..')
|
|
self.update_status.sync()
|
|
self.update_status.worker_thread.sync_output.connect(
|
|
self.on_sync_complete_for_fast_registration)
|
|
else:
|
|
self.custom_window.navigator.navigate("fast_registration")
|
|
return
|
|
|
|
if not self.connection_manager.is_synced():
|
|
self.custom_window.navigator.navigate("sync_screen")
|
|
else:
|
|
self.custom_window.navigator.navigate("protocol")
|
|
|
|
def on_sync_complete_for_fast_registration(self, available_locations, available_browsers, status, is_tor, locations, all_browsers):
|
|
if status:
|
|
self.custom_window.navigator.navigate("fast_registration")
|
|
else:
|
|
self.update_status.update_status(
|
|
'Sync failed. Please try again later.')
|
|
|
|
def change_connect_button(self):
|
|
profile = ProfileController.get(int(self.reverse_id))
|
|
is_connected = self.connection_manager.is_profile_connected(
|
|
int(self.reverse_id))
|
|
is_session_profile = isinstance(profile, SessionProfile)
|
|
|
|
self.update_button_visibility(is_connected, is_session_profile)
|
|
self.update_status_message(profile, is_connected)
|
|
|
|
def update_button_visibility(self, is_connected, is_session_profile):
|
|
self.boton_just_session.setVisible(
|
|
not is_connected and is_session_profile)
|
|
self.boton_just.setVisible(not is_connected and not is_session_profile)
|
|
self.disconnect_button.setVisible(is_connected and is_session_profile)
|
|
self.disconnect_system_wide_button.setVisible(
|
|
is_connected and not is_session_profile)
|
|
|
|
def update_status_message(self, profile, is_connected):
|
|
if is_connected:
|
|
message = Worker.generate_profile_message(profile, is_enabled=True)
|
|
self.update_status.enable_marquee(message)
|
|
else:
|
|
message = Worker.generate_profile_message(
|
|
profile, is_enabled=True, idle=True)
|
|
self.update_status.update_status(message)
|
|
|
|
def edit_prof(self):
|
|
auto_sync = self._config_flag("registrations", "auto_sync_enabled")
|
|
if auto_sync and not self.connection_manager.is_synced():
|
|
self.update_status.update_status('Syncing in progress..')
|
|
self.update_status.sync()
|
|
self.update_status.worker_thread.sync_output.connect(
|
|
self.on_sync_complete_for_edit_profile)
|
|
else:
|
|
self.custom_window.navigator.navigate("editor")
|
|
|
|
def on_sync_complete_for_edit_profile(self, available_locations, available_browsers, status, is_tor, locations, all_browsers):
|
|
if status:
|
|
self.custom_window.navigator.navigate("editor")
|
|
else:
|
|
self.update_status.update_status(
|
|
'Sync failed. Please try again later.')
|
|
|
|
def settings_gui(self):
|
|
self.custom_window.navigator.navigate("settings")
|
|
|
|
def eliminacion(self):
|
|
current_state = {
|
|
'is_tor_mode': self.is_tor_mode,
|
|
'is_animating': self.is_animating,
|
|
'animation_step': self.animation_step
|
|
}
|
|
|
|
self.main_window.toggle_bg.setParent(None)
|
|
self.main_window.toggle_handle.setParent(None)
|
|
self.main_window.on_label.setParent(None)
|
|
self.main_window.off_label.setParent(None)
|
|
|
|
self.refresh_menu_buttons()
|
|
|
|
self.main_window.toggle_bg.setParent(self.main_window.toggle_button)
|
|
self.main_window.toggle_handle.setParent(
|
|
self.main_window.toggle_button)
|
|
self.main_window.on_label.setParent(self.main_window.toggle_button)
|
|
self.main_window.off_label.setParent(self.main_window.toggle_button)
|
|
|
|
self.main_window.toggle_bg.show()
|
|
self.main_window.toggle_handle.show()
|
|
self.main_window.on_label.show()
|
|
self.main_window.off_label.show()
|
|
|
|
self.is_tor_mode = current_state['is_tor_mode']
|
|
self.is_animating = current_state['is_animating']
|
|
self.animation_step = current_state['animation_step']
|
|
|
|
def get_billing_code_by_id(self, force: bool = False):
|
|
profile_id = self.update_status.current_profile_id
|
|
profile_data = {
|
|
'id': profile_id,
|
|
'force': force
|
|
}
|
|
self.update_status.update_status('Attempting to connect...')
|
|
|
|
self.enabling_profile(profile_data)
|
|
|
|
def enabling_profile(self, profile_data):
|
|
self.worker = Worker(profile_data)
|
|
self.worker.update_signal.connect(self.update_gui_main_thread)
|
|
self.worker.change_page.connect(self.change_app_page)
|
|
self.worker.ticket_data_loss.connect(self.show_ticket_data_loss_popup)
|
|
|
|
thread = threading.Thread(target=self.worker.run)
|
|
thread.start()
|
|
|
|
def show_ticket_data_loss_popup(self, ticket_number, billing_code):
|
|
self.custom_window.navigator.navigate("menu")
|
|
self.update_status.update_status('Critical: invalid billing code from ticket.')
|
|
self.popup = TicketDataLossPopup(
|
|
self,
|
|
ticket_number=ticket_number,
|
|
billing_code=billing_code,
|
|
)
|
|
self.popup.show()
|
|
|
|
def DisplayInstallScreen(self, package_name):
|
|
self.custom_window.navigator.navigate("install_system_package")
|
|
install_page = self.custom_window.navigator.get_cached("install_system_package")
|
|
if install_page is not None:
|
|
install_page.configure(package_name=package_name, distro='debian')
|
|
|
|
def handle_enable_force_result(self, result):
|
|
if result:
|
|
profile_data = {'id': self.reverse_id,
|
|
'ignore_profile_state_conflict': True}
|
|
if hasattr(self, '_pending_endpoint_verification_ignore') and self._pending_endpoint_verification_ignore:
|
|
profile_data['ignore_endpoint_verification'] = True
|
|
self.enabling_profile(profile_data)
|
|
else:
|
|
pass
|
|
if hasattr(self, '_pending_profile_state_conflict_ignore'):
|
|
delattr(self, '_pending_profile_state_conflict_ignore')
|
|
if hasattr(self, '_pending_endpoint_verification_ignore'):
|
|
delattr(self, '_pending_endpoint_verification_ignore')
|
|
self.popup.close()
|
|
|
|
def handle_endpoint_verification_result(self, result, action, profile_id):
|
|
if action == "continue":
|
|
profile_data = {'id': profile_id,
|
|
'ignore_endpoint_verification': True}
|
|
if hasattr(self, '_pending_profile_state_conflict_ignore') and self._pending_profile_state_conflict_ignore:
|
|
profile_data['ignore_profile_state_conflict'] = True
|
|
self.enabling_profile(profile_data)
|
|
elif action == "sync":
|
|
self.update_status.update_status("Syncing...")
|
|
self.worker_thread = WorkerThread('SYNC')
|
|
self.worker_thread.finished.connect(
|
|
lambda: self.handle_sync_after_verification(profile_id))
|
|
self.worker_thread.sync_output.connect(
|
|
self.update_status.update_values)
|
|
self.worker_thread.start()
|
|
else:
|
|
self.update_status.update_status("Profile enable aborted")
|
|
if hasattr(self, 'popup'):
|
|
self.popup.close()
|
|
|
|
def handle_sync_after_verification(self, profile_id):
|
|
profile_data = {'id': profile_id}
|
|
if hasattr(self, '_pending_profile_state_conflict_ignore') and self._pending_profile_state_conflict_ignore:
|
|
profile_data['ignore_profile_state_conflict'] = True
|
|
self.enabling_profile(profile_data)
|
|
|
|
def update_gui_main_thread(self, text, is_enabled, profile_id, profile_type, profile_connection):
|
|
if text == "ENDPOINT_VERIFICATION_ERROR":
|
|
message = "Operator verification failed. Do you want to continue anyway?"
|
|
self.popup = EndpointVerificationPopup(self, message=message)
|
|
self.popup.finished.connect(
|
|
lambda result, action: self.handle_endpoint_verification_result(result, action, profile_id))
|
|
self.popup.show()
|
|
return
|
|
|
|
if text == "PROFILE_STATE_CONFLICT_ERROR":
|
|
message = f'The profile is already enabled. Do you want to force enable it anyway?'
|
|
self._pending_profile_state_conflict_ignore = True
|
|
self.popup = self._make_confirmation_popup(message, button_text='Proceed')
|
|
self.popup.finished.connect(
|
|
lambda result: self.handle_enable_force_result(result))
|
|
self.popup.show()
|
|
return
|
|
|
|
if profile_id < 0:
|
|
self.DisplayInstallScreen(text)
|
|
return
|
|
if is_enabled:
|
|
self.update_status.enable_marquee(str(text))
|
|
else:
|
|
self.update_status.update_status(str(text))
|
|
if profile_id is not None:
|
|
self.on_finished_update_gui(
|
|
is_enabled, profile_id, profile_type, profile_connection)
|
|
self.boton_just.setEnabled(True)
|
|
self.boton_just_session.setEnabled(True)
|
|
|
|
def change_app_page(self, text, Is_changed):
|
|
self.update_status.update_status(str(text))
|
|
if Is_changed:
|
|
current_connection = self.update_status.get_current_connection()
|
|
profile = ProfileController.get(int(self.reverse_id))
|
|
if current_connection != 'tor' and profile.connection.code == 'tor':
|
|
message = f'You are using a Tor profile, but the profile subscription is missing or expired. If you want the billing to be done thourgh Tor, you must switch to a Tor connection. Otherwise, proceed with a clearweb connection.'
|
|
self.popup = self._make_confirmation_popup(message, button_text='Proceed')
|
|
self.popup.finished.connect(
|
|
lambda result: self._handle_popup_result(result, True))
|
|
self.popup.show()
|
|
else:
|
|
self._route_to_billing_entry()
|
|
|
|
self.boton_just.setEnabled(True)
|
|
self.boton_just_session.setEnabled(True)
|
|
|
|
def on_finished_update_gui(self, is_enabled, profile_id, profile_type, profile_connection):
|
|
try:
|
|
if is_enabled:
|
|
self.connection_manager.add_connected_profile(profile_id)
|
|
target_button: QPushButton = self.connection_manager.get_profile_button_objects(
|
|
profile_id)
|
|
|
|
if profile_type == 1:
|
|
if target_button:
|
|
icon_path = self.connected_tor_profile if profile_connection == 'tor' else self.connected_profile
|
|
try:
|
|
target_button.setIcon(QIcon(icon_path))
|
|
except RuntimeError:
|
|
pass
|
|
self.button_states[profile_id] = icon_path
|
|
else:
|
|
if target_button:
|
|
try:
|
|
target_button.setIcon(
|
|
QIcon(self.system_wide_connected_profile))
|
|
except RuntimeError:
|
|
pass
|
|
self.update_status.update_image(
|
|
appearance_value='background_connected')
|
|
self.button_states[profile_id] = self.system_wide_connected_profile
|
|
self.IsSystem += 1
|
|
|
|
if target_button:
|
|
self.show_disconnect_button(
|
|
True, profile_type, target_button)
|
|
self.boton_edit.setEnabled(False)
|
|
|
|
if profile_id == self.reverse_id:
|
|
profile_obj = ProfileController.get(profile_id)
|
|
if profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard' and ConfigurationController.get_endpoint_verification_enabled():
|
|
self.print_profile_details(f"Profile_{profile_id}")
|
|
else:
|
|
self.connection_manager.remove_connected_profile(profile_id)
|
|
if profile_type == 1:
|
|
if profile_id in self.button_states:
|
|
del self.button_states[profile_id]
|
|
else:
|
|
self.IsSystem -= 1
|
|
if profile_id in self.button_states:
|
|
del self.button_states[profile_id]
|
|
if self.IsSystem == 0:
|
|
self.update_status.update_image()
|
|
|
|
self.boton_edit.setEnabled(True)
|
|
|
|
if profile_id == self.reverse_id:
|
|
self.print_profile_details(f"Profile_{profile_id}")
|
|
|
|
self.refresh_menu_buttons()
|
|
except IndexError:
|
|
self.update_status.update_status(
|
|
'An error occurred while updating profile state.')
|