Skip to content

Commit

Permalink
FIXUP: Make dummy into unsafe conversion
Browse files Browse the repository at this point in the history
We previously had used the dummy conversion as a way to test dangerzone
on systems that did not support nested virtualization. However, we now
support a pseudo on-host conversion. This means that we can now do this
type on conversion on those system.

FIXUP: remove "ProcessBased" subclasses
  • Loading branch information
deeplow committed Jan 2, 2024
1 parent c4db485 commit 42dc3c6
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 194 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
windows:
runs-on: windows-latest
env:
DUMMY_CONVERSION: True
UNSAFE_CONVERSION: True
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
Expand All @@ -31,7 +31,7 @@ jobs:
macOS:
runs-on: macos-latest
env:
DUMMY_CONVERSION: True
UNSAFE_CONVERSION: True
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
Expand Down
10 changes: 5 additions & 5 deletions dangerzone/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from . import args, errors
from .document import ARCHIVE_SUBDIR, SAFE_EXTENSION
from .isolation_provider.container import Container
from .isolation_provider.dummy import Dummy
from .isolation_provider.unsafe import UnsafeConverter
from .isolation_provider.qubes import Qubes, is_qubes_native_conversion
from .logic import DangerzoneCore
from .util import get_version
Expand All @@ -35,7 +35,7 @@ def print_header(s: str) -> None:
help=f"Archives the unsafe version in a subdirectory named '{ARCHIVE_SUBDIR}'",
)
@click.option(
"--unsafe-dummy-conversion", "dummy_conversion", flag_value=True, hidden=True
"--unsafe-conversion", "unsafe_conversion", flag_value=True, hidden=True
)
@click.option(
"--enable-timeouts / --disable-timeouts",
Expand All @@ -58,12 +58,12 @@ def cli_main(
enable_timeouts: bool,
filenames: List[str],
archive: bool,
dummy_conversion: bool,
unsafe_conversion: bool,
) -> None:
setup_logging()

if getattr(sys, "dangerzone_dev", False) and dummy_conversion:
dangerzone = DangerzoneCore(Dummy())
if getattr(sys, "dangerzone_dev", False) and unsafe_conversion:
dangerzone = DangerzoneCore(UnsafeConverter())
elif is_qubes_native_conversion():
dangerzone = DangerzoneCore(Qubes())
else:
Expand Down
12 changes: 6 additions & 6 deletions dangerzone/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from .. import args, errors
from ..document import Document
from ..isolation_provider.container import Container
from ..isolation_provider.dummy import Dummy
from ..isolation_provider.unsafe import UnsafeConverter
from ..isolation_provider.qubes import Qubes, is_qubes_native_conversion
from ..util import get_resource_path, get_version
from .logic import DangerzoneGui
Expand Down Expand Up @@ -94,7 +94,7 @@ def infer_os_color_mode(self) -> OSColorMode:

@click.command()
@click.option(
"--unsafe-dummy-conversion", "dummy_conversion", flag_value=True, hidden=True
"--unsafe-conversion", "unsafe_conversion", flag_value=True, hidden=True
)
@click.option(
"--enable-timeouts / --disable-timeouts",
Expand All @@ -112,7 +112,7 @@ def infer_os_color_mode(self) -> OSColorMode:
@click.version_option(version=get_version(), message="%(version)s")
@errors.handle_document_errors
def gui_main(
dummy_conversion: bool, filenames: Optional[List[str]], enable_timeouts: bool
unsafe_conversion: bool, filenames: Optional[List[str]], enable_timeouts: bool
) -> bool:
setup_logging()

Expand All @@ -131,9 +131,9 @@ def gui_main(
app = Application()

# Common objects
if getattr(sys, "dangerzone_dev", False) and dummy_conversion:
dummy = Dummy()
dangerzone = DangerzoneGui(app, isolation_provider=dummy)
if getattr(sys, "dangerzone_dev", False) and unsafe_conversion:
unsafe_converter = UnsafeConverter()
dangerzone = DangerzoneGui(app, isolation_provider=unsafe_converter)
elif is_qubes_native_conversion():
qubes = Qubes()
dangerzone = DangerzoneGui(app, isolation_provider=qubes)
Expand Down
6 changes: 3 additions & 3 deletions dangerzone/gui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from .. import errors
from ..document import SAFE_EXTENSION, Document
from ..isolation_provider.container import Container, NoContainerTechException
from ..isolation_provider.dummy import Dummy
from ..isolation_provider.unsafe import UnsafeConverter
from ..isolation_provider.qubes import Qubes, is_qubes_native_conversion
from ..util import get_resource_path, get_subprocess_startupinfo, get_version
from .logic import Alert, CollapsibleBox, DangerzoneGui, UpdateDialog
Expand Down Expand Up @@ -134,10 +134,10 @@ def __init__(self, dangerzone: DangerzoneGui) -> None:
self.waiting_widget: WaitingWidget = WaitingWidgetContainer(self.dangerzone)
self.waiting_widget.finished.connect(self.waiting_finished)

elif isinstance(self.dangerzone.isolation_provider, Dummy) or isinstance(
elif isinstance(self.dangerzone.isolation_provider, UnsafeConverter) or isinstance(
self.dangerzone.isolation_provider, Qubes
):
# Don't wait with dummy converter and on Qubes.
# Don't wait with unsafe converter and on Qubes.
self.waiting_widget = WaitingWidget()
self.dangerzone.is_waiting_finished = True

Expand Down
147 changes: 61 additions & 86 deletions dangerzone/isolation_provider/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ class IsolationProvider(ABC):
Abstracts an isolation provider
"""

STARTUP_TIME_SECONDS = 0 # The maximum time it takes a the provider to start up.

def __init__(self) -> None:
self.percentage = 0.0
self.proc: Optional[subprocess.Popen] = None

@abstractmethod
def install(self) -> bool:
Expand All @@ -64,102 +67,30 @@ def convert(
) -> None:
self.progress_callback = progress_callback
document.mark_as_converting()
try:
success = self._convert(document, ocr_lang)
except errors.ConversionException as e:
success = False
self.print_progress_trusted(document, True, str(e), 0)
except Exception as e:
success = False
log.exception(
f"An exception occurred while converting document '{document.id}'"
)
self.print_progress_trusted(document, True, str(e), 0)
if success:
document.mark_as_safe()
if document.archive_after_conversion:
document.archive()
else:
document.mark_as_failed()

@abstractmethod
def _convert(
self,
document: Document,
ocr_lang: Optional[str],
) -> bool:
pass

def _print_progress(
self, document: Document, error: bool, text: str, percentage: float
) -> None:
s = Style.BRIGHT + Fore.YELLOW + f"[doc {document.id}] "
s += Fore.CYAN + f"{percentage}% " + Style.RESET_ALL
if error:
s += Fore.RED + text + Style.RESET_ALL
log.error(s)
else:
s += text
log.info(s)

if self.progress_callback:
self.progress_callback(error, text, percentage)

def print_progress_trusted(
self, document: Document, error: bool, text: str, percentage: float
) -> None:
return self._print_progress(document, error, text, int(percentage))

def print_progress(
self, document: Document, error: bool, untrusted_text: str, percentage: float
) -> None:
text = replace_control_chars(untrusted_text)
return self.print_progress_trusted(
document, error, "UNTRUSTED> " + text, percentage
)

@abstractmethod
def get_max_parallel_conversions(self) -> int:
pass

def sanitize_conversion_str(self, untrusted_conversion_str: str) -> str:
conversion_string = replace_control_chars(untrusted_conversion_str)

# Add armor (gpg-style)
armor_start = f"{DOC_TO_PIXELS_LOG_START}\n"
armor_end = DOC_TO_PIXELS_LOG_END
return armor_start + conversion_string + armor_end


class ProcessBasedIsolationProvider(IsolationProvider):
# The maximum time it takes a the provider to start up.
STARTUP_TIME_SECONDS = 0

def __init__(self) -> None:
self.proc: Optional[subprocess.Popen] = None
super().__init__()

@abstractmethod
def start_doc_to_pixels_proc(self) -> subprocess.Popen:
pass

def _convert(
self,
document: Document,
ocr_lang: Optional[str] = None,
) -> bool:
try:
with tempfile.TemporaryDirectory() as t:
Path(f"{t}/pixels").mkdir()
self.doc_to_pixels(document, t)
# TODO: validate convert to pixels output
self.pixels_to_pdf(document, t, ocr_lang)
return True
document.mark_as_safe()
if document.archive_after_conversion:
document.archive()
except errors.InterruptedConversion:
assert self.proc is not None
error_code = self.proc.wait(3)
# XXX Reconstruct exception from error code
raise errors.exception_from_error_code(error_code) # type: ignore [misc]
exception = errors.exception_from_error_code(error_code)
document.mark_as_failed()
except errors.ConversionException as e:
self.print_progress_trusted(document, True, str(e), 0)
document.mark_as_failed()
except Exception as e:
log.exception(
f"An exception occurred while converting document '{document.id}'"
)
self.print_progress_trusted(document, True, str(e), 0)
document.mark_as_failed()

def doc_to_pixels(self, document: Document, tempdir: str) -> None:
with open(document.input_filename, "rb") as f:
Expand Down Expand Up @@ -237,6 +168,50 @@ def pixels_to_pdf(
) -> None:
pass

def _print_progress(
self, document: Document, error: bool, text: str, percentage: float
) -> None:
s = Style.BRIGHT + Fore.YELLOW + f"[doc {document.id}] "
s += Fore.CYAN + f"{percentage}% " + Style.RESET_ALL
if error:
s += Fore.RED + text + Style.RESET_ALL
log.error(s)
else:
s += text
log.info(s)

if self.progress_callback:
self.progress_callback(error, text, percentage)

def print_progress_trusted(
self, document: Document, error: bool, text: str, percentage: float
) -> None:
return self._print_progress(document, error, text, int(percentage))

def print_progress(
self, document: Document, error: bool, untrusted_text: str, percentage: float
) -> None:
text = replace_control_chars(untrusted_text)
return self.print_progress_trusted(
document, error, "UNTRUSTED> " + text, percentage
)

@abstractmethod
def get_max_parallel_conversions(self) -> int:
pass

def sanitize_conversion_str(self, untrusted_conversion_str: str) -> str:
conversion_string = replace_control_chars(untrusted_conversion_str)

# Add armor (gpg-style)
armor_start = f"{DOC_TO_PIXELS_LOG_START}\n"
armor_end = DOC_TO_PIXELS_LOG_END
return armor_start + conversion_string + armor_end

@abstractmethod
def start_doc_to_pixels_proc(self) -> subprocess.Popen:
pass


# From global_common:

Expand Down
4 changes: 2 additions & 2 deletions dangerzone/isolation_provider/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
get_tmp_dir,
replace_control_chars,
)
from .base import ProcessBasedIsolationProvider
from .base import IsolationProvider

# Define startupinfo for subprocesses
if platform.system() == "Windows":
Expand All @@ -35,7 +35,7 @@ def __init__(self, container_tech: str) -> None:
super().__init__(f"{container_tech} is not installed")


class Container(ProcessBasedIsolationProvider):
class Container(IsolationProvider):
# Name of the dangerzone container
CONTAINER_NAME = "dangerzone.rocks/dangerzone"
STARTUP_TIME_SECONDS = 5
Expand Down
72 changes: 0 additions & 72 deletions dangerzone/isolation_provider/dummy.py

This file was deleted.

Loading

0 comments on commit 42dc3c6

Please sign in to comment.