diff --git a/lib/gnome-software.h b/lib/gnome-software.h index a78b767c97289f46db69124a8549688a88b79596..1594bfc1479ea542804103064f729bff05e36bd9 100644 --- a/lib/gnome-software.h +++ b/lib/gnome-software.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/gs-app-permissions.c b/lib/gs-app-permissions.c new file mode 100644 index 0000000000000000000000000000000000000000..bbae07c1a873246a8c4c6e64228b80097f2397d2 --- /dev/null +++ b/lib/gs-app-permissions.c @@ -0,0 +1,430 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2022 Red Hat + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/** + * SECTION:gs-app-permissions + * @short_description: A representation of the permissions requested by an app + * + * #GsAppPermissions is an object to represent the permissions requested by an app. + * + * While some common permissions are handled with the #GsAppPermissionsFlags, + * the object allows more detailed permissions to be represented, such as + * specific file system path access. + * + * Since: 43 + */ + +#include "config.h" + +#include + +#include +#include + +#include "gs-app-permissions.h" + +#define DOES_NOT_CONTAIN ((guint) ~0) + +struct _GsAppPermissions +{ + GObject parent; + + gboolean is_sealed; + GsAppPermissionsFlags flags; + GPtrArray *filesystem_read; /* (owner) (nullable) (element-type utf-8) */ + GPtrArray *filesystem_full; /* (owner) (nullable) (element-type utf-8) */ +}; + +G_DEFINE_TYPE (GsAppPermissions, gs_app_permissions, G_TYPE_OBJECT) + +static gint +cmp_filename_qsort (gconstpointer item1, + gconstpointer item2) +{ + const gchar * const *pitem1 = item1; + const gchar * const *pitem2 = item2; + return strcmp (*pitem1, *pitem2); +} + +static gint +cmp_filename_bsearch (gconstpointer item1, + gconstpointer item2) +{ + return strcmp (item1, item2); +} + +static void +gs_app_permissions_finalize (GObject *object) +{ + GsAppPermissions *self = GS_APP_PERMISSIONS (object); + + g_clear_pointer (&self->filesystem_read, g_ptr_array_unref); + g_clear_pointer (&self->filesystem_full, g_ptr_array_unref); + + G_OBJECT_CLASS (gs_app_permissions_parent_class)->finalize (object); +} + +static void +gs_app_permissions_class_init (GsAppPermissionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gs_app_permissions_finalize; +} + +static void +gs_app_permissions_init (GsAppPermissions *self) +{ +} + +/** + * gs_app_permissions_new: + * + * Create a new #GsAppPermissions containing the app permissions. + * + * Returns: (transfer full): a new #GsAppPermissions + * Since: 43 + */ +GsAppPermissions * +gs_app_permissions_new (void) +{ + return g_object_new (GS_TYPE_APP_PERMISSIONS, NULL); +} + +/** + * gs_app_permissions_seal: + * @self: a #GsAppPermissions + * + * Seal the @self. After being called, no modifications can be + * done on the @self. + * + * Since: 43 + **/ +void +gs_app_permissions_seal (GsAppPermissions *self) +{ + g_return_if_fail (GS_IS_APP_PERMISSIONS (self)); + + if (self->is_sealed) + return; + + self->is_sealed = TRUE; + + /* Sort the arrays, which will help with searching */ + if (self->filesystem_read) + qsort (self->filesystem_read->pdata, self->filesystem_read->len, sizeof (gpointer), cmp_filename_qsort); + + if (self->filesystem_full) + qsort (self->filesystem_full->pdata, self->filesystem_full->len, sizeof (gpointer), cmp_filename_qsort); +} + +/** + * gs_app_permissions_is_sealed: + * @self: a #GsAppPermissions + * + * Checks whether the @self had been sealed. Once the @self is sealed, + * no modifications can be made to it. + * + * Returns: whether the @self had been sealed + * + * Since: 43 + **/ +gboolean +gs_app_permissions_is_sealed (GsAppPermissions *self) +{ + g_return_val_if_fail (GS_IS_APP_PERMISSIONS (self), TRUE); + + return self->is_sealed; +} + +/** + * gs_app_permissions_set_flags: + * @self: a #GsAppPermissions + * @flags: a #GsAppPermissionsFlags to set + * + * Set the permission flags, overwriting any previously set flags. + * Compare to gs_app_permissions_add_flag() and + * gs_app_permissions_remove_flag(). + * + * Since: 43 + */ +void +gs_app_permissions_set_flags (GsAppPermissions *self, + GsAppPermissionsFlags flags) +{ + g_return_if_fail (GS_IS_APP_PERMISSIONS (self)); + + g_assert (!self->is_sealed); + + self->flags = flags; +} + +/** + * gs_app_permissions_get_flags: + * @self: a #GsAppPermissions + * + * Get the permission flags. + * + * Returns: the permission flags + * Since: 43 + */ +GsAppPermissionsFlags +gs_app_permissions_get_flags (GsAppPermissions *self) +{ + g_return_val_if_fail (GS_IS_APP_PERMISSIONS (self), GS_APP_PERMISSIONS_FLAGS_UNKNOWN); + + return self->flags; +} + +/** + * gs_app_permissions_add_flag: + * @self: a #GsAppPermissions + * @flags: a #GsAppPermissionsFlags to add + * + * Add the @flags into the already set flags. The @flags cannot contain + * #GS_APP_PERMISSIONS_FLAGS_NONE, neither cannot be #GS_APP_PERMISSIONS_FLAGS_UNKNOWN. + * To set these two use gs_app_permissions_set_flags() instead. + * + * In case the current flags contain #GS_APP_PERMISSIONS_FLAGS_NONE, it's + * automatically unset. + * + * Since: 43 + */ +void +gs_app_permissions_add_flag (GsAppPermissions *self, + GsAppPermissionsFlags flags) +{ + g_return_if_fail (GS_IS_APP_PERMISSIONS (self)); + g_return_if_fail (flags != GS_APP_PERMISSIONS_FLAGS_UNKNOWN); + g_return_if_fail ((flags & GS_APP_PERMISSIONS_FLAGS_NONE) == 0); + + g_assert (!self->is_sealed); + + self->flags = (self->flags & (~GS_APP_PERMISSIONS_FLAGS_NONE)) | flags; +} + +/** + * gs_app_permissions_remove_flag: + * @self: a #GsAppPermissions + * @flags: a #GsAppPermissionsFlags to remove + * + * Remove the @flags from the already set flags. The @flags cannot contain + * #GS_APP_PERMISSIONS_FLAGS_NONE, neither cannot be #GS_APP_PERMISSIONS_FLAGS_UNKNOWN. + * To set these two use gs_app_permissions_set_flags() instead. + * + * In case the result of the removal would lead to no flag set the #GS_APP_PERMISSIONS_FLAGS_NONE + * is set automatically. + * + * Since: 43 + */ +void +gs_app_permissions_remove_flag (GsAppPermissions *self, + GsAppPermissionsFlags flags) +{ + g_return_if_fail (GS_IS_APP_PERMISSIONS (self)); + g_return_if_fail (flags != GS_APP_PERMISSIONS_FLAGS_UNKNOWN); + g_return_if_fail ((flags & GS_APP_PERMISSIONS_FLAGS_NONE) == 0); + + g_assert (!self->is_sealed); + + self->flags = (self->flags & (~flags)); + + if (!self->flags) + self->flags = GS_APP_PERMISSIONS_FLAGS_NONE; +} + +static guint +app_permissions_get_array_index (GPtrArray *array, + const gchar *filename) +{ + g_return_val_if_fail (filename != NULL, DOES_NOT_CONTAIN); + + if (array == NULL) + return DOES_NOT_CONTAIN; + + for (guint i = 0; i < array->len; i++) { + const gchar *item = g_ptr_array_index (array, i); + if (g_strcmp0 (item, filename) == 0) + return 0; + } + + return DOES_NOT_CONTAIN; +} + +/** + * gs_app_permissions_add_filesystem_read: + * @self: a #GsAppPermissions + * @filename: a filename to access + * + * Add @filename as a file to access for read. The @filename + * can be either a path or a localized pretty name of it, like "Documents". + * The addition is ignored in case the same @filename is part of + * the read or full access file names. + * + * Since: 43 + */ +void +gs_app_permissions_add_filesystem_read (GsAppPermissions *self, + const gchar *filename) +{ + g_return_if_fail (GS_IS_APP_PERMISSIONS (self)); + g_return_if_fail (filename != NULL); + + g_assert (!self->is_sealed); + + /* Already known */ + if (app_permissions_get_array_index (self->filesystem_read, filename) != DOES_NOT_CONTAIN || + app_permissions_get_array_index (self->filesystem_full, filename) != DOES_NOT_CONTAIN) + return; + + if (self->filesystem_read == NULL) + self->filesystem_read = g_ptr_array_new_with_free_func (g_free); + + g_ptr_array_add (self->filesystem_read, g_strdup (filename)); +} + +/** + * gs_app_permissions_get_filesystem_read: + * @self: a #GsAppPermissions + * + * Get the list of filesystem file names requested for read access using + * gs_app_permissions_add_filesystem_read(). + * The array is owned by the @self and should not be modified by any way. + * It can be %NULL, when no file access was set. + * + * Returns: (nullable) (transfer none) (element-type utf-8): an array of + * file names requesting read access or %NULL, when none was set. + * + * Since: 43 + */ +const GPtrArray * +gs_app_permissions_get_filesystem_read (GsAppPermissions *self) +{ + g_return_val_if_fail (GS_IS_APP_PERMISSIONS (self), NULL); + + return self->filesystem_read; +} + +static gboolean +array_contains_filename (GPtrArray *array, + const gchar *filename) +{ + if (array == NULL) + return FALSE; + + return bsearch (filename, array->pdata, array->len, sizeof (gpointer), cmp_filename_bsearch) != NULL; +} + +/** + * gs_app_permissions_contains_filesystem_read: + * @self: a #GsAppPermissions + * @filename: a file name to search for + * + * Checks whether the @filename is included in the filesystem read permissions. + * This can be called only after the @self is sealed. + * + * Returns: whether the @filename is part of the filesystem read permissions + * + * Since: 43 + **/ +gboolean +gs_app_permissions_contains_filesystem_read (GsAppPermissions *self, + const gchar *filename) +{ + g_return_val_if_fail (GS_IS_APP_PERMISSIONS (self), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (self->is_sealed, FALSE); + + return array_contains_filename (self->filesystem_read, filename); +} + +/** + * gs_app_permissions_add_filesystem_full: + * @self: a #GsAppPermissions + * @filename: a filename to access + * + * Add @filename as a file to access for read and write. The @filename + * can be either a path or a localized pretty name of it, like "Documents". + * The addition is ignored in case the same @filename is include in the list + * already. The @filename is removed from the read list, if it's part of it. + * + * Since: 43 + */ +void +gs_app_permissions_add_filesystem_full (GsAppPermissions *self, + const gchar *filename) +{ + guint read_index; + + g_return_if_fail (GS_IS_APP_PERMISSIONS (self)); + g_return_if_fail (filename != NULL); + + g_assert (!self->is_sealed); + + /* Already known */ + if (app_permissions_get_array_index (self->filesystem_full, filename) != DOES_NOT_CONTAIN) + return; + + if (self->filesystem_full == NULL) + self->filesystem_full = g_ptr_array_new_with_free_func (g_free); + + g_ptr_array_add (self->filesystem_full, g_strdup (filename)); + + /* Remove from the read list and free the read list if becomes empty */ + read_index = app_permissions_get_array_index (self->filesystem_read, filename); + 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); + } +} + +/** + * gs_app_permissions_get_filesystem_full: + * @self: a #GsAppPermissions + * + * Get the list of filesystem file names requested for read and write access using + * gs_app_permissions_add_filesystem_full(). + * The array is owned by the @self and should not be modified by any way. + * It can be %NULL, when no file access was set. + * + * Returns: (nullable) (transfer none) (element-type utf-8): an array of + * file names requesting read and write access or %NULL, when none was set. + * + * Since: 43 + */ +const GPtrArray * +gs_app_permissions_get_filesystem_full (GsAppPermissions *self) +{ + g_return_val_if_fail (GS_IS_APP_PERMISSIONS (self), NULL); + + return self->filesystem_full; +} + +/** + * gs_app_permissions_contains_filesystem_full: + * @self: a #GsAppPermissions + * @filename: a file name to search for + * + * Checks whether the @filename is included in the filesystem full permissions. + * This can be called only after the @self is sealed. + * + * Returns: whether the @filename is part of the filesystem full permissions + * + * Since: 43 + **/ +gboolean +gs_app_permissions_contains_filesystem_full (GsAppPermissions *self, + const gchar *filename) +{ + g_return_val_if_fail (GS_IS_APP_PERMISSIONS (self), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (self->is_sealed, FALSE); + + return array_contains_filename (self->filesystem_full, filename); +} diff --git a/lib/gs-app-permissions.h b/lib/gs-app-permissions.h new file mode 100644 index 0000000000000000000000000000000000000000..96c482bc3334afdb44d7b086081f95bc48c2931f --- /dev/null +++ b/lib/gs-app-permissions.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2022 Red Hat + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +typedef enum { + GS_APP_PERMISSIONS_FLAGS_UNKNOWN = 0, + GS_APP_PERMISSIONS_FLAGS_NONE = 1 << 0, + GS_APP_PERMISSIONS_FLAGS_NETWORK = 1 << 1, + GS_APP_PERMISSIONS_FLAGS_SYSTEM_BUS = 1 << 2, + GS_APP_PERMISSIONS_FLAGS_SESSION_BUS = 1 << 3, + GS_APP_PERMISSIONS_FLAGS_DEVICES = 1 << 4, + GS_APP_PERMISSIONS_FLAGS_HOME_FULL = 1 << 5, + GS_APP_PERMISSIONS_FLAGS_HOME_READ = 1 << 6, + GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL = 1 << 7, + GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ = 1 << 8, + GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL = 1 << 9, + GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ = 1 << 10, + GS_APP_PERMISSIONS_FLAGS_SETTINGS = 1 << 11, + GS_APP_PERMISSIONS_FLAGS_X11 = 1 << 12, + GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX = 1 << 13, + GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_OTHER = 1 << 14, + GS_APP_PERMISSIONS_FLAGS_LAST /*< skip >*/ +} GsAppPermissionsFlags; + +#define LIMITED_PERMISSIONS (GS_APP_PERMISSIONS_FLAGS_SETTINGS | \ + GS_APP_PERMISSIONS_FLAGS_NETWORK | \ + GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ | \ + GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL) +#define MEDIUM_PERMISSIONS (LIMITED_PERMISSIONS | \ + GS_APP_PERMISSIONS_FLAGS_X11) + +#define GS_TYPE_APP_PERMISSIONS (gs_app_permissions_get_type ()) + +G_DECLARE_FINAL_TYPE (GsAppPermissions, gs_app_permissions, GS, APP_PERMISSIONS, GObject) + +GsAppPermissions *gs_app_permissions_new (void); +void gs_app_permissions_seal (GsAppPermissions *self); +gboolean gs_app_permissions_is_sealed (GsAppPermissions *self); +void gs_app_permissions_set_flags (GsAppPermissions *self, + GsAppPermissionsFlags flags); +GsAppPermissionsFlags gs_app_permissions_get_flags (GsAppPermissions *self); +void gs_app_permissions_add_flag (GsAppPermissions *self, + GsAppPermissionsFlags flags); +void gs_app_permissions_remove_flag (GsAppPermissions *self, + GsAppPermissionsFlags flags); +void gs_app_permissions_add_filesystem_read + (GsAppPermissions *self, + const gchar *filename); +const GPtrArray *gs_app_permissions_get_filesystem_read + (GsAppPermissions *self); +gboolean gs_app_permissions_contains_filesystem_read + (GsAppPermissions *self, + const gchar *filename); +void gs_app_permissions_add_filesystem_full + (GsAppPermissions *self, + const gchar *filename); +const GPtrArray *gs_app_permissions_get_filesystem_full + (GsAppPermissions *self); +gboolean gs_app_permissions_contains_filesystem_full + (GsAppPermissions *self, + const gchar *filename); + +G_END_DECLS diff --git a/lib/gs-app.c b/lib/gs-app.c index 4666c3c85fffd3a47569811ad54616191ea4f5c4..29898765b7d36f4428cd8c0dbee67d2db1b36b4c 100644 --- a/lib/gs-app.c +++ b/lib/gs-app.c @@ -93,7 +93,7 @@ typedef struct gchar *update_version_ui; gchar *update_details_markup; AsUrgencyKind update_urgency; - GsAppPermissions update_permissions; + GsAppPermissions *update_permissions; GWeakRef management_plugin_weak; /* (element-type GsPlugin) */ guint match_value; guint priority; @@ -135,7 +135,7 @@ typedef struct AsScreenshot *action_screenshot; /* (nullable) (owned) */ GCancellable *cancellable; GsPluginAction pending_action; - GsAppPermissions permissions; + GsAppPermissions *permissions; gboolean is_update_downloaded; GPtrArray *version_history; /* (element-type AsRelease) (nullable) (owned) */ GPtrArray *relations; /* (nullable) (element-type AsRelation) (owned) */ @@ -5328,7 +5328,7 @@ gs_app_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec * break; } case PROP_PERMISSIONS: - g_value_set_flags (value, priv->permissions); + g_value_take_object (value, gs_app_dup_permissions (app)); break; case PROP_RELATIONS: g_value_take_boxed (value, gs_app_get_relations (app)); @@ -5456,7 +5456,7 @@ gs_app_set_property (GObject *object, guint prop_id, const GValue *value, GParam gs_app_set_size_user_data (app, priv->size_user_data_type, g_value_get_uint64 (value)); break; case PROP_PERMISSIONS: - gs_app_set_permissions (app, g_value_get_flags (value)); + gs_app_set_permissions (app, g_value_get_object (value)); break; case PROP_RELATIONS: gs_app_set_relations (app, g_value_get_boxed (value)); @@ -5537,6 +5537,8 @@ gs_app_finalize (GObject *object) g_clear_object (&priv->local_file); g_clear_object (&priv->content_rating); g_clear_object (&priv->action_screenshot); + g_clear_object (&priv->update_permissions); + g_clear_object (&priv->permissions); G_OBJECT_CLASS (gs_app_parent_class)->finalize (object); } @@ -5927,16 +5929,16 @@ gs_app_class_init (GsAppClass *klass) /** * GsApp:permissions * - * The permissions the app requires to run. + * The permissions the app requires to run, as a #GsAppPermissions object. * - * This is %GS_APP_PERMISSIONS_UNKNOWN if the permissions are unknown. + * This is %NULL, if the permissions are unknown. * - * Since: 41 + * Since: 43 */ obj_props[PROP_PERMISSIONS] = - g_param_spec_flags ("permissions", NULL, NULL, - GS_TYPE_APP_PERMISSIONS, GS_APP_PERMISSIONS_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + g_param_spec_object ("permissions", NULL, NULL, + GS_TYPE_APP_PERMISSIONS, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); /** * GsApp:relations: (nullable) (element-type AsRelation) @@ -6315,40 +6317,110 @@ gs_app_subsume_metadata (GsApp *app, GsApp *donor) } } -GsAppPermissions -gs_app_get_permissions (GsApp *app) +/** + * gs_app_dup_permissions: + * @app: a #GsApp + * + * Get a reference to the @app permissions. The returned value can + * be %NULL, when the app's permissions are unknown. Free the returned pointer, + * if not %NULL, with g_object_unref(), when no longer needed. + * + * Returns: (nullable) (transfer full): referenced #GsAppPermissions, + * or %NULL + * + * Since: 43 + **/ +GsAppPermissions * +gs_app_dup_permissions (GsApp *app) { GsAppPrivate *priv = gs_app_get_instance_private (app); - g_return_val_if_fail (GS_IS_APP (app), GS_APP_PERMISSIONS_UNKNOWN); - return priv->permissions; + g_autoptr(GMutexLocker) locker = NULL; + g_return_val_if_fail (GS_IS_APP (app), NULL); + locker = g_mutex_locker_new (&priv->mutex); + return priv->permissions ? g_object_ref (priv->permissions) : NULL; } +/** + * gs_app_set_permissions: + * @app: a #GsApp + * @permissions: (nullable) (transfer none): a #GsAppPermissions, or %NULL + * + * Set permissions for the @app. The @permissions is referenced, + * if not %NULL. + * + * Note the @permissions need to be sealed. + * + * Since: 43 + **/ void -gs_app_set_permissions (GsApp *app, GsAppPermissions permissions) +gs_app_set_permissions (GsApp *app, + GsAppPermissions *permissions) { GsAppPrivate *priv = gs_app_get_instance_private (app); + g_autoptr(GMutexLocker) locker = NULL; g_return_if_fail (GS_IS_APP (app)); + g_return_if_fail (permissions == NULL || gs_app_permissions_is_sealed (permissions)); + locker = g_mutex_locker_new (&priv->mutex); if (priv->permissions == permissions) return; - priv->permissions = permissions; + g_clear_object (&priv->permissions); + if (permissions != NULL) + priv->permissions = g_object_ref (permissions); gs_app_queue_notify (app, obj_props[PROP_PERMISSIONS]); } -GsAppPermissions -gs_app_get_update_permissions (GsApp *app) +/** + * gs_app_dup_update_permissions: + * @app: a #GsApp + * + * Get a reference to the update permissions. The returned value can + * be %NULL, when no update permissions had been set. Free + * the returned pointer, if not %NULL, with g_object_unref(), when + * no longer needed. + * + * Returns: (nullable) (transfer full): referenced #GsAppPermissions, + * or %NULL + * + * Since: 43 + **/ +GsAppPermissions * +gs_app_dup_update_permissions (GsApp *app) { GsAppPrivate *priv = gs_app_get_instance_private (app); - g_return_val_if_fail (GS_IS_APP (app), GS_APP_PERMISSIONS_UNKNOWN); - return priv->update_permissions; + g_autoptr(GMutexLocker) locker = NULL; + g_return_val_if_fail (GS_IS_APP (app), NULL); + locker = g_mutex_locker_new (&priv->mutex); + return priv->update_permissions ? g_object_ref (priv->update_permissions) : NULL; } +/** + * gs_app_set_update_permissions: + * @app: a #GsApp + * @update_permissions: (nullable) (transfer none): a #GsAppPermissions, or %NULL + * + * Set update permissions for the @app, that is, the permissions, which change + * in an update or similar reasons. The @update_permissions is referenced, + * if not %NULL. + * + * Note the @update_permissions need to be sealed. + * + * Since: 43 + **/ void -gs_app_set_update_permissions (GsApp *app, GsAppPermissions update_permissions) +gs_app_set_update_permissions (GsApp *app, + GsAppPermissions *update_permissions) { GsAppPrivate *priv = gs_app_get_instance_private (app); + g_autoptr(GMutexLocker) locker = NULL; g_return_if_fail (GS_IS_APP (app)); - priv->update_permissions = update_permissions; + g_return_if_fail (update_permissions == NULL || gs_app_permissions_is_sealed (update_permissions)); + locker = g_mutex_locker_new (&priv->mutex); + if (priv->update_permissions != update_permissions) { + g_clear_object (&priv->update_permissions); + if (update_permissions != NULL) + priv->update_permissions = g_object_ref (update_permissions); + } } /** diff --git a/lib/gs-app.h b/lib/gs-app.h index 663a65677a77e2ef1318ce7139e9869d63a58458..d78503c3e0f068764d9c8c8aab042b3cf7504195 100644 --- a/lib/gs-app.h +++ b/lib/gs-app.h @@ -15,6 +15,8 @@ #include #include +#include + G_BEGIN_DECLS /* Dependency loop means we can’t include the header. */ @@ -208,33 +210,6 @@ typedef enum { GS_APP_QUALITY_LAST /*< skip >*/ } GsAppQuality; -typedef enum { - GS_APP_PERMISSIONS_UNKNOWN = 0, - GS_APP_PERMISSIONS_NONE = 1 << 0, - GS_APP_PERMISSIONS_NETWORK = 1 << 1, - GS_APP_PERMISSIONS_SYSTEM_BUS = 1 << 2, - GS_APP_PERMISSIONS_SESSION_BUS = 1 << 3, - GS_APP_PERMISSIONS_DEVICES = 1 << 4, - GS_APP_PERMISSIONS_HOME_FULL = 1 << 5, - GS_APP_PERMISSIONS_HOME_READ = 1 << 6, - GS_APP_PERMISSIONS_FILESYSTEM_FULL = 1 << 7, - GS_APP_PERMISSIONS_FILESYSTEM_READ = 1 << 8, - GS_APP_PERMISSIONS_DOWNLOADS_FULL = 1 << 9, - GS_APP_PERMISSIONS_DOWNLOADS_READ = 1 << 10, - GS_APP_PERMISSIONS_SETTINGS = 1 << 11, - GS_APP_PERMISSIONS_X11 = 1 << 12, - GS_APP_PERMISSIONS_ESCAPE_SANDBOX = 1 << 13, - GS_APP_PERMISSIONS_FILESYSTEM_OTHER = 1 << 14, - GS_APP_PERMISSIONS_LAST /*< skip >*/ -} GsAppPermissions; - -#define LIMITED_PERMISSIONS (GS_APP_PERMISSIONS_SETTINGS | \ - GS_APP_PERMISSIONS_NETWORK | \ - GS_APP_PERMISSIONS_DOWNLOADS_READ | \ - GS_APP_PERMISSIONS_DOWNLOADS_FULL) -#define MEDIUM_PERMISSIONS (LIMITED_PERMISSIONS | \ - GS_APP_PERMISSIONS_X11) - /** * GS_APP_PROGRESS_UNKNOWN: * @@ -516,12 +491,14 @@ gchar *gs_app_get_packaging_format (GsApp *app); const gchar *gs_app_get_packaging_format_raw(GsApp *app); void gs_app_subsume_metadata (GsApp *app, GsApp *donor); -GsAppPermissions gs_app_get_permissions (GsApp *app); +GsAppPermissions * + gs_app_dup_permissions (GsApp *app); void gs_app_set_permissions (GsApp *app, - GsAppPermissions permissions); -GsAppPermissions gs_app_get_update_permissions (GsApp *app); + GsAppPermissions *permissions); +GsAppPermissions * + gs_app_dup_update_permissions (GsApp *app); void gs_app_set_update_permissions (GsApp *app, - GsAppPermissions update_permissions); + GsAppPermissions *update_permissions); GPtrArray *gs_app_get_version_history (GsApp *app); void gs_app_set_version_history (GsApp *app, GPtrArray *version_history); diff --git a/lib/meson.build b/lib/meson.build index 036bb3068e874b7ed961d7fefc228e2f79c085f3..a99a80abb7455d953f70ef18e2922562b22d1ced 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -6,6 +6,7 @@ libgnomesoftware_public_headers = [ 'gs-app.h', 'gs-app-collation.h', 'gs-app-list.h', + 'gs-app-permissions.h', 'gs-app-query.h', 'gs-appstream.h', 'gs-category.h', @@ -83,6 +84,7 @@ libgnomesoftware = library( sources : [ 'gs-app.c', 'gs-app-list.c', + 'gs-app-permissions.c', 'gs-app-query.c', 'gs-appstream.c', 'gs-category.c', diff --git a/plugins/epiphany/gs-plugin-epiphany.c b/plugins/epiphany/gs-plugin-epiphany.c index 78e5e2af5182a9f0034d4e49e7e4351bb4172291..4eafad6bd88b1f7088a4a1f7ed3d00c741b1c4d7 100644 --- a/plugins/epiphany/gs-plugin-epiphany.c +++ b/plugins/epiphany/gs-plugin-epiphany.c @@ -61,6 +61,9 @@ struct _GsPluginEpiphany /* installed_apps_cached: whether the plugin cache has all installed apps */ gboolean installed_apps_cached; GHashTable *url_id_map; /* (owned) (not nullable) (element-type utf8 utf8) */ + + /* default permissions, shared between all applications */ + GsAppPermissions *permissions; /* (owned) (not nullable) */ }; G_DEFINE_TYPE (GsPluginEpiphany, gs_plugin_epiphany, GS_TYPE_PLUGIN) @@ -312,6 +315,12 @@ gs_plugin_epiphany_shutdown_finish (GsPlugin *plugin, static void gs_plugin_epiphany_init (GsPluginEpiphany *self) { + /* Re-used permissions by all GsApp instances; do not modify it out + of this place. */ + self->permissions = gs_app_permissions_new (); + gs_app_permissions_set_flags (self->permissions, GS_APP_PERMISSIONS_FLAGS_NETWORK); + gs_app_permissions_seal (self->permissions); + /* set name of MetaInfo file */ gs_plugin_set_appstream_id (GS_PLUGIN (self), "org.gnome.Software.Plugin.Epiphany"); @@ -347,6 +356,7 @@ gs_plugin_epiphany_finalize (GObject *object) GsPluginEpiphany *self = GS_PLUGIN_EPIPHANY (object); g_mutex_clear (&self->installed_apps_mutex); + g_clear_object (&self->permissions); G_OBJECT_CLASS (gs_plugin_epiphany_parent_class)->finalize (object); } @@ -487,7 +497,8 @@ refine_app (GsPluginEpiphany *self, gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 0); - gs_app_set_permissions (app, GS_APP_PERMISSIONS_NETWORK); + /* Use the default permissions */ + gs_app_set_permissions (app, self->permissions); if (gs_app_get_url (app, AS_URL_KIND_HOMEPAGE) == NULL) gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, url); diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c index 4ac84022cddbe330e24e98af57f36d7f19469f4c..bc3e9d8e6be965d3d0510d5f4f98772e4d211bc2 100644 --- a/plugins/flatpak/gs-flatpak.c +++ b/plugins/flatpak/gs-flatpak.c @@ -212,95 +212,207 @@ gs_flatpak_set_kind_from_flatpak (GsApp *app, FlatpakRef *xref) } } -static GsAppPermissions +static guint +gs_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; +} + +static GsAppPermissions * perms_from_metadata (GKeyFile *keyfile) { char **strv; char *str; - GsAppPermissions permissions = GS_APP_PERMISSIONS_UNKNOWN; + GsAppPermissions *permissions = gs_app_permissions_new (); + GsAppPermissionsFlags flags = GS_APP_PERMISSIONS_FLAGS_UNKNOWN; strv = g_key_file_get_string_list (keyfile, "Context", "sockets", NULL, NULL); if (strv != NULL && g_strv_contains ((const gchar * const*)strv, "system-bus")) - permissions |= GS_APP_PERMISSIONS_SYSTEM_BUS; + flags |= GS_APP_PERMISSIONS_FLAGS_SYSTEM_BUS; if (strv != NULL && g_strv_contains ((const gchar * const*)strv, "session-bus")) - permissions |= GS_APP_PERMISSIONS_SESSION_BUS; + flags |= GS_APP_PERMISSIONS_FLAGS_SESSION_BUS; if (strv != NULL && !g_strv_contains ((const gchar * const*)strv, "fallback-x11") && g_strv_contains ((const gchar * const*)strv, "x11")) - permissions |= GS_APP_PERMISSIONS_X11; + flags |= GS_APP_PERMISSIONS_FLAGS_X11; 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")) - permissions |= GS_APP_PERMISSIONS_DEVICES; + flags |= GS_APP_PERMISSIONS_FLAGS_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")) - permissions |= GS_APP_PERMISSIONS_NETWORK; + flags |= GS_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; - GsAppPermissions perm; + GsAppPermissionsFlags perm; } filesystems_access[] = { /* Reference: https://docs.flatpak.org/en/latest/flatpak-command-reference.html#idm45858571325264 */ - { "home", GS_APP_PERMISSIONS_HOME_FULL }, - { "home:rw", GS_APP_PERMISSIONS_HOME_FULL }, - { "home:ro", GS_APP_PERMISSIONS_HOME_READ }, - { "host", GS_APP_PERMISSIONS_FILESYSTEM_FULL }, - { "host:rw", GS_APP_PERMISSIONS_FILESYSTEM_FULL }, - { "host:ro", GS_APP_PERMISSIONS_FILESYSTEM_READ }, - { "xdg-download", GS_APP_PERMISSIONS_DOWNLOADS_FULL }, - { "xdg-download:rw", GS_APP_PERMISSIONS_DOWNLOADS_FULL }, - { "xdg-download:ro", GS_APP_PERMISSIONS_DOWNLOADS_READ }, - { "xdg-data/flatpak/overrides:create", GS_APP_PERMISSIONS_ESCAPE_SANDBOX } + { "home", GS_APP_PERMISSIONS_FLAGS_HOME_FULL }, + { "home:rw", GS_APP_PERMISSIONS_FLAGS_HOME_FULL }, + { "home:ro", GS_APP_PERMISSIONS_FLAGS_HOME_READ }, + { "~", GS_APP_PERMISSIONS_FLAGS_HOME_FULL }, + { "~:rw", GS_APP_PERMISSIONS_FLAGS_HOME_FULL }, + { "~:ro", GS_APP_PERMISSIONS_FLAGS_HOME_READ }, + { "host", GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL }, + { "host:rw", GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL }, + { "host:ro", GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ }, + { "xdg-download", GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL }, + { "xdg-download:rw", GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL }, + { "xdg-download:ro", GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ }, + { "xdg-data/flatpak/overrides:create", GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX } }; guint filesystems_hits = 0; + guint strv_len = g_strv_length (strv); for (guint i = 0; i < G_N_ELEMENTS (filesystems_access); i++) { - if (g_strv_contains ((const gchar * const *) strv, filesystems_access[i].key)) { - permissions |= filesystems_access[i].perm; + guint index = gs_get_strv_index ((const gchar * const *) strv, filesystems_access[i].key); + if (index < strv_len) { + flags |= filesystems_access[i].perm; filesystems_hits++; + /* Mark it as used */ + strv[index][0] = '\0'; } } - if ((permissions & GS_APP_PERMISSIONS_HOME_FULL) != 0) - permissions = permissions & ~GS_APP_PERMISSIONS_HOME_READ; - if ((permissions & GS_APP_PERMISSIONS_FILESYSTEM_FULL) != 0) - permissions = permissions & ~GS_APP_PERMISSIONS_FILESYSTEM_READ; - if ((permissions & GS_APP_PERMISSIONS_DOWNLOADS_FULL) != 0) - permissions = permissions & ~GS_APP_PERMISSIONS_DOWNLOADS_READ; + if ((flags & GS_APP_PERMISSIONS_FLAGS_HOME_FULL) != 0) + flags = flags & ~GS_APP_PERMISSIONS_FLAGS_HOME_READ; + if ((flags & GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL) != 0) + flags = flags & ~GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ; + if ((flags & GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL) != 0) + flags = flags & ~GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ; + + if (strv_len > filesystems_hits) { + /* Cover those not being part of the above filesystem_access array */ + const struct { + const gchar *prefix; + const gchar *title; + const gchar *title_subdir; + } filesystems_other[] = { + /* Reference: https://docs.flatpak.org/en/latest/flatpak-command-reference.html#idm45858571325264 */ + { "/", NULL, N_("System folder %s") }, + { "home/", NULL, N_("Home subfolder %s") }, + { "~/", NULL, N_("Home subfolder %s") }, + { "host-os", N_("Host system folders"), NULL }, + { "host-etc", N_("Host system configuration from /etc"), NULL }, + { "xdg-desktop", N_("Desktop folder"), N_("Desktop subfolder %s") }, + { "xdg-documents", N_("Documents folder"), N_("Documents subfolder %s") }, + { "xdg-music", N_("Music folder"), N_("Music subfolder %s") }, + { "xdg-pictures", N_("Pictures folder"), N_("Pictures subfolder %s") }, + { "xdg-public-share", N_("Public Share folder"), N_("Public Share subfolder %s") }, + { "xdg-videos", N_("Videos folder"), N_("Videos subfolder %s") }, + { "xdg-templates", N_("Templates folder"), N_("Templates subfolder %s") }, + { "xdg-cache", N_("User cache folder"), N_("User cache subfolder %s") }, + { "xdg-config", N_("User configuration folder"), N_("User configuration subfolder %s") }, + { "xdg-data", N_("User data folder"), N_("User data subfolder %s") }, + { "xdg-run", N_("User runtime folder"), N_("User runtime subfolder %s") } + }; + + flags |= GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_OTHER; + + for (guint j = 0; strv[j]; j++) { + gchar *perm = strv[j]; + gboolean is_readonly; + gchar *colon; + guint i; + + /* Already handled by the flags */ + if (!perm[0]) + continue; + + is_readonly = g_str_has_suffix (perm, ":ro"); + colon = strrchr (perm, ':'); + /* modifiers are ":ro", ":rw", ":create", where ":create" is ":rw" + create + and ":rw" is default; treat ":create" as ":rw" */ + if (colon) { + /* Completeness check */ + if (!g_str_equal (colon, ":ro") && + !g_str_equal (colon, ":rw") && + !g_str_equal (colon, ":create")) + g_debug ("Unknown filesystem permission modifier '%s' from '%s'", colon, perm); + /* cut it off */ + *colon = '\0'; + } + + for (i = 0; i < G_N_ELEMENTS (filesystems_other); i++) { + if (g_str_has_prefix (perm, filesystems_other[i].prefix)) { + g_autofree gchar *title_tmp = NULL; + const gchar *slash, *title = NULL; + slash = strchr (perm, '/'); + /* Catch and ignore invalid permission definitions */ + if (slash && filesystems_other[i].title_subdir != NULL) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-nonliteral" + title_tmp = g_strdup_printf ( + _(filesystems_other[i].title_subdir), + slash + (slash == perm ? 0 : 1)); + #pragma GCC diagnostic pop + title = title_tmp; + } else if (!slash && filesystems_other[i].title != NULL) { + title = _(filesystems_other[i].title); + } + if (title != NULL) { + if (is_readonly) + gs_app_permissions_add_filesystem_read (permissions, title); + else + gs_app_permissions_add_filesystem_full (permissions, title); + } + break; + } + } - if (g_strv_length (strv) > filesystems_hits) - permissions |= GS_APP_PERMISSIONS_FILESYSTEM_OTHER; + /* Nothing matched, use a generic entry */ + if (i == G_N_ELEMENTS (filesystems_other)) { + g_autofree gchar *title = g_strdup_printf (_("Filesystem access to %s"), perm); + if (is_readonly) + gs_app_permissions_add_filesystem_read (permissions, title); + else + gs_app_permissions_add_filesystem_full (permissions, title); + } + } + } } g_strfreev (strv); str = g_key_file_get_string (keyfile, "Session Bus Policy", "ca.desrt.dconf", NULL); if (str != NULL && g_str_equal (str, "talk")) - permissions |= GS_APP_PERMISSIONS_SETTINGS; + flags |= GS_APP_PERMISSIONS_FLAGS_SETTINGS; g_free (str); - if (!(permissions & GS_APP_PERMISSIONS_ESCAPE_SANDBOX)) { + if (!(flags & GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX)) { str = g_key_file_get_string (keyfile, "Session Bus Policy", "org.freedesktop.Flatpak", NULL); if (str != NULL && g_str_equal (str, "talk")) - permissions |= GS_APP_PERMISSIONS_ESCAPE_SANDBOX; + flags |= GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX; g_free (str); } - if (!(permissions & GS_APP_PERMISSIONS_ESCAPE_SANDBOX)) { + if (!(flags & GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX)) { str = g_key_file_get_string (keyfile, "Session Bus Policy", "org.freedesktop.impl.portal.PermissionStore", NULL); if (str != NULL && g_str_equal (str, "talk")) - permissions |= GS_APP_PERMISSIONS_ESCAPE_SANDBOX; + flags |= GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX; g_free (str); } /* no permissions set */ - if (permissions == GS_APP_PERMISSIONS_UNKNOWN) - return GS_APP_PERMISSIONS_NONE; + if (flags == GS_APP_PERMISSIONS_FLAGS_UNKNOWN) + flags = GS_APP_PERMISSIONS_FLAGS_NONE; + + gs_app_permissions_set_flags (permissions, flags); + gs_app_permissions_seal (permissions); return permissions; } @@ -316,7 +428,7 @@ gs_flatpak_set_update_permissions (GsFlatpak *self, g_autoptr(GKeyFile) old_keyfile = NULL; g_autoptr(GBytes) bytes = NULL; g_autoptr(GKeyFile) keyfile = NULL; - GsAppPermissions permissions; + g_autoptr(GsAppPermissions) additional_permissions = gs_app_permissions_new (); g_autoptr(GError) error_local = NULL; old_bytes = flatpak_installed_ref_load_metadata (FLATPAK_INSTALLED_REF (xref), NULL, NULL); @@ -335,23 +447,48 @@ gs_flatpak_set_update_permissions (GsFlatpak *self, g_debug ("Failed to get metadata for remote ‘%s’: %s", gs_app_get_origin (app), error_local->message); g_clear_error (&error_local); - permissions = GS_APP_PERMISSIONS_UNKNOWN; + gs_app_permissions_set_flags (additional_permissions, GS_APP_PERMISSIONS_FLAGS_UNKNOWN); } else { + g_autoptr(GsAppPermissions) old_permissions = NULL; + g_autoptr(GsAppPermissions) new_permissions = NULL; + const GPtrArray *new_paths; + keyfile = g_key_file_new (); g_key_file_load_from_data (keyfile, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), 0, NULL); - permissions = perms_from_metadata (keyfile) & ~perms_from_metadata (old_keyfile); + + old_permissions = perms_from_metadata (old_keyfile); + new_permissions = perms_from_metadata (keyfile); + + gs_app_permissions_set_flags (additional_permissions, + gs_app_permissions_get_flags (new_permissions) & + ~gs_app_permissions_get_flags (old_permissions)); + + new_paths = gs_app_permissions_get_filesystem_read (new_permissions); + for (guint i = 0; new_paths && i < new_paths->len; i++) { + const gchar *new_path = g_ptr_array_index (new_paths, i); + if (!gs_app_permissions_contains_filesystem_read (old_permissions, new_path)) + gs_app_permissions_add_filesystem_read (additional_permissions, new_path); + } + + new_paths = gs_app_permissions_get_filesystem_full (new_permissions); + for (guint i = 0; new_paths && i < new_paths->len; i++) { + const gchar *new_path = g_ptr_array_index (new_paths, i); + if (!gs_app_permissions_contains_filesystem_full (old_permissions, new_path)) + gs_app_permissions_add_filesystem_full (additional_permissions, new_path); + } } /* no new permissions set */ - if (permissions == GS_APP_PERMISSIONS_UNKNOWN) - permissions = GS_APP_PERMISSIONS_NONE; + if (gs_app_permissions_get_flags (additional_permissions) == GS_APP_PERMISSIONS_FLAGS_UNKNOWN) + gs_app_permissions_set_flags (additional_permissions, GS_APP_PERMISSIONS_FLAGS_NONE); - gs_app_set_update_permissions (app, permissions); + gs_app_permissions_seal (additional_permissions); + gs_app_set_update_permissions (app, additional_permissions); - if (permissions != GS_APP_PERMISSIONS_NONE) + if (gs_app_permissions_get_flags (additional_permissions) != GS_APP_PERMISSIONS_FLAGS_NONE) gs_app_add_quirk (app, GS_APP_QUIRK_NEW_PERMISSIONS); } @@ -2459,6 +2596,7 @@ gs_flatpak_set_app_metadata (GsFlatpak *self, g_autofree gchar *runtime = NULL; g_autoptr(GKeyFile) kf = NULL; g_autoptr(GsApp) app_runtime = NULL; + g_autoptr(GsAppPermissions) permissions = NULL; g_auto(GStrv) shared = NULL; g_auto(GStrv) sockets = NULL; g_auto(GStrv) filesystems = NULL; @@ -2499,7 +2637,8 @@ gs_flatpak_set_app_metadata (GsFlatpak *self, secure = FALSE; } - gs_app_set_permissions (app, perms_from_metadata (kf)); + permissions = perms_from_metadata (kf); + gs_app_set_permissions (app, permissions); /* this is actually quite hard to achieve */ if (secure) gs_app_add_kudo (app, GS_APP_KUDO_SANDBOXED_SECURE); diff --git a/src/gs-app-context-bar.c b/src/gs-app-context-bar.c index d2ff4b95b73d253023597df25176db4a49db309f..2a21c870b9e949711d2843734788054d12ec7968 100644 --- a/src/gs-app-context-bar.c +++ b/src/gs-app-context-bar.c @@ -254,9 +254,10 @@ static void update_safety_tile (GsAppContextBar *self) { const gchar *icon_name, *title, *css_class; - g_autoptr(GPtrArray) descriptions = g_ptr_array_new_with_free_func (NULL); g_autofree gchar *description = NULL; - GsAppPermissions permissions; + g_autoptr(GPtrArray) descriptions = g_ptr_array_new_with_free_func (NULL); + g_autoptr(GsAppPermissions) permissions = NULL; + GsAppPermissionsFlags perm_flags = GS_APP_PERMISSIONS_FLAGS_UNKNOWN; GtkStyleContext *context; /* Treat everything as safe to begin with, and downgrade its safety @@ -265,20 +266,22 @@ update_safety_tile (GsAppContextBar *self) g_assert (self->app != NULL); - permissions = gs_app_get_permissions (self->app); - for (GsAppPermissions i = GS_APP_PERMISSIONS_NONE; i < GS_APP_PERMISSIONS_LAST; i <<= 1) { - if (!(permissions & i)) + permissions = gs_app_dup_permissions (self->app); + if (permissions != NULL) + perm_flags = gs_app_permissions_get_flags (permissions); + for (GsAppPermissionsFlags i = GS_APP_PERMISSIONS_FLAGS_NONE; i < GS_APP_PERMISSIONS_FLAGS_LAST; i <<= 1) { + if (!(perm_flags & i)) continue; switch (i) { - case GS_APP_PERMISSIONS_NONE: + case GS_APP_PERMISSIONS_FLAGS_NONE: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_SAFE, /* Translators: This indicates an app requires no permissions to run. * It’s used in a context tile, so should be short. */ _("No permissions")); break; - case GS_APP_PERMISSIONS_NETWORK: + case GS_APP_PERMISSIONS_FLAGS_NETWORK: add_to_safety_rating (&chosen_rating, descriptions, /* This isn’t actually safe (network access can expand a local * vulnerability into a remotely exploitable one), but it’s @@ -289,31 +292,31 @@ update_safety_tile (GsAppContextBar *self) * It’s used in a context tile, so should be short. */ _("Has network access")); break; - case GS_APP_PERMISSIONS_SYSTEM_BUS: + case GS_APP_PERMISSIONS_FLAGS_SYSTEM_BUS: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_POTENTIALLY_UNSAFE, /* Translators: This indicates an app uses D-Bus system services. * It’s used in a context tile, so should be short. */ _("Uses system services")); break; - case GS_APP_PERMISSIONS_SESSION_BUS: + case GS_APP_PERMISSIONS_FLAGS_SESSION_BUS: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_UNSAFE, /* Translators: This indicates an app uses D-Bus session services. * It’s used in a context tile, so should be short. */ _("Uses session services")); break; - case GS_APP_PERMISSIONS_DEVICES: + case GS_APP_PERMISSIONS_FLAGS_DEVICES: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_POTENTIALLY_UNSAFE, /* Translators: This indicates an app can access arbitrary hardware devices. * It’s used in a context tile, so should be short. */ _("Can access hardware devices")); break; - case GS_APP_PERMISSIONS_HOME_FULL: - case GS_APP_PERMISSIONS_FILESYSTEM_FULL: + case GS_APP_PERMISSIONS_FLAGS_HOME_FULL: + case GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL: /* Don’t add twice. */ - if (i == GS_APP_PERMISSIONS_HOME_FULL && (permissions & GS_APP_PERMISSIONS_FILESYSTEM_FULL)) + if (i == GS_APP_PERMISSIONS_FLAGS_HOME_FULL && (perm_flags & GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL)) break; add_to_safety_rating (&chosen_rating, descriptions, @@ -322,10 +325,10 @@ update_safety_tile (GsAppContextBar *self) * It’s used in a context tile, so should be short. */ _("Can read/write all your data")); break; - case GS_APP_PERMISSIONS_HOME_READ: - case GS_APP_PERMISSIONS_FILESYSTEM_READ: + case GS_APP_PERMISSIONS_FLAGS_HOME_READ: + case GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ: /* Don’t add twice. */ - if (i == GS_APP_PERMISSIONS_HOME_READ && (permissions & GS_APP_PERMISSIONS_FILESYSTEM_READ)) + if (i == GS_APP_PERMISSIONS_FLAGS_HOME_READ && (perm_flags & GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ)) break; add_to_safety_rating (&chosen_rating, descriptions, @@ -334,42 +337,42 @@ update_safety_tile (GsAppContextBar *self) * It’s used in a context tile, so should be short. */ _("Can read all your data")); break; - case GS_APP_PERMISSIONS_DOWNLOADS_FULL: + case GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_POTENTIALLY_UNSAFE, /* Translators: This indicates an app can read/write to the user’s Downloads directory. * It’s used in a context tile, so should be short. */ _("Can read/write your downloads")); break; - case GS_APP_PERMISSIONS_DOWNLOADS_READ: + case GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_POTENTIALLY_UNSAFE, /* Translators: This indicates an app can read (but not write) from the user’s Downloads directory. * It’s used in a context tile, so should be short. */ _("Can read your downloads")); break; - case GS_APP_PERMISSIONS_FILESYSTEM_OTHER: + case GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_OTHER: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_POTENTIALLY_UNSAFE, /* Translators: This indicates an app can access data in the system unknown to the Software. * It’s used in a context tile, so should be short. */ _("Can access arbitrary files")); break; - case GS_APP_PERMISSIONS_SETTINGS: + case GS_APP_PERMISSIONS_FLAGS_SETTINGS: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_POTENTIALLY_UNSAFE, /* Translators: This indicates an app can access or change user settings. * It’s used in a context tile, so should be short. */ _("Can access and change user settings")); break; - case GS_APP_PERMISSIONS_X11: + case GS_APP_PERMISSIONS_FLAGS_X11: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_UNSAFE, /* Translators: This indicates an app uses the X11 windowing system. * It’s used in a context tile, so should be short. */ _("Uses a legacy windowing system")); break; - case GS_APP_PERMISSIONS_ESCAPE_SANDBOX: + case GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX: add_to_safety_rating (&chosen_rating, descriptions, SAFETY_UNSAFE, /* Translators: This indicates an app can escape its sandbox. @@ -393,7 +396,7 @@ update_safety_tile (GsAppContextBar *self) * FIXME: We could do better by potentially adding a ‘trusted’ state * to indicate that something is probably safe, but isn’t sandboxed. * See https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1451 */ - if (permissions == GS_APP_PERMISSIONS_UNKNOWN && + if (perm_flags == GS_APP_PERMISSIONS_FLAGS_UNKNOWN && gs_app_has_quirk (self->app, GS_APP_QUIRK_PROVENANCE)) add_to_safety_rating (&chosen_rating, descriptions, SAFETY_SAFE, @@ -401,7 +404,7 @@ update_safety_tile (GsAppContextBar *self) * by the user’s distribution and is safe. * It’s used in a context tile, so should be short. */ _("Reviewed by your distribution")); - else if (permissions == GS_APP_PERMISSIONS_UNKNOWN) + else if (perm_flags == GS_APP_PERMISSIONS_FLAGS_UNKNOWN) add_to_safety_rating (&chosen_rating, descriptions, SAFETY_POTENTIALLY_UNSAFE, /* Translators: This indicates that an application has been packaged diff --git a/src/gs-app-details-page.c b/src/gs-app-details-page.c index 6eb39317dc0fa7535acfc86d3c4f30b55403363a..27a296d98acfd9c0f75b0c0d2bd0930622d8e70c 100644 --- a/src/gs-app-details-page.c +++ b/src/gs-app-details-page.c @@ -61,52 +61,91 @@ struct _GsAppDetailsPage G_DEFINE_TYPE (GsAppDetailsPage, gs_app_details_page, GTK_TYPE_BOX) static const struct { - GsAppPermissions permission; + GsAppPermissionsFlags permission; const char *title; const char *subtitle; } permission_display_data[] = { - { GS_APP_PERMISSIONS_NETWORK, N_("Network"), N_("Can communicate over the network") }, - { GS_APP_PERMISSIONS_SYSTEM_BUS, N_("System Services"), N_("Can access D-Bus services on the system bus") }, - { GS_APP_PERMISSIONS_SESSION_BUS, N_("Session Services"), N_("Can access D-Bus services on the session bus") }, - { GS_APP_PERMISSIONS_DEVICES, N_("Devices"), N_("Can access system device files") }, - { GS_APP_PERMISSIONS_HOME_FULL, N_("Home folder"), N_("Can view, edit and create files") }, - { GS_APP_PERMISSIONS_HOME_READ, N_("Home folder"), N_("Can view files") }, - { GS_APP_PERMISSIONS_FILESYSTEM_FULL, N_("File system"), N_("Can view, edit and create files") }, - { GS_APP_PERMISSIONS_FILESYSTEM_READ, N_("File system"), N_("Can view files") }, - { GS_APP_PERMISSIONS_FILESYSTEM_OTHER, N_("File system"), N_("Can access arbitrary files") }, - { GS_APP_PERMISSIONS_DOWNLOADS_FULL, N_("Downloads folder"), N_("Can view, edit and create files") }, - { GS_APP_PERMISSIONS_DOWNLOADS_READ, N_("Downloads folder"), N_("Can view files") }, - { GS_APP_PERMISSIONS_SETTINGS, N_("Settings"), N_("Can view and change any settings") }, - { GS_APP_PERMISSIONS_X11, N_("Legacy display system"), N_("Uses an old, insecure display system") }, - { GS_APP_PERMISSIONS_ESCAPE_SANDBOX, N_("Sandbox escape"), N_("Can escape the sandbox and circumvent any other restrictions") }, + { GS_APP_PERMISSIONS_FLAGS_NETWORK, N_("Network"), N_("Can communicate over the network") }, + { GS_APP_PERMISSIONS_FLAGS_SYSTEM_BUS, N_("System Services"), N_("Can access D-Bus services on the system bus") }, + { GS_APP_PERMISSIONS_FLAGS_SESSION_BUS, N_("Session Services"), N_("Can access D-Bus services on the session bus") }, + { GS_APP_PERMISSIONS_FLAGS_DEVICES, N_("Devices"), N_("Can access system device files") }, + { GS_APP_PERMISSIONS_FLAGS_HOME_FULL, N_("Home folder"), N_("Can view, edit and create files") }, + { GS_APP_PERMISSIONS_FLAGS_HOME_READ, N_("Home folder"), N_("Can view files") }, + { GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL, N_("File system"), N_("Can view, edit and create files") }, + { GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ, N_("File system"), N_("Can view files") }, + /* The GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_OTHER is used only as a flag, with actual files being part of the read/full lists */ + { GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL, N_("Downloads folder"), N_("Can view, edit and create files") }, + { GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ, N_("Downloads folder"), N_("Can view files") }, + { GS_APP_PERMISSIONS_FLAGS_SETTINGS, N_("Settings"), N_("Can view and change any settings") }, + { GS_APP_PERMISSIONS_FLAGS_X11, N_("Legacy display system"), N_("Uses an old, insecure display system") }, + { GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX, N_("Sandbox escape"), N_("Can escape the sandbox and circumvent any other restrictions") }, }; static void -populate_permissions_section (GsAppDetailsPage *page, GsAppPermissions permissions) +add_permissions_row (GsAppDetailsPage *page, + const gchar *title, + const gchar *subtitle, + gboolean is_warning_row) { - gs_widget_remove_all (page->permissions_section_list, (GsRemoveFunc) gtk_list_box_remove); + GtkWidget *row, *image; - for (gsize i = 0; i < G_N_ELEMENTS (permission_display_data); i++) { - GtkWidget *row, *image; + row = adw_action_row_new (); + if (is_warning_row) + gtk_style_context_add_class (gtk_widget_get_style_context (row), "permission-row-warning"); - if ((permissions & permission_display_data[i].permission) == 0) - continue; + image = gtk_image_new_from_icon_name ("dialog-warning-symbolic"); + if (!is_warning_row) + gtk_widget_set_opacity (image, 0); + + adw_action_row_add_prefix (ADW_ACTION_ROW (row), image); + adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), title); + adw_action_row_set_subtitle (ADW_ACTION_ROW (row), subtitle); + + gtk_list_box_append (GTK_LIST_BOX (page->permissions_section_list), row); +} + +static void +populate_permissions_filesystem (GsAppDetailsPage *page, + const GPtrArray *titles, /* (element-type utf-8) */ + const gchar *subtitle, + gboolean is_warning_row) +{ + if (titles == NULL) + return; - row = adw_action_row_new (); - if ((permission_display_data[i].permission & ~MEDIUM_PERMISSIONS) != 0) { - gtk_style_context_add_class (gtk_widget_get_style_context (row), "permission-row-warning"); - } + for (guint i = 0; i < titles->len; i++) { + const gchar *title = g_ptr_array_index (titles, i); + add_permissions_row (page, title, subtitle, is_warning_row); + } +} + +static void +populate_permissions_section (GsAppDetailsPage *page, + GsAppPermissions *permissions) +{ + GsAppPermissionsFlags flags = gs_app_permissions_get_flags (permissions); - image = gtk_image_new_from_icon_name ("dialog-warning-symbolic"); - if ((permission_display_data[i].permission & ~MEDIUM_PERMISSIONS) == 0) - gtk_widget_set_opacity (image, 0); + gs_widget_remove_all (page->permissions_section_list, (GsRemoveFunc) gtk_list_box_remove); - adw_action_row_add_prefix (ADW_ACTION_ROW (row), image); - adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), _(permission_display_data[i].title)); - adw_action_row_set_subtitle (ADW_ACTION_ROW (row), _(permission_display_data[i].subtitle)); + for (gsize i = 0; i < G_N_ELEMENTS (permission_display_data); i++) { + if ((flags & permission_display_data[i].permission) == 0) + continue; - gtk_list_box_append (GTK_LIST_BOX (page->permissions_section_list), row); + add_permissions_row (page, + _(permission_display_data[i].title), + _(permission_display_data[i].subtitle), + (permission_display_data[i].permission & ~MEDIUM_PERMISSIONS) != 0); } + + populate_permissions_filesystem (page, + gs_app_permissions_get_filesystem_read (permissions), + _("Can view files"), + (GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ & ~MEDIUM_PERMISSIONS) != 0); + + populate_permissions_filesystem (page, + gs_app_permissions_get_filesystem_full (permissions), + _("Can view, edit and create files"), + (GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL & ~MEDIUM_PERMISSIONS) != 0); } static void @@ -192,8 +231,9 @@ set_updates_description_ui (GsAppDetailsPage *page, GsApp *app) adw_status_page_set_paintable (ADW_STATUS_PAGE (page->status_page), GDK_PAINTABLE (paintable)); if (gs_app_has_quirk (app, GS_APP_QUIRK_NEW_PERMISSIONS)) { + g_autoptr(GsAppPermissions) permissions = gs_app_dup_update_permissions (app); gtk_widget_show (page->permissions_section); - populate_permissions_section (page, gs_app_get_update_permissions (app)); + populate_permissions_section (page, permissions); } else { gtk_widget_hide (page->permissions_section); } diff --git a/src/gs-safety-context-dialog.c b/src/gs-safety-context-dialog.c index a05a3543b736154a5d340d9ba2189a38fa9d2f06..deccb38aa917eede49465c0ff8861fb350074b0c 100644 --- a/src/gs-safety-context-dialog.c +++ b/src/gs-safety-context-dialog.c @@ -108,9 +108,10 @@ update_permissions_list (GsSafetyContextDialog *self) { const gchar *icon_name, *css_class; g_autofree gchar *title = NULL; - g_autoptr(GPtrArray) descriptions = g_ptr_array_new_with_free_func (NULL); g_autofree gchar *description = NULL; - GsAppPermissions permissions; + g_autoptr(GPtrArray) descriptions = g_ptr_array_new_with_free_func (NULL); + g_autoptr(GsAppPermissions) permissions = NULL; + GsAppPermissionsFlags perm_flags = GS_APP_PERMISSIONS_FLAGS_UNKNOWN; GtkStyleContext *context; GsContextDialogRowImportance chosen_rating; @@ -124,15 +125,17 @@ update_permissions_list (GsSafetyContextDialog *self) if (self->app == NULL) return; - permissions = gs_app_get_permissions (self->app); + permissions = gs_app_dup_permissions (self->app); + if (permissions != NULL) + perm_flags = gs_app_permissions_get_flags (permissions); /* Handle unknown permissions. This means the application isn’t * sandboxed, so we can only really base decisions on whether it was * packaged by an organisation we trust or not. * - * FIXME: See the comment for GS_APP_PERMISSIONS_UNKNOWN in + * FIXME: See the comment for GS_APP_PERMISSIONS_FLAGS_UNKNOWN in * gs-app-context-bar.c. */ - if (permissions == GS_APP_PERMISSIONS_UNKNOWN) { + if (perm_flags == GS_APP_PERMISSIONS_FLAGS_UNKNOWN) { add_permission_row (self->permissions_list, &chosen_rating, !gs_app_has_quirk (self->app, GS_APP_QUIRK_PROVENANCE), GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, @@ -143,8 +146,14 @@ update_permissions_list (GsSafetyContextDialog *self) _("Reviewed by your distribution"), _("Application isn’t sandboxed but the distribution has checked that it is not malicious")); } else { + const GPtrArray *filesystem_read, *filesystem_full; + + filesystem_read = gs_app_permissions_get_filesystem_read (permissions); + filesystem_full = gs_app_permissions_get_filesystem_full (permissions); + add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_NONE) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_NONE) != 0 && + filesystem_read == NULL && filesystem_full == NULL, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_UNIMPORTANT, "folder-documents-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -152,7 +161,7 @@ update_permissions_list (GsSafetyContextDialog *self) _("App is fully sandboxed"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_NETWORK) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_NETWORK) != 0, /* This isn’t actually unimportant (network access can expand a local * vulnerability into a remotely exploitable one), but it’s * needed commonly enough that marking it as @@ -167,7 +176,7 @@ update_permissions_list (GsSafetyContextDialog *self) _("No Network Access"), _("Cannot access the internet")); add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_SYSTEM_BUS) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_SYSTEM_BUS) != 0, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, "emblem-system-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -175,7 +184,7 @@ update_permissions_list (GsSafetyContextDialog *self) _("Can request data from system services"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_SESSION_BUS) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_SESSION_BUS) != 0, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_IMPORTANT, "emblem-system-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -183,7 +192,7 @@ update_permissions_list (GsSafetyContextDialog *self) _("Can request data from session services"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_DEVICES) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_DEVICES) != 0, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, "camera-photo-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -194,7 +203,7 @@ update_permissions_list (GsSafetyContextDialog *self) _("No Device Access"), _("Cannot access devices such as webcams or gaming controllers")); add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_X11) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_X11) != 0, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_IMPORTANT, "desktop-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -202,7 +211,7 @@ update_permissions_list (GsSafetyContextDialog *self) _("Uses a legacy windowing system"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_ESCAPE_SANDBOX) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_ESCAPE_SANDBOX) != 0, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_IMPORTANT, "dialog-warning-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -210,7 +219,7 @@ update_permissions_list (GsSafetyContextDialog *self) _("Can acquire arbitrary permissions"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_SETTINGS) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_SETTINGS) != 0, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, "preferences-system-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -222,7 +231,7 @@ update_permissions_list (GsSafetyContextDialog *self) * varying scopes of what’s readable/writable, and a difference between * read-only and writable access. */ add_permission_row (self->permissions_list, &chosen_rating, - (permissions & GS_APP_PERMISSIONS_FILESYSTEM_FULL) != 0, + (perm_flags & GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL) != 0, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_IMPORTANT, "folder-documents-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -230,8 +239,8 @@ update_permissions_list (GsSafetyContextDialog *self) _("Can read and write all data on the file system"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - ((permissions & GS_APP_PERMISSIONS_HOME_FULL) != 0 && - !(permissions & GS_APP_PERMISSIONS_FILESYSTEM_FULL)), + ((perm_flags & GS_APP_PERMISSIONS_FLAGS_HOME_FULL) != 0 && + !(perm_flags & GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL)), GS_CONTEXT_DIALOG_ROW_IMPORTANCE_IMPORTANT, "user-home-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -239,8 +248,8 @@ update_permissions_list (GsSafetyContextDialog *self) _("Can read and write all data in your home directory"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - ((permissions & GS_APP_PERMISSIONS_FILESYSTEM_READ) != 0 && - !(permissions & GS_APP_PERMISSIONS_FILESYSTEM_FULL)), + ((perm_flags & GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ) != 0 && + !(perm_flags & GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL)), GS_CONTEXT_DIALOG_ROW_IMPORTANCE_IMPORTANT, "folder-documents-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -248,9 +257,9 @@ update_permissions_list (GsSafetyContextDialog *self) _("Can read all data on the file system"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - ((permissions & GS_APP_PERMISSIONS_HOME_READ) != 0 && - !(permissions & (GS_APP_PERMISSIONS_FILESYSTEM_FULL | - GS_APP_PERMISSIONS_FILESYSTEM_READ))), + ((perm_flags & GS_APP_PERMISSIONS_FLAGS_HOME_READ) != 0 && + !(perm_flags & (GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL | + GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ))), GS_CONTEXT_DIALOG_ROW_IMPORTANCE_IMPORTANT, "user-home-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -258,9 +267,9 @@ update_permissions_list (GsSafetyContextDialog *self) _("Can read all data in your home directory"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - ((permissions & GS_APP_PERMISSIONS_DOWNLOADS_FULL) != 0 && - !(permissions & (GS_APP_PERMISSIONS_FILESYSTEM_FULL | - GS_APP_PERMISSIONS_HOME_FULL))), + ((perm_flags & GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL) != 0 && + !(perm_flags & (GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL | + GS_APP_PERMISSIONS_FLAGS_HOME_FULL))), GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, "folder-download-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ @@ -268,38 +277,49 @@ update_permissions_list (GsSafetyContextDialog *self) _("Can read and write all data in your downloads directory"), NULL, NULL, NULL); add_permission_row (self->permissions_list, &chosen_rating, - ((permissions & GS_APP_PERMISSIONS_DOWNLOADS_READ) != 0 && - !(permissions & (GS_APP_PERMISSIONS_FILESYSTEM_FULL | - GS_APP_PERMISSIONS_FILESYSTEM_READ | - GS_APP_PERMISSIONS_HOME_FULL | - GS_APP_PERMISSIONS_HOME_READ))), + ((perm_flags & GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ) != 0 && + !(perm_flags & (GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL | + GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ | + GS_APP_PERMISSIONS_FLAGS_HOME_FULL | + GS_APP_PERMISSIONS_FLAGS_HOME_READ))), GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, "folder-download-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ _("Download Folder Read Access"), _("Can read all data in your downloads directory"), NULL, NULL, NULL); - add_permission_row (self->permissions_list, &chosen_rating, - ((permissions & GS_APP_PERMISSIONS_FILESYSTEM_OTHER) != 0 && - !(permissions & (GS_APP_PERMISSIONS_FILESYSTEM_FULL | - GS_APP_PERMISSIONS_FILESYSTEM_READ | - GS_APP_PERMISSIONS_HOME_FULL | - GS_APP_PERMISSIONS_HOME_READ))), - GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, - "folder-documents-symbolic", - /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */ - _("Access arbitrary files"), - _("Can access arbitrary files on the file system"), - NULL, NULL, NULL); + + for (guint i = 0; filesystem_full != NULL && i < filesystem_full->len; i++) { + const gchar *fs_title = g_ptr_array_index (filesystem_full, i); + add_permission_row (self->permissions_list, &chosen_rating, + TRUE, + GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, + "folder-documents-symbolic", + fs_title, + _("Can read and write all data in the directory"), + NULL, NULL, NULL); + } + + for (guint i = 0; filesystem_read != NULL && i < filesystem_read->len; i++) { + const gchar *fs_title = g_ptr_array_index (filesystem_read, i); + add_permission_row (self->permissions_list, &chosen_rating, + TRUE, + GS_CONTEXT_DIALOG_ROW_IMPORTANCE_WARNING, + "folder-documents-symbolic", + fs_title, + _("Can read all data in the directory"), + NULL, NULL, NULL); + } add_permission_row (self->permissions_list, &chosen_rating, - !(permissions & (GS_APP_PERMISSIONS_FILESYSTEM_FULL | - GS_APP_PERMISSIONS_FILESYSTEM_READ | - GS_APP_PERMISSIONS_FILESYSTEM_OTHER | - GS_APP_PERMISSIONS_HOME_FULL | - GS_APP_PERMISSIONS_HOME_READ | - GS_APP_PERMISSIONS_DOWNLOADS_FULL | - GS_APP_PERMISSIONS_DOWNLOADS_READ)), + !(perm_flags & (GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_FULL | + GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_READ | + GS_APP_PERMISSIONS_FLAGS_FILESYSTEM_OTHER | + GS_APP_PERMISSIONS_FLAGS_HOME_FULL | + GS_APP_PERMISSIONS_FLAGS_HOME_READ | + GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_FULL | + GS_APP_PERMISSIONS_FLAGS_DOWNLOADS_READ)) && + filesystem_read == NULL && filesystem_full == NULL, GS_CONTEXT_DIALOG_ROW_IMPORTANCE_UNIMPORTANT, "folder-documents-symbolic", /* Translators: This refers to permissions (for example, from flatpak) which an app requests from the user. */