Debian – How to make xdg-open follow mailcap settings in Debian

debiandefaultsmime-typesxdg-open

tl;dr:

In Debian xdg-open (through xdg-mime) does not follow the mailcap specifications used by run-mailcap which I prefer to use. Is there some systematic way to make xdg-open follow the mailcap entries? Ideally less hacky than to modify the xdg-open script to always call run-mailcap.

Context

Debian (as of 9.5 stretch) has several systems for setting up default applications, or more specifically, associating file extensions, MIME types and programs (or program actions). They are briefly reviewed in this answer. The two main ones with the greatest granularity (in terms of MIME associations) are

  1. The "more modern" XDG standard aimed at desktop environments
  2. The older mailcap system aimed more at mail agents and terminal environments (or desktop agnostic). It is described quite well in this answer.

The goal

I prefer to use the second mailcap system, because its wildcard mailcap.order approach makes it very convenient to automatically associate an application with all (or a subset of) the MIME types it supports and can also differentiate between viewers and editors for a given MIME type. This system is also well tied into system updates, i.e. updating packages does not change the association order.

Execution entry points

Two two methods mentioned above have different entry points

  1. XDG uses xdg-open
  2. the mailcap system uses run-mailcap or its aliases (view, edit)

However, different programs call different entry points, so the results may not be consistent. I'm trying to achieve that though.

xdg-open may fallback on run-mailcap

Upon inspection of /usr/bin/xdg-open provided by xdg-utils-1.1.1-1+deb9u1 it seems that xdg-open will try to use xdg-mime if a desktop is active (in a very generic sense, not necessarily a desktop environment), otherwise it may fallback on run-mailcap if it is available.

# ... line 787 in /usr/bin/xdg-open
open_generic()
{
    if is_file_url_or_path "$1"; then
        local file="$(file_url_to_path "$1")"

        check_input_file "$file"

        if [ -n "$DISPLAY" ]; then
            filetype=`xdg-mime query filetype "$file" | sed "s/;.*//"`
            open_generic_xdg_mime "$file" "$filetype"
        fi

        if which run-mailcap 2>/dev/null 1>&2; then
            run-mailcap --action=view "$file"
            if [ $? -eq 0 ]; then
                exit_success
            fi
        fi
# ...

Specific example of inconsistency

The goal is to have Evince as the default viewer for PDF, Djvu, etc.

I have evince:*/* in /etc/mailcap.order so after running (possibly automatically after updating packages) update-mime entries such application/pdf; evince %s; test=test -n "$DISPLAY" will come first in /etc/mailcap. So launchers using run-mailcap use Evince as I want.

However, when something uses xdg-open, it will open PDF in Libreoffice Draw. The reason for this is (determined by inserting set -x in the script):

  1. xdg-open calls xdg-mime query filetype file.pdf to determine that its MIME type is application/pdf
  2. it then queries xdg-mime whether there is some default application for this MIME type.
  3. As I have no actual association, it starts looking for *.dekstop entries supporting that MIME type. For each *.desktop file it looks at the InitialPreference field to determine a good candidate.
  4. as libreoffice-draw.desktop happens to have initialPreference=5 it uses it.

Here is the detailed log of the xdg-mime query default call with debugging information:

$ XDG_UTILS_DEBUG_LEVEL=3 xdg-mime query default application/pdf              
Checking /home/$USER/.config/mimeapps.list
Checking /etc/xdg/mimeapps.list
Checking /home/$USER/.local/share/applications/mimeapps.list
Checking /usr/share//applications/mimeapps.list
Checking /home/$USER/.local/share/applications/defaults.list and 
        /home/$USER/.local/share/applications/mimeinfo.cache
Checking /home/$USER/.local/share/applications/defaults.list and 
         /home/$USER/.local/share/applications/mimeinfo.cache
Checking /usr/local/share//applications/defaults.list and 
         /usr/local/share//applications/mimeinfo.cache
Checking /usr/local/share//applications/defaults.list and 
/usr/local/share//applications/mimeinfo.cache
Checking /usr/share//applications/defaults.list and 
         /usr/share//applications/mimeinfo.cache
Checking /usr/share//applications/defaults.list and 
         /usr/share//applications/mimeinfo.cache
 Checking /home/$USER/.local/share/applications/wine-extension-pdf.desktop
   Select /home/$USER/.local/share/applications/wine-extension-pdf.desktop 
           [ -1 => 0 ]
 Checking /usr/share//applications/FoxitReader.desktop
 Checking /usr/share//applications/evince.desktop
 Checking /usr/share//applications/gimp.desktop
 Checking /usr/share//applications/inkscape.desktop
 Checking /usr/share//applications/libreoffice-draw.desktop
   Select /usr/share//applications/libreoffice-draw.desktop [ 0 => 05 ]
 Checking /usr/share//applications/mcomix.desktop
 Checking /usr/share//applications/mupdf.desktop
 Checking /usr/share//applications/pdf-presenter-console.desktop
 Checking /usr/share//applications/vprerex.desktop
 Checking /usr/share//applications/xpdf.desktop
 Checking /usr/share//applications/zathura-pdf-poppler.desktop
 libreoffice-draw.desktop

The numbers in [ X => ] are obtained by calls such as awk -F= /InitialPreference=/ {print($2)} /usr/share//applications/libreoffice-draw.desktop, I determined this by putting set -x into the xdg-mime script.

Proposed workarounds so far

Here are workarounds I thought of so far. I'm looking for some nicer, ideally more systematic solution.

  • One option is to modify the xdg-open script to always fallback to run-mailcap. But that is a bit of a hack. Even if I use dpkg-divert, it still means I have to maintain it myself.

  • Use the XDG system and put somethign like */*:xdg-open in mailcap. But might result in circular calling in terminal sessions.

Best Answer

I'm frequently puzzled by various xdg-open limitations, too.

I don't pretend to be complete here, but I'm using the following desktop entry:

[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
NoDisplay=true
Exec=run-mailcap %u
Name=run-mailcap
Comment=run-mailcap

and delegate everything to it in my mimeapps.list:

[Default Applications]
application/ogg=run-mailcap.desktop
application/pdf=run-mailcap.desktop
application/postscript=run-mailcap.desktop
application/rar=run-mailcap.desktop
application/rss+xml=run-mailcap.desktop
application/vnd.openxmlformats-officedocument.wordprocessingml.document=run-mailcap.desktop
application/x-cue=run-mailcap.desktop
application/x-extension-eml=run-mailcap.desktop
application/x-extension-htm=run-mailcap.desktop
application/x-extension-html=run-mailcap.desktop
application/x-extension-rss=run-mailcap.desktop
application/x-extension-xhtml=run-mailcap.desktop
application/x-ms-dos-executable=run-mailcap.desktop
application/x-xpinstall=run-mailcap.desktop
application/xhtml+xml=run-mailcap.desktop
application/xml=run-mailcap.desktop
application/xspf+xml=run-mailcap.desktop
application/zip=run-mailcap.desktop
audio/midi=run-mailcap.desktop
audio/prs.sid=run-mailcap.desktop
...

Theoretically, it's possible to convert this and/or your custom diversions of xdg-open to a virtual xdg-utils implementation.

Related Question