| /* | 
 |  *  Copyright 2022 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 | #include "modules/portal/xdg_desktop_portal_utils.h" | 
 |  | 
 | #include <string> | 
 |  | 
 | #include "absl/strings/string_view.h" | 
 | #include "modules/portal/scoped_glib.h" | 
 | #include "rtc_base/logging.h" | 
 |  | 
 | namespace webrtc { | 
 | namespace xdg_portal { | 
 |  | 
 | std::string RequestResponseToString(RequestResponse request) { | 
 |   switch (request) { | 
 |     case RequestResponse::kUnknown: | 
 |       return "kUnknown"; | 
 |     case RequestResponse::kSuccess: | 
 |       return "kSuccess"; | 
 |     case RequestResponse::kUserCancelled: | 
 |       return "kUserCancelled"; | 
 |     case RequestResponse::kError: | 
 |       return "kError"; | 
 |     default: | 
 |       return "Uknown"; | 
 |   } | 
 | } | 
 |  | 
 | RequestResponse RequestResponseFromPortalResponse(uint32_t portal_response) { | 
 |   // See: | 
 |   //  https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-signal-org-freedesktop-portal-Request.Response | 
 |   switch (portal_response) { | 
 |     case 0: | 
 |       return RequestResponse::kSuccess; | 
 |     case 1: | 
 |       return RequestResponse::kUserCancelled; | 
 |     case 2: | 
 |       return RequestResponse::kError; | 
 |     default: | 
 |       return RequestResponse::kUnknown; | 
 |   } | 
 | } | 
 |  | 
 | std::string PrepareSignalHandle(absl::string_view token, | 
 |                                 GDBusConnection* connection) { | 
 |   Scoped<char> sender( | 
 |       g_strdup(g_dbus_connection_get_unique_name(connection) + 1)); | 
 |   for (int i = 0; sender.get()[i]; ++i) { | 
 |     if (sender.get()[i] == '.') { | 
 |       sender.get()[i] = '_'; | 
 |     } | 
 |   } | 
 |   const char* handle = | 
 |       g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), "/", | 
 |                   std::string(token).c_str(), /*end of varargs*/ nullptr); | 
 |   return handle; | 
 | } | 
 |  | 
 | uint32_t SetupRequestResponseSignal(absl::string_view object_path, | 
 |                                     const GDBusSignalCallback callback, | 
 |                                     gpointer user_data, | 
 |                                     GDBusConnection* connection) { | 
 |   return g_dbus_connection_signal_subscribe( | 
 |       connection, kDesktopBusName, kRequestInterfaceName, "Response", | 
 |       std::string(object_path).c_str(), /*arg0=*/nullptr, | 
 |       G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, callback, user_data, | 
 |       /*user_data_free_func=*/nullptr); | 
 | } | 
 |  | 
 | void RequestSessionProxy(absl::string_view interface_name, | 
 |                          const ProxyRequestCallback proxy_request_callback, | 
 |                          GCancellable* cancellable, | 
 |                          gpointer user_data) { | 
 |   g_dbus_proxy_new_for_bus( | 
 |       G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, | 
 |       kDesktopBusName, kDesktopObjectPath, std::string(interface_name).c_str(), | 
 |       cancellable, | 
 |       reinterpret_cast<GAsyncReadyCallback>(proxy_request_callback), user_data); | 
 | } | 
 |  | 
 | void SetupSessionRequestHandlers( | 
 |     absl::string_view portal_prefix, | 
 |     const SessionRequestCallback session_request_callback, | 
 |     const SessionRequestResponseSignalHandler request_response_signale_handler, | 
 |     GDBusConnection* connection, | 
 |     GDBusProxy* proxy, | 
 |     GCancellable* cancellable, | 
 |     std::string& portal_handle, | 
 |     guint& session_request_signal_id, | 
 |     gpointer user_data) { | 
 |   GVariantBuilder builder; | 
 |   Scoped<char> variant_string; | 
 |  | 
 |   g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); | 
 |   variant_string = | 
 |       g_strdup_printf("%.*s_session%d", static_cast<int>(portal_prefix.size()), | 
 |                       portal_prefix.data(), g_random_int_range(0, G_MAXINT)); | 
 |   g_variant_builder_add(&builder, "{sv}", "session_handle_token", | 
 |                         g_variant_new_string(variant_string.get())); | 
 |  | 
 |   variant_string = | 
 |       g_strdup_printf("%.*s_%d", static_cast<int>(portal_prefix.size()), | 
 |                       portal_prefix.data(), g_random_int_range(0, G_MAXINT)); | 
 |   g_variant_builder_add(&builder, "{sv}", "handle_token", | 
 |                         g_variant_new_string(variant_string.get())); | 
 |  | 
 |   portal_handle = PrepareSignalHandle(variant_string.get(), connection); | 
 |   session_request_signal_id = SetupRequestResponseSignal( | 
 |       portal_handle.c_str(), request_response_signale_handler, user_data, | 
 |       connection); | 
 |  | 
 |   RTC_LOG(LS_INFO) << "Desktop session requested."; | 
 |   g_dbus_proxy_call( | 
 |       proxy, "CreateSession", g_variant_new("(a{sv})", &builder), | 
 |       G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable, | 
 |       reinterpret_cast<GAsyncReadyCallback>(session_request_callback), | 
 |       user_data); | 
 | } | 
 |  | 
 | void StartSessionRequest( | 
 |     absl::string_view prefix, | 
 |     absl::string_view session_handle, | 
 |     const StartRequestResponseSignalHandler signal_handler, | 
 |     const SessionStartRequestedHandler session_started_handler, | 
 |     GDBusProxy* proxy, | 
 |     GDBusConnection* connection, | 
 |     GCancellable* cancellable, | 
 |     guint& start_request_signal_id, | 
 |     std::string& start_handle, | 
 |     gpointer user_data) { | 
 |   GVariantBuilder builder; | 
 |   Scoped<char> variant_string; | 
 |  | 
 |   g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); | 
 |   variant_string = | 
 |       g_strdup_printf("%.*s%d", static_cast<int>(prefix.size()), prefix.data(), | 
 |                       g_random_int_range(0, G_MAXINT)); | 
 |   g_variant_builder_add(&builder, "{sv}", "handle_token", | 
 |                         g_variant_new_string(variant_string.get())); | 
 |  | 
 |   start_handle = PrepareSignalHandle(variant_string.get(), connection); | 
 |   start_request_signal_id = SetupRequestResponseSignal( | 
 |       start_handle.c_str(), signal_handler, user_data, connection); | 
 |  | 
 |   // "Identifier for the application window", this is Wayland, so not "x11:...". | 
 |   const char parent_window[] = ""; | 
 |  | 
 |   RTC_LOG(LS_INFO) << "Starting the portal session."; | 
 |   g_dbus_proxy_call( | 
 |       proxy, "Start", | 
 |       g_variant_new("(osa{sv})", std::string(session_handle).c_str(), | 
 |                     parent_window, &builder), | 
 |       G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable, | 
 |       reinterpret_cast<GAsyncReadyCallback>(session_started_handler), | 
 |       user_data); | 
 | } | 
 |  | 
 | void TearDownSession(absl::string_view session_handle, | 
 |                      GDBusProxy* proxy, | 
 |                      GCancellable* cancellable, | 
 |                      GDBusConnection* connection) { | 
 |   if (!session_handle.empty()) { | 
 |     Scoped<GDBusMessage> message(g_dbus_message_new_method_call( | 
 |         kDesktopBusName, std::string(session_handle).c_str(), | 
 |         kSessionInterfaceName, "Close")); | 
 |     if (message.get()) { | 
 |       Scoped<GError> error; | 
 |       g_dbus_connection_send_message(connection, message.get(), | 
 |                                      G_DBUS_SEND_MESSAGE_FLAGS_NONE, | 
 |                                      /*out_serial=*/nullptr, error.receive()); | 
 |       if (error.get()) { | 
 |         RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (cancellable) { | 
 |     g_cancellable_cancel(cancellable); | 
 |     g_object_unref(cancellable); | 
 |   } | 
 |  | 
 |   if (proxy) { | 
 |     g_object_unref(proxy); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace xdg_portal | 
 | }  // namespace webrtc |