mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00
Order list of PDF viewers and return default application first (Linux).
This commit is contained in:
parent
f03bc71855
commit
54ab9ce98f
2 changed files with 131 additions and 5 deletions
|
@ -4,6 +4,7 @@ import platform
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import typing
|
import typing
|
||||||
|
from collections import OrderedDict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ class DangerzoneGui(DangerzoneCore):
|
||||||
# Preload font
|
# Preload font
|
||||||
self.fixed_font = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)
|
self.fixed_font = QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont)
|
||||||
|
|
||||||
# Preload list of PDF viewers on computer
|
# Preload ordered list of PDF viewers on computer, starting with default
|
||||||
self.pdf_viewers = self._find_pdf_viewers()
|
self.pdf_viewers = self._find_pdf_viewers()
|
||||||
|
|
||||||
# Are we done waiting (for Docker Desktop to be installed, or for container to install)
|
# Are we done waiting (for Docker Desktop to be installed, or for container to install)
|
||||||
|
@ -93,9 +94,22 @@ class DangerzoneGui(DangerzoneCore):
|
||||||
log.info(Fore.YELLOW + "> " + Fore.CYAN + args_str)
|
log.info(Fore.YELLOW + "> " + Fore.CYAN + args_str)
|
||||||
subprocess.Popen(args)
|
subprocess.Popen(args)
|
||||||
|
|
||||||
def _find_pdf_viewers(self) -> Dict[str, str]:
|
def _find_pdf_viewers(self) -> OrderedDict[str, str]:
|
||||||
pdf_viewers: Dict[str, str] = {}
|
pdf_viewers: OrderedDict[str, str] = OrderedDict()
|
||||||
if platform.system() == "Linux":
|
if platform.system() == "Linux":
|
||||||
|
# Opportunistically query for default pdf handler
|
||||||
|
default_pdf_viewer = None
|
||||||
|
try:
|
||||||
|
default_pdf_viewer = subprocess.check_output(
|
||||||
|
["xdg-mime", "query", "default", "application/pdf"]
|
||||||
|
).decode()
|
||||||
|
except (FileNotFoundError, subprocess.CalledProcessError) as e:
|
||||||
|
# Log it and continue
|
||||||
|
log.info(
|
||||||
|
"xdg-mime query failed, default PDF handler could not be found."
|
||||||
|
)
|
||||||
|
log.debug(f"xdg-mime query failed: {e}")
|
||||||
|
|
||||||
# Find all .desktop files
|
# Find all .desktop files
|
||||||
for search_path in [
|
for search_path in [
|
||||||
"/usr/share/applications",
|
"/usr/share/applications",
|
||||||
|
@ -108,14 +122,26 @@ class DangerzoneGui(DangerzoneCore):
|
||||||
if os.path.splitext(filename)[1] == ".desktop":
|
if os.path.splitext(filename)[1] == ".desktop":
|
||||||
# See which ones can open PDFs
|
# See which ones can open PDFs
|
||||||
desktop_entry = DesktopEntry(full_filename)
|
desktop_entry = DesktopEntry(full_filename)
|
||||||
|
desktop_entry_name = desktop_entry.getName()
|
||||||
if (
|
if (
|
||||||
"application/pdf" in desktop_entry.getMimeTypes()
|
"application/pdf" in desktop_entry.getMimeTypes()
|
||||||
and "dangerzone" not in desktop_entry.getName().lower()
|
and "dangerzone" not in desktop_entry_name.lower()
|
||||||
):
|
):
|
||||||
pdf_viewers[desktop_entry.getName()] = (
|
pdf_viewers[desktop_entry_name] = (
|
||||||
desktop_entry.getExec()
|
desktop_entry.getExec()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Put the default entry first
|
||||||
|
if filename == default_pdf_viewer:
|
||||||
|
try:
|
||||||
|
pdf_viewers.move_to_end(
|
||||||
|
desktop_entry_name, last=False
|
||||||
|
)
|
||||||
|
except KeyError as e:
|
||||||
|
# Should be unreachable
|
||||||
|
log.error(
|
||||||
|
f"Problem reordering applications: {e}"
|
||||||
|
)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
100
tests/gui/test_logic.py
Normal file
100
tests/gui/test_logic.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from dangerzone.gui.logic import DangerzoneGui
|
||||||
|
|
||||||
|
if platform.system() == "Linux":
|
||||||
|
from xdg.DesktopEntry import DesktopEntry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(platform.system() != "Linux", reason="Linux-only test")
|
||||||
|
def test_order_mime_handers() -> None:
|
||||||
|
"""
|
||||||
|
Given a default mime handler returned by xdg-mime,
|
||||||
|
ensure it is the first item available in the list
|
||||||
|
of compatible applications.
|
||||||
|
"""
|
||||||
|
mock_app = mock.MagicMock()
|
||||||
|
dummy = mock.MagicMock()
|
||||||
|
|
||||||
|
mock_desktop = mock.MagicMock(spec=DesktopEntry)
|
||||||
|
mock_desktop.getMimeTypes.return_value = "application/pdf"
|
||||||
|
mock_desktop.getExec.side_effect = [
|
||||||
|
"/usr/bin/madeup-evince",
|
||||||
|
"/usr/local/bin/madeup-mupdf",
|
||||||
|
"/usr/local/bin/madeup-libredraw",
|
||||||
|
]
|
||||||
|
mock_desktop.getName.side_effect = [
|
||||||
|
"Evince",
|
||||||
|
"MuPDF",
|
||||||
|
"LibreOffice",
|
||||||
|
]
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"subprocess.check_output", return_value=b"libreoffice-draw.desktop"
|
||||||
|
) as mock_default_mime_hander, mock.patch(
|
||||||
|
"os.listdir",
|
||||||
|
side_effect=[
|
||||||
|
["org.gnome.Evince.desktop"],
|
||||||
|
["org.pwmt.zathura-pdf-mupdf.desktop"],
|
||||||
|
["libreoffice-draw.desktop"],
|
||||||
|
],
|
||||||
|
) as mock_list, mock.patch(
|
||||||
|
"dangerzone.gui.logic.DesktopEntry", return_value=mock_desktop
|
||||||
|
):
|
||||||
|
dz = DangerzoneGui(mock_app, dummy)
|
||||||
|
|
||||||
|
mock_default_mime_hander.assert_called_once_with(
|
||||||
|
["xdg-mime", "query", "default", "application/pdf"]
|
||||||
|
)
|
||||||
|
mock_list.assert_called()
|
||||||
|
assert len(dz.pdf_viewers) == 3
|
||||||
|
assert dz.pdf_viewers.popitem(last=False)[0] == "LibreOffice"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(platform.system() != "Linux", reason="Linux-only test")
|
||||||
|
def test_mime_handers_succeeds_no_default_found() -> None:
|
||||||
|
"""
|
||||||
|
Given a failure to return default mime handler,
|
||||||
|
ensure compatible applications are still returned.
|
||||||
|
"""
|
||||||
|
mock_app = mock.MagicMock()
|
||||||
|
dummy = mock.MagicMock()
|
||||||
|
|
||||||
|
mock_desktop = mock.MagicMock(spec=DesktopEntry)
|
||||||
|
mock_desktop.getMimeTypes.return_value = "application/pdf"
|
||||||
|
mock_desktop.getExec.side_effect = [
|
||||||
|
"/usr/bin/madeup-evince",
|
||||||
|
"/usr/local/bin/madeup-mupdf",
|
||||||
|
"/usr/local/bin/madeup-libredraw",
|
||||||
|
]
|
||||||
|
mock_desktop.getName.side_effect = [
|
||||||
|
"Evince",
|
||||||
|
"MuPDF",
|
||||||
|
"LibreOffice",
|
||||||
|
]
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"subprocess.check_output",
|
||||||
|
side_effect=subprocess.CalledProcessError(1, "Oh no, xdg-mime error!)"),
|
||||||
|
) as mock_default_mime_hander, mock.patch(
|
||||||
|
"os.listdir",
|
||||||
|
side_effect=[
|
||||||
|
["org.gnome.Evince.desktop"],
|
||||||
|
["org.pwmt.zathura-pdf-mupdf.desktop"],
|
||||||
|
["libreoffice-draw.desktop"],
|
||||||
|
],
|
||||||
|
) as mock_list, mock.patch(
|
||||||
|
"dangerzone.gui.logic.DesktopEntry", return_value=mock_desktop
|
||||||
|
):
|
||||||
|
dz = DangerzoneGui(mock_app, dummy)
|
||||||
|
|
||||||
|
mock_default_mime_hander.assert_called_once_with(
|
||||||
|
["xdg-mime", "query", "default", "application/pdf"]
|
||||||
|
)
|
||||||
|
mock_list.assert_called()
|
||||||
|
assert len(dz.pdf_viewers) == 3
|
||||||
|
assert dz.pdf_viewers.popitem(last=False)[0] == "Evince"
|
Loading…
Reference in a new issue