mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00
This creates a separate script dangerzone-container which is a wrapper for running the container. This lets us run dangerzone as unprivileged, but dangerzone-container as privileged, to avoid adding the user to the dangerzone group.
This commit is contained in:
parent
0b1cd9e9ef
commit
cf367adcfa
12 changed files with 424 additions and 215 deletions
|
@ -1,146 +1,13 @@
|
|||
from PyQt5 import QtCore, QtWidgets
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import platform
|
||||
import click
|
||||
import time
|
||||
import uuid
|
||||
import subprocess
|
||||
|
||||
from .global_common import GlobalCommon
|
||||
from .main_window import MainWindow
|
||||
from .docker_installer import (
|
||||
is_docker_installed,
|
||||
is_docker_ready,
|
||||
launch_docker_windows,
|
||||
DockerInstaller,
|
||||
)
|
||||
from .container import container_main
|
||||
|
||||
dangerzone_version = "0.1"
|
||||
|
||||
|
||||
class Application(QtWidgets.QApplication):
|
||||
document_selected = QtCore.pyqtSignal(str)
|
||||
application_activated = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
QtWidgets.QApplication.__init__(self, sys.argv)
|
||||
|
||||
def event(self, event):
|
||||
# In macOS, handle the file open event
|
||||
if event.type() == QtCore.QEvent.FileOpen:
|
||||
self.document_selected.emit(event.file())
|
||||
return True
|
||||
elif event.type() == QtCore.QEvent.ApplicationActivate:
|
||||
self.application_activated.emit()
|
||||
return True
|
||||
|
||||
return QtWidgets.QApplication.event(self, event)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--custom-container") # Use this container instead of flmcode/dangerzone
|
||||
@click.argument("filename", required=False)
|
||||
def main(custom_container, filename):
|
||||
click.echo(f"dangerzone {dangerzone_version}")
|
||||
|
||||
# Create the Qt app
|
||||
app = Application()
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
|
||||
# GlobalCommon object
|
||||
global_common = GlobalCommon(app)
|
||||
|
||||
if custom_container:
|
||||
# Do we have this container?
|
||||
output = subprocess.check_output(
|
||||
[global_common.container_runtime, "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
|
||||
|
||||
global_common.custom_container = custom_container
|
||||
|
||||
# Allow Ctrl-C to smoothly quit the program instead of throwing an exception
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
# If we're using Linux and docker, see if we need to add the user to the docker group
|
||||
if (
|
||||
platform.system() == "Linux"
|
||||
and global_common.container_runtime == "/usr/bin/docker"
|
||||
):
|
||||
if not global_common.ensure_user_is_in_docker_group():
|
||||
click.echo("Failed to add user to docker group")
|
||||
return
|
||||
if not global_common.ensure_docker_service_is_started():
|
||||
click.echo("Failed to start docker service")
|
||||
return
|
||||
|
||||
# See if we need to install Docker...
|
||||
if (platform.system() == "Darwin" or platform.system() == "Windows") and (
|
||||
not is_docker_installed(global_common) or not is_docker_ready(global_common)
|
||||
):
|
||||
click.echo("Docker is either not installed or not running")
|
||||
docker_installer = DockerInstaller(global_common)
|
||||
docker_installer.start()
|
||||
return
|
||||
|
||||
closed_windows = {}
|
||||
windows = {}
|
||||
|
||||
def delete_window(window_id):
|
||||
closed_windows[window_id] = windows[window_id]
|
||||
del windows[window_id]
|
||||
|
||||
# Open a document in a window
|
||||
def select_document(filename=None):
|
||||
if (
|
||||
len(windows) == 1
|
||||
and windows[list(windows.keys())[0]].common.document_filename == None
|
||||
):
|
||||
window = windows[list(windows.keys())[0]]
|
||||
else:
|
||||
window_id = uuid.uuid4().hex
|
||||
window = MainWindow(global_common, window_id)
|
||||
window.delete_window.connect(delete_window)
|
||||
windows[window_id] = window
|
||||
|
||||
if filename:
|
||||
# Validate filename
|
||||
filename = os.path.abspath(os.path.expanduser(filename))
|
||||
try:
|
||||
open(filename, "rb")
|
||||
except FileNotFoundError:
|
||||
click.echo("File not found")
|
||||
return False
|
||||
except PermissionError:
|
||||
click.echo("Permission denied")
|
||||
return False
|
||||
window.common.document_filename = filename
|
||||
window.doc_selection_widget.document_selected.emit()
|
||||
|
||||
return True
|
||||
|
||||
# Open a new window if not filename is passed
|
||||
if filename is None:
|
||||
select_document()
|
||||
else:
|
||||
# If filename is passed as an argument, open it
|
||||
if not select_document(filename):
|
||||
return True
|
||||
|
||||
# Open a new window, if all windows are closed
|
||||
def application_activated():
|
||||
if len(windows) == 0:
|
||||
select_document()
|
||||
|
||||
# If we get a file open event, open it
|
||||
app.document_selected.connect(select_document)
|
||||
|
||||
# If the application is activated and all windows are closed, open a new one
|
||||
app.application_activated.connect(application_activated)
|
||||
|
||||
sys.exit(app.exec_())
|
||||
# This is a hack for Windows and Mac to be able to run dangerzone-container, even though
|
||||
# PyInstaller builds a single binary
|
||||
if os.path.basename(sys.argv[0]) == "dangerzone-container":
|
||||
main = container_main
|
||||
else:
|
||||
# If the binary isn't "dangerzone-contatiner", then launch the GUI
|
||||
from .gui import gui_main as main
|
||||
|
|
115
dangerzone/container.py
Normal file
115
dangerzone/container.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
import click
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import pipes
|
||||
import getpass
|
||||
|
||||
# What is the container runtime for this platform?
|
||||
if platform.system() == "Darwin":
|
||||
container_runtime = "/usr/local/bin/docker"
|
||||
elif platform.system() == "Windows":
|
||||
container_runtime = "C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe"
|
||||
else:
|
||||
container_runtime = "/usr/bin/docker"
|
||||
|
||||
# Define startupinfo for subprocesses
|
||||
if platform.system() == "Windows":
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
else:
|
||||
startupinfo = None
|
||||
|
||||
|
||||
def exec_container(args):
|
||||
args = [container_runtime] + args
|
||||
|
||||
args_str = " ".join(pipes.quote(s) for s in args)
|
||||
sys.stdout.write(f"Executing: {args_str}\n\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
with subprocess.Popen(
|
||||
args,
|
||||
stdin=None,
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
bufsize=1,
|
||||
universal_newlines=True,
|
||||
startupinfo=startupinfo,
|
||||
) as p:
|
||||
p.communicate()
|
||||
return p.returncode
|
||||
|
||||
|
||||
@click.group()
|
||||
def container_main():
|
||||
"""
|
||||
Dangerzone container commands with elevated privileges.
|
||||
Humans don't need to run this command by themselves.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@container_main.command()
|
||||
@click.option("--container-name", default="flmcode/dangerzone")
|
||||
def image_ls(container_name):
|
||||
"""docker image ls [container_name]"""
|
||||
sys.exit(exec_container(["image", "ls", container_name]))
|
||||
|
||||
|
||||
@container_main.command()
|
||||
def pull():
|
||||
"""docker pull flmcode/dangerzone"""
|
||||
sys.exit(exec_container(["pull", "flmcode/dangerzone"]))
|
||||
|
||||
|
||||
@container_main.command()
|
||||
@click.option("--document-filename", required=True)
|
||||
@click.option("--pixel-dir", required=True)
|
||||
@click.option("--container-name", default="flmcode/dangerzone")
|
||||
def document_to_pixels(document_filename, pixel_dir, container_name):
|
||||
"""docker run --network none -v [document_filename]:/tmp/input_file -v [pixel_dir]:/dangerzone [container_name] document-to-pixels"""
|
||||
sys.exit(
|
||||
exec_container(
|
||||
[
|
||||
"run",
|
||||
"--network",
|
||||
"none",
|
||||
"-v",
|
||||
f"{document_filename}:/tmp/input_file",
|
||||
"-v",
|
||||
f"{pixel_dir}:/dangerzone",
|
||||
container_name,
|
||||
"document-to-pixels",
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@container_main.command()
|
||||
@click.option("--pixel-dir", required=True)
|
||||
@click.option("--safe-dir", required=True)
|
||||
@click.option("--container-name", default="flmcode/dangerzone")
|
||||
@click.option("--ocr", required=True)
|
||||
@click.option("--ocr-lang", required=True)
|
||||
def pixels_to_pdf(pixel_dir, safe_dir, container_name, ocr, ocr_lang):
|
||||
"""docker run --network none -v [pixel_dir]:/dangerzone -v [safe_dir]:/safezone [container_name] -e OCR=[ocr] -e OCR_LANGUAGE=[ocr_lang] pixels-to-pdf"""
|
||||
sys.exit(
|
||||
exec_container(
|
||||
[
|
||||
"run",
|
||||
"--network",
|
||||
"none",
|
||||
"-v",
|
||||
f"{pixel_dir}:/dangerzone",
|
||||
"-v",
|
||||
f"{safe_dir}:/safezone",
|
||||
"-e",
|
||||
f"OCR={ocr}",
|
||||
"-e",
|
||||
f"OCR_LANGUAGE={ocr_lang}",
|
||||
container_name,
|
||||
"pixels-to-pdf",
|
||||
]
|
||||
)
|
||||
)
|
|
@ -8,29 +8,31 @@ import time
|
|||
import platform
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from .container import container_runtime
|
||||
|
||||
|
||||
def is_docker_installed(global_common):
|
||||
if platform.system() == "Darwin":
|
||||
# Does the docker binary exist?
|
||||
if os.path.isdir("/Applications/Docker.app") and os.path.exists(
|
||||
global_common.container_runtime
|
||||
container_runtime
|
||||
):
|
||||
# Is it executable?
|
||||
st = os.stat(global_common.container_runtime)
|
||||
st = os.stat(container_runtime)
|
||||
return bool(st.st_mode & stat.S_IXOTH)
|
||||
|
||||
if platform.system() == "Windows":
|
||||
return os.path.exists(global_common.container_runtime)
|
||||
return os.path.exists(container_runtime)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def is_docker_ready(global_common):
|
||||
# Run `docker ps` without an error
|
||||
# Run `docker image ls` without an error
|
||||
try:
|
||||
print(global_common.get_dangerzone_container_args())
|
||||
subprocess.run(
|
||||
[global_common.container_runtime, "ps"],
|
||||
check=True,
|
||||
global_common.get_dangerzone_container_args() + ["image-ls"],
|
||||
startupinfo=global_common.get_subprocess_startupinfo(),
|
||||
)
|
||||
return True
|
||||
|
|
|
@ -57,26 +57,12 @@ class GlobalCommon(object):
|
|||
# App data folder
|
||||
self.appdata_path = appdirs.user_config_dir("dangerzone")
|
||||
|
||||
# Container runtime
|
||||
if platform.system() == "Darwin":
|
||||
self.container_runtime = "/usr/local/bin/docker"
|
||||
elif platform.system() == "Windows":
|
||||
self.container_runtime = (
|
||||
"C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe"
|
||||
)
|
||||
else:
|
||||
# Linux
|
||||
|
||||
# If this is fedora-like, use podman
|
||||
if os.path.exists("/usr/bin/dnf"):
|
||||
self.container_runtime = "/usr/bin/podman"
|
||||
# Otherwise, use docker
|
||||
else:
|
||||
self.container_runtime = "/usr/bin/docker"
|
||||
|
||||
# In case we have a custom container
|
||||
self.custom_container = None
|
||||
|
||||
# dangerzone-container path
|
||||
self.dz_container_path = self.get_dangerzone_container_path()
|
||||
|
||||
# Preload list of PDF viewers on computer
|
||||
self.pdf_viewers = self._find_pdf_viewers()
|
||||
|
||||
|
@ -278,6 +264,35 @@ class GlobalCommon(object):
|
|||
resource_path = os.path.join(prefix, filename)
|
||||
return resource_path
|
||||
|
||||
def get_dangerzone_container_path(self):
|
||||
if getattr(sys, "dangerzone_dev", False):
|
||||
# Look for resources directory relative to python file
|
||||
return os.path.join(
|
||||
os.path.dirname(
|
||||
os.path.dirname(
|
||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||
)
|
||||
),
|
||||
"dev_scripts",
|
||||
"dangerzone-container",
|
||||
)
|
||||
else:
|
||||
if platform.system() == "Darwin":
|
||||
return os.path.join(os.path.dirname(sys.executable), "dangerzone-container")
|
||||
elif platform.system() == "Windows":
|
||||
return os.path.join(os.path.dirname(sys.executable), "dangerzone-container.exe")
|
||||
else:
|
||||
return "/usr/bin/dangerzone-container"
|
||||
|
||||
def get_dangerzone_container_args(self):
|
||||
if platform.system() == "Linux":
|
||||
if self.settings.get("linux_prefers_typing_password"):
|
||||
return ["/usr/bin/pkexec", self.dz_container_path]
|
||||
else:
|
||||
return [self.dz_container_path]
|
||||
else:
|
||||
return [self.dz_container_path]
|
||||
|
||||
def get_window_icon(self):
|
||||
if platform.system() == "Windows":
|
||||
path = self.get_resource_path("dangerzone.ico")
|
||||
|
@ -372,18 +387,40 @@ class GlobalCommon(object):
|
|||
|
||||
return pdf_viewers
|
||||
|
||||
def ensure_user_is_in_docker_group(self):
|
||||
def ensure_docker_group_preference(self):
|
||||
# If the user prefers typing their password
|
||||
if self.settings.get("linux_prefers_typing_password") == True:
|
||||
return True
|
||||
|
||||
# Get the docker group
|
||||
try:
|
||||
groupinfo = grp.getgrnam("docker")
|
||||
except:
|
||||
# Ignore if group is not found
|
||||
return True
|
||||
|
||||
# See if the user is in the group
|
||||
username = getpass.getuser()
|
||||
if username not in groupinfo.gr_mem:
|
||||
# User is not in docker group, so prompt about adding the user to the docker group
|
||||
message = "<b>Dangerzone requires Docker.</b><br><br>Click Ok to add your user to the 'docker' group. You will have to type your login password."
|
||||
if Alert(self, message).launch():
|
||||
# User is not in the docker group, ask if they prefer typing their password
|
||||
message = "<b>Dangerzone requires Docker</b><br><br>In order to use Docker, your user must be in the 'docker' group or you'll need to type your password each time you run dangerzone.<br><br><b>Adding your user to the 'docker' group is more convenient but less secure</b>, and will require just typing your password once. Which do you prefer?"
|
||||
return_code = Alert(
|
||||
self,
|
||||
message,
|
||||
ok_text="I'll type my password each time",
|
||||
extra_button_text="Add my user to the 'docker' group",
|
||||
).launch()
|
||||
if return_code == QtWidgets.QDialog.Accepted:
|
||||
# Prefers typing password
|
||||
self.settings.set("linux_prefers_typing_password", True)
|
||||
self.settings.save()
|
||||
return True
|
||||
elif return_code == 2:
|
||||
# Prefers being in the docker group
|
||||
self.settings.set("linux_prefers_typing_password", False)
|
||||
self.settings.save()
|
||||
|
||||
# Add user to the docker group
|
||||
p = subprocess.run(
|
||||
[
|
||||
"/usr/bin/pkexec",
|
||||
|
@ -402,13 +439,16 @@ class GlobalCommon(object):
|
|||
Alert(self, message).launch()
|
||||
|
||||
return False
|
||||
else:
|
||||
# Cancel
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def ensure_docker_service_is_started(self):
|
||||
if not is_docker_ready(self):
|
||||
message = "<b>Dangerzone requires Docker.</b><br><br>Docker should be installed, but it looks like it's not running in the background.<br><br>Click Ok to try starting the docker service. You will have to type your login password."
|
||||
if Alert(self, message).launch():
|
||||
message = "<b>Dangerzone requires Docker</b><br><br>Docker should be installed, but it looks like it's not running in the background.<br><br>Click Ok to try starting the docker service. You will have to type your login password."
|
||||
if Alert(self, message).launch() == QtWidgets.QDialog.Accepted:
|
||||
p = subprocess.run(
|
||||
[
|
||||
"/usr/bin/pkexec",
|
||||
|
@ -440,7 +480,7 @@ class GlobalCommon(object):
|
|||
|
||||
|
||||
class Alert(QtWidgets.QDialog):
|
||||
def __init__(self, common, message):
|
||||
def __init__(self, common, message, ok_text="Ok", extra_button_text=None):
|
||||
super(Alert, self).__init__()
|
||||
self.common = common
|
||||
|
||||
|
@ -470,22 +510,38 @@ class Alert(QtWidgets.QDialog):
|
|||
|
||||
message_layout = QtWidgets.QHBoxLayout()
|
||||
message_layout.addWidget(logo)
|
||||
message_layout.addWidget(label)
|
||||
message_layout.addSpacing(10)
|
||||
message_layout.addWidget(label, stretch=1)
|
||||
|
||||
ok_button = QtWidgets.QPushButton("Ok")
|
||||
ok_button.clicked.connect(self.accept)
|
||||
ok_button = QtWidgets.QPushButton(ok_text)
|
||||
ok_button.clicked.connect(self.clicked_ok)
|
||||
if extra_button_text:
|
||||
extra_button = QtWidgets.QPushButton(extra_button_text)
|
||||
extra_button.clicked.connect(self.clicked_extra)
|
||||
cancel_button = QtWidgets.QPushButton("Cancel")
|
||||
cancel_button.clicked.connect(self.reject)
|
||||
cancel_button.clicked.connect(self.clicked_cancel)
|
||||
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.addStretch()
|
||||
buttons_layout.addWidget(ok_button)
|
||||
if extra_button_text:
|
||||
buttons_layout.addWidget(extra_button)
|
||||
buttons_layout.addWidget(cancel_button)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.addLayout(message_layout)
|
||||
layout.addSpacing(10)
|
||||
layout.addLayout(buttons_layout)
|
||||
self.setLayout(layout)
|
||||
|
||||
def clicked_ok(self):
|
||||
self.done(QtWidgets.QDialog.Accepted)
|
||||
|
||||
def clicked_extra(self):
|
||||
self.done(2)
|
||||
|
||||
def clicked_cancel(self):
|
||||
self.done(QtWidgets.QDialog.Rejected)
|
||||
|
||||
def launch(self):
|
||||
return self.exec_() == QtWidgets.QDialog.Accepted
|
||||
return self.exec_()
|
||||
|
|
140
dangerzone/gui.py
Normal file
140
dangerzone/gui.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
from PyQt5 import QtCore, QtWidgets
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import platform
|
||||
import click
|
||||
import time
|
||||
import uuid
|
||||
import subprocess
|
||||
|
||||
from .global_common import GlobalCommon
|
||||
from .main_window import MainWindow
|
||||
from .docker_installer import (
|
||||
is_docker_installed,
|
||||
is_docker_ready,
|
||||
launch_docker_windows,
|
||||
DockerInstaller,
|
||||
)
|
||||
from .container import container_runtime
|
||||
|
||||
|
||||
class Application(QtWidgets.QApplication):
|
||||
document_selected = QtCore.pyqtSignal(str)
|
||||
application_activated = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
QtWidgets.QApplication.__init__(self, sys.argv)
|
||||
|
||||
def event(self, event):
|
||||
# In macOS, handle the file open event
|
||||
if event.type() == QtCore.QEvent.FileOpen:
|
||||
self.document_selected.emit(event.file())
|
||||
return True
|
||||
elif event.type() == QtCore.QEvent.ApplicationActivate:
|
||||
self.application_activated.emit()
|
||||
return True
|
||||
|
||||
return QtWidgets.QApplication.event(self, event)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("--custom-container") # Use this container instead of flmcode/dangerzone
|
||||
@click.argument("filename", required=False)
|
||||
def gui_main(custom_container, filename):
|
||||
# Create the Qt app
|
||||
app = Application()
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
|
||||
# GlobalCommon object
|
||||
global_common = GlobalCommon(app)
|
||||
|
||||
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
|
||||
|
||||
global_common.custom_container = custom_container
|
||||
|
||||
# Allow Ctrl-C to smoothly quit the program instead of throwing an exception
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
# If we're using Linux and docker, see if we need to add the user to the docker group or if the user prefers typing their password
|
||||
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")
|
||||
return
|
||||
|
||||
# See if we need to install Docker...
|
||||
if (platform.system() == "Darwin" or platform.system() == "Windows") and (
|
||||
not is_docker_installed(global_common) or not is_docker_ready(global_common)
|
||||
):
|
||||
click.echo("Docker is either not installed or not running")
|
||||
docker_installer = DockerInstaller(global_common)
|
||||
docker_installer.start()
|
||||
return
|
||||
|
||||
closed_windows = {}
|
||||
windows = {}
|
||||
|
||||
def delete_window(window_id):
|
||||
closed_windows[window_id] = windows[window_id]
|
||||
del windows[window_id]
|
||||
|
||||
# Open a document in a window
|
||||
def select_document(filename=None):
|
||||
if (
|
||||
len(windows) == 1
|
||||
and windows[list(windows.keys())[0]].common.document_filename == None
|
||||
):
|
||||
window = windows[list(windows.keys())[0]]
|
||||
else:
|
||||
window_id = uuid.uuid4().hex
|
||||
window = MainWindow(global_common, window_id)
|
||||
window.delete_window.connect(delete_window)
|
||||
windows[window_id] = window
|
||||
|
||||
if filename:
|
||||
# Validate filename
|
||||
filename = os.path.abspath(os.path.expanduser(filename))
|
||||
try:
|
||||
open(filename, "rb")
|
||||
except FileNotFoundError:
|
||||
click.echo("File not found")
|
||||
return False
|
||||
except PermissionError:
|
||||
click.echo("Permission denied")
|
||||
return False
|
||||
window.common.document_filename = filename
|
||||
window.doc_selection_widget.document_selected.emit()
|
||||
|
||||
return True
|
||||
|
||||
# Open a new window if not filename is passed
|
||||
if filename is None:
|
||||
select_document()
|
||||
else:
|
||||
# If filename is passed as an argument, open it
|
||||
if not select_document(filename):
|
||||
return True
|
||||
|
||||
# Open a new window, if all windows are closed
|
||||
def application_activated():
|
||||
if len(windows) == 0:
|
||||
select_document()
|
||||
|
||||
# If we get a file open event, open it
|
||||
app.document_selected.connect(select_document)
|
||||
|
||||
# If the application is activated and all windows are closed, open a new one
|
||||
app.application_activated.connect(application_activated)
|
||||
|
||||
sys.exit(app.exec_())
|
|
@ -20,6 +20,7 @@ class Settings:
|
|||
"open": True,
|
||||
"open_app": default_pdf_viewer,
|
||||
"update_container": True,
|
||||
"linux_prefers_typing_password": None,
|
||||
}
|
||||
|
||||
self.load()
|
||||
|
|
|
@ -141,7 +141,12 @@ class SettingsWidget(QtWidgets.QWidget):
|
|||
self.update_checkbox.hide()
|
||||
else:
|
||||
output = subprocess.check_output(
|
||||
[self.global_common.container_runtime, "image", "ls", self.global_common.get_container_name()],
|
||||
self.global_common.get_dangerzone_container_args()
|
||||
+ [
|
||||
"image-ls",
|
||||
"--container-name",
|
||||
self.global_common.get_container_name(),
|
||||
],
|
||||
startupinfo=self.global_common.get_subprocess_startupinfo(),
|
||||
)
|
||||
if b"dangerzone" not in output:
|
||||
|
|
|
@ -16,12 +16,8 @@ class TaskBase(QtCore.QThread):
|
|||
super(TaskBase, self).__init__()
|
||||
|
||||
def exec_container(self, args):
|
||||
args = [self.global_common.container_runtime] + args
|
||||
args_str = " ".join(pipes.quote(s) for s in args)
|
||||
|
||||
print()
|
||||
print(f"Executing: {args_str}")
|
||||
output = f"Executing: {args_str}\n\n"
|
||||
args = self.global_common.get_dangerzone_container_args() + args
|
||||
output = ""
|
||||
self.update_details.emit(output)
|
||||
|
||||
with subprocess.Popen(
|
||||
|
@ -38,9 +34,12 @@ class TaskBase(QtCore.QThread):
|
|||
print(line, end="")
|
||||
self.update_details.emit(output)
|
||||
|
||||
output += p.stderr.read()
|
||||
stderr = p.stderr.read()
|
||||
output += stderr
|
||||
print(stderr)
|
||||
self.update_details.emit(output)
|
||||
|
||||
print("")
|
||||
return p.returncode, output
|
||||
|
||||
|
||||
|
@ -55,7 +54,7 @@ class PullImageTask(TaskBase):
|
|||
"Pulling container image (this might take a few minutes)"
|
||||
)
|
||||
self.update_details.emit("")
|
||||
args = ["pull", "flmcode/dangerzone"]
|
||||
args = ["pull"]
|
||||
returncode, _ = self.exec_container(args)
|
||||
|
||||
if returncode != 0:
|
||||
|
@ -78,15 +77,13 @@ class ConvertToPixels(TaskBase):
|
|||
def run(self):
|
||||
self.update_label.emit("Converting document to pixels")
|
||||
args = [
|
||||
"run",
|
||||
"--network",
|
||||
"none",
|
||||
"-v",
|
||||
f"{self.common.document_filename}:/tmp/input_file",
|
||||
"-v",
|
||||
f"{self.common.pixel_dir.name}:/dangerzone",
|
||||
self.global_common.get_container_name(),
|
||||
"document-to-pixels",
|
||||
"--document-filename",
|
||||
self.common.document_filename,
|
||||
"--pixel-dir",
|
||||
self.common.pixel_dir.name,
|
||||
"--container-name",
|
||||
self.global_common.get_container_name(),
|
||||
]
|
||||
returncode, output = self.exec_container(args)
|
||||
|
||||
|
@ -173,29 +170,27 @@ class ConvertToPDF(TaskBase):
|
|||
self.update_label.emit("Converting pixels to safe PDF")
|
||||
|
||||
# Build environment variables list
|
||||
envs = []
|
||||
if self.global_common.settings.get("ocr"):
|
||||
envs += ["-e", "OCR=1"]
|
||||
ocr = "1"
|
||||
else:
|
||||
envs += ["-e", "OCR=0"]
|
||||
envs += [
|
||||
"-e",
|
||||
f"OCR_LANGUAGE={self.global_common.ocr_languages[self.global_common.settings.get('ocr_language')]}",
|
||||
ocr = "0"
|
||||
ocr_lang = self.global_common.ocr_languages[
|
||||
self.global_common.settings.get("ocr_language")
|
||||
]
|
||||
|
||||
args = (
|
||||
[
|
||||
"run",
|
||||
"--network",
|
||||
"none",
|
||||
"-v",
|
||||
f"{self.common.pixel_dir.name}:/dangerzone",
|
||||
"-v",
|
||||
f"{self.common.safe_dir.name}:/safezone",
|
||||
args = [
|
||||
"pixels-to-pdf",
|
||||
"--pixel-dir",
|
||||
self.common.pixel_dir.name,
|
||||
"--safe-dir",
|
||||
self.common.safe_dir.name,
|
||||
"--container-name",
|
||||
self.global_common.get_container_name(),
|
||||
"--ocr",
|
||||
ocr,
|
||||
"--ocr-lang",
|
||||
ocr_lang,
|
||||
]
|
||||
+ envs
|
||||
+ [self.global_common.get_container_name(), "pixels-to-pdf",]
|
||||
)
|
||||
returncode, output = self.exec_container(args)
|
||||
|
||||
if returncode != 0:
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
# Load dangerzone module and resources from the source code tree
|
||||
import os, sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
sys.dangerzone_dev = True
|
||||
|
||||
import dangerzone
|
||||
|
||||
dangerzone.main()
|
1
dev_scripts/dangerzone-container
Symbolic link
1
dev_scripts/dangerzone-container
Symbolic link
|
@ -0,0 +1 @@
|
|||
dangerzone
|
16
install/linux/media.firstlook.dangerzone-container.policy
Normal file
16
install/linux/media.firstlook.dangerzone-container.policy
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
<action id="org.freedesktop.policykit.pkexec.dangerzone">
|
||||
<description>Run Dangerzone Container</description>
|
||||
<message>Dangerzone needs you to authenticate to run containers</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin_keep</allow_any>
|
||||
<allow_inactive>auth_admin_keep</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">/usr/bin/dangerzone-container</annotate>
|
||||
</action>
|
||||
</policyconfig>
|
11
setup.py
11
setup.py
|
@ -29,11 +29,20 @@ setuptools.setup(
|
|||
["install/linux/media.firstlook.dangerzone.png"],
|
||||
),
|
||||
("share/dangerzone", file_list("share")),
|
||||
(
|
||||
"share/polkit-1/actions",
|
||||
["install/linux/media.firstlook.dangerzone-container.policy"],
|
||||
),
|
||||
],
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
entry_points={"console_scripts": ["dangerzone = dangerzone:main"]},
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"dangerzone = dangerzone:main",
|
||||
"dangerzone-container = dangerzone:container_main",
|
||||
]
|
||||
},
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue