diff --git a/bin/meson.build b/bin/meson.build index 8604b408549ae78432cd1add5e49d14255049c71..d0d1975479c3430cf7e72ca3a5fd81be96de28d9 100644 --- a/bin/meson.build +++ b/bin/meson.build @@ -61,12 +61,23 @@ executable( install_rpath : bin_rpath, ) +executable( + 'steam-runtime-urlopen', + 'urlopen.c', + dependencies : [glib, gio_unix, libglnx_dep, libsteamrt_static_dep], + install : true, + # Use GLib from the adjacent libdir, ignoring LD_LIBRARY_PATH + build_rpath : bin_rpath, + install_rpath : bin_rpath, +) + if get_option('man') foreach bin_name : [ 'check-requirements', 'input-monitor', 'steam-remote', 'system-info', + 'urlopen', ] custom_target( 'steam-runtime-' + bin_name + '.1', diff --git a/bin/urlopen.c b/bin/urlopen.c new file mode 100644 index 0000000000000000000000000000000000000000..f9297d2557c538324e7fd7e086d450eecd89bf9b --- /dev/null +++ b/bin/urlopen.c @@ -0,0 +1,217 @@ +/* + * Copyright © 2017 Red Hat, Inc + * Copyright © 2021 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Florian Müllner <fmuellner@gnome.org> + * Matthias Clasen <mclasen@redhat.com> + */ + +/* + * Alternative executable to the canonical 'xdg-open' with a better handling + * of Steam's URLs. + * Loosely based on the xdg-open implementation of flatpak-xdg-utils. + */ + +#include <libglnx.h> + +#include "steam-runtime-tools/glib-backports-internal.h" + +#include <glib.h> +#include <glib-object.h> +#include <gio/gunixfdlist.h> + +#include <steam-runtime-tools/utils-internal.h> + +typedef GUnixFDList AutoUnixFDList; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(AutoUnixFDList, g_object_unref) + +static gchar **uris = NULL; +static gboolean opt_print_help = FALSE; +static gboolean opt_print_version = FALSE; + +static const GOptionEntry option_entries[] = +{ + /* Imitate the allowed options of the real 'xdg-open' */ + { "manual", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_print_help, + NULL, NULL }, + { "version", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_print_version, + "Print version number and exit", NULL }, + { G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME_ARRAY, + &uris, NULL, NULL }, + { NULL } +}; + +static gboolean +open_with_portal (const char *uri_or_filename, + GError **error) +{ + g_autoptr(GDBusConnection) connection = NULL; + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); + + if (connection != NULL) + { + const gchar *portal_bus_name = "org.freedesktop.portal.Desktop"; + const gchar *portal_object_path = "/org/freedesktop/portal/desktop"; + const gchar *portal_iface_name = "org.freedesktop.portal.OpenURI"; + const gchar *method_name = NULL; + GVariant *arguments = NULL; /* floating */ + GVariantBuilder opt_builder; + g_autoptr(AutoUnixFDList) fd_list = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GVariant) result = NULL; + + g_debug ("Trying the D-Bus desktop portal"); + + g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT); + file = g_file_new_for_commandline_arg (uri_or_filename); + + if (g_file_is_native (file)) + { + /* The canonical 'xdg-open' also handles paths. We try to replicate + * that too, but it might not always work because the container + * inside and outside filesystem structure might be different. */ + g_autofree gchar *path = NULL; + int fd; + + path = g_file_get_path (file); + fd = open (path, O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY); + if (fd < 0) + { + return glnx_throw_errno_prefix (error, "Failed to open '%s'", path); + } + + fd_list = g_unix_fd_list_new_from_array (&fd, 1); /* adopts the fd */ + + arguments = g_variant_new ("(sh@a{sv})", + "", 0, + g_variant_builder_end (&opt_builder)); + method_name = "OpenFile"; + } + else + { + arguments = g_variant_new ("(ss@a{sv})", + "", uri_or_filename, + g_variant_builder_end (&opt_builder)); + method_name = "OpenURI"; + } + + result = g_dbus_connection_call_with_unix_fd_list_sync (connection, + portal_bus_name, + portal_object_path, + portal_iface_name, + method_name, + /* sinks floating reference */ + g_steal_pointer (&arguments), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + fd_list, + NULL, + NULL, + error); + + if (result == NULL) + return glnx_prefix_error (error, + "Unable to open URL with xdg-desktop-portal"); + else + return TRUE; + } + else + { + return glnx_prefix_error (error, + "Unable to connect to D-Bus session bus"); + } +} + +int +main (int argc, + char **argv) +{ + const gchar *uri; + g_autofree gchar *scheme = NULL; + g_autoptr(GOptionContext) option_context = NULL; + g_autoptr(GError) portal_error = NULL; + g_autoptr(GError) error = NULL; + + g_set_prgname ("steam-runtime-urlopen"); + + option_context = g_option_context_new ("{ file | URL }"); + g_option_context_add_main_entries (option_context, option_entries, NULL); + + if (!g_option_context_parse (option_context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", g_get_prgname (), error->message); + return 1; + } + + if (opt_print_version) + { + /* Simply print the version number, similarly to the real xdg-open */ + g_print ("%s\n", VERSION); + return EXIT_SUCCESS; + } + + if (uris == NULL || g_strv_length (uris) > 1) + { + g_autofree gchar *help = g_option_context_get_help (option_context, TRUE, NULL); + g_print ("%s\n", help); + return 1; + } + + /* In reality this could also be a path, but we call it "uri" for simplicity */ + uri = uris[0]; + + scheme = g_uri_parse_scheme (uri); + + if (scheme != NULL && (g_ascii_strcasecmp (scheme, "steamlink") == 0 + || g_ascii_strcasecmp (scheme, "steam") == 0)) + { + g_debug ("Passing the URL '%s' to the Steam pipe", uri); + if (!_srt_steam_command_via_pipe (&uri, 1, &error)) + { + g_printerr ("%s: %s\n", g_get_prgname (), error->message); + return 4; + } + + return EXIT_SUCCESS; + } + + if (open_with_portal (uri, &portal_error)) + return EXIT_SUCCESS; + + if (scheme != NULL && (g_ascii_strcasecmp (scheme, "http") == 0 + || g_ascii_strcasecmp (scheme, "https") == 0)) + { + g_autofree gchar *steam_url = NULL; + steam_url = g_strjoin ("/", "steam://openurl", uri, NULL); + + g_debug ("Passing the URL '%s' to the Steam pipe", steam_url); + if (!_srt_steam_command_via_pipe ((const gchar **) &steam_url, 1, &error)) + { + g_printerr ("%s: %s\n", g_get_prgname (), error->message); + return 4; + } + + return EXIT_SUCCESS; + } + + g_printerr ("%s: %s\n", g_get_prgname (), portal_error->message); + return 4; +} diff --git a/bin/urlopen.md b/bin/urlopen.md new file mode 100644 index 0000000000000000000000000000000000000000..3cf8ab92317d731aa3904627f64e3331db7abe5e --- /dev/null +++ b/bin/urlopen.md @@ -0,0 +1,44 @@ +--- +title: steam-runtime-urlopen +section: 1 +... + +# NAME + +steam-runtime-urlopen - Alternative xdg-open executable + +# SYNOPSIS + +**steam-runtime-urlopen** [*OPTIONS*]... {*file* | *URL*} + +# DESCRIPTION + +**steam-runtime-urlopen** is an alternative xdg-open executable that provides +better handling for Steam URLs. This tool is expected to be executed from +inside a Steam Runtime container. + +# OPTIONS + +**--version** +: Instead of opening the file/URL, write in output the version number. + +# OUTPUT + +On success, the output will be empty. + +On error, a human-readable message is shown on standard error. + +# EXIT STATUS + +The exit status is intended to be the same as for **xdg-open**(1): + +0 +: Success. + +1 +: Error in command line syntax. + +4 +: The action failed. + +<!-- vim:set sw=4 sts=4 et: --> diff --git a/debian/steam-runtime-tools-bin.install b/debian/steam-runtime-tools-bin.install index 6a6434d18d43c22e0612150172a254dac2430d44..173b84854220c33a3597e955353e2c075c233231 100644 --- a/debian/steam-runtime-tools-bin.install +++ b/debian/steam-runtime-tools-bin.install @@ -1,5 +1,2 @@ -usr/bin/steam-runtime-check-requirements -usr/bin/steam-runtime-input-monitor -usr/bin/steam-runtime-steam-remote -usr/bin/steam-runtime-system-info +usr/bin/steam-runtime-* usr/share/man/man1/steam-runtime-*