tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

camera_portal.cc (9344B)


      1 /*
      2 *  Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "modules/video_capture/linux/camera_portal.h"
     12 
     13 #include <gio/gunixfdlist.h>
     14 
     15 #include <cstdint>
     16 #include <memory>
     17 #include <string>
     18 
     19 #include "modules/portal/pipewire_utils.h"
     20 #include "modules/portal/portal_request_response.h"
     21 #include "modules/portal/scoped_glib.h"
     22 #include "modules/portal/xdg_desktop_portal_utils.h"
     23 #include "rtc_base/checks.h"
     24 #include "rtc_base/logging.h"
     25 #include "rtc_base/synchronization/mutex.h"
     26 #include "rtc_base/thread_annotations.h"
     27 
     28 namespace webrtc {
     29 
     30 using xdg_portal::RequestResponse;
     31 using xdg_portal::RequestResponseFromPortalResponse;
     32 using xdg_portal::RequestSessionProxy;
     33 
     34 constexpr char kCameraInterfaceName[] = "org.freedesktop.portal.Camera";
     35 
     36 class CameraPortalPrivate {
     37 public:
     38  explicit CameraPortalPrivate(CameraPortal::PortalNotifier* notifier);
     39  ~CameraPortalPrivate();
     40 
     41  void Start();
     42 
     43 private:
     44  void OnPortalDone(xdg_portal::RequestResponse result,
     45                    int fd = kInvalidPipeWireFd);
     46 
     47  static void OnProxyRequested(GObject* object,
     48                               GAsyncResult* result,
     49                               gpointer user_data);
     50  void ProxyRequested(GDBusProxy* proxy);
     51 
     52  static void OnAccessResponse(GDBusProxy* proxy,
     53                               GAsyncResult* result,
     54                               gpointer user_data);
     55  static void OnResponseSignalEmitted(GDBusConnection* connection,
     56                                      const char* sender_name,
     57                                      const char* object_path,
     58                                      const char* interface_name,
     59                                      const char* signal_name,
     60                                      GVariant* parameters,
     61                                      gpointer user_data);
     62  static void OnOpenResponse(GDBusProxy* proxy,
     63                             GAsyncResult* result,
     64                             gpointer user_data);
     65 
     66  Mutex notifier_lock_;
     67  CameraPortal::PortalNotifier* notifier_ RTC_GUARDED_BY(&notifier_lock_) =
     68      nullptr;
     69 
     70  GDBusConnection* connection_ = nullptr;
     71  GDBusProxy* proxy_ = nullptr;
     72  GCancellable* cancellable_ = nullptr;
     73  guint access_request_signal_id_ = 0;
     74 };
     75 
     76 CameraPortalPrivate::CameraPortalPrivate(CameraPortal::PortalNotifier* notifier)
     77    : notifier_(notifier) {}
     78 
     79 CameraPortalPrivate::~CameraPortalPrivate() {
     80  {
     81    MutexLock lock(&notifier_lock_);
     82    notifier_ = nullptr;
     83  }
     84 
     85  if (access_request_signal_id_) {
     86    g_dbus_connection_signal_unsubscribe(connection_,
     87                                         access_request_signal_id_);
     88    access_request_signal_id_ = 0;
     89  }
     90  if (cancellable_) {
     91    g_cancellable_cancel(cancellable_);
     92    g_object_unref(cancellable_);
     93    cancellable_ = nullptr;
     94  }
     95  if (proxy_) {
     96    g_object_unref(proxy_);
     97    proxy_ = nullptr;
     98    connection_ = nullptr;
     99  }
    100 }
    101 
    102 void CameraPortalPrivate::Start() {
    103  cancellable_ = g_cancellable_new();
    104  Scoped<GError> error;
    105  RequestSessionProxy(kCameraInterfaceName, OnProxyRequested, cancellable_,
    106                      this);
    107 }
    108 
    109 // static
    110 void CameraPortalPrivate::OnProxyRequested(GObject* gobject,
    111                                           GAsyncResult* result,
    112                                           gpointer user_data) {
    113  CameraPortalPrivate* that = static_cast<CameraPortalPrivate*>(user_data);
    114  Scoped<GError> error;
    115  GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive());
    116  if (!proxy) {
    117    // Ignore the error caused by user cancelling the request via `cancellable_`
    118    if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
    119      return;
    120    RTC_LOG(LS_ERROR) << "Failed to get a proxy for the portal: "
    121                      << error->message;
    122    that->OnPortalDone(RequestResponse::kError);
    123    return;
    124  }
    125 
    126  RTC_LOG(LS_VERBOSE) << "Successfully created proxy for the portal.";
    127  that->ProxyRequested(proxy);
    128 }
    129 
    130 void CameraPortalPrivate::ProxyRequested(GDBusProxy* proxy) {
    131  GVariantBuilder builder;
    132  Scoped<char> variant_string;
    133  std::string access_handle;
    134 
    135  proxy_ = proxy;
    136  connection_ = g_dbus_proxy_get_connection(proxy);
    137 
    138  g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
    139  variant_string =
    140      g_strdup_printf("capture%d", g_random_int_range(0, G_MAXINT));
    141  g_variant_builder_add(&builder, "{sv}", "handle_token",
    142                        g_variant_new_string(variant_string.get()));
    143 
    144  access_handle =
    145      xdg_portal::PrepareSignalHandle(variant_string.get(), connection_);
    146  access_request_signal_id_ = xdg_portal::SetupRequestResponseSignal(
    147      access_handle.c_str(), OnResponseSignalEmitted, this, connection_);
    148 
    149  RTC_LOG(LS_VERBOSE) << "Requesting camera access from the portal.";
    150  g_dbus_proxy_call(proxy_, "AccessCamera", g_variant_new("(a{sv})", &builder),
    151                    G_DBUS_CALL_FLAGS_NONE, /*timeout_msec=*/-1, cancellable_,
    152                    reinterpret_cast<GAsyncReadyCallback>(OnAccessResponse),
    153                    this);
    154 }
    155 
    156 // static
    157 void CameraPortalPrivate::OnAccessResponse(GDBusProxy* proxy,
    158                                           GAsyncResult* result,
    159                                           gpointer user_data) {
    160  CameraPortalPrivate* that = static_cast<CameraPortalPrivate*>(user_data);
    161  RTC_DCHECK(that);
    162 
    163  Scoped<GError> error;
    164  Scoped<GVariant> variant(
    165      g_dbus_proxy_call_finish(proxy, result, error.receive()));
    166  if (!variant) {
    167    if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
    168      return;
    169    RTC_LOG(LS_ERROR) << "Failed to access portal:" << error->message;
    170    if (that->access_request_signal_id_) {
    171      g_dbus_connection_signal_unsubscribe(that->connection_,
    172                                           that->access_request_signal_id_);
    173      that->access_request_signal_id_ = 0;
    174    }
    175    that->OnPortalDone(RequestResponse::kError);
    176  }
    177 }
    178 
    179 // static
    180 void CameraPortalPrivate::OnResponseSignalEmitted(GDBusConnection* connection,
    181                                                  const char* sender_name,
    182                                                  const char* object_path,
    183                                                  const char* interface_name,
    184                                                  const char* signal_name,
    185                                                  GVariant* parameters,
    186                                                  gpointer user_data) {
    187  CameraPortalPrivate* that = static_cast<CameraPortalPrivate*>(user_data);
    188  RTC_DCHECK(that);
    189 
    190  uint32_t portal_response;
    191  g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr);
    192  if (portal_response) {
    193    RTC_LOG(LS_INFO) << "Camera access denied by the XDG portal.";
    194    that->OnPortalDone(RequestResponseFromPortalResponse(portal_response));
    195    return;
    196  }
    197 
    198  RTC_LOG(LS_VERBOSE) << "Camera access granted by the XDG portal.";
    199 
    200  GVariantBuilder builder;
    201  g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
    202 
    203  g_dbus_proxy_call(
    204      that->proxy_, "OpenPipeWireRemote", g_variant_new("(a{sv})", &builder),
    205      G_DBUS_CALL_FLAGS_NONE, /*timeout_msec=*/-1, that->cancellable_,
    206      reinterpret_cast<GAsyncReadyCallback>(OnOpenResponse), that);
    207 }
    208 
    209 void CameraPortalPrivate::OnOpenResponse(GDBusProxy* proxy,
    210                                         GAsyncResult* result,
    211                                         gpointer user_data) {
    212  CameraPortalPrivate* that = static_cast<CameraPortalPrivate*>(user_data);
    213  RTC_DCHECK(that);
    214 
    215  Scoped<GError> error;
    216  Scoped<GUnixFDList> outlist;
    217  Scoped<GVariant> variant(g_dbus_proxy_call_with_unix_fd_list_finish(
    218      proxy, outlist.receive(), result, error.receive()));
    219  if (!variant) {
    220    if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
    221      return;
    222    RTC_LOG(LS_ERROR) << "Failed to open PipeWire remote:" << error->message;
    223    if (that->access_request_signal_id_) {
    224      g_dbus_connection_signal_unsubscribe(that->connection_,
    225                                           that->access_request_signal_id_);
    226      that->access_request_signal_id_ = 0;
    227    }
    228    that->OnPortalDone(RequestResponse::kError);
    229    return;
    230  }
    231 
    232  int32_t index;
    233  g_variant_get(variant.get(), "(h)", &index);
    234 
    235  int fd = g_unix_fd_list_get(outlist.get(), index, error.receive());
    236 
    237  if (fd == kInvalidPipeWireFd) {
    238    RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: "
    239                      << error->message;
    240    that->OnPortalDone(RequestResponse::kError);
    241    return;
    242  }
    243 
    244  that->OnPortalDone(RequestResponse::kSuccess, fd);
    245 }
    246 
    247 void CameraPortalPrivate::OnPortalDone(RequestResponse result, int fd) {
    248  MutexLock lock(&notifier_lock_);
    249  if (notifier_) {
    250    notifier_->OnCameraRequestResult(result, fd);
    251    notifier_ = nullptr;
    252  }
    253 }
    254 
    255 CameraPortal::CameraPortal(PortalNotifier* notifier)
    256    : private_(std::make_unique<CameraPortalPrivate>(notifier)) {}
    257 
    258 CameraPortal::~CameraPortal() {}
    259 
    260 void CameraPortal::Start() {
    261  private_->Start();
    262 }
    263 
    264 }  // namespace webrtc