forked from CastroFidel/winehelper
(gui): fix security and stability issues
- add null-safety for path variables (USER_WORK_PATH, DATA_PATH) - fix potential crash in handle_process_output with uninitialized process - improve env vars and executable path handling - enhance .desktop files and menu processing
This commit is contained in:
@@ -1310,7 +1310,8 @@ class CreatePrefixDialog(QDialog):
|
||||
ErrorReporter.show_error(self, "Ошибка", error_msg)
|
||||
return
|
||||
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
if os.path.exists(prefix_path):
|
||||
error_msg = f"Префикс с именем '{prefix_name}' уже существует."
|
||||
ErrorReporter.show_error(self, "Ошибка", error_msg)
|
||||
@@ -1589,7 +1590,7 @@ class WineHelperGUI(QMainWindow):
|
||||
"""
|
||||
|
||||
# Основные переменные
|
||||
self.winehelper_path: str = Var.RUN_SCRIPT
|
||||
self.winehelper_path: str = Var.RUN_SCRIPT or ""
|
||||
self.process: Optional[QProcess] = None
|
||||
self.current_script: Optional[str] = None
|
||||
self.install_process: Optional[QProcess] = None
|
||||
@@ -2051,7 +2052,8 @@ class WineHelperGUI(QMainWindow):
|
||||
"""
|
||||
button_index = 0
|
||||
for script in scripts_list:
|
||||
script_path = os.path.join(Var.DATA_PATH, script_folder, script)
|
||||
data_path = Var.DATA_PATH or ""
|
||||
script_path = os.path.join(data_path, script_folder, script)
|
||||
prog_name = ScriptParser.extract_prog_name_from_script(script_path)
|
||||
|
||||
# Создаем кнопку, только если для скрипта указано имя программы
|
||||
@@ -2059,7 +2061,8 @@ class WineHelperGUI(QMainWindow):
|
||||
continue
|
||||
|
||||
icon_names = ScriptParser.extract_icons_from_script(script_path)
|
||||
icon_paths = [os.path.join(Var.DATA_PATH, "image", f"{name}.png") for name in icon_names]
|
||||
data_path = Var.DATA_PATH or ""
|
||||
icon_paths = [os.path.join(data_path, "image", f"{name}.png") for name in icon_names]
|
||||
|
||||
# Выбираем стиль в зависимости от папки
|
||||
if script_folder == 'testinstall':
|
||||
@@ -2486,7 +2489,8 @@ class WineHelperGUI(QMainWindow):
|
||||
|
||||
def _get_current_prefixes(self):
|
||||
"""Возвращает множество имен существующих префиксов."""
|
||||
prefixes_root_path = os.path.join(Var.USER_WORK_PATH, "prefixes")
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefixes_root_path = os.path.join(user_work_path, "prefixes")
|
||||
if not os.path.isdir(prefixes_root_path):
|
||||
return set()
|
||||
try:
|
||||
@@ -2506,7 +2510,8 @@ class WineHelperGUI(QMainWindow):
|
||||
|
||||
def _load_created_prefixes(self):
|
||||
"""Загружает и обновляет список созданных префиксов в выпадающем списке."""
|
||||
prefixes_root_path = os.path.join(Var.USER_WORK_PATH, "prefixes")
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefixes_root_path = os.path.join(user_work_path, "prefixes")
|
||||
has_prefixes_dir = os.path.isdir(prefixes_root_path)
|
||||
if not has_prefixes_dir:
|
||||
return
|
||||
@@ -2667,7 +2672,8 @@ class WineHelperGUI(QMainWindow):
|
||||
if not prefix_name:
|
||||
return
|
||||
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
if os.path.isdir(prefix_path):
|
||||
try:
|
||||
subprocess.Popen(['xdg-open', prefix_path])
|
||||
@@ -2706,7 +2712,8 @@ class WineHelperGUI(QMainWindow):
|
||||
self.fsync_button.setChecked(False)
|
||||
return
|
||||
|
||||
last_conf_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name, "last.conf")
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
last_conf_path = os.path.join(user_work_path, "prefixes", prefix_name, "last.conf")
|
||||
|
||||
if not os.path.exists(last_conf_path):
|
||||
self.prefix_info_display.setHtml(f"<p>Файл конфигурации last.conf не найден для префикса '{prefix_name}'.</p>")
|
||||
@@ -2751,7 +2758,8 @@ class WineHelperGUI(QMainWindow):
|
||||
self.fsync_button.blockSignals(False)
|
||||
|
||||
# --- Чтение и отображение установленных компонентов Winetricks ---
|
||||
winetricks_log_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name, "winetricks.log")
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
winetricks_log_path = os.path.join(user_work_path, "prefixes", prefix_name, "winetricks.log")
|
||||
installed_verbs = []
|
||||
if os.path.exists(winetricks_log_path):
|
||||
try:
|
||||
@@ -2872,7 +2880,8 @@ class WineHelperGUI(QMainWindow):
|
||||
if not prefix_name:
|
||||
return None
|
||||
|
||||
last_conf_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name, "last.conf")
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
last_conf_path = os.path.join(user_work_path, "prefixes", prefix_name, "last.conf")
|
||||
if not os.path.exists(last_conf_path):
|
||||
return None
|
||||
|
||||
@@ -2953,7 +2962,8 @@ class WineHelperGUI(QMainWindow):
|
||||
return
|
||||
|
||||
# Определяем архитектуру префикса
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
last_conf_path = os.path.join(prefix_path, "last.conf")
|
||||
architecture = "win64" # По умолчанию
|
||||
if os.path.exists(last_conf_path):
|
||||
@@ -3009,7 +3019,8 @@ class WineHelperGUI(QMainWindow):
|
||||
)
|
||||
|
||||
env = QProcessEnvironment.systemEnvironment()
|
||||
env.insert("WINEPREFIX", os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name))
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
env.insert("WINEPREFIX", os.path.join(user_work_path, "prefixes", prefix_name))
|
||||
self.command_process.setProcessEnvironment(env)
|
||||
|
||||
args = ["change-wine", new_version]
|
||||
@@ -3019,7 +3030,8 @@ class WineHelperGUI(QMainWindow):
|
||||
|
||||
def run_component_install_command(self, prefix_name, command, version):
|
||||
"""Выполняет команду установки компонента (DXVK/VKD3D) через winehelper."""
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
|
||||
self.command_dialog = QDialog(self)
|
||||
self.command_dialog.setWindowTitle(f"Выполнение: {command} {version}")
|
||||
@@ -3106,7 +3118,8 @@ class WineHelperGUI(QMainWindow):
|
||||
def run_update_associations_command(self, prefix_name, new_associations):
|
||||
"""Выполняет команду обновления ассоциаций файлов."""
|
||||
# --- Прямое редактирование last.conf, чтобы обойти перезапись переменных в winehelper ---
|
||||
last_conf_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name, "last.conf")
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
last_conf_path = os.path.join(user_work_path, "prefixes", prefix_name, "last.conf")
|
||||
if not os.path.exists(last_conf_path):
|
||||
QMessageBox.critical(self, "Ошибка", f"Файл конфигурации last.conf не найден для префикса '{prefix_name}'.")
|
||||
return
|
||||
@@ -3139,7 +3152,8 @@ class WineHelperGUI(QMainWindow):
|
||||
QMessageBox.critical(self, "Ошибка записи", f"Не удалось обновить файл last.conf: {str(e)}")
|
||||
return
|
||||
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
|
||||
def on_finished(exit_code, exit_status):
|
||||
self._handle_component_install_finished(prefix_name, exit_code, exit_status)
|
||||
@@ -3167,7 +3181,8 @@ class WineHelperGUI(QMainWindow):
|
||||
QMessageBox.warning(self, "Ошибка", "Сначала выберите префикс.")
|
||||
return
|
||||
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
drive_c_path = os.path.join(prefix_path, "drive_c")
|
||||
|
||||
if not os.path.isdir(drive_c_path):
|
||||
@@ -3402,7 +3417,8 @@ class WineHelperGUI(QMainWindow):
|
||||
if self.created_prefix_selector.count() > 0:
|
||||
self.created_prefix_selector.setCurrentIndex(-1)
|
||||
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
|
||||
self.command_dialog = QDialog(self)
|
||||
self.command_dialog.setWindowTitle(f"Создание префикса: {prefix_name}")
|
||||
@@ -3486,12 +3502,13 @@ class WineHelperGUI(QMainWindow):
|
||||
widget.deleteLater()
|
||||
self.installed_buttons.clear()
|
||||
|
||||
if not os.path.exists(Var.USER_WORK_PATH):
|
||||
os.makedirs(Var.USER_WORK_PATH, exist_ok=True)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
if not os.path.exists(user_work_path):
|
||||
os.makedirs(user_work_path, exist_ok=True)
|
||||
return
|
||||
|
||||
desktop_files = []
|
||||
for entry in os.scandir(Var.USER_WORK_PATH):
|
||||
for entry in os.scandir(user_work_path):
|
||||
if entry.is_file() and entry.name.endswith('.desktop'):
|
||||
desktop_files.append(entry.path)
|
||||
|
||||
@@ -3829,13 +3846,14 @@ class WineHelperGUI(QMainWindow):
|
||||
ErrorReporter.show_error(self, "Менеджер Winetricks", error_msg)
|
||||
return
|
||||
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
if not os.path.isdir(prefix_path):
|
||||
error_msg = f"Каталог префикса не найден:\n{prefix_path}"
|
||||
ErrorReporter.show_error(self, "Ошибка", error_msg)
|
||||
return
|
||||
|
||||
winetricks_path = Var.WH_WINETRICKS
|
||||
winetricks_path = Var.WH_WINETRICKS or ""
|
||||
wine_executable = self._get_wine_executable_for_prefix(prefix_name)
|
||||
dialog = WinetricksManagerDialog(prefix_path, winetricks_path, self, wine_executable=wine_executable)
|
||||
dialog.installation_complete.connect(lambda: self.update_prefix_info_display(prefix_name))
|
||||
@@ -3843,7 +3861,8 @@ class WineHelperGUI(QMainWindow):
|
||||
|
||||
def _get_wine_executable_for_prefix(self, prefix_name):
|
||||
"""Определяет и возвращает путь к исполняемому файлу wine для указанного префикса."""
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
last_conf_path = os.path.join(prefix_path, "last.conf")
|
||||
wh_wine_use = None
|
||||
|
||||
@@ -3866,7 +3885,8 @@ class WineHelperGUI(QMainWindow):
|
||||
print(f"Предупреждение: не удалось прочитать или обработать {last_conf_path}: {e}")
|
||||
|
||||
if wh_wine_use and not wh_wine_use.startswith('system'):
|
||||
local_wine_path = os.path.join(Var.USER_WORK_PATH, "dist", wh_wine_use, "bin", "wine")
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
local_wine_path = os.path.join(user_work_path, "dist", wh_wine_use, "bin", "wine")
|
||||
if os.path.exists(local_wine_path):
|
||||
return local_wine_path
|
||||
QMessageBox.warning(self, "Предупреждение",
|
||||
@@ -3880,7 +3900,8 @@ class WineHelperGUI(QMainWindow):
|
||||
if not prefix_name:
|
||||
return
|
||||
|
||||
last_conf_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name, "last.conf")
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
last_conf_path = os.path.join(user_work_path, "prefixes", prefix_name, "last.conf")
|
||||
if not os.path.exists(last_conf_path):
|
||||
error_msg = f"Файл last.conf не найден для префикса '{prefix_name}'."
|
||||
ErrorReporter.show_error(self, "Ошибка", error_msg)
|
||||
@@ -3928,7 +3949,8 @@ class WineHelperGUI(QMainWindow):
|
||||
ErrorReporter.show_error(self, "Ошибка", error_msg)
|
||||
return
|
||||
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
if not os.path.isdir(prefix_path):
|
||||
error_msg = f"Каталог префикса не найден:\n{prefix_path}"
|
||||
ErrorReporter.show_error(self, "Ошибка", error_msg)
|
||||
@@ -3993,7 +4015,8 @@ class WineHelperGUI(QMainWindow):
|
||||
process.kill()
|
||||
return
|
||||
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
wine_executable = self._get_wine_executable_for_prefix(prefix_name)
|
||||
|
||||
wineserver_path = os.path.join(os.path.dirname(wine_executable), "wineserver")
|
||||
@@ -4256,7 +4279,8 @@ class WineHelperGUI(QMainWindow):
|
||||
if msg_box.clickedButton() == yes_button:
|
||||
try:
|
||||
# Полный путь к префиксу
|
||||
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||
user_work_path = Var.USER_WORK_PATH or ""
|
||||
prefix_path = os.path.join(user_work_path, "prefixes", prefix_name)
|
||||
|
||||
# 1. Сначала собираем ВСЕ .desktop файлы, связанные с этим префиксом
|
||||
all_desktop_files = set()
|
||||
@@ -4265,7 +4289,7 @@ class WineHelperGUI(QMainWindow):
|
||||
all_desktop_files.add(self.current_selected_app['desktop_path'])
|
||||
|
||||
desktop_locations = [
|
||||
Var.USER_WORK_PATH,
|
||||
user_work_path,
|
||||
os.path.join(os.path.expanduser("~"), ".local/share/applications/WineHelper"),
|
||||
os.path.join(os.path.expanduser("~"), "Desktop"),
|
||||
os.path.join(os.path.expanduser("~"), "Рабочий стол"),
|
||||
@@ -4324,16 +4348,16 @@ class WineHelperGUI(QMainWindow):
|
||||
".local/share/desktop-directories/WineHelper.directory"),
|
||||
os.path.join(os.path.expanduser("~"), ".config/menus/applications-merged/WineHelper.menu")
|
||||
]
|
||||
for f in menu_files:
|
||||
if os.path.exists(f):
|
||||
for menu_file in menu_files:
|
||||
if os.path.exists(menu_file):
|
||||
try:
|
||||
os.remove(f)
|
||||
os.remove(menu_file)
|
||||
except FileNotFoundError:
|
||||
print(f"Файл меню не найден для удаления: {f}")
|
||||
print(f"Файл меню не найден для удаления: {menu_file}")
|
||||
except PermissionError:
|
||||
print(f"Нет доступа к файлу меню для удаления: {f}")
|
||||
print(f"Нет доступа к файлу меню для удаления: {menu_file}")
|
||||
except Exception as e:
|
||||
print(f"Ошибка удаления файла меню {f}: {str(e)}")
|
||||
print(f"Ошибка удаления файла меню {menu_file}: {str(e)}")
|
||||
except FileNotFoundError:
|
||||
print(f"Директория меню не найдена для удаления: {menu_dir}")
|
||||
except PermissionError:
|
||||
@@ -4418,13 +4442,16 @@ class WineHelperGUI(QMainWindow):
|
||||
|
||||
# Определяем виджеты и действия в зависимости от типа скрипта
|
||||
if script_name in self.autoinstall_scripts:
|
||||
script_path = os.path.join(Var.DATA_PATH, "autoinstall", script_name)
|
||||
data_path = Var.DATA_PATH or ""
|
||||
script_path = os.path.join(data_path, "autoinstall", script_name)
|
||||
tab_type = 'auto'
|
||||
if not os.path.exists(script_path): # Проверяем в testinstall, если не нашли в autoinstall
|
||||
script_path = os.path.join(Var.DATA_PATH, "testinstall", script_name)
|
||||
data_path = Var.DATA_PATH or ""
|
||||
script_path = os.path.join(data_path, "testinstall", script_name)
|
||||
self.manual_install_path_widget.setVisible(False)
|
||||
else:
|
||||
script_path = os.path.join(Var.DATA_PATH, "manualinstall", script_name)
|
||||
data_path = Var.DATA_PATH or ""
|
||||
script_path = os.path.join(data_path, "manualinstall", script_name)
|
||||
tab_type = 'manual'
|
||||
self.manual_install_path_widget.setVisible(True)
|
||||
|
||||
@@ -4454,7 +4481,8 @@ class WineHelperGUI(QMainWindow):
|
||||
|
||||
if icon_names:
|
||||
# Для заголовка используем первую иконку из списка
|
||||
icon_path = os.path.join(Var.DATA_PATH, "image", f"{icon_names[0]}.png")
|
||||
data_path = Var.DATA_PATH or ""
|
||||
icon_path = os.path.join(data_path, "image", f"{icon_names[0]}.png")
|
||||
if os.path.exists(icon_path):
|
||||
self.script_title.setPixmap(QPixmap(icon_path).scaled(64, 64, Qt.KeepAspectRatio))
|
||||
else:
|
||||
@@ -4592,11 +4620,13 @@ class WineHelperGUI(QMainWindow):
|
||||
self._reset_log_state() # Сбрасываем состояние для обработки лога
|
||||
|
||||
winehelper_path = self.winehelper_path
|
||||
script_path = os.path.join(Var.DATA_PATH,
|
||||
"autoinstall" if os.path.exists(os.path.join(Var.DATA_PATH, "autoinstall", self.current_script))
|
||||
else "testinstall" if os.path.exists(os.path.join(Var.DATA_PATH, "testinstall", self.current_script))
|
||||
data_path = Var.DATA_PATH or ""
|
||||
current_script = self.current_script or ""
|
||||
script_path = os.path.join(data_path,
|
||||
"autoinstall" if os.path.exists(os.path.join(data_path, "autoinstall", current_script))
|
||||
else "testinstall" if os.path.exists(os.path.join(data_path, "testinstall", current_script))
|
||||
else "manualinstall",
|
||||
self.current_script)
|
||||
current_script)
|
||||
|
||||
if not os.path.exists(winehelper_path):
|
||||
error_msg = f"winehelper не найден по пути:\n{winehelper_path}"
|
||||
@@ -4769,6 +4799,8 @@ class WineHelperGUI(QMainWindow):
|
||||
|
||||
def handle_process_output(self):
|
||||
"""Обрабатывает вывод процесса, корректно отображая однострочный прогресс."""
|
||||
if self.install_process is None:
|
||||
return
|
||||
new_data = self.install_process.readAllStandardOutput().data().decode('utf-8', errors='ignore')
|
||||
self.output_buffer += new_data
|
||||
|
||||
|
||||
Reference in New Issue
Block a user