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 <andre@endlessm.com>
This commit is contained in:
Andre Moreira Magalhaes 2019-06-12 21:34:43 -03:00
parent bbd1b2bdff
commit da0e63fe99
2 changed files with 141 additions and 5 deletions

View File

@ -1,6 +1,6 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- /* -*- 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -18,6 +18,7 @@
* *
* Authors: * Authors:
* - Philip Withnall <withnall@endlessm.com> * - Philip Withnall <withnall@endlessm.com>
* - Andre Moreira Magalhaes <andre@endlessm.com>
*/ */
#include "config.h" #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: * mct_app_filter_is_flatpak_ref_allowed:
* @filter: an #MctAppFilter * @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 != NULL, FALSE);
g_return_val_if_fail (filter->ref_count >= 1, 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 (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, gboolean ref_in_list = g_strv_contains ((const gchar * const *) filter->app_list,
app_ref); app_ref);
@ -205,9 +235,8 @@ mct_app_filter_is_flatpak_app_allowed (MctAppFilter *filter,
gboolean id_in_list = FALSE; gboolean id_in_list = FALSE;
for (gsize i = 0; filter->app_list[i] != NULL; i++) for (gsize i = 0; filter->app_list[i] != NULL; i++)
{ {
/* Avoid using flatpak_ref_parse() to avoid a dependency on libflatpak if (is_valid_flatpak_ref (filter->app_list[i]) &&
* just for that one function. */ g_str_has_prefix (filter->app_list[i], "app/") &&
if (g_str_has_prefix (filter->app_list[i], "app/") &&
strncmp (filter->app_list[i] + strlen ("app/"), app_id, app_id_len) == 0 && strncmp (filter->app_list[i] + strlen ("app/"), app_id, app_id_len) == 0 &&
filter->app_list[i][strlen ("app/") + app_id_len] == '/') filter->app_list[i][strlen ("app/") + app_id_len] == '/')
{ {
@ -246,6 +275,7 @@ mct_app_filter_is_appinfo_allowed (MctAppFilter *filter,
GAppInfo *app_info) GAppInfo *app_info)
{ {
g_autofree gchar *abs_path = NULL; 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 != NULL, FALSE);
g_return_val_if_fail (filter->ref_count >= 1, 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)) !mct_app_filter_is_path_allowed (filter, abs_path))
return FALSE; 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)) if (G_IS_DESKTOP_APP_INFO (app_info))
{ {
g_autofree gchar *flatpak_app = NULL; g_autofree gchar *flatpak_app = NULL;
@ -296,6 +333,67 @@ mct_app_filter_is_appinfo_allowed (MctAppFilter *filter,
return TRUE; 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 doesnt 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 static gint
strcmp_cb (gconstpointer a, strcmp_cb (gconstpointer a,
gconstpointer b) 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 != NULL);
g_return_if_fail (_builder->blacklist != NULL); g_return_if_fail (_builder->blacklist != NULL);
g_return_if_fail (app_ref != 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, if (!g_ptr_array_find_with_equal_func (_builder->blacklist,
app_ref, g_str_equal, NULL)) app_ref, g_str_equal, NULL))
g_ptr_array_add (_builder->blacklist, g_strdup (app_ref)); 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 its already been
* added.
*
* Note that this method doesnt 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: * mct_app_filter_builder_set_oars_value:
* @builder: an initialised #MctAppFilterBuilder * @builder: an initialised #MctAppFilterBuilder

View File

@ -1,6 +1,6 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- /* -*- 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -18,6 +18,7 @@
* *
* Authors: * Authors:
* - Philip Withnall <withnall@endlessm.com> * - Philip Withnall <withnall@endlessm.com>
* - Andre Moreira Magalhaes <andre@endlessm.com>
*/ */
#pragma once #pragma once
@ -109,6 +110,8 @@ gboolean mct_app_filter_is_flatpak_app_allowed (MctAppFilter *filter,
const gchar *app_id); const gchar *app_id);
gboolean mct_app_filter_is_appinfo_allowed (MctAppFilter *filter, gboolean mct_app_filter_is_appinfo_allowed (MctAppFilter *filter,
GAppInfo *app_info); 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); const gchar **mct_app_filter_get_oars_sections (MctAppFilter *filter);
MctAppFilterOarsValue mct_app_filter_get_oars_value (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); const gchar *path);
void mct_app_filter_builder_blacklist_flatpak_ref (MctAppFilterBuilder *builder, void mct_app_filter_builder_blacklist_flatpak_ref (MctAppFilterBuilder *builder,
const gchar *app_ref); 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, void mct_app_filter_builder_set_oars_value (MctAppFilterBuilder *builder,
const gchar *oars_section, const gchar *oars_section,
MctAppFilterOarsValue value); MctAppFilterOarsValue value);