*
* Copyright 2026 Alexander Vanhee
* Copyright (C) 2022 Red Hat <www.redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include <glib-object.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <stdlib.h>
#include "bz-app-permissions.h"
#define DOES_NOT_CONTAIN ((guint) ~0)
G_DEFINE_FLAGS_TYPE (
BzAppPermissionsFlags,
bz_app_permissions_flags,
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_NONE, "none"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_NETWORK, "network"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_SYSTEM_BUS, "system-bus"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_SESSION_BUS, "session-bus"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_DEVICES, "devices"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_HOME_FULL, "home-full"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_HOME_READ, "home-read"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL, "filesystem-full"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ, "filesystem-read"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL, "downloads-full"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ, "downloads-read"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_SETTINGS, "settings"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_X11, "x11"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX, "escape-sandbox"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_OTHER, "filesystem-other"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_BUS_POLICY_OTHER, "bus-policy-other"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_AUDIO_DEVICES, "audio-devices"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_INPUT_DEVICES, "input-devices"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_SYSTEM_DEVICES, "system-devices"),
G_DEFINE_ENUM_VALUE (BZ_APP_PERMISSIONS_FLAGS_SCREEN, "screen"))
G_DEFINE_ENUM_TYPE (
BzFilesystemPathType,
bz_filesystem_path_type,
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_SYSTEM_ROOT, "system-root"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_HOME_SUBDIR, "home-subdir"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_HOST_OS, "host-os"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_HOST_ETC, "host-etc"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_DESKTOP, "xdg-desktop"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_DOCUMENTS, "xdg-documents"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_MUSIC, "xdg-music"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_PICTURES, "xdg-pictures"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_PUBLIC_SHARE, "xdg-public-share"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_VIDEOS, "xdg-videos"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_TEMPLATES, "xdg-templates"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_CACHE, "xdg-cache"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_CONFIG, "xdg-config"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_DATA, "xdg-data"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_XDG_RUN, "xdg-run"),
G_DEFINE_ENUM_VALUE (BZ_FILESYSTEM_PATH_CUSTOM, "custom"))
G_DEFINE_ENUM_TYPE (
BzBusPolicyPermission,
bz_bus_policy_permission,
G_DEFINE_ENUM_VALUE (BZ_BUS_POLICY_PERMISSION_UNKNOWN, "unknown"),
G_DEFINE_ENUM_VALUE (BZ_BUS_POLICY_PERMISSION_NONE, "none"),
G_DEFINE_ENUM_VALUE (BZ_BUS_POLICY_PERMISSION_SEE, "see"),
G_DEFINE_ENUM_VALUE (BZ_BUS_POLICY_PERMISSION_TALK, "talk"),
G_DEFINE_ENUM_VALUE (BZ_BUS_POLICY_PERMISSION_OWN, "own"))
struct _BzAppPermissions
{
GObject parent;
gboolean is_sealed;
BzAppPermissionsFlags flags;
GPtrArray *filesystem_read;
GPtrArray *filesystem_full;
GPtrArray *bus_policies;
};
G_DEFINE_FINAL_TYPE (BzAppPermissions, bz_app_permissions, G_TYPE_OBJECT)
static gint cmp_filesystem_path_pointers (gconstpointer item1,
gconstpointer item2);
static int cmp_bus_policy_qsort (const void *item1,
const void *item2);
static guint app_permissions_get_array_index (GPtrArray *array,
BzFilesystemPathType type,
const char *subpath);
static guint get_strv_index (const gchar *const *strv,
const gchar *value);
static gboolean array_contains_filesystem_path (GPtrArray *array,
BzFilesystemPathType type,
const char *subpath);
static int bus_policy_compare (const BzBusPolicy *policy1,
const BzBusPolicy *policy2);
static int bus_policy_compare_lopsided_with_permissions (const BzBusPolicy *policy1,
const BzBusPolicy *policy2);
static BzBusPolicy *bus_policy_copy (const BzBusPolicy *policy,
void *user_data);
static GPtrArray *ptr_array_diff (GPtrArray *array1,
GPtrArray *array2,
GCompareFunc compare_func,
GCopyFunc copy_func,
void *copy_func_data,
GDestroyNotify free_func);
static void
bz_app_permissions_finalize (GObject *object)
{
BzAppPermissions *self = BZ_APP_PERMISSIONS (object);
g_clear_pointer (&self->filesystem_read, g_ptr_array_unref);
g_clear_pointer (&self->filesystem_full, g_ptr_array_unref);
g_clear_pointer (&self->bus_policies, g_ptr_array_unref);
G_OBJECT_CLASS (bz_app_permissions_parent_class)->finalize (object);
}
static void
bz_app_permissions_class_init (BzAppPermissionsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = bz_app_permissions_finalize;
}
static void
bz_app_permissions_init (BzAppPermissions *self)
{
}
BzFilesystemPath *
bz_filesystem_path_new (BzFilesystemPathType type,
const char *subpath)
{
BzFilesystemPath *path = g_new0 (BzFilesystemPath, 1);
path->type = type;
path->subpath = g_strdup (subpath);
return path;
}
void
bz_filesystem_path_free (BzFilesystemPath *path)
{
g_return_if_fail (path != NULL);
g_free (path->subpath);
g_free (path);
}
char *
bz_filesystem_path_to_display_string (const BzFilesystemPath *path)
{
g_return_val_if_fail (path != NULL, NULL);
switch (path->type)
{
case BZ_FILESYSTEM_PATH_SYSTEM_ROOT:
return g_strdup_printf (_ ("System folder %s"), path->subpath ? path->subpath : "/");
case BZ_FILESYSTEM_PATH_HOME_SUBDIR:
return g_strdup_printf (_ ("Home subfolder %s"), path->subpath);
case BZ_FILESYSTEM_PATH_HOST_OS:
return g_strdup (_ ("Host system folders"));
case BZ_FILESYSTEM_PATH_HOST_ETC:
return g_strdup (_ ("Host system configuration from /etc"));
case BZ_FILESYSTEM_PATH_XDG_DESKTOP:
if (path->subpath)
return g_strdup_printf (_ ("Desktop subfolder %s"), path->subpath);
return g_strdup (_ ("Desktop folder"));
case BZ_FILESYSTEM_PATH_XDG_DOCUMENTS:
if (path->subpath)
return g_strdup_printf (_ ("Documents subfolder %s"), path->subpath);
return g_strdup (_ ("Documents folder"));
case BZ_FILESYSTEM_PATH_XDG_MUSIC:
if (path->subpath)
return g_strdup_printf (_ ("Music subfolder %s"), path->subpath);
return g_strdup (_ ("Music folder"));
case BZ_FILESYSTEM_PATH_XDG_PICTURES:
if (path->subpath)
return g_strdup_printf (_ ("Pictures subfolder %s"), path->subpath);
return g_strdup (_ ("Pictures folder"));
case BZ_FILESYSTEM_PATH_XDG_PUBLIC_SHARE:
if (path->subpath)
return g_strdup_printf (_ ("Public Share subfolder %s"), path->subpath);
return g_strdup (_ ("Public Share folder"));
case BZ_FILESYSTEM_PATH_XDG_VIDEOS:
if (path->subpath)
return g_strdup_printf (_ ("Videos subfolder %s"), path->subpath);
return g_strdup (_ ("Videos folder"));
case BZ_FILESYSTEM_PATH_XDG_TEMPLATES:
if (path->subpath)
return g_strdup_printf (_ ("Templates subfolder %s"), path->subpath);
return g_strdup (_ ("Templates folder"));
case BZ_FILESYSTEM_PATH_XDG_CACHE:
if (path->subpath)
return g_strdup_printf (_ ("User cache subfolder %s"), path->subpath);
return g_strdup (_ ("User cache folder"));
case BZ_FILESYSTEM_PATH_XDG_CONFIG:
if (path->subpath)
return g_strdup_printf (_ ("User configuration subfolder %s"), path->subpath);
return g_strdup (_ ("User configuration folder"));
case BZ_FILESYSTEM_PATH_XDG_DATA:
if (path->subpath)
return g_strdup_printf (_ ("User data subfolder %s"), path->subpath);
return g_strdup (_ ("User data folder"));
case BZ_FILESYSTEM_PATH_XDG_RUN:
if (path->subpath)
return g_strdup_printf (_ ("User runtime subfolder %s"), path->subpath);
return g_strdup (_ ("User runtime folder"));
case BZ_FILESYSTEM_PATH_CUSTOM:
return g_strdup_printf (_ ("Filesystem access to %s"), path->subpath);
default:
return g_strdup (_ ("Unknown filesystem path"));
}
}
const char *
bz_filesystem_path_to_icon_name (const BzFilesystemPath *path)
{
switch (path->type)
{
case BZ_FILESYSTEM_PATH_HOST_OS:
return "computer-symbolic";
case BZ_FILESYSTEM_PATH_HOST_ETC:
return "emblem-system-symbolic";
case BZ_FILESYSTEM_PATH_XDG_DESKTOP:
return "user-desktop-symbolic";
case BZ_FILESYSTEM_PATH_XDG_DOCUMENTS:
return "folder-documents-symbolic";
case BZ_FILESYSTEM_PATH_XDG_MUSIC:
return "folder-music-symbolic";
case BZ_FILESYSTEM_PATH_XDG_PICTURES:
return "folder-pictures-symbolic";
case BZ_FILESYSTEM_PATH_XDG_PUBLIC_SHARE:
return "folder-publicshare-symbolic";
case BZ_FILESYSTEM_PATH_XDG_VIDEOS:
return "folder-videos-symbolic";
case BZ_FILESYSTEM_PATH_XDG_CONFIG:
return "emblem-system-symbolic";
case BZ_FILESYSTEM_PATH_SYSTEM_ROOT:
case BZ_FILESYSTEM_PATH_HOME_SUBDIR:
case BZ_FILESYSTEM_PATH_XDG_TEMPLATES:
case BZ_FILESYSTEM_PATH_XDG_CACHE:
case BZ_FILESYSTEM_PATH_XDG_DATA:
case BZ_FILESYSTEM_PATH_XDG_RUN:
case BZ_FILESYSTEM_PATH_CUSTOM:
default:
return "folder-symbolic";
}
}
BzBusPolicy *
bz_bus_policy_new (GBusType bus_type,
const char *bus_name,
BzBusPolicyPermission permission)
{
BzBusPolicy *policy = NULL;
g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, NULL);
g_return_val_if_fail (bus_name != NULL && *bus_name != '\0', NULL);
policy = g_new0 (BzBusPolicy, 1);
policy->bus_type = bus_type;
policy->bus_name = g_strdup (bus_name);
policy->permission = permission;
return policy;
}
void
bz_bus_policy_free (BzBusPolicy *self)
{
g_return_if_fail (self != NULL);
g_free (self->bus_name);
g_free (self);
}
BzAppPermissions *
bz_app_permissions_new (void)
{
return g_object_new (BZ_TYPE_APP_PERMISSIONS, NULL);
}
void
bz_app_permissions_seal (BzAppPermissions *self)
{
g_return_if_fail (BZ_IS_APP_PERMISSIONS (self));
if (self->is_sealed)
return;
self->is_sealed = TRUE;
if (self->filesystem_read)
qsort (self->filesystem_read->pdata, self->filesystem_read->len, sizeof (gpointer), cmp_filesystem_path_pointers);
if (self->filesystem_full)
qsort (self->filesystem_full->pdata, self->filesystem_full->len, sizeof (gpointer), cmp_filesystem_path_pointers);
if (self->bus_policies)
qsort (self->bus_policies->pdata, self->bus_policies->len, sizeof (gpointer), cmp_bus_policy_qsort);
}
gboolean
bz_app_permissions_is_sealed (BzAppPermissions *self)
{
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (self), TRUE);
return self->is_sealed;
}
gboolean
bz_app_permissions_is_empty (BzAppPermissions *self)
{
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (self), TRUE);
return (self->flags == BZ_APP_PERMISSIONS_FLAGS_NONE &&
(self->filesystem_read == NULL || self->filesystem_read->len == 0) &&
(self->filesystem_full == NULL || self->filesystem_full->len == 0) &&
(self->bus_policies == NULL || self->bus_policies->len == 0));
}
static gboolean
array_contains_filesystem_path (GPtrArray *array,
BzFilesystemPathType type,
const char *subpath)
{
if (array == NULL)
return FALSE;
for (guint i = 0; i < array->len; i++)
{
const BzFilesystemPath *path = g_ptr_array_index (array, i);
if (path->type == type && g_strcmp0 (path->subpath, subpath) == 0)
return TRUE;
}
return FALSE;
}
static int
bus_policy_compare (const BzBusPolicy *policy1,
const BzBusPolicy *policy2)
{
if (policy1->bus_type != policy2->bus_type)
return policy1->bus_type - policy2->bus_type;
return strcmp (policy1->bus_name, policy2->bus_name);
}
static int
bus_policy_compare_lopsided_with_permissions (const BzBusPolicy *policy1,
const BzBusPolicy *policy2)
{
int compare_keys = bus_policy_compare (policy1, policy2);
if (compare_keys != 0)
return compare_keys;
if (policy2->permission > policy1->permission)
return 1;
return 0;
}
static BzBusPolicy *
bus_policy_copy (const BzBusPolicy *policy,
void *user_data)
{
return bz_bus_policy_new (policy->bus_type, policy->bus_name, policy->permission);
}
static GPtrArray *
ptr_array_diff (GPtrArray *array1,
GPtrArray *array2,
GCompareFunc compare_func,
GCopyFunc copy_func,
void *copy_func_data,
GDestroyNotify free_func)
{
g_autoptr (GPtrArray) diff = NULL;
if (array2 == NULL)
return NULL;
if (array1 == NULL)
return g_ptr_array_copy (array2, copy_func, copy_func_data);
diff = g_ptr_array_new_with_free_func (free_func);
for (unsigned int index1 = 0, index2 = 0;
index1 < array1->len || index2 < array2->len;
)
{
const void *element1, *element2;
int comparison;
if (index1 >= array1->len)
{
element2 = g_ptr_array_index (array2, index2);
comparison = 1;
}
else if (index2 >= array2->len)
{
element1 = g_ptr_array_index (array1, index1);
comparison = -1;
}
else
{
element1 = g_ptr_array_index (array1, index1);
element2 = g_ptr_array_index (array2, index2);
comparison = compare_func (element1, element2);
}
if (comparison < 0)
{
index1++;
}
else if (comparison == 0)
{
index1++;
index2++;
}
else
{
g_ptr_array_add (diff, copy_func (element2, copy_func_data));
index2++;
}
}
return (diff->len > 0) ? g_steal_pointer (&diff) : NULL;
}
BzAppPermissions *
bz_app_permissions_diff (BzAppPermissions *self,
BzAppPermissions *other)
{
g_autoptr (BzAppPermissions) diff = bz_app_permissions_new ();
const GPtrArray *new_paths;
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (self), NULL);
g_return_val_if_fail (self->is_sealed, NULL);
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (other), NULL);
g_return_val_if_fail (other->is_sealed, NULL);
bz_app_permissions_set_flags (diff, other->flags & ~self->flags);
new_paths = bz_app_permissions_get_filesystem_read (other);
for (guint i = 0; new_paths != NULL && i < new_paths->len; i++)
{
const BzFilesystemPath *path = g_ptr_array_index (new_paths, i);
if (!array_contains_filesystem_path (self->filesystem_read, path->type, path->subpath))
bz_app_permissions_add_filesystem_read (diff, path->type, path->subpath);
}
new_paths = bz_app_permissions_get_filesystem_full (other);
for (guint i = 0; new_paths != NULL && i < new_paths->len; i++)
{
const BzFilesystemPath *path = g_ptr_array_index (new_paths, i);
if (!array_contains_filesystem_path (self->filesystem_full, path->type, path->subpath))
bz_app_permissions_add_filesystem_full (diff, path->type, path->subpath);
}
diff->bus_policies = ptr_array_diff (self->bus_policies, other->bus_policies,
(GCompareFunc) bus_policy_compare_lopsided_with_permissions,
(GCopyFunc) bus_policy_copy, NULL,
(GDestroyNotify) bz_bus_policy_free);
bz_app_permissions_seal (diff);
return g_steal_pointer (&diff);
}
void
bz_app_permissions_set_flags (BzAppPermissions *self,
BzAppPermissionsFlags flags)
{
g_return_if_fail (BZ_IS_APP_PERMISSIONS (self));
g_assert (!self->is_sealed);
self->flags = flags;
}
BzAppPermissionsFlags
bz_app_permissions_get_flags (BzAppPermissions *self)
{
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (self), BZ_APP_PERMISSIONS_FLAGS_NONE);
return self->flags;
}
void
bz_app_permissions_add_flag (BzAppPermissions *self,
BzAppPermissionsFlags flags)
{
g_return_if_fail (BZ_IS_APP_PERMISSIONS (self));
g_return_if_fail (flags != BZ_APP_PERMISSIONS_FLAGS_NONE);
g_assert (!self->is_sealed);
self->flags = self->flags | flags;
}
void
bz_app_permissions_remove_flag (BzAppPermissions *self,
BzAppPermissionsFlags flags)
{
g_return_if_fail (BZ_IS_APP_PERMISSIONS (self));
g_return_if_fail (flags != BZ_APP_PERMISSIONS_FLAGS_NONE);
g_assert (!self->is_sealed);
self->flags = (self->flags & (~flags));
}
void
bz_app_permissions_add_filesystem_read (BzAppPermissions *self,
BzFilesystemPathType type,
const char *subpath)
{
g_return_if_fail (BZ_IS_APP_PERMISSIONS (self));
g_assert (!self->is_sealed);
if (app_permissions_get_array_index (self->filesystem_read, type, subpath) != DOES_NOT_CONTAIN ||
app_permissions_get_array_index (self->filesystem_full, type, subpath) != DOES_NOT_CONTAIN)
return;
if (self->filesystem_read == NULL)
self->filesystem_read = g_ptr_array_new_with_free_func ((GDestroyNotify) bz_filesystem_path_free);
g_ptr_array_add (self->filesystem_read, bz_filesystem_path_new (type, subpath));
}
const GPtrArray *
bz_app_permissions_get_filesystem_read (BzAppPermissions *self)
{
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (self), NULL);
return self->filesystem_read;
}
void
bz_app_permissions_add_filesystem_full (BzAppPermissions *self,
BzFilesystemPathType type,
const char *subpath)
{
guint read_index = DOES_NOT_CONTAIN;
g_return_if_fail (BZ_IS_APP_PERMISSIONS (self));
g_assert (!self->is_sealed);
if (app_permissions_get_array_index (self->filesystem_full, type, subpath) != DOES_NOT_CONTAIN)
return;
if (self->filesystem_full == NULL)
self->filesystem_full = g_ptr_array_new_with_free_func ((GDestroyNotify) bz_filesystem_path_free);
g_ptr_array_add (self->filesystem_full, bz_filesystem_path_new (type, subpath));
read_index = app_permissions_get_array_index (self->filesystem_read, type, subpath);
if (read_index != DOES_NOT_CONTAIN)
{
g_ptr_array_remove_index (self->filesystem_read, read_index);
if (self->filesystem_read->len == 0)
g_clear_pointer (&self->filesystem_read, g_ptr_array_unref);
}
}
const GPtrArray *
bz_app_permissions_get_filesystem_full (BzAppPermissions *self)
{
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (self), NULL);
return self->filesystem_full;
}
void
bz_app_permissions_add_bus_policy (BzAppPermissions *self,
GBusType bus_type,
const char *bus_name,
BzBusPolicyPermission permission)
{
g_return_if_fail (BZ_IS_APP_PERMISSIONS (self));
g_return_if_fail (bus_type != G_BUS_TYPE_NONE);
g_return_if_fail (bus_name != NULL && *bus_name != '\0');
g_return_if_fail (permission != BZ_BUS_POLICY_PERMISSION_UNKNOWN);
g_assert (!self->is_sealed);
for (unsigned int i = 0; self->bus_policies != NULL && i < self->bus_policies->len; i++)
{
BzBusPolicy *policy = g_ptr_array_index (self->bus_policies, i);
if (policy->bus_type == bus_type &&
g_str_equal (policy->bus_name, bus_name))
{
policy->permission = MAX (policy->permission, permission);
return;
}
}
if (permission == BZ_BUS_POLICY_PERMISSION_NONE)
return;
if (self->bus_policies == NULL)
self->bus_policies = g_ptr_array_new_with_free_func ((GDestroyNotify) bz_bus_policy_free);
g_ptr_array_add (self->bus_policies, bz_bus_policy_new (bus_type, bus_name, permission));
}
const BzBusPolicy *const *
bz_app_permissions_get_bus_policies (BzAppPermissions *self,
size_t *out_n_bus_policies)
{
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (self), NULL);
g_return_val_if_fail (self->is_sealed, NULL);
if (out_n_bus_policies != NULL)
*out_n_bus_policies = (self->bus_policies != NULL) ? self->bus_policies->len : 0;
return (self->bus_policies != NULL && self->bus_policies->len > 0) ? (const BzBusPolicy *const *) self->bus_policies->pdata : NULL;
}
BzAppPermissions *
bz_app_permissions_new_from_metadata (GKeyFile *keyfile,
GError **error)
{
char **strv = NULL;
BzAppPermissions *permissions = NULL;
BzAppPermissionsFlags flags = BZ_APP_PERMISSIONS_FLAGS_NONE;
g_autofree char *app_id = NULL;
g_autofree char *mpris_id = NULL;
g_autofree char *app_id_non_devel = NULL;
g_autofree char *mpris_id_non_devel = NULL;
g_return_val_if_fail (keyfile != NULL, NULL);
permissions = bz_app_permissions_new ();
app_id = g_key_file_get_value (keyfile, "Application", "name", NULL);
strv = g_key_file_get_string_list (keyfile, "Context", "sockets", NULL, NULL);
if (strv != NULL && g_strv_contains ((const gchar *const *) strv, "system-bus"))
flags |= BZ_APP_PERMISSIONS_FLAGS_SYSTEM_BUS | BZ_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX;
if (strv != NULL && g_strv_contains ((const gchar *const *) strv, "session-bus"))
flags |= BZ_APP_PERMISSIONS_FLAGS_SESSION_BUS | BZ_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX;
if (strv != NULL &&
!g_strv_contains ((const gchar *const *) strv, "fallback-x11") &&
g_strv_contains ((const gchar *const *) strv, "x11"))
flags |= BZ_APP_PERMISSIONS_FLAGS_X11;
if (strv != NULL && g_strv_contains ((const gchar *const *) strv, "fallback-x11") &&
!g_strv_contains ((const gchar *const *) strv, "wayland"))
flags |= BZ_APP_PERMISSIONS_FLAGS_X11;
if (strv != NULL && g_strv_contains ((const gchar *const *) strv, "pulseaudio"))
flags |= BZ_APP_PERMISSIONS_FLAGS_AUDIO_DEVICES;
if (strv != NULL && g_strv_contains ((const char *const *) strv, "gpg-agent"))
flags |= BZ_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX;
g_strfreev (strv);
strv = g_key_file_get_string_list (keyfile, "Context", "devices", NULL, NULL);
if (strv != NULL && g_strv_contains ((const gchar *const *) strv, "all"))
flags |= BZ_APP_PERMISSIONS_FLAGS_DEVICES;
if (strv != NULL && g_strv_contains ((const gchar *const *) strv, "input"))
flags |= BZ_APP_PERMISSIONS_FLAGS_INPUT_DEVICES;
if (strv != NULL && (g_strv_contains ((const gchar *const *) strv, "shm") ||
g_strv_contains ((const gchar *const *) strv, "kvm")))
flags |= BZ_APP_PERMISSIONS_FLAGS_SYSTEM_DEVICES;
g_strfreev (strv);
strv = g_key_file_get_string_list (keyfile, "Context", "shared", NULL, NULL);
if (strv != NULL && g_strv_contains ((const gchar *const *) strv, "network"))
flags |= BZ_APP_PERMISSIONS_FLAGS_NETWORK;
g_strfreev (strv);
strv = g_key_file_get_string_list (keyfile, "Context", "filesystems", NULL, NULL);
if (strv != NULL)
{
const struct
{
const gchar *key;
BzAppPermissionsFlags perm;
} filesystems_access[] = {
{ "home", BZ_APP_PERMISSIONS_FLAGS_HOME_FULL },
{ "home:rw", BZ_APP_PERMISSIONS_FLAGS_HOME_FULL },
{ "home:ro", BZ_APP_PERMISSIONS_FLAGS_HOME_READ },
{ "~", BZ_APP_PERMISSIONS_FLAGS_HOME_FULL },
{ "~:rw", BZ_APP_PERMISSIONS_FLAGS_HOME_FULL },
{ "~:ro", BZ_APP_PERMISSIONS_FLAGS_HOME_READ },
{ "host", BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL },
{ "host:rw", BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL },
{ "host:ro", BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ },
{ "xdg-config/kdeglobals:ro", BZ_APP_PERMISSIONS_FLAGS_NONE },
{ "xdg-download", BZ_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL },
{ "xdg-download:rw", BZ_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL },
{ "xdg-download:ro", BZ_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ },
{ "xdg-data/flatpak/overrides:create", BZ_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX },
{ "xdg-run/pipewire-0", BZ_APP_PERMISSIONS_FLAGS_SCREEN | BZ_APP_PERMISSIONS_FLAGS_AUDIO_DEVICES },
{ "xdg-run/pipewire-0:rw", BZ_APP_PERMISSIONS_FLAGS_SCREEN | BZ_APP_PERMISSIONS_FLAGS_AUDIO_DEVICES },
{ "xdg-run/pipewire-0:ro", BZ_APP_PERMISSIONS_FLAGS_SCREEN | BZ_APP_PERMISSIONS_FLAGS_AUDIO_DEVICES },
{ "xdg-run/gvfsd", BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL },
};
guint filesystems_hits = 0;
guint strv_len = g_strv_length (strv);
for (guint i = 0; i < G_N_ELEMENTS (filesystems_access); i++)
{
guint index = get_strv_index ((const gchar *const *) strv, filesystems_access[i].key);
if (index < strv_len)
{
flags |= filesystems_access[i].perm;
filesystems_hits++;
strv[index][0] = '\0';
}
}
if ((flags & BZ_APP_PERMISSIONS_FLAGS_HOME_FULL) != 0)
flags = flags & ~BZ_APP_PERMISSIONS_FLAGS_HOME_READ;
if ((flags & BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL) != 0)
flags = flags & ~BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ;
if ((flags & BZ_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL) != 0)
flags = flags & ~BZ_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ;
if (strv_len > filesystems_hits)
{
const struct
{
const gchar *prefix;
BzFilesystemPathType type;
} filesystems_other[] = {
{ "/", BZ_FILESYSTEM_PATH_SYSTEM_ROOT },
{ "home/", BZ_FILESYSTEM_PATH_HOME_SUBDIR },
{ "~/", BZ_FILESYSTEM_PATH_HOME_SUBDIR },
{ "host-os", BZ_FILESYSTEM_PATH_HOST_OS },
{ "host-etc", BZ_FILESYSTEM_PATH_HOST_ETC },
{ "xdg-desktop", BZ_FILESYSTEM_PATH_XDG_DESKTOP },
{ "xdg-documents", BZ_FILESYSTEM_PATH_XDG_DOCUMENTS },
{ "xdg-music", BZ_FILESYSTEM_PATH_XDG_MUSIC },
{ "xdg-pictures", BZ_FILESYSTEM_PATH_XDG_PICTURES },
{ "xdg-public-share", BZ_FILESYSTEM_PATH_XDG_PUBLIC_SHARE },
{ "xdg-videos", BZ_FILESYSTEM_PATH_XDG_VIDEOS },
{ "xdg-templates", BZ_FILESYSTEM_PATH_XDG_TEMPLATES },
{ "xdg-cache", BZ_FILESYSTEM_PATH_XDG_CACHE },
{ "xdg-config", BZ_FILESYSTEM_PATH_XDG_CONFIG },
{ "xdg-data", BZ_FILESYSTEM_PATH_XDG_DATA },
{ "xdg-run", BZ_FILESYSTEM_PATH_XDG_RUN }
};
flags |= BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_OTHER;
for (guint j = 0; strv[j]; j++)
{
gchar *perm = strv[j];
gboolean is_readonly = FALSE;
gchar *colon = NULL;
guint i = 0;
const gchar *subpath = NULL;
if (!perm[0])
continue;
is_readonly = g_str_has_suffix (perm, ":ro");
colon = strrchr (perm, ':');
if (colon)
*colon = '\0';
for (i = 0; i < G_N_ELEMENTS (filesystems_other); i++)
{
if (g_str_has_prefix (perm, filesystems_other[i].prefix))
{
const gchar *slash = NULL;
if (filesystems_other[i].type == BZ_FILESYSTEM_PATH_SYSTEM_ROOT)
{
if (perm[1] != '\0')
subpath = perm + 1;
else
subpath = NULL;
}
else
{
slash = strchr (perm, '/');
if (slash && slash != perm)
subpath = slash + 1;
}
if (is_readonly)
bz_app_permissions_add_filesystem_read (permissions, filesystems_other[i].type, subpath);
else
bz_app_permissions_add_filesystem_full (permissions, filesystems_other[i].type, subpath);
break;
}
}
if (i == G_N_ELEMENTS (filesystems_other))
{
if (is_readonly)
bz_app_permissions_add_filesystem_read (permissions, BZ_FILESYSTEM_PATH_CUSTOM, perm);
else
bz_app_permissions_add_filesystem_full (permissions, BZ_FILESYSTEM_PATH_CUSTOM, perm);
}
}
}
}
g_strfreev (strv);
if (!(flags & (BZ_APP_PERMISSIONS_FLAGS_SYSTEM_BUS | BZ_APP_PERMISSIONS_FLAGS_SESSION_BUS)))
{
const struct
{
GBusType bus_type;
const char *keyfile_group;
BzAppPermissionsFlags unfiltered_flag;
} bus_policy_types[] = {
{ G_BUS_TYPE_SESSION, "Session Bus Policy", BZ_APP_PERMISSIONS_FLAGS_SESSION_BUS },
{ G_BUS_TYPE_SYSTEM, "System Bus Policy", BZ_APP_PERMISSIONS_FLAGS_SYSTEM_BUS },
};
if (app_id != NULL)
{
mpris_id = g_strconcat ("org.mpris.MediaPlayer2.", app_id, NULL);
app_id_non_devel = g_str_has_suffix (app_id, ".Devel") ? g_strndup (app_id, strlen (app_id) - strlen (".Devel")) : NULL;
mpris_id_non_devel = (app_id_non_devel != NULL) ? g_strconcat ("org.mpris.MediaPlayer2.", app_id_non_devel, NULL) : NULL;
}
for (size_t h = 0; h < G_N_ELEMENTS (bus_policy_types); h++)
{
g_auto (GStrv) bus_policies = NULL;
if (flags & bus_policy_types[h].unfiltered_flag)
continue;
bus_policies = g_key_file_get_keys (keyfile, bus_policy_types[h].keyfile_group, NULL, NULL);
for (size_t i = 0; bus_policies != NULL && bus_policies[i] != NULL; i++)
{
const struct
{
GBusType bus_type;
const char *bus_name;
gboolean is_prefix;
BzBusPolicyPermission permission_is_at_least;
BzAppPermissionsFlags flags;
} bus_policy_permissions[] = {
{ G_BUS_TYPE_SESSION, "ca.desrt.dconf", FALSE, BZ_BUS_POLICY_PERMISSION_TALK, BZ_APP_PERMISSIONS_FLAGS_SETTINGS },
{ G_BUS_TYPE_SESSION, "org.freedesktop.Flatpak", FALSE, BZ_BUS_POLICY_PERMISSION_TALK, BZ_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX },
{ G_BUS_TYPE_SESSION, "org.freedesktop.impl.portal.PermissionStore", FALSE, BZ_BUS_POLICY_PERMISSION_TALK, BZ_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX },
{ G_BUS_TYPE_SESSION, "org.gtk.vfs.", TRUE, BZ_BUS_POLICY_PERMISSION_TALK, BZ_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL },
};
const char *bus_name_pattern = bus_policies[i];
g_autofree char *bus_policy_str = NULL;
BzBusPolicyPermission bus_policy = BZ_BUS_POLICY_PERMISSION_UNKNOWN;
size_t j = 0;
bus_policy_str = g_key_file_get_string (keyfile, bus_policy_types[h].keyfile_group, bus_name_pattern, NULL);
g_assert (bus_policy_str != NULL);
if (g_str_equal (bus_policy_str, "none"))
bus_policy = BZ_BUS_POLICY_PERMISSION_NONE;
else if (g_str_equal (bus_policy_str, "see"))
bus_policy = BZ_BUS_POLICY_PERMISSION_SEE;
else if (g_str_equal (bus_policy_str, "talk"))
bus_policy = BZ_BUS_POLICY_PERMISSION_TALK;
else if (g_str_equal (bus_policy_str, "own"))
bus_policy = BZ_BUS_POLICY_PERMISSION_OWN;
else
continue;
if (app_id != NULL &&
bus_policy_types[h].bus_type == G_BUS_TYPE_SESSION &&
(g_str_equal (bus_name_pattern, app_id) ||
(g_str_has_prefix (bus_name_pattern, app_id) && bus_name_pattern[strlen (app_id)] == '.') ||
g_str_equal (bus_name_pattern, mpris_id) ||
g_str_equal (bus_name_pattern, "org.freedesktop.DBus") ||
g_str_has_prefix (bus_name_pattern, "org.freedesktop.portal.")))
continue;
if (app_id_non_devel != NULL &&
bus_policy_types[h].bus_type == G_BUS_TYPE_SESSION &&
(g_str_equal (bus_name_pattern, app_id_non_devel) ||
(g_str_has_prefix (bus_name_pattern, app_id_non_devel) && bus_name_pattern[strlen (app_id_non_devel)] == '.') ||
g_str_equal (bus_name_pattern, mpris_id_non_devel)))
continue;
for (j = 0; j < G_N_ELEMENTS (bus_policy_permissions); j++)
{
if (bus_policy_permissions[j].bus_type == bus_policy_types[h].bus_type &&
((!bus_policy_permissions[j].is_prefix && g_str_equal (bus_name_pattern, bus_policy_permissions[j].bus_name)) ||
(bus_policy_permissions[j].is_prefix && g_str_has_prefix (bus_name_pattern, bus_policy_permissions[j].bus_name))) &&
bus_policy >= bus_policy_permissions[j].permission_is_at_least)
{
flags |= bus_policy_permissions[j].flags;
break;
}
}
if (j == G_N_ELEMENTS (bus_policy_permissions))
{
flags |= BZ_APP_PERMISSIONS_FLAGS_BUS_POLICY_OTHER;
bz_app_permissions_add_bus_policy (permissions,
bus_policy_types[h].bus_type,
bus_name_pattern,
bus_policy);
}
}
}
}
bz_app_permissions_set_flags (permissions, flags);
bz_app_permissions_seal (permissions);
return permissions;
}
void
bz_app_permissions_serialize (BzAppPermissions *self,
GVariantBuilder *builder)
{
GFlagsClass *flags_class = NULL;
guint i = 0;
g_return_if_fail (BZ_IS_APP_PERMISSIONS (self));
g_return_if_fail (builder != NULL);
flags_class = g_type_class_ref (BZ_TYPE_APP_PERMISSIONS_FLAGS);
if (self->flags != BZ_APP_PERMISSIONS_FLAGS_NONE)
{
g_autoptr (GVariantBuilder) sub_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
for (i = 0; i < flags_class->n_values; i++)
{
if (self->flags & flags_class->values[i].value)
g_variant_builder_add (sub_builder, "s", flags_class->values[i].value_nick);
}
g_variant_builder_add (builder, "{sv}", "permissions-flags", g_variant_builder_end (sub_builder));
}
g_type_class_unref (flags_class);
if (self->filesystem_read != NULL && self->filesystem_read->len > 0)
{
GEnumClass *enum_class = NULL;
g_autoptr (GVariantBuilder) sub_builder = NULL;
enum_class = g_type_class_ref (BZ_TYPE_FILESYSTEM_PATH_TYPE);
sub_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ss)"));
for (i = 0; i < self->filesystem_read->len; i++)
{
const BzFilesystemPath *path = g_ptr_array_index (self->filesystem_read, i);
GEnumValue *enum_value = g_enum_get_value (enum_class, path->type);
g_variant_builder_add (sub_builder, "(ss)",
enum_value->value_nick,
path->subpath ? path->subpath : "");
}
g_variant_builder_add (builder, "{sv}", "permissions-filesystem-read", g_variant_builder_end (sub_builder));
g_type_class_unref (enum_class);
}
if (self->filesystem_full != NULL && self->filesystem_full->len > 0)
{
GEnumClass *enum_class = NULL;
g_autoptr (GVariantBuilder) sub_builder = NULL;
enum_class = g_type_class_ref (BZ_TYPE_FILESYSTEM_PATH_TYPE);
sub_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ss)"));
for (i = 0; i < self->filesystem_full->len; i++)
{
const BzFilesystemPath *path = g_ptr_array_index (self->filesystem_full, i);
GEnumValue *enum_value = g_enum_get_value (enum_class, path->type);
g_variant_builder_add (sub_builder, "(ss)",
enum_value->value_nick,
path->subpath ? path->subpath : "");
}
g_variant_builder_add (builder, "{sv}", "permissions-filesystem-full", g_variant_builder_end (sub_builder));
g_type_class_unref (enum_class);
}
if (self->bus_policies != NULL && self->bus_policies->len > 0)
{
GEnumClass *enum_class = NULL;
g_autoptr (GVariantBuilder) sub_builder = NULL;
enum_class = g_type_class_ref (BZ_TYPE_BUS_POLICY_PERMISSION);
sub_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(iss)"));
for (i = 0; i < self->bus_policies->len; i++)
{
BzBusPolicy *policy = g_ptr_array_index (self->bus_policies, i);
GEnumValue *enum_value = g_enum_get_value (enum_class, policy->permission);
g_variant_builder_add (sub_builder, "(iss)",
(gint32) policy->bus_type,
policy->bus_name,
enum_value->value_nick);
}
g_variant_builder_add (builder, "{sv}", "permissions-bus-policies", g_variant_builder_end (sub_builder));
g_type_class_unref (enum_class);
}
}
gboolean
bz_app_permissions_deserialize (BzAppPermissions *self,
GVariant *import,
GError **error)
{
g_autoptr (GVariantIter) iter = NULL;
g_return_val_if_fail (BZ_IS_APP_PERMISSIONS (self), FALSE);
g_return_val_if_fail (import != NULL, FALSE);
iter = g_variant_iter_new (import);
for (;;)
{
g_autofree char *key = NULL;
g_autoptr (GVariant) value = NULL;
if (!g_variant_iter_next (iter, "{sv}", &key, &value))
break;
if (g_strcmp0 (key, "permissions-flags") == 0)
{
GFlagsClass *flags_class = NULL;
g_autoptr (GVariantIter) flags_iter = NULL;
flags_class = g_type_class_ref (BZ_TYPE_APP_PERMISSIONS_FLAGS);
flags_iter = g_variant_iter_new (value);
self->flags = BZ_APP_PERMISSIONS_FLAGS_NONE;
for (;;)
{
g_autofree char *flag_nick = NULL;
if (!g_variant_iter_next (flags_iter, "s", &flag_nick))
break;
for (guint i = 0; i < flags_class->n_values; i++)
{
if (g_str_equal (flag_nick, flags_class->values[i].value_nick))
{
self->flags |= flags_class->values[i].value;
break;
}
}
}
g_type_class_unref (flags_class);
}
else if (g_strcmp0 (key, "permissions-filesystem-read") == 0)
{
GEnumClass *enum_class = NULL;
g_autoptr (GVariantIter) paths_iter = NULL;
enum_class = g_type_class_ref (BZ_TYPE_FILESYSTEM_PATH_TYPE);
if (self->filesystem_read == NULL)
self->filesystem_read = g_ptr_array_new_with_free_func ((GDestroyNotify) bz_filesystem_path_free);
paths_iter = g_variant_iter_new (value);
for (;;)
{
g_autofree char *type_nick = NULL;
g_autofree char *subpath = NULL;
if (!g_variant_iter_next (paths_iter, "(ss)", &type_nick, &subpath))
break;
for (guint i = 0; i < enum_class->n_values; i++)
{
if (g_str_equal (type_nick, enum_class->values[i].value_nick))
{
g_ptr_array_add (self->filesystem_read,
bz_filesystem_path_new (enum_class->values[i].value,
subpath[0] ? subpath : NULL));
break;
}
}
}
g_type_class_unref (enum_class);
}
else if (g_strcmp0 (key, "permissions-filesystem-full") == 0)
{
GEnumClass *enum_class = NULL;
g_autoptr (GVariantIter) paths_iter = NULL;
enum_class = g_type_class_ref (BZ_TYPE_FILESYSTEM_PATH_TYPE);
if (self->filesystem_full == NULL)
self->filesystem_full = g_ptr_array_new_with_free_func ((GDestroyNotify) bz_filesystem_path_free);
paths_iter = g_variant_iter_new (value);
for (;;)
{
g_autofree char *type_nick = NULL;
g_autofree char *subpath = NULL;
if (!g_variant_iter_next (paths_iter, "(ss)", &type_nick, &subpath))
break;
for (guint i = 0; i < enum_class->n_values; i++)
{
if (g_str_equal (type_nick, enum_class->values[i].value_nick))
{
g_ptr_array_add (self->filesystem_full,
bz_filesystem_path_new (enum_class->values[i].value,
subpath[0] ? subpath : NULL));
break;
}
}
}
g_type_class_unref (enum_class);
}
else if (g_strcmp0 (key, "permissions-bus-policies") == 0)
{
GEnumClass *enum_class = NULL;
g_autoptr (GVariantIter) policies_iter = NULL;
enum_class = g_type_class_ref (BZ_TYPE_BUS_POLICY_PERMISSION);
if (self->bus_policies == NULL)
self->bus_policies = g_ptr_array_new_with_free_func ((GDestroyNotify) bz_bus_policy_free);
policies_iter = g_variant_iter_new (value);
for (;;)
{
gint32 bus_type_int = 0;
g_autofree char *bus_name = NULL;
g_autofree char *permission_nick = NULL;
if (!g_variant_iter_next (policies_iter, "(iss)", &bus_type_int, &bus_name, &permission_nick))
break;
for (guint i = 0; i < enum_class->n_values; i++)
{
if (g_str_equal (permission_nick, enum_class->values[i].value_nick))
{
g_ptr_array_add (self->bus_policies,
bz_bus_policy_new ((GBusType) bus_type_int,
bus_name,
enum_class->values[i].value));
break;
}
}
}
g_type_class_unref (enum_class);
}
}
bz_app_permissions_seal (self);
return TRUE;
}
static gint
cmp_filesystem_path_pointers (gconstpointer item1,
gconstpointer item2)
{
const BzFilesystemPath *const *pitem1 = item1;
const BzFilesystemPath *const *pitem2 = item2;
const BzFilesystemPath *path1 = *pitem1;
const BzFilesystemPath *path2 = *pitem2;
if (path1->type != path2->type)
return path1->type - path2->type;
return g_strcmp0 (path1->subpath, path2->subpath);
}
static int
cmp_bus_policy_qsort (const void *item1,
const void *item2)
{
const BzBusPolicy *const *pitem1 = item1;
const BzBusPolicy *const *pitem2 = item2;
const BzBusPolicy *policy1 = *pitem1;
const BzBusPolicy *policy2 = *pitem2;
if (policy1->bus_type != policy2->bus_type)
return policy1->bus_type - policy2->bus_type;
return strcmp (policy1->bus_name, policy2->bus_name);
}
static guint
app_permissions_get_array_index (GPtrArray *array,
BzFilesystemPathType type,
const char *subpath)
{
if (array == NULL)
return DOES_NOT_CONTAIN;
for (guint i = 0; i < array->len; i++)
{
const BzFilesystemPath *path = g_ptr_array_index (array, i);
if (path->type == type && g_strcmp0 (path->subpath, subpath) == 0)
return i;
}
return DOES_NOT_CONTAIN;
}
static guint
get_strv_index (const gchar *const *strv,
const gchar *value)
{
guint ii;
for (ii = 0; strv[ii]; ii++)
{
if (g_str_equal (strv[ii], value))
break;
}
return ii;
}