Xdg-open or gvfs-open [QDesktopServices::openUrl("file:///somelocation/file.txt")] wont open the file

I wrote an app in Qt called Plumber , which wants to open Files and Directory using default apps set by user to Open such type of files on host system.

Qt uses bool QDesktopServices::openUrl(QUrl url) function to open URLs passed to it, this works fine when app run outside snap confinement, while do nothing when run in X11 session on latest Ubuntu 20.04 and returns false when executed in wayland session.

Qt version 5.13.0 
snap    2.44.3+20.04
snapd   2.44.3+20.04
series  16
ubuntu  20.04
kernel  5.4.0-26-generic
1 Like

For now am using a direct call to xdg-open via QProcess in my Qt code which just works fine.

Things to notice here:
If from Qt version 5.12 they are using xdg-desktop-portal to handle bool QDesktopServices::openUrl(QUrl url) calls, then this is a bug in xdg-desktop-portal openURIs module’s file:// handler. Since if i call QDesktopServices::openUrl(QUrl("snap://plumber")); it works and present me a dialog to choose the available applications to handle the request, it also open a web browser when
i call QDesktopServices::openUrl(QUrl("https://snapcraft.io/plumber")); and it only won’t work with QDesktopServices::openUrl(QUrl("file:///home/someLocation/file.mp4"));

Please check the docs of the desktop portal: https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-org.freedesktop.portal.OpenURI It clearly states that for OpenURI() method:

file:// uris are explicitly not supported by this method To request opening local files, use OpenFile().

You probably need to call a different function from QDesktopServices. If there’s no support for OpenFile(), then you should file a bug report in Qt.

There is no other way. openUrl() handles everything, and works fine outside confinement.
If xdg-desktop-portal want set some new norms and break existing than they will be breaking many Qt applications, i will not be the only guy facing these issues.
in recent update openUrl(QUrl("file:///someDir/file.mkv")) open the portal dialog and let choose app to open file, while openUrl(QUrl("file:///someDir/")) to open file manager crashes xdg-desktop-portal. Guess the issue is with desktop-portal and not with Qt.

Alos QDesktopServices::openUrl(QUrl) is cross platform way of handling URLs, it can handle any URL is very smartly written to perform its task, xdg-desktop-portal OpenURI is different than Qt’s function, a Qt code will use Qt’s cross platform function and not the xdg-desktop-portal.

This sounds like you need to file a bug report for Qt. The xdg-desktop-portal spec I linked expliciltly states that file:// isn’t supported by OpenURI() invoked on portal.

Any reference telling that QDesktopServices::openUrl(QUrl) is using OpenURI() of xdg-desktop-portal ?

Question is how my bug filling to Qt will fix all the legacy app code and make them use specs set by xdg-desktop-portal ?

Qt code works fine outside confinement on normal systems, and Qt don’t need to understand random confinement people set and run their apps on. It’s out of their scope isn’t it ?

Qt already understands confinement. Looking at Qt code:

The portal handler is used when there’s either a flatpak-info file or SNAP environment variable is set (the case here). There is no fallback to xdg-open, which would have worked in this scenario. IMO this is a bug in Qt. For seamless operation, the file:// scheme handler should call xdgDesktopPortalOpenFile(). I believe the same problem will happen if you package you app as a flatpak.

Looking at this code in QDesktopServices::openURL(), there’s this bit:


Most likely, if you drop file://, you will trigger a path that calls to QGenericUnixServices::openDocument() that in turn calls xdgDesktopPortalOpenFile().

HTH

Tested on Qt 5.13 (both calls works outside sandbox)

  • Opens app chooser dialog when called URL with this scheme - QUrl(“file:///home/bulld/Downloads/Plumber/3Y9bSybn3s.gif”)
  • Won’t do anything when called URL with this scheme -QUrl("/home/bulld/Downloads/Plumber")

A graphics app chooser right? Is this snapd from edge? If so, then it’s the Gtk/KDE chooser from corresponding xdg-desktop-portal implementation, and a sign of things working correctly.

Sorry, I misread the source code. You apparently need to supply a well formatted URL.

The problem is if i call QUrl("file:///home/bulld/Downloads/Plumber") (a call which opens file manager opening the path )from sandbox nothing happens in few seconds Apport reports that xdg-desktop-portal has crashed and thats sounds like issue with xdg-desktop-portal and not with Qt or my code.

At this point, I think you should report a bug to your distro about xdg-desktop-portal.

@kenvandine @jamesh is this something you can take a look at?

It looks like a bug in Qt. Looking at this line:

It creates a file descriptor with O_PATH. If it opens a standard O_RDONLY descriptor, things would likely work correctly. I’ve reported this bug upstream here:

If you are seeing crashes in xdg-desktop-portal on Ubuntu though, I’d be interested in a bug report on that. Please send the crash reports through.

1 Like

Looks like Qt folks don’t care. Could the Qt from core20 be patched to eliminate this bug? Or should each Qt app be patched to open links in case of snap packaging?

I had a go at building Qt 5.15.2 from source with the O_RDONLY change applied as a patch and to be bundled into the yuzu snap. Yuzu has a menu item to “Open yuzu folder”, which behind the scenes makes use of the QDesktopServices::openUrl(QUrl url) function with the argument passed in being a file:// URI pointing to a directory. I didn’t have any luck with getting the portal to open a folder for me with just the O_RDONLY change. I managed to get the functionality I expected by altering the writable option to be set to false :

diff --git a/src/platformsupport/services/genericunix/qgenericunixservices.cpp b/src/platformsupport/services/genericunix/qgenericunixservices.cpp
index 1a3cab275d..80c12159fc 100644
--- a/src/platformsupport/services/genericunix/qgenericunixservices.cpp
+++ b/src/platformsupport/services/genericunix/qgenericunixservices.cpp
@@ -203,8 +203,8 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
     // handle_token (s) -  A string that will be used as the last element of the @handle.
     // writable (b) - Whether to allow the chosen application to write to the file.
 
-#ifdef O_PATH
-    const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH);
+#ifdef O_RDONLY
+    const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY);
     if (fd != -1) {
         QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"),
                                                               QLatin1String("/org/freedesktop/portal/desktop"),
@@ -214,7 +214,7 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
         QDBusUnixFileDescriptor descriptor;
         descriptor.giveFileDescriptor(fd);
 
-        const QVariantMap options = {{QLatin1String("writable"), true}};
+        const QVariantMap options = {{QLatin1String("writable"), false}};
 
         // FIXME parent_window_id
         message << QString() << QVariant::fromValue(descriptor) << options;

which had the result of opening a nautilus window at the correct directory. I also used this patch on another snap (modified source code from the application on this forum post QT File Dialog (open folder) and KDE-NEON Extension made available on the store under test-openfolder-qt-2 if anyone wants to test) which used a file URI pointing to an image file, which resulted in having to pick from a list of suitable applications.

Alternatively I also experimented with the following patch, which changes the writable option to be ask:

diff --git a/src/platformsupport/services/genericunix/qgenericunixservices.cpp b/src/platformsupport/services/genericunix/qgenericunixservices.cpp
index 1a3cab275d..64bf2d3f3b 100644
--- a/src/platformsupport/services/genericunix/qgenericunixservices.cpp
+++ b/src/platformsupport/services/genericunix/qgenericunixservices.cpp
@@ -203,8 +203,8 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
     // handle_token (s) -  A string that will be used as the last element of the @handle.
     // writable (b) - Whether to allow the chosen application to write to the file.
 
-#ifdef O_PATH
-    const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_PATH);
+#ifdef O_RDONLY
+    const int fd = qt_safe_open(QFile::encodeName(url.toLocalFile()), O_RDONLY);
     if (fd != -1) {
         QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"),
                                                               QLatin1String("/org/freedesktop/portal/desktop"),
@@ -214,7 +214,7 @@ static inline QDBusMessage xdgDesktopPortalOpenFile(const QUrl &url)
         QDBusUnixFileDescriptor descriptor;
         descriptor.giveFileDescriptor(fd);
 
-        const QVariantMap options = {{QLatin1String("writable"), true}};
+        const QVariantMap options = {{QLatin1String("ask"), true}};
 
         // FIXME parent_window_id
         message << QString() << QVariant::fromValue(descriptor) << options;

This patch also allowed me to open the directory but presented me with the choice to pick from a list of suitable applications beforehand.

I won’t pretend to have in depth knowledge into portal and it’s OpenURI API, so I don’t know if writable = true is meant be the correct way to handle opening file URIs, but there’s a bug with the Qt implementation? Hopefully someone with more knowledge on this can comment. I created my patches based on the portal documentation/API reference located here https://flatpak.github.io/xdg-desktop-portal/portal-docs.html#gdbus-method-org-freedesktop-portal-OpenURI.OpenFile

Would be nice to have this functionality fixed in Qt from the apt repos/kde-neon extension, at present I’m having to provide instructions to users to navigate to the directory manually which confuses non tech-savvy users unfamiliar with hidden directories, and the snap directories living in a different location from what they are used to.

Looks like snap people don’t have interest to make Qt apps a first-class citizen for snap… Flatpak works a way better with Qt, it gets latest Qt version really fast and it works as it should.

With snap you either stick to your self-built Qt and snap’s fontconfig bugs or use an outdated version of Qt from kde-neon extension that may not fit your needs.

Wouldn’t describe myself as an expert here but I dabbled with this interface the other week,

Writable=true might not have the effect you think it has, it’s purpose is to make sure the application that is launched from the portal is capable of opening the resource requested. If the sandboxing wouldn’t interfere at all, it doesn’t do anything. If the sandboxing of the spawned app would interfere, it uses the Documents Portal to mount the resource into $XDG_RUNTIME_DIR/docs

At a guess, xdg-desktop-portals 1.6, which is the version in use in Ubuntu 20.04, doesn’t support directories in the Document portal and as a result the writable flag breaks the request entirely. If you tried again on 1.8 (e.g, Ubuntu 21.04) , you might find the request succeeds.

Practically speaking, the file manager is unlikely to be sandboxed to begin with though, so you might find that you never need writable for directories in most systems, unless you were reasonably expecting them to be passed to something else that is sandboxed and that might handle raw folders like some disk resource utilisation viewer.

Tl;Dr writable on directories might only make sense on xdg-desktop-portals >= 1.7, which 20.04 doesn’t have. In this case, you might just prefer to patch it out entirely, unless either you probed the version of the portals in use or time passed on and we could reasonably assume the old ones are not in use anymore.

1 Like

Thanks for responding @James-Carroll . Your explanation of the writable property makes sense in relation to what the OpenFile method documentation is saying:

writable b

Whether to allow the chosen application to write to the file.

This key only takes effect the uri points to a local file that is exported in the document portal, and the chosen application is sandboxed itself.

The latter part seems to echo what you are saying, but to my understanding the first part seems to be about saving any changes to the file?

I experimented with writable=false and a PNG image file in my home directory. Opened the file with the Pinta snap and was able to edit and save the file. So that would contradict what the documentation is saying? Again I could be misunderstanding what the documentation is saying.

With writable=true, I experience the same issue of being unable to open a folder or file on 21.04 with xdg-desktop-portal v1.8.1-1 that I experience on 20.04 xdg-desktop-portal v1.6.0-1

1 Like

Pinta probably didn’t need the mediation to access the file in $HOME. If you disconnected the home interface, Pinta should need the mediation to open the file, which in theory means writable=true would actually let you open the png then and Pinta should fail without. You might run into the problem though that xdg-desktop-portals only gained support for writable=true in snaps in Ubuntu 21.04. (Relevant commit) which means that even if Pinta did need writable=true to read access the file, it wouldn’t get it on 20.04.

So it might be worth testing a Flatpak to see how writable=true works, and aside from installing e.g the Pinta flatpak for testing, flatpak permissions shows the state of the documents portal including snaps too, which is handy to have. If writable=true is having an effect, you’d see it being added here.

It’s unfortunate that writable=true doesn’t work on directories in 1.8. I’m unsure whether that’s because the concept doesn’t make sense or it’s just a bug/oversight, it might be worth making a Github issue to check.

I did make my own wrapper to replace snaps xdg-open that tries use writable where possible, if you want to take a look.

1 Like