diff --git a/libmalcontent/app-filter.c b/libmalcontent/app-filter.c index 9340caf..3fdc1e9 100644 --- a/libmalcontent/app-filter.c +++ b/libmalcontent/app-filter.c @@ -123,6 +123,54 @@ oars_str_to_enum (const gchar *value_str) return MCT_APP_FILTER_OARS_VALUE_UNKNOWN; } +/** + * mct_app_filter_is_enabled: + * @filter: an #MctAppFilter + * + * Check whether the app filter is enabled and is going to impose at least one + * restriction on the user. This gives a high level view of whether app filter + * parental controls are ‘enabled’ for the given user. + * + * Returns: %TRUE if the app filter contains at least one non-default value, + * %FALSE if it’s entirely default + * Since: 0.7.0 + */ +gboolean +mct_app_filter_is_enabled (MctAppFilter *filter) +{ + gboolean oars_ratings_all_intense_or_unknown; + GVariantIter iter; + const gchar *oars_value; + + g_return_val_if_fail (filter != NULL, FALSE); + g_return_val_if_fail (filter->ref_count >= 1, FALSE); + + /* The least restrictive OARS filter has all values as intense, or unknown. */ + oars_ratings_all_intense_or_unknown = TRUE; + g_variant_iter_init (&iter, filter->oars_ratings); + + while (g_variant_iter_loop (&iter, "{&s&s}", NULL, &oars_value)) + { + MctAppFilterOarsValue value = oars_str_to_enum (oars_value); + + if (value != MCT_APP_FILTER_OARS_VALUE_UNKNOWN && + value != MCT_APP_FILTER_OARS_VALUE_INTENSE) + { + oars_ratings_all_intense_or_unknown = FALSE; + break; + } + } + + /* Check all fields against their default values. Ignore + * `allow_system_installation` since it’s false by default, so the default + * value is already the most restrictive. */ + return ((filter->app_list_type == MCT_APP_FILTER_LIST_BLACKLIST && + filter->app_list[0] != NULL) || + filter->app_list_type == MCT_APP_FILTER_LIST_WHITELIST || + !oars_ratings_all_intense_or_unknown || + !filter->allow_user_installation); +} + /** * mct_app_filter_is_path_allowed: * @filter: an #MctAppFilter diff --git a/libmalcontent/app-filter.h b/libmalcontent/app-filter.h index b5d8fb9..3902f87 100644 --- a/libmalcontent/app-filter.h +++ b/libmalcontent/app-filter.h @@ -78,6 +78,9 @@ void mct_app_filter_unref (MctAppFilter *filter); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MctAppFilter, mct_app_filter_unref) uid_t mct_app_filter_get_user_id (MctAppFilter *filter); + +gboolean mct_app_filter_is_enabled (MctAppFilter *filter); + gboolean mct_app_filter_is_path_allowed (MctAppFilter *filter, const gchar *path); gboolean mct_app_filter_is_flatpak_ref_allowed (MctAppFilter *filter, diff --git a/libmalcontent/tests/app-filter.c b/libmalcontent/tests/app-filter.c index f5f9eca..00c6d43 100644 --- a/libmalcontent/tests/app-filter.c +++ b/libmalcontent/tests/app-filter.c @@ -162,6 +162,55 @@ test_app_filter_deserialize_invalid (void) } } +/* Test that mct_app_filter_is_enabled() returns the correct results on various + * app filters. */ +static void +test_app_filter_is_enabled (void) +{ + const struct + { + const gchar *serialized; + gboolean is_enabled; + } + app_filters[] = + { + { "@a{sv} {}", FALSE }, + { "{ 'AppFilter': <(true, @as [])> }", TRUE }, + { "{ 'AppFilter': <(false, @as [])> }", FALSE }, + { "{ 'AppFilter': <(false, @as [ '/usr/bin/gnome-software' ])> }", TRUE }, + { "{ 'OarsFilter': <('oars-1.1', @a{ss} {})> }", FALSE }, + { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'mild' })> }", TRUE }, + { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'intense' })> }", FALSE }, + { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': '' })> }", FALSE }, /* technically an invalid serialisation */ + { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'none' })> }", TRUE }, + { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'mild', 'violence-realistic': 'intense' })> }", TRUE }, + { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'mild', 'violence-realistic': 'none' })> }", TRUE }, + { "{ 'AllowUserInstallation': }", FALSE }, + { "{ 'AllowUserInstallation': }", TRUE }, + { "{ 'AllowSystemInstallation': }", FALSE }, + { "{ 'AllowSystemInstallation': }", FALSE }, + }; + + for (gsize i = 0; i < G_N_ELEMENTS (app_filters); i++) + { + g_autoptr(GVariant) variant = NULL; + g_autoptr(MctAppFilter) filter = NULL; + + g_test_message ("%" G_GSIZE_FORMAT ": %s", i, app_filters[i].serialized); + + variant = g_variant_parse (NULL, app_filters[i].serialized, NULL, NULL, NULL); + g_assert (variant != NULL); + + filter = mct_app_filter_deserialize (variant, 1, NULL); + g_assert (filter != NULL); + + if (app_filters[i].is_enabled) + g_assert_true (mct_app_filter_is_enabled (filter)); + else + g_assert_false (mct_app_filter_is_enabled (filter)); + } +} + /* Fixture for tests which use an #MctAppFilterBuilder. The builder can either * be heap- or stack-allocated. @builder will always be a valid pointer to it. */ @@ -244,6 +293,8 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture, filter = mct_app_filter_builder_end (fixture->builder); + g_assert_true (mct_app_filter_is_enabled (filter)); + g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false")); g_assert_false (mct_app_filter_is_path_allowed (filter, "/usr/bin/gnome-software")); @@ -285,6 +336,8 @@ test_app_filter_builder_empty (BuilderFixture *fixture, filter = mct_app_filter_builder_end (fixture->builder); + g_assert_false (mct_app_filter_is_enabled (filter)); + g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false")); g_assert_true (mct_app_filter_is_path_allowed (filter, "/usr/bin/gnome-software")); @@ -332,6 +385,7 @@ test_app_filter_builder_copy_empty (void) "x-scheme-handler/http"); filter = mct_app_filter_builder_end (builder_copy); + g_assert_true (mct_app_filter_is_enabled (filter)); g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false")); g_assert_false (mct_app_filter_is_path_allowed (filter, "/bin/true")); g_assert_true (mct_app_filter_is_content_type_allowed (filter, @@ -359,6 +413,7 @@ test_app_filter_builder_copy_full (void) builder_copy = mct_app_filter_builder_copy (builder); filter = mct_app_filter_builder_end (builder_copy); + g_assert_true (mct_app_filter_is_enabled (filter)); g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false")); g_assert_false (mct_app_filter_is_path_allowed (filter, "/bin/true")); g_assert_true (mct_app_filter_is_content_type_allowed (filter, @@ -691,6 +746,7 @@ test_app_filter_bus_get (BusFixture *fixture, /* Check the app filter properties. */ g_assert_cmpuint (mct_app_filter_get_user_id (app_filter), ==, fixture->valid_uid); + g_assert_true (mct_app_filter_is_enabled (app_filter)); g_assert_false (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Builder")); g_assert_true (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Chess")); } @@ -736,6 +792,7 @@ test_app_filter_bus_get_whitelist (BusFixture *fixture, /* Check the app filter properties. The returned filter is a whitelist, * whereas typically a blacklist is returned. */ g_assert_cmpuint (mct_app_filter_get_user_id (app_filter), ==, fixture->valid_uid); + g_assert_true (mct_app_filter_is_enabled (app_filter)); g_assert_false (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Builder")); g_assert_true (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Whitelisted1")); g_assert_true (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Whitelisted2")); @@ -791,6 +848,7 @@ test_app_filter_bus_get_all_oars_values (BusFixture *fixture, /* Check the OARS filter properties. Each OARS value should have been parsed * correctly, except for the unknown `other` one. */ g_assert_cmpuint (mct_app_filter_get_user_id (app_filter), ==, fixture->valid_uid); + g_assert_true (mct_app_filter_is_enabled (app_filter)); g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "violence-bloodshed"), ==, MCT_APP_FILTER_OARS_VALUE_NONE); g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "violence-sexual"), ==, @@ -838,6 +896,7 @@ test_app_filter_bus_get_defaults (BusFixture *fixture, /* Check the default values for the properties. */ g_assert_cmpuint (mct_app_filter_get_user_id (app_filter), ==, fixture->valid_uid); + g_assert_false (mct_app_filter_is_enabled (app_filter)); oars_sections = mct_app_filter_get_oars_sections (app_filter); g_assert_cmpuint (g_strv_length ((gchar **) oars_sections), ==, 0); g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "violence-bloodshed"), ==, @@ -1462,6 +1521,8 @@ main (int argc, g_test_add_func ("/app-filter/deserialize", test_app_filter_deserialize); g_test_add_func ("/app-filter/deserialize/invalid", test_app_filter_deserialize_invalid); + g_test_add_func ("/app-filter/is-enabled", test_app_filter_is_enabled); + g_test_add ("/app-filter/builder/stack/non-empty", BuilderFixture, NULL, builder_set_up_stack, test_app_filter_builder_non_empty, builder_tear_down_stack);