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
class AuthorizationFailed(Exception):
pass
def is_docker_installed(global_common):
if platform.system() == "Darwin":
# Does the docker binary exist?
@ -29,15 +33,18 @@ def is_docker_installed(global_common):
def is_docker_ready(global_common):
# Run `docker image ls` without an error
try:
print(global_common.get_dangerzone_container_args())
subprocess.run(
global_common.get_dangerzone_container_args() + ["image-ls"],
startupinfo=global_common.get_subprocess_startupinfo(),
)
return True
except subprocess.CalledProcessError:
return False
with global_common.exec_dangerzone_container(["image-ls"]) as p:
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:
return False
def launch_docker_windows(global_common):

View file

@ -6,6 +6,7 @@ import appdirs
import platform
import subprocess
import shlex
import pipes
from PyQt5 import QtCore, QtGui, QtWidgets
if platform.system() == "Darwin":
@ -284,14 +285,25 @@ class GlobalCommon(object):
else:
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 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:
return [self.dz_container_path]
args = [self.dz_container_path] + args
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):
if platform.system() == "Windows":

View file

@ -15,6 +15,7 @@ from .docker_installer import (
is_docker_ready,
launch_docker_windows,
DockerInstaller,
AuthorizationFailed,
)
from .container import container_runtime
@ -51,14 +52,23 @@ def gui_main(custom_container, filename):
if custom_container:
# Do we have this container?
output = subprocess.check_output(
global_common.get_dangerzone_container_args()
+ ["image-ls", custom_container],
startupinfo=global_common.get_subprocess_startupinfo(),
)
if custom_container.encode() not in output:
click.echo(f"Container '{container}' not found")
return
with global_common.exec_dangerzone_container(
["image-ls", "--container-name", custom_container]
) as p:
stdout_data, stderr_data = p.communicate()
# 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")
return
global_common.custom_container = custom_container
@ -69,8 +79,12 @@ def gui_main(custom_container, filename):
if platform.system() == "Linux" and container_runtime == "/usr/bin/docker":
if not global_common.ensure_docker_group_preference():
return
if not global_common.ensure_docker_service_is_started():
click.echo("Failed to start docker service")
try:
if not global_common.ensure_docker_service_is_started():
click.echo("Failed to start docker service")
return
except AuthorizationFailed:
click.echo("Authorization failed")
return
# See if we need to install Docker...

View file

@ -52,7 +52,11 @@ class MainWindow(QtWidgets.QMainWindow):
self.settings_widget.document_selected
)
self.settings_widget.start_clicked.connect(self.start_clicked)
self.settings_widget.close_window.connect(self.close)
self.settings_widget.hide()
QtCore.QTimer.singleShot(
1, self.settings_widget.check_update_container_default_state
)
# Tasks
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):
start_clicked = QtCore.pyqtSignal()
close_window = QtCore.pyqtSignal()
def __init__(self, global_common, common):
super(SettingsWidget, self).__init__()
@ -134,24 +135,31 @@ class SettingsWidget(QtWidgets.QWidget):
else:
self.update_checkbox.setCheckState(QtCore.Qt.Unchecked)
def check_update_container_default_state(self):
# Is update containers required?
if self.global_common.custom_container:
self.update_checkbox.setCheckState(QtCore.Qt.Unchecked)
self.update_checkbox.setEnabled(False)
self.update_checkbox.hide()
else:
output = subprocess.check_output(
self.global_common.get_dangerzone_container_args()
+ [
with self.global_common.exec_dangerzone_container(
[
"image-ls",
"--container-name",
self.global_common.get_container_name(),
],
startupinfo=self.global_common.get_subprocess_startupinfo(),
)
if b"dangerzone" not in output:
self.update_checkbox.setCheckState(QtCore.Qt.Checked)
self.update_checkbox.setEnabled(False)
]
) as p:
stdout_data, stderror_data = p.communicate()
# 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.setEnabled(False)
def update_ui(self):
if platform.system() == "Windows":

View file

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