From 42ce884419dab2b5582acc374950a363e7e5065f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 22 Nov 2021 14:06:31 -0800 Subject: [PATCH] Work on changing the WaitingWidget to check for Docker --- .gitmodules | 6 -- dangerzone/global_common.py | 30 ++++----- dangerzone/gui/__init__.py | 15 ----- dangerzone/gui/common.py | 3 +- dangerzone/gui/docker_installer.py | 99 ------------------------------ dangerzone/gui/main_window.py | 86 +++++++++++++++++++++----- vendor/hyperkit | 1 - vendor/vpnkit | 1 - 8 files changed, 83 insertions(+), 158 deletions(-) delete mode 100644 .gitmodules delete mode 100644 dangerzone/gui/docker_installer.py delete mode 160000 vendor/hyperkit delete mode 160000 vendor/vpnkit diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index c646cbe..0000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "vendor/hyperkit"] - path = vendor/hyperkit - url = https://github.com/moby/hyperkit.git -[submodule "vendor/vpnkit"] - path = vendor/vpnkit - url = https://github.com/moby/vpnkit.git diff --git a/dangerzone/global_common.py b/dangerzone/global_common.py index 53af5c0..b80faa2 100644 --- a/dangerzone/global_common.py +++ b/dangerzone/global_common.py @@ -35,8 +35,8 @@ class GlobalCommon(object): # App data folder self.appdata_path = appdirs.user_config_dir("dangerzone") - # In case we have a custom container - self.custom_container = None + # Container name + self.container_name = "dangerzone.rocks/dangerzone" # Languages supported by tesseract self.ocr_languages = { @@ -380,12 +380,6 @@ class GlobalCommon(object): ) print(Back.BLACK + Fore.YELLOW + Style.DIM + "╰──────────────────────────╯") - def get_container_name(self): - if self.custom_container: - return self.custom_container - else: - return "docker.io/flmcode/dangerzone" - def get_resource_path(self, filename): if getattr(sys, "dangerzone_dev", False): # Look for resources directory relative to python file @@ -414,17 +408,17 @@ class GlobalCommon(object): def exec_dangerzone_container(self, input_filename, output_filename, ocr_lang): convert(self, input_filename, output_filename, ocr_lang) - args = [self.dz_container_path] + args - args_str = " ".join(pipes.quote(s) for s in args) - print(Style.DIM + "> " + Style.NORMAL + Fore.CYAN + args_str) + # args = [self.dz_container_path] + args + # args_str = " ".join(pipes.quote(s) for s in args) + # print(Style.DIM + "> " + Style.NORMAL + Fore.CYAN + args_str) - # Execute dangerzone-container - return subprocess.Popen( - args, - startupinfo=self.get_subprocess_startupinfo(), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) + # # Execute dangerzone-container + # return subprocess.Popen( + # args, + # startupinfo=self.get_subprocess_startupinfo(), + # stdout=subprocess.PIPE, + # stderr=subprocess.PIPE, + # ) def get_subprocess_startupinfo(self): if platform.system() == "Windows": diff --git a/dangerzone/gui/__init__.py b/dangerzone/gui/__init__.py index b21996b..4c8fe0f 100644 --- a/dangerzone/gui/__init__.py +++ b/dangerzone/gui/__init__.py @@ -9,12 +9,6 @@ from PySide2 import QtCore, QtWidgets from .common import GuiCommon from .main_window import MainWindow from .systray import SysTray -from .docker_installer import ( - is_docker_installed, - is_docker_ready, - DockerInstaller, - AuthorizationFailed, -) from ..global_common import GlobalCommon @@ -86,15 +80,6 @@ def gui_main(filename): # Allow Ctrl-C to smoothly quit the program instead of throwing an exception signal.signal(signal.SIGINT, signal.SIG_DFL) - # See if we need to install Docker (Windows-only) - if (platform.system() == "Windows" or platform.system() == "Darwin") and ( - not is_docker_installed() or not is_docker_ready(global_common) - ): - click.echo("Docker is either not installed or not running") - docker_installer = DockerInstaller(gui_common) - docker_installer.start() - return - # Create the system tray systray = SysTray(global_common, gui_common, app, app_wrapper) diff --git a/dangerzone/gui/common.py b/dangerzone/gui/common.py index a5c8f51..aa478f8 100644 --- a/dangerzone/gui/common.py +++ b/dangerzone/gui/common.py @@ -14,7 +14,6 @@ elif platform.system() == "Linux": import getpass from xdg.DesktopEntry import DesktopEntry -from .docker_installer import is_docker_ready from ..settings import Settings @@ -36,7 +35,7 @@ class GuiCommon(object): # Preload list of PDF viewers on computer self.pdf_viewers = self._find_pdf_viewers() - # Are we done waiting (for VM to start, or container to install) + # Are we done waiting (for Docker Desktop to be installed, or for container to install) self.is_waiting_finished = False def get_window_icon(self): diff --git a/dangerzone/gui/docker_installer.py b/dangerzone/gui/docker_installer.py deleted file mode 100644 index d113e36..0000000 --- a/dangerzone/gui/docker_installer.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import subprocess -import shutil -import platform -from PySide2 import QtCore, QtWidgets - - -class AuthorizationFailed(Exception): - pass - - -container_runtime = shutil.which("docker") - - -def is_docker_installed(): - return container_runtime is not None - - -def is_docker_ready(global_common): - # Run `docker image ls` without an error - with subprocess.Popen([container_runtime, "image", "ls"]) as p: - outs, errs = p.communicate() - - # The user canceled, or permission denied - if p.returncode == 126 or p.returncode == 127: - raise AuthorizationFailed - - # Return true if it succeeds - if p.returncode == 0: - return True - else: - print(outs) - print(errs) - return False - - -def launch_docker_windows(global_common): - docker_desktop_path = "C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe" - subprocess.Popen( - [docker_desktop_path], startupinfo=global_common.get_subprocess_startupinfo() - ) - - -class DockerInstaller(QtWidgets.QDialog): - def __init__(self, gui_common): - super(DockerInstaller, self).__init__() - - self.setWindowTitle("Dangerzone") - self.setWindowIcon(gui_common.get_window_icon()) - # self.setMinimumHeight(170) - - label = QtWidgets.QLabel() - if platform.system() == "Darwin": - label.setText("Dangerzone for macOS requires Docker") - elif platform.system() == "Windows": - label.setText("Dangerzone for Windows requires Docker") - label.setStyleSheet("QLabel { font-weight: bold; }") - label.setAlignment(QtCore.Qt.AlignCenter) - - self.task_label = QtWidgets.QLabel() - self.task_label.setAlignment(QtCore.Qt.AlignCenter) - self.task_label.setWordWrap(True) - self.task_label.setOpenExternalLinks(True) - - self.ok_button = QtWidgets.QPushButton("OK") - self.ok_button.clicked.connect(self.ok_clicked) - - buttons_layout = QtWidgets.QHBoxLayout() - buttons_layout.addStretch() - buttons_layout.addWidget(self.ok_button) - buttons_layout.addStretch() - - layout = QtWidgets.QVBoxLayout() - layout.addWidget(label) - layout.addWidget(self.task_label) - layout.addLayout(buttons_layout) - layout.addStretch() - self.setLayout(layout) - - if platform.system() == "Darwin": - self.docker_path = "/Applications/Docker.app/Contents/Resources/bin/docker" - elif platform.system() == "Windows": - self.docker_path = shutil.which("docker.exe") - - def ok_clicked(self): - self.accept() - - def start(self): - if not os.path.exists(self.docker_path): - self.task_label.setText( - "Download Docker Desktop, install it, and then run Dangerzone again." - ) - self.task_label.setTextFormat(QtCore.Qt.RichText) - else: - self.task_label.setText( - "Docker Desktop is installed, but you must launch it first. Open Docker, make sure it's running, and then open Dangerzone again." - ) - - return self.exec_() == QtWidgets.QDialog.Accepted diff --git a/dangerzone/gui/main_window.py b/dangerzone/gui/main_window.py index 0dcf881..56cb8bd 100644 --- a/dangerzone/gui/main_window.py +++ b/dangerzone/gui/main_window.py @@ -3,6 +3,7 @@ import platform import tempfile import subprocess import json +import shutil from PySide2 import QtCore, QtGui, QtWidgets from colorama import Style, Fore @@ -43,7 +44,7 @@ class MainWindow(QtWidgets.QMainWindow): header_layout.addWidget(header_label) header_layout.addStretch() - # Waiting widget, replaces content widget while VM is booting + # Waiting widget, replaces content widget while container runtime isn't available self.waiting_widget = WaitingWidget(self.global_common, self.gui_common) self.waiting_widget.finished.connect(self.waiting_finished) @@ -53,7 +54,7 @@ class MainWindow(QtWidgets.QMainWindow): ) self.content_widget.close_window.connect(self.close) - # Only use the waiting widget if we have a VM + # Only use the waiting widget if container runtime isn't available if self.gui_common.is_waiting_finished: self.waiting_widget.hide() self.content_widget.show() @@ -101,6 +102,15 @@ class InstallContainerThread(QtCore.QThread): class WaitingWidget(QtWidgets.QWidget): + # These are the possible states that the WaitingWidget can show. + # + # Windows and macOS states: + # - "not_installed" + # - "not_running" + # - "install_container" + # + # Linux states + # - "install_container" finished = QtCore.Signal() def __init__(self, global_common, gui_common): @@ -110,34 +120,78 @@ class WaitingWidget(QtWidgets.QWidget): self.label = QtWidgets.QLabel() self.label.setAlignment(QtCore.Qt.AlignCenter) + self.label.setTextFormat(QtCore.Qt.RichText) self.label.setStyleSheet("QLabel { font-size: 20px; }") + # Buttons + check_button = QtWidgets.QPushButton("Check Again") + check_button.clicked.connect(self.check_state) + buttons_layout = QtWidgets.QHBoxLayout() + buttons_layout.addStretch() + buttons_layout.addWidget(check_button) + buttons_layout.addStretch() + self.buttons = QtWidgets.QWidget() + self.buttons.setLayout(buttons_layout) + # Layout layout = QtWidgets.QVBoxLayout() layout.addStretch() layout.addWidget(self.label) + layout.addWidget(self.buttons) layout.addStretch() self.setLayout(layout) - if platform.system() == "Darwin": - self.label.setText("Waiting for the Dangerzone virtual machine to start...") - self.global_common.vm.vm_state_change.connect(self.vm_state_change) + # Check the state + self.check_state() - elif platform.system() == "Linux": - self.label.setText("Installing the Dangerzone container...") + def check_state(self): + state = None + + # Can we find the container runtime binary binary + if platform.system() == "Linux": + container_runtime = shutil.which("podman") + else: + container_runtime = shutil.which("docker") + + if container_runtime is None: + print("Docker is not installed") + state = "not_installed" + + else: + # Can we run `docker image ls` without an error + with subprocess.Popen([container_runtime, "image", "ls"]) as p: + p.communicate() + if p.returncode != 0: + print("Docker is not running") + state = "not_running" + else: + # Always try installing the container + print("Ensuring the container is installed") + state = "install_container" + + # Update the state + self.state_change(state) + + def state_change(self, state): + if state == "not_installed": + self.label.setText( + "Dangerzone requires Docker

Download Docker Desktop and install it." + ) + self.buttons.show() + elif state == "not_running": + self.label.setText( + "Docker Desktop is installed, but you must launch it first. Open Docker and make sure it's running in the background." + ) + self.buttons.show() + else: + self.label.setText( + "Installing the Dangerzone container..." + ) + self.buttons.hide() self.install_container_t = InstallContainerThread(self.global_common) self.install_container_t.finished.connect(self.finished) self.install_container_t.start() - else: - self.label.setText("Platform not implemented yet") - - def vm_state_change(self, state): - if state == self.global_common.vm.STATE_ON: - self.finished.emit() - elif state == self.global_common.vm.STATE_FAIL: - self.label.setText("Dangerzone virtual machine failed to start :(") - class ContentWidget(QtWidgets.QWidget): close_window = QtCore.Signal() diff --git a/vendor/hyperkit b/vendor/hyperkit deleted file mode 160000 index 09fe920..0000000 --- a/vendor/hyperkit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 09fe9202a29a56a532d07505fd91831989b9afeb diff --git a/vendor/vpnkit b/vendor/vpnkit deleted file mode 160000 index 16ed722..0000000 --- a/vendor/vpnkit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 16ed722e6dc24307e99aee931ffd0eb80a9487d0