mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	portal: add portal module again
Add this again for now until we can integrate this into the session manager somehow.
This commit is contained in:
		
							parent
							
								
									0bb3de0dca
								
							
						
					
					
						commit
						9e6a047788
					
				
					 3 changed files with 793 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -50,6 +50,7 @@ load-module libpipewire-module-spa-node-factory
 | 
			
		|||
load-module libpipewire-module-client-node
 | 
			
		||||
load-module libpipewire-module-client-device
 | 
			
		||||
load-module libpipewire-module-access
 | 
			
		||||
load-module libpipewire-module-portal
 | 
			
		||||
load-module libpipewire-module-adapter
 | 
			
		||||
load-module libpipewire-module-link-factory
 | 
			
		||||
load-module libpipewire-module-session-manager
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,14 @@ pipewire_module_rtkit = shared_library('pipewire-module-rtkit', [ 'module-rtkit.
 | 
			
		|||
  install_rpath: modules_install_dir,
 | 
			
		||||
  dependencies : [dbus_dep, mathlib, dl_lib, pipewire_dep],
 | 
			
		||||
)
 | 
			
		||||
pipewire_module_portal = shared_library('pipewire-module-portal', [ 'module-portal.c' ],
 | 
			
		||||
  c_args : pipewire_module_c_args,
 | 
			
		||||
  include_directories : [configinc, spa_inc],
 | 
			
		||||
  install : true,
 | 
			
		||||
  install_dir : modules_install_dir,
 | 
			
		||||
  install_rpath: modules_install_dir,
 | 
			
		||||
  dependencies : [dbus_dep, mathlib, dl_lib, pipewire_dep],
 | 
			
		||||
)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
pipewire_module_client_node = shared_library('pipewire-module-client-device',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										784
									
								
								src/modules/module-portal.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										784
									
								
								src/modules/module-portal.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,784 @@
 | 
			
		|||
/* PipeWire
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 * Copyright (C) 2019 Red Hat Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 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
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include <dbus/dbus.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/support/dbus.h>
 | 
			
		||||
 | 
			
		||||
#include "pipewire/context.h"
 | 
			
		||||
#include "pipewire/impl-client.h"
 | 
			
		||||
#include "pipewire/log.h"
 | 
			
		||||
#include "pipewire/module.h"
 | 
			
		||||
#include "pipewire/utils.h"
 | 
			
		||||
 | 
			
		||||
enum media_role {
 | 
			
		||||
	MEDIA_ROLE_INVALID = -1,
 | 
			
		||||
	MEDIA_ROLE_NONE = 0,
 | 
			
		||||
	MEDIA_ROLE_CAMERA = 1 << 0,
 | 
			
		||||
};
 | 
			
		||||
#define MEDIA_ROLE_ALL (MEDIA_ROLE_CAMERA)
 | 
			
		||||
 | 
			
		||||
struct impl {
 | 
			
		||||
	struct pw_context *context;
 | 
			
		||||
	struct pw_properties *properties;
 | 
			
		||||
 | 
			
		||||
	struct spa_dbus_connection *conn;
 | 
			
		||||
	DBusConnection *bus;
 | 
			
		||||
 | 
			
		||||
	struct spa_hook context_listener;
 | 
			
		||||
	struct spa_hook module_listener;
 | 
			
		||||
 | 
			
		||||
	struct spa_list client_list;
 | 
			
		||||
 | 
			
		||||
	DBusPendingCall *portal_pid_pending;
 | 
			
		||||
	pid_t portal_pid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct client_info {
 | 
			
		||||
	struct spa_list link;
 | 
			
		||||
	struct impl *impl;
 | 
			
		||||
	struct pw_impl_client *client;
 | 
			
		||||
	struct spa_hook client_listener;
 | 
			
		||||
        struct spa_list resources;
 | 
			
		||||
 | 
			
		||||
	bool portal_managed;
 | 
			
		||||
	bool setup_complete;
 | 
			
		||||
	bool is_portal;
 | 
			
		||||
	char *app_id;
 | 
			
		||||
	enum media_role media_roles;
 | 
			
		||||
	enum media_role allowed_media_roles;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct client_info *find_client_info(struct impl *impl, struct pw_impl_client *client)
 | 
			
		||||
{
 | 
			
		||||
	struct client_info *info;
 | 
			
		||||
 | 
			
		||||
	spa_list_for_each(info, &impl->client_list, link) {
 | 
			
		||||
		if (info->client == client)
 | 
			
		||||
			return info;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void client_info_free(struct client_info *cinfo)
 | 
			
		||||
{
 | 
			
		||||
	spa_hook_remove(&cinfo->client_listener);
 | 
			
		||||
	spa_list_remove(&cinfo->link);
 | 
			
		||||
	free(cinfo->app_id);
 | 
			
		||||
	free(cinfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum media_role media_role_from_string(const char *media_role_str)
 | 
			
		||||
{
 | 
			
		||||
	if (strcmp(media_role_str, "Camera") == 0)
 | 
			
		||||
		return MEDIA_ROLE_CAMERA;
 | 
			
		||||
	else
 | 
			
		||||
		return MEDIA_ROLE_INVALID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum media_role parse_media_roles(const char *media_types_str)
 | 
			
		||||
{
 | 
			
		||||
	enum media_role media_roles = 0;
 | 
			
		||||
	char *buf_orig;
 | 
			
		||||
	char *buf;
 | 
			
		||||
 | 
			
		||||
	buf_orig = strdup(media_types_str);
 | 
			
		||||
	buf = buf_orig;
 | 
			
		||||
	while (buf) {
 | 
			
		||||
		char *media_role_str;
 | 
			
		||||
		enum media_role media_role;
 | 
			
		||||
 | 
			
		||||
		media_role_str = buf;
 | 
			
		||||
		strsep(&buf, ",");
 | 
			
		||||
 | 
			
		||||
		media_role = media_role_from_string(media_role_str);
 | 
			
		||||
		if (media_role != MEDIA_ROLE_INVALID) {
 | 
			
		||||
			media_roles |= MEDIA_ROLE_CAMERA;
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			pw_log_debug("Client specified unknown media role '%s'",
 | 
			
		||||
				     media_role_str);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	free(buf);
 | 
			
		||||
 | 
			
		||||
	return media_roles;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum media_role media_role_from_properties(const struct pw_properties *props)
 | 
			
		||||
{
 | 
			
		||||
	const char *media_class_str;
 | 
			
		||||
	const char *media_role_str;
 | 
			
		||||
 | 
			
		||||
	media_class_str = pw_properties_get(props, "media.class");
 | 
			
		||||
	media_role_str = pw_properties_get(props, "media.role");
 | 
			
		||||
 | 
			
		||||
	if (media_class_str == NULL)
 | 
			
		||||
		return MEDIA_ROLE_INVALID;
 | 
			
		||||
 | 
			
		||||
	if (media_role_str == NULL)
 | 
			
		||||
		return MEDIA_ROLE_INVALID;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(media_class_str, "Video/Source") != 0)
 | 
			
		||||
		return MEDIA_ROLE_INVALID;
 | 
			
		||||
 | 
			
		||||
	return media_role_from_string(media_role_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void check_portal_managed(struct client_info *cinfo)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = cinfo->impl;
 | 
			
		||||
	const struct pw_properties *props;
 | 
			
		||||
 | 
			
		||||
	if (impl->portal_pid == 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	props = pw_impl_client_get_properties(cinfo->client);
 | 
			
		||||
	if (props) {
 | 
			
		||||
		const char *pid_str;
 | 
			
		||||
		pid_t pid;
 | 
			
		||||
 | 
			
		||||
		pid_str = pw_properties_get(props, PW_KEY_SEC_PID);
 | 
			
		||||
 | 
			
		||||
		pid = atoi(pid_str);
 | 
			
		||||
 | 
			
		||||
		if (pid == impl->portal_pid) {
 | 
			
		||||
			cinfo->portal_managed = true;
 | 
			
		||||
			pw_log_info("module %p: portal managed client %p added",
 | 
			
		||||
				     impl, cinfo->client);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
set_global_permissions(void *data, struct pw_global *global)
 | 
			
		||||
{
 | 
			
		||||
	struct client_info *cinfo = data;
 | 
			
		||||
	struct pw_impl_client *client = cinfo->client;
 | 
			
		||||
	const struct pw_properties *props;
 | 
			
		||||
	struct pw_permission permissions[1];
 | 
			
		||||
	int n_permissions = 0;
 | 
			
		||||
	bool set_permission;
 | 
			
		||||
	bool allowed = false;
 | 
			
		||||
 | 
			
		||||
	props = pw_global_get_properties(global);
 | 
			
		||||
 | 
			
		||||
	if (pw_global_is_type(global, PW_TYPE_INTERFACE_Core)) {
 | 
			
		||||
		set_permission = true;
 | 
			
		||||
		allowed = true;
 | 
			
		||||
	}
 | 
			
		||||
	else if (props) {
 | 
			
		||||
		if (pw_global_is_type(global, PW_TYPE_INTERFACE_Factory)) {
 | 
			
		||||
			const char *factory_name;
 | 
			
		||||
 | 
			
		||||
			factory_name = pw_properties_get(props, "factory.name");
 | 
			
		||||
			if (factory_name &&
 | 
			
		||||
			    strcmp(factory_name, "client-node") == 0) {
 | 
			
		||||
				set_permission = true;
 | 
			
		||||
				allowed = true;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				set_permission = false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if (pw_global_is_type(global, PW_TYPE_INTERFACE_Module)) {
 | 
			
		||||
			set_permission = true;
 | 
			
		||||
			allowed = true;
 | 
			
		||||
		}
 | 
			
		||||
		else if (pw_global_is_type(global, PW_TYPE_INTERFACE_Node)) {
 | 
			
		||||
			enum media_role media_role;
 | 
			
		||||
 | 
			
		||||
			media_role = media_role_from_properties(props);
 | 
			
		||||
 | 
			
		||||
			if (media_role == MEDIA_ROLE_INVALID) {
 | 
			
		||||
				set_permission = false;
 | 
			
		||||
			}
 | 
			
		||||
			else if (cinfo->allowed_media_roles & media_role) {
 | 
			
		||||
				set_permission = true;
 | 
			
		||||
				allowed = true;
 | 
			
		||||
			}
 | 
			
		||||
			else if (cinfo->media_roles & media_role) {
 | 
			
		||||
				set_permission = true;
 | 
			
		||||
				allowed = false;
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				set_permission = false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			set_permission = false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		set_permission = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (set_permission) {
 | 
			
		||||
		permissions[n_permissions++] =
 | 
			
		||||
			PW_PERMISSION_INIT(pw_global_get_id(global), allowed ? PW_PERM_RWX : 0);
 | 
			
		||||
 | 
			
		||||
		pw_impl_client_update_permissions(client, n_permissions, permissions);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
check_permission_allowed(DBusMessageIter *iter)
 | 
			
		||||
{
 | 
			
		||||
	bool allowed = false;
 | 
			
		||||
 | 
			
		||||
	while (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_INVALID) {
 | 
			
		||||
		const char *permission_value;
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_get_basic(iter, &permission_value);
 | 
			
		||||
 | 
			
		||||
		if (strcmp(permission_value, "yes") == 0) {
 | 
			
		||||
			allowed = true;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		dbus_message_iter_next(iter);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return allowed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_permission_store_check(struct client_info *cinfo)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = cinfo->impl;
 | 
			
		||||
	struct pw_impl_client *client = cinfo->client;
 | 
			
		||||
	DBusMessage *m = NULL, *r = NULL;
 | 
			
		||||
	DBusError error;
 | 
			
		||||
	DBusMessageIter msg_iter;
 | 
			
		||||
	const char *table;
 | 
			
		||||
	const char *id;
 | 
			
		||||
	DBusMessageIter r_iter;
 | 
			
		||||
	DBusMessageIter permissions_iter;
 | 
			
		||||
 | 
			
		||||
	if (cinfo->app_id == NULL) {
 | 
			
		||||
		pw_log_debug("Ignoring portal check for broken portal managed client %p",
 | 
			
		||||
			     client);
 | 
			
		||||
		goto err_not_allowed;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cinfo->media_roles == 0) {
 | 
			
		||||
		pw_log_debug("Ignoring portal check for portal client %p with static permissions",
 | 
			
		||||
			     client);
 | 
			
		||||
		pw_context_for_each_global(cinfo->impl->context,
 | 
			
		||||
					set_global_permissions,
 | 
			
		||||
					cinfo);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(cinfo->app_id, "") == 0) {
 | 
			
		||||
		pw_log_debug("Ignoring portal check for non-sandboxed portal client %p",
 | 
			
		||||
			     client);
 | 
			
		||||
		cinfo->allowed_media_roles = MEDIA_ROLE_ALL;
 | 
			
		||||
		pw_context_for_each_global(cinfo->impl->context,
 | 
			
		||||
					set_global_permissions,
 | 
			
		||||
					cinfo);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cinfo->allowed_media_roles = MEDIA_ROLE_NONE;
 | 
			
		||||
 | 
			
		||||
	dbus_error_init(&error);
 | 
			
		||||
 | 
			
		||||
	m = dbus_message_new_method_call("org.freedesktop.impl.portal.PermissionStore",
 | 
			
		||||
					 "/org/freedesktop/impl/portal/PermissionStore",
 | 
			
		||||
					 "org.freedesktop.impl.portal.PermissionStore",
 | 
			
		||||
					 "Lookup");
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_init_append(m, &msg_iter);
 | 
			
		||||
	table = "devices";
 | 
			
		||||
	dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &table);
 | 
			
		||||
	id = "camera";
 | 
			
		||||
	dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &id);
 | 
			
		||||
 | 
			
		||||
	if (!(r = dbus_connection_send_with_reply_and_block(impl->bus, m, -1, &error))) {
 | 
			
		||||
		pw_log_error("Failed to call permission store: %s", error.message);
 | 
			
		||||
		dbus_error_free(&error);
 | 
			
		||||
		goto err_not_allowed;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbus_message_unref(m);
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_init(r, &r_iter);
 | 
			
		||||
	dbus_message_iter_recurse(&r_iter, &permissions_iter);
 | 
			
		||||
	while (dbus_message_iter_get_arg_type(&permissions_iter) !=
 | 
			
		||||
	       DBUS_TYPE_INVALID) {
 | 
			
		||||
		DBusMessageIter permissions_entry_iter;
 | 
			
		||||
		const char *app_id;
 | 
			
		||||
		DBusMessageIter permission_values_iter;
 | 
			
		||||
		bool camera_allowed;
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_recurse(&permissions_iter,
 | 
			
		||||
					  &permissions_entry_iter);
 | 
			
		||||
		dbus_message_iter_get_basic(&permissions_entry_iter, &app_id);
 | 
			
		||||
 | 
			
		||||
		pw_log_info("permissions %s", app_id);
 | 
			
		||||
		if (strcmp(app_id, cinfo->app_id) != 0) {
 | 
			
		||||
			dbus_message_iter_next(&permissions_iter);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_next(&permissions_entry_iter);
 | 
			
		||||
		dbus_message_iter_recurse(&permissions_entry_iter,
 | 
			
		||||
					  &permission_values_iter);
 | 
			
		||||
 | 
			
		||||
		camera_allowed = check_permission_allowed(&permission_values_iter);
 | 
			
		||||
		pw_log_info("allowed %d", camera_allowed);
 | 
			
		||||
		cinfo->allowed_media_roles |=
 | 
			
		||||
			camera_allowed ? MEDIA_ROLE_CAMERA : MEDIA_ROLE_NONE;
 | 
			
		||||
		pw_context_for_each_global(cinfo->impl->context,
 | 
			
		||||
					set_global_permissions,
 | 
			
		||||
					cinfo);
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbus_message_unref(r);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
      err_not_allowed:
 | 
			
		||||
	pw_resource_error(pw_impl_client_get_core_resource(client), -EPERM, "not allowed");
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void client_info_changed(void *data, const struct pw_client_info *info)
 | 
			
		||||
{
 | 
			
		||||
	struct client_info *cinfo = data;
 | 
			
		||||
	const struct pw_properties *properties;
 | 
			
		||||
	const char *is_portal;
 | 
			
		||||
	const char *app_id;
 | 
			
		||||
	const char *media_roles;
 | 
			
		||||
 | 
			
		||||
	if (!cinfo->portal_managed)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (info->props == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (cinfo->setup_complete)
 | 
			
		||||
		return;
 | 
			
		||||
	cinfo->setup_complete = true;
 | 
			
		||||
 | 
			
		||||
	properties = pw_impl_client_get_properties(cinfo->client);
 | 
			
		||||
	if (properties == NULL) {
 | 
			
		||||
		pw_log_error("Portal managed client didn't have any properties");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	is_portal = pw_properties_get(properties,
 | 
			
		||||
				      "pipewire.access.portal.is_portal");
 | 
			
		||||
	if (is_portal != NULL &&
 | 
			
		||||
	    (strcmp(is_portal, "yes") == 0 || pw_properties_parse_bool(is_portal))) {
 | 
			
		||||
		pw_log_debug("module %p: client %p is the portal itself",
 | 
			
		||||
			     cinfo->impl, cinfo->client);
 | 
			
		||||
		cinfo->is_portal = true;
 | 
			
		||||
		return;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	app_id = pw_properties_get(properties,
 | 
			
		||||
				   "pipewire.access.portal.app_id");
 | 
			
		||||
	if (app_id == NULL) {
 | 
			
		||||
		pw_log_error("Portal managed client didn't set app_id");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	media_roles = pw_properties_get(properties,
 | 
			
		||||
					"pipewire.access.portal.media_roles");
 | 
			
		||||
 | 
			
		||||
	if (media_roles == NULL) {
 | 
			
		||||
		pw_log_error("Portal managed client didn't set media_roles");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cinfo->app_id = strdup(app_id);
 | 
			
		||||
	cinfo->media_roles = parse_media_roles(media_roles);
 | 
			
		||||
 | 
			
		||||
	pw_log_info("module %p: client %p with app_id '%s' set to portal access",
 | 
			
		||||
		     cinfo->impl, cinfo->client, cinfo->app_id);
 | 
			
		||||
	do_permission_store_check(cinfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct pw_impl_client_events client_events = {
 | 
			
		||||
	PW_VERSION_IMPL_CLIENT_EVENTS,
 | 
			
		||||
	.info_changed = client_info_changed
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
core_global_added(void *data, struct pw_global *global)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = data;
 | 
			
		||||
	struct client_info *cinfo;
 | 
			
		||||
 | 
			
		||||
	if (pw_global_is_type(global, PW_TYPE_INTERFACE_Client)) {
 | 
			
		||||
		struct pw_impl_client *client = pw_global_get_object(global);
 | 
			
		||||
 | 
			
		||||
		cinfo = calloc(1, sizeof(struct client_info));
 | 
			
		||||
		cinfo->impl = impl;
 | 
			
		||||
		cinfo->client = client;
 | 
			
		||||
		pw_impl_client_add_listener(client, &cinfo->client_listener, &client_events, cinfo);
 | 
			
		||||
 | 
			
		||||
		spa_list_append(&impl->client_list, &cinfo->link);
 | 
			
		||||
 | 
			
		||||
		check_portal_managed(cinfo);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		spa_list_for_each(cinfo, &impl->client_list, link) {
 | 
			
		||||
			if (cinfo->portal_managed &&
 | 
			
		||||
			    !cinfo->is_portal)
 | 
			
		||||
				set_global_permissions(cinfo, global);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
core_global_removed(void *data, struct pw_global *global)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = data;
 | 
			
		||||
 | 
			
		||||
	if (pw_global_is_type(global, PW_TYPE_INTERFACE_Client)) {
 | 
			
		||||
		struct pw_impl_client *client = pw_global_get_object(global);
 | 
			
		||||
		struct client_info *cinfo;
 | 
			
		||||
 | 
			
		||||
		if ((cinfo = find_client_info(impl, client)))
 | 
			
		||||
			client_info_free(cinfo);
 | 
			
		||||
 | 
			
		||||
		pw_log_debug("module %p: client %p removed", impl, client);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct pw_context_events context_events = {
 | 
			
		||||
	PW_VERSION_CORE_EVENTS,
 | 
			
		||||
	.global_added = core_global_added,
 | 
			
		||||
	.global_removed = core_global_removed,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void module_destroy(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = data;
 | 
			
		||||
	struct client_info *info, *t;
 | 
			
		||||
 | 
			
		||||
	spa_hook_remove(&impl->context_listener);
 | 
			
		||||
	spa_hook_remove(&impl->module_listener);
 | 
			
		||||
 | 
			
		||||
	spa_dbus_connection_destroy(impl->conn);
 | 
			
		||||
 | 
			
		||||
	spa_list_for_each_safe(info, t, &impl->client_list, link)
 | 
			
		||||
		client_info_free(info);
 | 
			
		||||
 | 
			
		||||
	if (impl->properties)
 | 
			
		||||
		pw_properties_free(impl->properties);
 | 
			
		||||
 | 
			
		||||
	free(impl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct pw_impl_module_events module_events = {
 | 
			
		||||
	PW_VERSION_IMPL_MODULE_EVENTS,
 | 
			
		||||
	.destroy = module_destroy,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void on_portal_pid_received(DBusPendingCall *pending,
 | 
			
		||||
				   void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = user_data;
 | 
			
		||||
	DBusMessage *m;
 | 
			
		||||
	DBusError error;
 | 
			
		||||
	uint32_t portal_pid = 0;
 | 
			
		||||
 | 
			
		||||
	m = dbus_pending_call_steal_reply(pending);
 | 
			
		||||
	dbus_pending_call_unref(pending);
 | 
			
		||||
	impl->portal_pid_pending = NULL;
 | 
			
		||||
 | 
			
		||||
	if (!m) {
 | 
			
		||||
		pw_log_error("Failed to receive portal pid");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbus_error_init(&error);
 | 
			
		||||
	dbus_message_get_args(m, &error, DBUS_TYPE_UINT32, &portal_pid,
 | 
			
		||||
			      DBUS_TYPE_INVALID);
 | 
			
		||||
	dbus_message_unref(m);
 | 
			
		||||
 | 
			
		||||
	if (dbus_error_is_set(&error)) {
 | 
			
		||||
		impl->portal_pid = 0;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		struct client_info *cinfo;
 | 
			
		||||
 | 
			
		||||
		impl->portal_pid = portal_pid;
 | 
			
		||||
 | 
			
		||||
		spa_list_for_each(cinfo, &impl->client_list, link) {
 | 
			
		||||
			if (cinfo->portal_managed)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			check_portal_managed(cinfo);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_portal_pid(struct impl *impl)
 | 
			
		||||
{
 | 
			
		||||
	DBusMessage *m;
 | 
			
		||||
	const char *name;
 | 
			
		||||
	DBusPendingCall *pending;
 | 
			
		||||
 | 
			
		||||
	impl->portal_pid = 0;
 | 
			
		||||
 | 
			
		||||
	m = dbus_message_new_method_call("org.freedesktop.DBus",
 | 
			
		||||
					 "/",
 | 
			
		||||
					 "org.freedesktop.DBus",
 | 
			
		||||
					 "GetConnectionUnixProcessID");
 | 
			
		||||
 | 
			
		||||
	name = "org.freedesktop.portal.Desktop";
 | 
			
		||||
	dbus_message_append_args(m,
 | 
			
		||||
				 DBUS_TYPE_STRING, &name,
 | 
			
		||||
				 DBUS_TYPE_INVALID);
 | 
			
		||||
 | 
			
		||||
	dbus_connection_send_with_reply(impl->bus, m, &pending, -1);
 | 
			
		||||
	dbus_pending_call_set_notify(pending, on_portal_pid_received, impl, NULL);
 | 
			
		||||
	if (impl->portal_pid_pending != NULL) {
 | 
			
		||||
		dbus_pending_call_cancel(impl->portal_pid_pending);
 | 
			
		||||
		dbus_pending_call_unref(impl->portal_pid_pending);
 | 
			
		||||
	}
 | 
			
		||||
	impl->portal_pid_pending = pending;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusHandlerResult name_owner_changed_handler(DBusConnection *connection,
 | 
			
		||||
						    DBusMessage *message,
 | 
			
		||||
						    void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = user_data;
 | 
			
		||||
	const char *name;
 | 
			
		||||
	const char *old_owner;
 | 
			
		||||
	const char *new_owner;
 | 
			
		||||
 | 
			
		||||
	if (!dbus_message_is_signal(message, "org.freedesktop.DBus",
 | 
			
		||||
				   "NameOwnerChanged"))
 | 
			
		||||
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
			
		||||
 | 
			
		||||
	if (!dbus_message_get_args(message, NULL,
 | 
			
		||||
				   DBUS_TYPE_STRING, &name,
 | 
			
		||||
				   DBUS_TYPE_STRING, &old_owner,
 | 
			
		||||
				   DBUS_TYPE_STRING, &new_owner,
 | 
			
		||||
				   DBUS_TYPE_INVALID)) {
 | 
			
		||||
		pw_log_error("Failed to get OwnerChanged args");
 | 
			
		||||
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (strcmp(name, "org.freedesktop.portal.Desktop") != 0)
 | 
			
		||||
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(new_owner, "") == 0) {
 | 
			
		||||
		impl->portal_pid = 0;
 | 
			
		||||
		if (impl->portal_pid_pending != NULL) {
 | 
			
		||||
			dbus_pending_call_cancel(impl->portal_pid_pending);
 | 
			
		||||
			dbus_pending_call_unref(impl->portal_pid_pending);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		update_portal_pid(impl);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusHandlerResult permission_store_changed_handler(DBusConnection *connection,
 | 
			
		||||
							  DBusMessage *message,
 | 
			
		||||
							  void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct impl *impl = user_data;
 | 
			
		||||
	struct client_info *cinfo;
 | 
			
		||||
	DBusMessageIter iter;
 | 
			
		||||
	const char *table;
 | 
			
		||||
	const char *id;
 | 
			
		||||
	dbus_bool_t deleted;
 | 
			
		||||
	DBusMessageIter permissions_iter;
 | 
			
		||||
 | 
			
		||||
	if (!dbus_message_is_signal(message, "org.freedesktop.impl.portal.PermissionStore",
 | 
			
		||||
				   "Changed"))
 | 
			
		||||
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
			
		||||
 | 
			
		||||
	spa_list_for_each(cinfo, &impl->client_list, link) {
 | 
			
		||||
		if (!cinfo->portal_managed)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cinfo->allowed_media_roles = MEDIA_ROLE_NONE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_init(message, &iter);
 | 
			
		||||
	dbus_message_iter_get_basic(&iter, &table);
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_next(&iter);
 | 
			
		||||
	dbus_message_iter_get_basic(&iter, &id);
 | 
			
		||||
 | 
			
		||||
	if (strcmp(table, "devices") != 0 || strcmp(id, "camera") != 0)
 | 
			
		||||
		return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_next(&iter);
 | 
			
		||||
	dbus_message_iter_get_basic(&iter, &deleted);
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_next(&iter);
 | 
			
		||||
	/* data variant (ignored) */
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_next(&iter);
 | 
			
		||||
	dbus_message_iter_recurse(&iter, &permissions_iter);
 | 
			
		||||
	while (dbus_message_iter_get_arg_type(&permissions_iter) !=
 | 
			
		||||
	       DBUS_TYPE_INVALID) {
 | 
			
		||||
		DBusMessageIter permissions_entry_iter;
 | 
			
		||||
		const char *app_id;
 | 
			
		||||
		DBusMessageIter permission_values_iter;
 | 
			
		||||
		bool camera_allowed;
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_recurse(&permissions_iter,
 | 
			
		||||
					  &permissions_entry_iter);
 | 
			
		||||
		dbus_message_iter_get_basic(&permissions_entry_iter, &app_id);
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_next(&permissions_entry_iter);
 | 
			
		||||
		dbus_message_iter_recurse(&permissions_entry_iter,
 | 
			
		||||
					  &permission_values_iter);
 | 
			
		||||
 | 
			
		||||
		camera_allowed = check_permission_allowed(&permission_values_iter);
 | 
			
		||||
 | 
			
		||||
		spa_list_for_each(cinfo, &impl->client_list, link) {
 | 
			
		||||
			if (!cinfo->portal_managed)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (cinfo->is_portal)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (cinfo->app_id == NULL ||
 | 
			
		||||
			    strcmp(cinfo->app_id, app_id) != 0)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (!(cinfo->media_roles & MEDIA_ROLE_CAMERA))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (camera_allowed)
 | 
			
		||||
				cinfo->allowed_media_roles |= MEDIA_ROLE_CAMERA;
 | 
			
		||||
			pw_context_for_each_global(cinfo->impl->context,
 | 
			
		||||
						set_global_permissions,
 | 
			
		||||
						cinfo);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_next(&permissions_iter);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int init_dbus_connection(struct impl *impl)
 | 
			
		||||
{
 | 
			
		||||
	DBusError error;
 | 
			
		||||
 | 
			
		||||
	impl->bus = spa_dbus_connection_get(impl->conn);
 | 
			
		||||
 | 
			
		||||
	dbus_error_init(&error);
 | 
			
		||||
 | 
			
		||||
	dbus_bus_add_match(impl->bus,
 | 
			
		||||
			   "type='signal',\
 | 
			
		||||
			   sender='org.freedesktop.DBus',\
 | 
			
		||||
			   interface='org.freedesktop.DBus',\
 | 
			
		||||
			   member='NameOwnerChanged'",
 | 
			
		||||
			   &error);
 | 
			
		||||
	if (dbus_error_is_set(&error)) {
 | 
			
		||||
		pw_log_error("Failed to add name owner changed listener: %s",
 | 
			
		||||
			     error.message);
 | 
			
		||||
		dbus_error_free(&error);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbus_bus_add_match(impl->bus,
 | 
			
		||||
			   "type='signal',\
 | 
			
		||||
			   sender='org.freedesktop.impl.portal.PermissionStore',\
 | 
			
		||||
			   interface='org.freedesktop.impl.portal.PermissionStore',\
 | 
			
		||||
			   member='Changed'",
 | 
			
		||||
			   &error);
 | 
			
		||||
	if (dbus_error_is_set(&error)) {
 | 
			
		||||
		pw_log_error("Failed to add permission store changed listener: %s",
 | 
			
		||||
			     error.message);
 | 
			
		||||
		dbus_error_free(&error);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbus_connection_add_filter(impl->bus, name_owner_changed_handler,
 | 
			
		||||
				   impl, NULL);
 | 
			
		||||
	dbus_connection_add_filter(impl->bus, permission_store_changed_handler,
 | 
			
		||||
				   impl, NULL);
 | 
			
		||||
	update_portal_pid(impl);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SPA_EXPORT
 | 
			
		||||
int pipewire__module_init(struct pw_impl_module *module, const char *args)
 | 
			
		||||
{
 | 
			
		||||
	struct pw_context *context = pw_impl_module_get_context(module);
 | 
			
		||||
	struct impl *impl;
 | 
			
		||||
	struct spa_dbus *dbus;
 | 
			
		||||
	const struct spa_support *support;
 | 
			
		||||
	uint32_t n_support;
 | 
			
		||||
 | 
			
		||||
	support = pw_context_get_support(context, &n_support);
 | 
			
		||||
 | 
			
		||||
	dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
 | 
			
		||||
        if (dbus == NULL)
 | 
			
		||||
                return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
	impl = calloc(1, sizeof(struct impl));
 | 
			
		||||
	if (impl == NULL)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	pw_log_debug("module %p: new", impl);
 | 
			
		||||
 | 
			
		||||
	impl->context = context;
 | 
			
		||||
	impl->properties = args ? pw_properties_new_string(args) : NULL;
 | 
			
		||||
 | 
			
		||||
	impl->conn = spa_dbus_get_connection(dbus, SPA_DBUS_TYPE_SESSION);
 | 
			
		||||
	if (impl->conn == NULL)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (init_dbus_connection(impl) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	spa_list_init(&impl->client_list);
 | 
			
		||||
 | 
			
		||||
	pw_context_add_listener(context, &impl->context_listener, &context_events, impl);
 | 
			
		||||
	pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
      error:
 | 
			
		||||
	free(impl);
 | 
			
		||||
	pw_log_error("Failed to connect to system bus");
 | 
			
		||||
	return -ENOMEM;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue