From da0e63fe99fd2b648f46cc899fc01544ae7d10db Mon Sep 17 00:00:00 2001 From: Andre Moreira Magalhaes Date: Wed, 12 Jun 2019 21:34:43 -0300 Subject: [PATCH] libmalcontent: Add support for filtering by content type This is useful for example if blacklisting all apps that can handle certain content types is desired. Signed-off-by: Andre Moreira Magalhaes --- libmalcontent/app-filter.c | 138 +++++++++++++++++++++++++++++++++++-- libmalcontent/app-filter.h | 8 ++- 2 files changed, 141 insertions(+), 5 deletions(-) diff --git a/libmalcontent/app-filter.c b/libmalcontent/app-filter.c index 624d14e..266b3d6 100644 --- a/libmalcontent/app-filter.c +++ b/libmalcontent/app-filter.c @@ -1,6 +1,6 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * - * Copyright © 2018 Endless Mobile, Inc. + * Copyright © 2018-2019 Endless Mobile, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,6 +18,7 @@ * * Authors: * - Philip Withnall + * - Andre Moreira Magalhaes */ #include "config.h" @@ -142,6 +143,34 @@ mct_app_filter_is_path_allowed (MctAppFilter *filter, } } +/* Check whether a given @ref is a valid flatpak ref. + * + * For simplicity and to avoid duplicating the whole logic behind + * flatpak_ref_parse() this method will only check whether: + * - the @ref contains exactly 3 slash chars + * - the @ref starts with either app/ or runtime/ + * - the name, arch and branch components of the @ref are not empty + * + * We avoid using flatpak_ref_parse() to allow for libflatpak + * to depend on malcontent without causing a cyclic dependency. + */ +static gboolean +is_valid_flatpak_ref (const gchar *ref) +{ + g_auto(GStrv) parts = NULL; + + if (ref == NULL) + return FALSE; + + parts = g_strsplit (ref, "/", 0); + return (g_strv_length (parts) == 4 && + (strcmp (parts[0], "app") == 0 || + strcmp (parts[0], "runtime") == 0) && + *parts[1] != '\0' && + *parts[2] != '\0' && + *parts[3] != '\0'); +} + /** * mct_app_filter_is_flatpak_ref_allowed: * @filter: an #MctAppFilter @@ -161,6 +190,7 @@ mct_app_filter_is_flatpak_ref_allowed (MctAppFilter *filter, g_return_val_if_fail (filter != NULL, FALSE); g_return_val_if_fail (filter->ref_count >= 1, FALSE); g_return_val_if_fail (app_ref != NULL, FALSE); + g_return_val_if_fail (is_valid_flatpak_ref (app_ref), FALSE); gboolean ref_in_list = g_strv_contains ((const gchar * const *) filter->app_list, app_ref); @@ -205,9 +235,8 @@ mct_app_filter_is_flatpak_app_allowed (MctAppFilter *filter, gboolean id_in_list = FALSE; for (gsize i = 0; filter->app_list[i] != NULL; i++) { - /* Avoid using flatpak_ref_parse() to avoid a dependency on libflatpak - * just for that one function. */ - if (g_str_has_prefix (filter->app_list[i], "app/") && + if (is_valid_flatpak_ref (filter->app_list[i]) && + g_str_has_prefix (filter->app_list[i], "app/") && strncmp (filter->app_list[i] + strlen ("app/"), app_id, app_id_len) == 0 && filter->app_list[i][strlen ("app/") + app_id_len] == '/') { @@ -246,6 +275,7 @@ mct_app_filter_is_appinfo_allowed (MctAppFilter *filter, GAppInfo *app_info) { g_autofree gchar *abs_path = NULL; + const gchar * const *types = NULL; g_return_val_if_fail (filter != NULL, FALSE); g_return_val_if_fail (filter->ref_count >= 1, FALSE); @@ -257,6 +287,13 @@ mct_app_filter_is_appinfo_allowed (MctAppFilter *filter, !mct_app_filter_is_path_allowed (filter, abs_path)) return FALSE; + types = g_app_info_get_supported_types (app_info); + for (gsize i = 0; types != NULL && types[i] != NULL; i++) + { + if (!mct_app_filter_is_content_type_allowed (filter, types[i])) + return FALSE; + } + if (G_IS_DESKTOP_APP_INFO (app_info)) { g_autofree gchar *flatpak_app = NULL; @@ -296,6 +333,67 @@ mct_app_filter_is_appinfo_allowed (MctAppFilter *filter, return TRUE; } +/* Check whether a given @content_type is valid. + * + * For simplicity this method will only check whether: + * - the @content_type contains exactly 1 slash char + * - the @content_type does not start with a slash char + * - the type and subtype components of the @content_type are not empty + */ +static gboolean +is_valid_content_type (const gchar *content_type) +{ + g_auto(GStrv) parts = NULL; + + if (content_type == NULL) + return FALSE; + + parts = g_strsplit (content_type, "/", 0); + return (g_strv_length (parts) == 2 && + *parts[0] != '\0' && + *parts[1] != '\0'); +} + +/** + * mct_app_filter_is_content_type_allowed: + * @filter: an #MctAppFilter + * @content_type: content type to check + * + * Check whether apps handling the given @content_type are allowed to be run + * according to this app filter. + * + * Note that this method doesn’t match content subtypes. For example, if + * `application/xml` is added to the blacklist but `application/xspf+xml` is not, + * a check for whether `application/xspf+xml` is blacklisted would return false. + * + * Returns: %TRUE if the user this @filter corresponds to is allowed to run + * programs handling @content_type according to the @filter policy; + * %FALSE otherwise + * Since: 0.4.0 + */ +gboolean +mct_app_filter_is_content_type_allowed (MctAppFilter *filter, + const gchar *content_type) +{ + g_return_val_if_fail (filter != NULL, FALSE); + g_return_val_if_fail (filter->ref_count >= 1, FALSE); + g_return_val_if_fail (content_type != NULL, FALSE); + g_return_val_if_fail (is_valid_content_type (content_type), FALSE); + + gboolean ref_in_list = g_strv_contains ((const gchar * const *) filter->app_list, + content_type); + + switch (filter->app_list_type) + { + case MCT_APP_FILTER_LIST_BLACKLIST: + return !ref_in_list; + case MCT_APP_FILTER_LIST_WHITELIST: + return ref_in_list; + default: + g_assert_not_reached (); + } +} + static gint strcmp_cb (gconstpointer a, gconstpointer b) @@ -700,12 +798,44 @@ mct_app_filter_builder_blacklist_flatpak_ref (MctAppFilterBuilder *builder, g_return_if_fail (_builder != NULL); g_return_if_fail (_builder->blacklist != NULL); g_return_if_fail (app_ref != NULL); + g_return_if_fail (is_valid_flatpak_ref (app_ref)); if (!g_ptr_array_find_with_equal_func (_builder->blacklist, app_ref, g_str_equal, NULL)) g_ptr_array_add (_builder->blacklist, g_strdup (app_ref)); } +/** + * mct_app_filter_builder_blacklist_content_type: + * @builder: an initialised #MctAppFilterBuilder + * @content_type: a content type to blacklist + * + * Add @content_type to the blacklist of content types in the filter under + * construction. The @content_type will not be added again if it’s already been + * added. + * + * Note that this method doesn’t handle content subtypes. For example, if + * `application/xml` is added to the blacklist but `application/xspf+xml` is not, + * a check for whether `application/xspf+xml` is blacklisted would return false. + * + * Since: 0.4.0 + */ +void +mct_app_filter_builder_blacklist_content_type (MctAppFilterBuilder *builder, + const gchar *content_type) +{ + MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder; + + g_return_if_fail (_builder != NULL); + g_return_if_fail (_builder->blacklist != NULL); + g_return_if_fail (content_type != NULL); + g_return_if_fail (is_valid_content_type (content_type)); + + if (!g_ptr_array_find_with_equal_func (_builder->blacklist, + content_type, g_str_equal, NULL)) + g_ptr_array_add (_builder->blacklist, g_strdup (content_type)); +} + /** * mct_app_filter_builder_set_oars_value: * @builder: an initialised #MctAppFilterBuilder diff --git a/libmalcontent/app-filter.h b/libmalcontent/app-filter.h index 5b7573b..263b4ee 100644 --- a/libmalcontent/app-filter.h +++ b/libmalcontent/app-filter.h @@ -1,6 +1,6 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * - * Copyright © 2018 Endless Mobile, Inc. + * Copyright © 2018-2019 Endless Mobile, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,6 +18,7 @@ * * Authors: * - Philip Withnall + * - Andre Moreira Magalhaes */ #pragma once @@ -109,6 +110,8 @@ gboolean mct_app_filter_is_flatpak_app_allowed (MctAppFilter *filter, const gchar *app_id); gboolean mct_app_filter_is_appinfo_allowed (MctAppFilter *filter, GAppInfo *app_info); +gboolean mct_app_filter_is_content_type_allowed (MctAppFilter *filter, + const gchar *content_type); const gchar **mct_app_filter_get_oars_sections (MctAppFilter *filter); MctAppFilterOarsValue mct_app_filter_get_oars_value (MctAppFilter *filter, @@ -182,6 +185,9 @@ void mct_app_filter_builder_blacklist_path (MctAppFilterBuilder *builde const gchar *path); void mct_app_filter_builder_blacklist_flatpak_ref (MctAppFilterBuilder *builder, const gchar *app_ref); +void mct_app_filter_builder_blacklist_content_type (MctAppFilterBuilder *builder, + const gchar *content_type); + void mct_app_filter_builder_set_oars_value (MctAppFilterBuilder *builder, const gchar *oars_section, MctAppFilterOarsValue value);