diff --git a/dangerzone/gui/__init__.py b/dangerzone/gui/__init__.py
index 664fe1c..0f51759 100644
--- a/dangerzone/gui/__init__.py
+++ b/dangerzone/gui/__init__.py
@@ -5,7 +5,7 @@ import platform
import signal
import sys
import typing
-from typing import Dict, List, Optional
+from typing import List, Optional
import click
import colorama
diff --git a/dangerzone/gui/main_window.py b/dangerzone/gui/main_window.py
index 4c739d0..27ca0bc 100644
--- a/dangerzone/gui/main_window.py
+++ b/dangerzone/gui/main_window.py
@@ -10,14 +10,18 @@ from typing import List, Optional
# FIXME: See https://github.com/freedomofpress/dangerzone/issues/320 for more details.
if typing.TYPE_CHECKING:
from PySide2 import QtCore, QtGui, QtSvg, QtWidgets
- from PySide2.QtWidgets import QAction
+ from PySide2.QtCore import Qt
+ from PySide2.QtWidgets import QAction, QTextEdit
else:
try:
from PySide6 import QtCore, QtGui, QtSvg, QtWidgets
+ from PySide6.QtCore import Qt
from PySide6.QtGui import QAction
+ from PySide6.QtWidgets import QTextEdit
except ImportError:
from PySide2 import QtCore, QtGui, QtSvg, QtWidgets
- from PySide2.QtWidgets import QAction
+ from PySide2.QtWidgets import QAction, QTextEdit
+ from PySide2.QtCore import Qt
from .. import errors
from ..document import SAFE_EXTENSION, Document
@@ -434,10 +438,29 @@ class WaitingWidgetContainer(WaitingWidget):
self.buttons = QtWidgets.QWidget()
self.buttons.setLayout(buttons_layout)
+ # Error
+ self.error_text = QTextEdit()
+ self.error_text.setReadOnly(True)
+ self.error_text.setStyleSheet(
+ """
+ QTextEdit {
+ font-family: Consolas, Monospace;
+ font-size: 12px;
+ background-color: #fff;
+ color: #000;
+ padding: 10px;
+ }
+ """
+ )
+ self.error_text.setVisible(False)
+ # Enable copying
+ self.error_text.setTextInteractionFlags(Qt.TextSelectableByMouse)
+
# Layout
layout = QtWidgets.QVBoxLayout()
layout.addStretch()
layout.addWidget(self.label)
+ layout.addWidget(self.error_text)
layout.addStretch()
layout.addWidget(self.buttons)
layout.addStretch()
@@ -448,49 +471,84 @@ class WaitingWidgetContainer(WaitingWidget):
def check_state(self) -> None:
state: Optional[str] = None
+ error: Optional[str] = None
try:
if isinstance( # Sanity check
self.dangerzone.isolation_provider, Container
):
container_runtime = self.dangerzone.isolation_provider.get_runtime()
+ runtime_name = self.dangerzone.isolation_provider.get_runtime_name()
except NoContainerTechException as e:
log.error(str(e))
state = "not_installed"
else:
- # Can we run `docker image ls` without an error
+ # Can we run `docker/podman image ls` without an error
with subprocess.Popen(
[container_runtime, "image", "ls"],
stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
startupinfo=get_subprocess_startupinfo(),
) as p:
- p.communicate()
+ _, stderr = p.communicate()
if p.returncode != 0:
- log.error("Docker is not running")
+ log.error(f"{runtime_name} is not running")
state = "not_running"
+ error = stderr.decode()
else:
# Always try installing the container
state = "install_container"
# Update the state
- self.state_change(state)
+ self.state_change(state, error)
- def state_change(self, state: str) -> None:
+ def state_change(self, state: str, error: str | None = None) -> None:
if state == "not_installed":
- self.label.setText(
- "Dangerzone Requires Docker Desktop
Download Docker Desktop, install it, and open it."
- )
+ if platform.system() == "Linux":
+ self.label.setText(
+ (
+ "Dangerzone requires Podman
"
+ "Install it and retry."
+ )
+ )
+ else:
+ self.label.setText(
+ (
+ "Dangerzone requires Docker Desktop
"
+ "Download Docker Desktop"
+ ", install it, and open it."
+ )
+ )
self.buttons.show()
elif state == "not_running":
- self.label.setText(
- "Dangerzone Requires Docker Desktop
Docker is installed but isn't running.
Open Docker and make sure it's running in the background."
- )
+ if platform.system() == "Linux":
+ # "not_running" here means that the `podman image ls` command failed.
+ message = (
+ "Dangerzone requires Podman
"
+ "Podman is installed but cannot run properly. See errors below"
+ )
+ if error:
+ self.error_text.setPlainText(error)
+ self.error_text.setVisible(True)
+
+ self.label.setText(message)
+
+ else:
+ self.label.setText(
+ (
+ "Dangerzone requires Docker Desktop
"
+ "Docker is installed but isn't running.
"
+ "Open Docker and make sure it's running in the background."
+ )
+ )
self.buttons.show()
else:
self.label.setText(
- "Installing the Dangerzone container image.
This might take a few minutes..."
+ (
+ "Installing the Dangerzone container image.
"
+ "This might take a few minutes..."
+ )
)
self.buttons.hide()
self.install_container_t = InstallContainerThread(self.dangerzone)
diff --git a/dangerzone/isolation_provider/container.py b/dangerzone/isolation_provider/container.py
index 76c4aa0..7137d57 100644
--- a/dangerzone/isolation_provider/container.py
+++ b/dangerzone/isolation_provider/container.py
@@ -242,7 +242,7 @@ class Container(IsolationProvider):
# `int`.
#
# See https://stackoverflow.com/a/37888668
- if not type(val) == _type:
+ if type(val) is not _type:
raise ValueError("Status field has incorrect type")
def parse_progress_trusted(self, document: Document, line: str) -> None: