Saturday, July 04, 2020

Re: Firefox and MIME

$OpenBSD$

Try to fix opening downloaded files with registered external mimetype handlers
after gio-launch-desktop removal in glib 2.64.

more or less replaces g_app_info_launch_uris() (which relies on /bin/sh in
2.64) calls by g_spawn_async() using a handler that should be previously
unveiled.

Index: toolkit/system/gnome/nsGIOService.cpp
--- toolkit/system/gnome/nsGIOService.cpp.orig
+++ toolkit/system/gnome/nsGIOService.cpp
@@ -243,10 +243,17 @@ nsGIOMimeApp::LaunchWithURI(nsIURI* aUri,
uris.data = const_cast<char*>(spec.get());

GError* error = nullptr;
- gboolean result = g_app_info_launch_uris(mApp, &uris, nullptr, &error);
+ gchar *path = g_filename_from_uri(spec.get(), NULL, NULL);
+ const gchar *bin = g_app_info_get_executable(mApp);
+ g_message("LaunchWithURI: spawning %s %s for %s", bin, path, spec.get());
+ const gchar * const argv[] = { bin, path, NULL };

+ GSpawnFlags flags = static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH |
+ G_SPAWN_DO_NOT_REAP_CHILD);
+ gboolean result = g_spawn_async( NULL, (char**) argv, NULL, flags, NULL, NULL, NULL, &error);
+
if (!result) {
- g_warning("Cannot launch application: %s", error->message);
+ g_warning("Cannot launch application %s with arg %s: %s", bin, path, error->message);
g_error_free(error);
return NS_ERROR_FAILURE;
}
@@ -501,20 +508,15 @@ nsGIOService::GetAppForMimeType(const nsACString& aMim
return NS_ERROR_NOT_AVAILABLE;
}

-#if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
- // g_app_info_get_default_for_type will fail on OpenBSD's veiled filesystem
- // since we most likely don't have direct access to the binaries that are
- // registered as defaults for this type. Fake it up by just executing
- // xdg-open via gio-launch-desktop (which we do have access to) and letting
- // it figure out which program to execute for this MIME type
- GAppInfo* app_info = g_app_info_create_from_commandline(
- "/usr/local/bin/xdg-open",
- nsPrintfCString("System default for %s", content_type).get(),
- G_APP_INFO_CREATE_NONE, NULL);
-#else
GAppInfo* app_info = g_app_info_get_default_for_type(content_type, false);
-#endif
if (app_info) {
+ char *t;
+ t = g_find_program_in_path(g_app_info_get_executable(app_info));
+ if (t != NULL) {
+ g_debug("%s is registered as handler for %s, binary available as %s", g_app_info_get_executable(app_info), content_type, t);
+ } else {
+ g_warning("%s is registered as handler for %s but not available in PATH (missing unveil ?)", g_app_info_get_executable(app_info), content_type);
+ }
nsGIOMimeApp* mozApp = new nsGIOMimeApp(app_info);
NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*aApp = mozApp);
@@ -551,7 +553,20 @@ nsGIOService::ShowURI(nsIURI* aURI) {
nsresult rv = aURI->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
GError* error = nullptr;
- if (!g_app_info_launch_default_for_uri(spec.get(), nullptr, &error)) {
+ gboolean result_uncertain;
+ gchar *path = g_filename_from_uri(spec.get(), NULL, NULL);
+ gchar *content_type = g_content_type_guess(path, NULL, 0, &result_uncertain);
+ if (content_type != NULL && !result_uncertain) {
+ GAppInfo* app_info = g_app_info_get_default_for_type(content_type, false);
+ const gchar *bin = g_app_info_get_executable(app_info);
+ g_message("ShowURI: spawning %s %s for %s (content type %s)", bin, path, spec.get(), content_type);
+ const gchar * const argv[] = { bin, path, NULL };
+ GSpawnFlags flags = static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH |
+ G_SPAWN_DO_NOT_REAP_CHILD);
+ g_spawn_async( NULL, (char**) argv, NULL, flags, NULL, NULL, NULL, &error);
+ }
+ g_free(content_type);
+ if (error) {
g_warning("Could not launch default application for URI: %s",
error->message);
g_error_free(error);
@@ -567,7 +582,18 @@ nsGIOService::ShowURIForInput(const nsACString& aUri)
nsresult rv = NS_ERROR_FAILURE;
GError* error = nullptr;

- g_app_info_launch_default_for_uri(spec, nullptr, &error);
+ gboolean result_uncertain;
+ gchar *path = g_filename_from_uri(spec, NULL, NULL);
+ gchar *content_type = g_content_type_guess(path, NULL, 0, &result_uncertain);
+ if (content_type != NULL && !result_uncertain) {
+ GAppInfo* app_info = g_app_info_get_default_for_type(content_type, false);
+ const gchar *bin = g_app_info_get_executable(app_info);
+ g_message("ShowURIForInput: spawning %s %s for %s (content type %s)", bin, path, spec, content_type);
+ const gchar * const argv[] = { bin, path, NULL };
+ GSpawnFlags flags = static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH |
+ G_SPAWN_DO_NOT_REAP_CHILD);
+ g_spawn_async( NULL, (char**) argv, NULL, flags, NULL, NULL, NULL, &error);
+ }
if (error) {
g_warning("Cannot launch default application: %s", error->message);
g_error_free(error);
@@ -576,6 +602,7 @@ nsGIOService::ShowURIForInput(const nsACString& aUri)
}
g_object_unref(file);
g_free(spec);
+ g_free(content_type);

return rv;
}
On Wed, Jun 10, 2020 at 08:55:53AM +0100, Laurence Tratt wrote:
> On Wed, Jun 03, 2020 at 12:12:31PM +0200, Landry Breuil wrote:
>
> Hello Landry,
>
> >> I'm not sure how best to handle this going forward, but unveiling /bin/sh
> >> is not a good idea.
> > Definitely. Filed https://gitlab.gnome.org/GNOME/glib/-/issues/2123 to try
> > to get upstream to revert said MR and reinstate gio-launch-desktop, thanks
> > for finding this change.
>
> It's not sounding very positive upstream :/
>
> >> Perhaps we include a small compiled utility with Firefox that just
> >> hard-codes execve("/usr/local/bin/xdg-open", ...) and then unveil that
> >> binary instead of gio-launch-desktop? Firefox would still need modifying
> >> to exec that utility directly instead of using Glib's
> >> g_app_info_create_from_commandline.
> > That's imo ugly, as it would only 'fix' it for firefox and not all
> > potential unveiled glib apps. Plus, it would have to be upstreamed first at
> > mozilla (you know my own policy..)
>
> Perhaps the idea of making a separate port (maybe called "gio_launch_desktop"
> or whatever) with this utility is, then, the way to go? That way, every
> unveiled port that needs it can have it as a dependency.
>
> Another alternative is that we could admit defeat and update pkg/README so
> that it no longer gives the impression that you can invoke apps via
> xdg-mime. That would be a bit disappointing in some ways, but at least users
> won't bash their heads trying something that can't work.

I've looked a bit at this, and its a bit more complicated than expected, as
spawning xdg-open to find the configured mime handler also calls the
same codepath, that now uses /bin/sh (in
https://gitlab.gnome.org/GNOME/glib/blob/master/gio/gdesktopappinfo.c#L2695)

So my idea it to avoid relying on all the glib
g_app_info_launch_something that end up calling
g_desktop_app_info_launch_uris() (which would require adding /bin/sh to
unveil.main), and instead fix all the 3 callers for g_app_info_launch*
(ie https://searchfox.org/mozilla-central/search?q=g_app_info_launch&path=)
to do a (simpler?) thing:
- in nsGIOService::GetAppForMimeType(), instead of calling xdg-open to
find the handler (which wouldnt work anymore since
https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1362) get the
mimetype handler via g_app_info_get_default_for_type() (at that point, i
think that works since /usr/local/share is unveiled, all .desktop files
are available), check that it exists in the PATH via
g_find_program_in_path() (ie it's properly unveiled), if not issue a
loud g_warning on stderr.

- in all the g_app_info_launch* callers (ie mainly
nsGIOMimeApp::LaunchWithURI(), but also sometimes
nsGIOService::ShowURIForInput(), havent found which codepath leads to
which), replace the call by:
- if we have an URI, find its mimetype via g_content_type_guess() and
get the corresponding handler via g_app_info_get_default_for_type()
- find the handler executable via g_app_info_get_executable()
- call it via g_spawn_async() (using G_SPAWN_SEARCH_PATH will look it up in the
path)

here, with those handlers added to unveil.main:
/usr/local/bin/mupdf rx
/usr/local/bin/xarchive rx
/usr/local/bin/xarchiver rx
/usr/local/bin/mplayer rx
/usr/local/bin/parole rx

clicking on a pdf or tar.gz or zip or flv link directly opens (after
downloading it to /tmp/mozilla_landry0/) the corresponding file in its
default handler configured via xdg-mime.

$xdg-mime query default application/x-compressed-tar
xarchive.desktop
$xdg-mime query default video/mp4
org.xfce.Parole.desktop
$xdg-mime query default application/pdf
mupdf.desktop

Not sure that's the right way to handle this, nor if it causes other
issues i havent thought about but it works for me using 79.0b3.

To test, drop the attached patch in
/usr/ports/www/mozilla-firefox/patches/patch-toolkit_system_gnome_nsGIOService_cpp
and use G_MESSAGES_DEBUG=all firefox if you want to see the g_debug()
msgs.

Landry

No comments:

Post a Comment