All dangerzone-container subprocesses get called from global_commons, and if the user cancels or fails the authentication dialog, handle gracefully

This commit is contained in:
Micah Lee 2020-03-16 14:26:07 -07:00
parent 171fda8017
commit 3a1b6d457f
No known key found for this signature in database
GPG key ID: 403C2657CD994F73
6 changed files with 87 additions and 49 deletions

View file

@ -11,6 +11,10 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from .container import container_runtime from .container import container_runtime
class AuthorizationFailed(Exception):
pass
def is_docker_installed(global_common): def is_docker_installed(global_common):
if platform.system() == "Darwin": if platform.system() == "Darwin":
# Does the docker binary exist? # Does the docker binary exist?
@ -29,14 +33,17 @@ def is_docker_installed(global_common):
def is_docker_ready(global_common): def is_docker_ready(global_common):
# Run `docker image ls` without an error # Run `docker image ls` without an error
try: with global_common.exec_dangerzone_container(["image-ls"]) as p:
print(global_common.get_dangerzone_container_args()) p.communicate()
subprocess.run(
global_common.get_dangerzone_container_args() + ["image-ls"], # The user canceled, or permission denied
startupinfo=global_common.get_subprocess_startupinfo(), if p.returncode == 126 or p.returncode == 127:
) raise AuthorizationFailed
# Return true if it succeeds
if p.returncode == 0:
return True return True
except subprocess.CalledProcessError: else:
return False return False

View file

@ -6,6 +6,7 @@ import appdirs
import platform import platform
import subprocess import subprocess
import shlex import shlex
import pipes
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
if platform.system() == "Darwin": if platform.system() == "Darwin":
@ -284,14 +285,25 @@ class GlobalCommon(object):
else: else:
return "/usr/bin/dangerzone-container" return "/usr/bin/dangerzone-container"
def get_dangerzone_container_args(self): def exec_dangerzone_container(self, args):
# Prefix the args with the retainer runtime, and in the case linux when the user isn't in the docker group, pkexec
if platform.system() == "Linux": if platform.system() == "Linux":
if self.settings.get("linux_prefers_typing_password"): if self.settings.get("linux_prefers_typing_password"):
return ["/usr/bin/pkexec", self.dz_container_path] args = ["/usr/bin/pkexec", self.dz_container_path] + args
else: else:
return [self.dz_container_path] args = [self.dz_container_path] + args
else: else:
return [self.dz_container_path] args = [self.dz_container_path] + args
# Execute dangerzone-container
args_str = " ".join(pipes.quote(s) for s in args)
print(f"Executing: {args_str}")
return subprocess.Popen(
args,
startupinfo=self.get_subprocess_startupinfo(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def get_window_icon(self): def get_window_icon(self):
if platform.system() == "Windows": if platform.system() == "Windows":

View file

@ -15,6 +15,7 @@ from .docker_installer import (
is_docker_ready, is_docker_ready,
launch_docker_windows, launch_docker_windows,
DockerInstaller, DockerInstaller,
AuthorizationFailed,
) )
from .container import container_runtime from .container import container_runtime
@ -51,12 +52,21 @@ def gui_main(custom_container, filename):
if custom_container: if custom_container:
# Do we have this container? # Do we have this container?
output = subprocess.check_output( with global_common.exec_dangerzone_container(
global_common.get_dangerzone_container_args() ["image-ls", "--container-name", custom_container]
+ ["image-ls", custom_container], ) as p:
startupinfo=global_common.get_subprocess_startupinfo(), stdout_data, stderr_data = p.communicate()
)
if custom_container.encode() not in output: # The user canceled, or permission denied
if p.returncode == 126 or p.returncode == 127:
click.echo("Authorization failed")
return
elif p.returncode != 0:
click.echo("Container error")
return
# Check the output
if custom_container.encode() not in stdout_data:
click.echo(f"Container '{container}' not found") click.echo(f"Container '{container}' not found")
return return
@ -69,9 +79,13 @@ def gui_main(custom_container, filename):
if platform.system() == "Linux" and container_runtime == "/usr/bin/docker": if platform.system() == "Linux" and container_runtime == "/usr/bin/docker":
if not global_common.ensure_docker_group_preference(): if not global_common.ensure_docker_group_preference():
return return
try:
if not global_common.ensure_docker_service_is_started(): if not global_common.ensure_docker_service_is_started():
click.echo("Failed to start docker service") click.echo("Failed to start docker service")
return return
except AuthorizationFailed:
click.echo("Authorization failed")
return
# See if we need to install Docker... # See if we need to install Docker...
if (platform.system() == "Darwin" or platform.system() == "Windows") and ( if (platform.system() == "Darwin" or platform.system() == "Windows") and (

View file

@ -52,7 +52,11 @@ class MainWindow(QtWidgets.QMainWindow):
self.settings_widget.document_selected self.settings_widget.document_selected
) )
self.settings_widget.start_clicked.connect(self.start_clicked) self.settings_widget.start_clicked.connect(self.start_clicked)
self.settings_widget.close_window.connect(self.close)
self.settings_widget.hide() self.settings_widget.hide()
QtCore.QTimer.singleShot(
1, self.settings_widget.check_update_container_default_state
)
# Tasks # Tasks
self.tasks_widget = TasksWidget(self.global_common, self.common) self.tasks_widget = TasksWidget(self.global_common, self.common)

View file

@ -6,6 +6,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class SettingsWidget(QtWidgets.QWidget): class SettingsWidget(QtWidgets.QWidget):
start_clicked = QtCore.pyqtSignal() start_clicked = QtCore.pyqtSignal()
close_window = QtCore.pyqtSignal()
def __init__(self, global_common, common): def __init__(self, global_common, common):
super(SettingsWidget, self).__init__() super(SettingsWidget, self).__init__()
@ -134,22 +135,29 @@ class SettingsWidget(QtWidgets.QWidget):
else: else:
self.update_checkbox.setCheckState(QtCore.Qt.Unchecked) self.update_checkbox.setCheckState(QtCore.Qt.Unchecked)
def check_update_container_default_state(self):
# Is update containers required? # Is update containers required?
if self.global_common.custom_container: if self.global_common.custom_container:
self.update_checkbox.setCheckState(QtCore.Qt.Unchecked) self.update_checkbox.setCheckState(QtCore.Qt.Unchecked)
self.update_checkbox.setEnabled(False) self.update_checkbox.setEnabled(False)
self.update_checkbox.hide() self.update_checkbox.hide()
else: else:
output = subprocess.check_output( with self.global_common.exec_dangerzone_container(
self.global_common.get_dangerzone_container_args() [
+ [
"image-ls", "image-ls",
"--container-name", "--container-name",
self.global_common.get_container_name(), self.global_common.get_container_name(),
], ]
startupinfo=self.global_common.get_subprocess_startupinfo(), ) as p:
) stdout_data, stderror_data = p.communicate()
if b"dangerzone" not in output:
# The user canceled, or permission denied
if p.returncode == 126 or p.returncode == 127:
self.close_window.emit()
return
# Check the output
if b"dangerzone" not in stdout_data:
self.update_checkbox.setCheckState(QtCore.Qt.Checked) self.update_checkbox.setCheckState(QtCore.Qt.Checked)
self.update_checkbox.setEnabled(False) self.update_checkbox.setEnabled(False)

View file

@ -16,29 +16,25 @@ class TaskBase(QtCore.QThread):
super(TaskBase, self).__init__() super(TaskBase, self).__init__()
def exec_container(self, args): def exec_container(self, args):
args = self.global_common.get_dangerzone_container_args() + args
output = "" output = ""
self.update_details.emit(output) self.update_details.emit(output)
with subprocess.Popen( with self.global_common.exec_dangerzone_container(args) as p:
args,
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=1,
universal_newlines=True,
startupinfo=self.global_common.get_subprocess_startupinfo(),
) as p:
for line in p.stdout: for line in p.stdout:
output += line output += line.decode()
print(line, end="") print(line.decode(), end="")
self.update_details.emit(output) self.update_details.emit(output)
stderr = p.stderr.read() stderr = p.stderr.read()
output += stderr output += stderr.decode()
print(stderr) print(stderr.decode())
self.update_details.emit(output) self.update_details.emit(output)
if p.returncode == 126 or p.returncode == 127:
self.task_failed.emit(f"Authorization failed")
elif p.returncode == 0:
self.task_failed.emit(f"Return code: {p.returncode}")
print("") print("")
return p.returncode, output return p.returncode, output
@ -58,7 +54,6 @@ class PullImageTask(TaskBase):
returncode, _ = self.exec_container(args) returncode, _ = self.exec_container(args)
if returncode != 0: if returncode != 0:
self.task_failed.emit(f"Return code: {returncode}")
return return
self.task_finished.emit() self.task_finished.emit()
@ -88,7 +83,6 @@ class ConvertToPixels(TaskBase):
returncode, output = self.exec_container(args) returncode, output = self.exec_container(args)
if returncode != 0: if returncode != 0:
self.task_failed.emit(f"Return code: {returncode}")
return return
# Did we hit an error? # Did we hit an error?
@ -194,7 +188,6 @@ class ConvertToPDF(TaskBase):
returncode, output = self.exec_container(args) returncode, output = self.exec_container(args)
if returncode != 0: if returncode != 0:
self.task_failed.emit(f"Return code: {returncode}")
return return
self.task_finished.emit() self.task_finished.emit()