Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2018-2019 Endless Mobile, Inc.
4 : : *
5 : : * This library is free software; you can redistribute it and/or
6 : : * modify it under the terms of the GNU Lesser General Public
7 : : * License as published by the Free Software Foundation; either
8 : : * version 2.1 of the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General Public
16 : : * License along with this library; if not, write to the Free Software
17 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : : *
19 : : * Authors:
20 : : * - Philip Withnall <withnall@endlessm.com>
21 : : * - Andre Moreira Magalhaes <andre@endlessm.com>
22 : : */
23 : :
24 : : #include "config.h"
25 : :
26 : : #include <glib.h>
27 : : #include <glib-object.h>
28 : : #include <glib/gi18n-lib.h>
29 : : #include <gio/gdesktopappinfo.h>
30 : : #include <gio/gio.h>
31 : : #include <libmalcontent/app-filter.h>
32 : :
33 : : #include "libmalcontent/app-filter-private.h"
34 : :
35 : :
36 : : /* FIXME: Eventually deprecate these compatibility fallbacks. */
37 : : GQuark
38 : 3 : mct_app_filter_error_quark (void)
39 : : {
40 : 3 : return mct_manager_error_quark ();
41 : : }
42 : :
43 : : /* struct _MctAppFilter is defined in app-filter-private.h */
44 : :
45 [ + + + - : 10 : G_DEFINE_BOXED_TYPE (MctAppFilter, mct_app_filter,
+ + ]
46 : : mct_app_filter_ref, mct_app_filter_unref)
47 : :
48 : : /**
49 : : * mct_app_filter_ref:
50 : : * @filter: (transfer none): an #MctAppFilter
51 : : *
52 : : * Increment the reference count of @filter, and return the same pointer to it.
53 : : *
54 : : * Returns: (transfer full): the same pointer as @filter
55 : : * Since: 0.2.0
56 : : */
57 : : MctAppFilter *
58 : 4 : mct_app_filter_ref (MctAppFilter *filter)
59 : : {
60 : 4 : g_return_val_if_fail (filter != NULL, NULL);
61 : 4 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
62 : 4 : g_return_val_if_fail (filter->ref_count <= G_MAXINT - 1, NULL);
63 : :
64 : 4 : filter->ref_count++;
65 : 4 : return filter;
66 : : }
67 : :
68 : : /**
69 : : * mct_app_filter_unref:
70 : : * @filter: (transfer full): an #MctAppFilter
71 : : *
72 : : * Decrement the reference count of @filter. If the reference count reaches
73 : : * zero, free the @filter and all its resources.
74 : : *
75 : : * Since: 0.2.0
76 : : */
77 : : void
78 : 94 : mct_app_filter_unref (MctAppFilter *filter)
79 : : {
80 : 94 : g_return_if_fail (filter != NULL);
81 : 94 : g_return_if_fail (filter->ref_count >= 1);
82 : :
83 : 94 : filter->ref_count--;
84 : :
85 [ + + ]: 94 : if (filter->ref_count <= 0)
86 : : {
87 : 90 : g_strfreev (filter->app_list);
88 : 90 : g_variant_unref (filter->oars_ratings);
89 : 90 : g_free (filter);
90 : : }
91 : : }
92 : :
93 : : /**
94 : : * mct_app_filter_get_user_id:
95 : : * @filter: an #MctAppFilter
96 : : *
97 : : * Get the user ID of the user this #MctAppFilter is for.
98 : : *
99 : : * Returns: user ID of the relevant user, or `(uid_t) -1` if unknown
100 : : * Since: 0.2.0
101 : : */
102 : : uid_t
103 : 5 : mct_app_filter_get_user_id (MctAppFilter *filter)
104 : : {
105 : 5 : g_return_val_if_fail (filter != NULL, FALSE);
106 : 5 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
107 : :
108 : 5 : return filter->user_id;
109 : : }
110 : :
111 : : static MctAppFilterOarsValue
112 : 38 : oars_str_to_enum (const gchar *value_str)
113 : : {
114 [ + + ]: 38 : if (g_str_equal (value_str, "none"))
115 : 4 : return MCT_APP_FILTER_OARS_VALUE_NONE;
116 [ + + ]: 34 : else if (g_str_equal (value_str, "mild"))
117 : 15 : return MCT_APP_FILTER_OARS_VALUE_MILD;
118 [ + + ]: 19 : else if (g_str_equal (value_str, "moderate"))
119 : 13 : return MCT_APP_FILTER_OARS_VALUE_MODERATE;
120 [ + + ]: 6 : else if (g_str_equal (value_str, "intense"))
121 : 3 : return MCT_APP_FILTER_OARS_VALUE_INTENSE;
122 : : else
123 : 3 : return MCT_APP_FILTER_OARS_VALUE_UNKNOWN;
124 : : }
125 : :
126 : : /**
127 : : * mct_app_filter_is_enabled:
128 : : * @filter: an #MctAppFilter
129 : : *
130 : : * Check whether the app filter is enabled and is going to impose at least one
131 : : * restriction on the user. This gives a high level view of whether app filter
132 : : * parental controls are ‘enabled’ for the given user.
133 : : *
134 : : * Returns: %TRUE if the app filter contains at least one non-default value,
135 : : * %FALSE if it’s entirely default
136 : : * Since: 0.7.0
137 : : */
138 : : gboolean
139 : 51 : mct_app_filter_is_enabled (MctAppFilter *filter)
140 : : {
141 : : gboolean oars_ratings_all_intense_or_unknown;
142 : : GVariantIter iter;
143 : : const gchar *oars_value;
144 : :
145 : 51 : g_return_val_if_fail (filter != NULL, FALSE);
146 : 51 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
147 : :
148 : : /* The least restrictive OARS filter has all values as intense, or unknown. */
149 : 51 : oars_ratings_all_intense_or_unknown = TRUE;
150 : 51 : g_variant_iter_init (&iter, filter->oars_ratings);
151 : :
152 [ + + ]: 55 : while (g_variant_iter_loop (&iter, "{&s&s}", NULL, &oars_value))
153 : : {
154 : 21 : MctAppFilterOarsValue value = oars_str_to_enum (oars_value);
155 : :
156 [ + + + + ]: 21 : if (value != MCT_APP_FILTER_OARS_VALUE_UNKNOWN &&
157 : : value != MCT_APP_FILTER_OARS_VALUE_INTENSE)
158 : : {
159 : 17 : oars_ratings_all_intense_or_unknown = FALSE;
160 : 17 : break;
161 : : }
162 : : }
163 : :
164 : : /* Check all fields against their default values. Ignore
165 : : * `allow_system_installation` since it’s false by default, so the default
166 : : * value is already the most restrictive. */
167 : 99 : return ((filter->app_list_type == MCT_APP_FILTER_LIST_BLOCKLIST &&
168 [ + + ]: 48 : filter->app_list[0] != NULL) ||
169 [ + + + + ]: 37 : filter->app_list_type == MCT_APP_FILTER_LIST_ALLOWLIST ||
170 [ + + ]: 102 : !oars_ratings_all_intense_or_unknown ||
171 [ + + ]: 25 : !filter->allow_user_installation);
172 : : }
173 : :
174 : : /**
175 : : * mct_app_filter_is_path_allowed:
176 : : * @filter: an #MctAppFilter
177 : : * @path: (type filename): absolute path of a program to check
178 : : *
179 : : * Check whether the program at @path is allowed to be run according to this
180 : : * app filter. @path will be canonicalised without doing any I/O.
181 : : *
182 : : * Returns: %TRUE if the user this @filter corresponds to is allowed to run the
183 : : * program at @path according to the @filter policy; %FALSE otherwise
184 : : * Since: 0.2.0
185 : : */
186 : : gboolean
187 : 66 : mct_app_filter_is_path_allowed (MctAppFilter *filter,
188 : : const gchar *path)
189 : : {
190 : 66 : g_return_val_if_fail (filter != NULL, FALSE);
191 : 66 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
192 : 66 : g_return_val_if_fail (path != NULL, FALSE);
193 : 66 : g_return_val_if_fail (g_path_is_absolute (path), FALSE);
194 : :
195 : 132 : g_autofree gchar *canonical_path = g_canonicalize_filename (path, "/");
196 : 132 : g_autofree gchar *canonical_path_utf8 = g_filename_to_utf8 (canonical_path, -1,
197 : : NULL, NULL, NULL);
198 : 66 : g_return_val_if_fail (canonical_path_utf8 != NULL, FALSE);
199 : :
200 : 66 : gboolean path_in_list = g_strv_contains ((const gchar * const *) filter->app_list,
201 : : canonical_path_utf8);
202 : :
203 [ + + - ]: 66 : switch (filter->app_list_type)
204 : : {
205 : 64 : case MCT_APP_FILTER_LIST_BLOCKLIST:
206 : 64 : return !path_in_list;
207 : 2 : case MCT_APP_FILTER_LIST_ALLOWLIST:
208 : 2 : return path_in_list;
209 : 0 : default:
210 : : g_assert_not_reached ();
211 : : }
212 : : }
213 : :
214 : : /* Check whether a given @ref is a valid flatpak ref.
215 : : *
216 : : * For simplicity and to avoid duplicating the whole logic behind
217 : : * flatpak_ref_parse() this method will only check whether:
218 : : * - the @ref contains exactly 3 slash chars
219 : : * - the @ref starts with either app/ or runtime/
220 : : * - the name, arch and branch components of the @ref are not empty
221 : : *
222 : : * We avoid using flatpak_ref_parse() to allow for libflatpak
223 : : * to depend on malcontent without causing a cyclic dependency.
224 : : */
225 : : static gboolean
226 : 149 : is_valid_flatpak_ref (const gchar *ref)
227 : : {
228 : 149 : g_auto(GStrv) parts = NULL;
229 : :
230 [ - + ]: 149 : if (ref == NULL)
231 : 0 : return FALSE;
232 : :
233 : 149 : parts = g_strsplit (ref, "/", 0);
234 : 149 : return (g_strv_length (parts) == 4 &&
235 [ + + ]: 92 : (strcmp (parts[0], "app") == 0 ||
236 [ - + ]: 13 : strcmp (parts[0], "runtime") == 0) &&
237 [ + - ]: 79 : *parts[1] != '\0' &&
238 [ + + + - ]: 320 : *parts[2] != '\0' &&
239 [ + - ]: 79 : *parts[3] != '\0');
240 : : }
241 : :
242 : : /**
243 : : * mct_app_filter_is_flatpak_ref_allowed:
244 : : * @filter: an #MctAppFilter
245 : : * @app_ref: flatpak ref for the app, for example `app/org.gnome.Builder/x86_64/master`
246 : : *
247 : : * Check whether the flatpak app with the given @app_ref is allowed to be run
248 : : * according to this app filter.
249 : : *
250 : : * Returns: %TRUE if the user this @filter corresponds to is allowed to run the
251 : : * flatpak called @app_ref according to the @filter policy; %FALSE otherwise
252 : : * Since: 0.2.0
253 : : */
254 : : gboolean
255 : 26 : mct_app_filter_is_flatpak_ref_allowed (MctAppFilter *filter,
256 : : const gchar *app_ref)
257 : : {
258 : 26 : g_return_val_if_fail (filter != NULL, FALSE);
259 : 26 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
260 : 26 : g_return_val_if_fail (app_ref != NULL, FALSE);
261 : 26 : g_return_val_if_fail (is_valid_flatpak_ref (app_ref), FALSE);
262 : :
263 : 26 : gboolean ref_in_list = g_strv_contains ((const gchar * const *) filter->app_list,
264 : : app_ref);
265 : :
266 [ + + - ]: 26 : switch (filter->app_list_type)
267 : : {
268 : 24 : case MCT_APP_FILTER_LIST_BLOCKLIST:
269 : 24 : return !ref_in_list;
270 : 2 : case MCT_APP_FILTER_LIST_ALLOWLIST:
271 : 2 : return ref_in_list;
272 : 0 : default:
273 : : g_assert_not_reached ();
274 : : }
275 : : }
276 : :
277 : : /**
278 : : * mct_app_filter_is_flatpak_app_allowed:
279 : : * @filter: an #MctAppFilter
280 : : * @app_id: flatpak ID for the app, for example `org.gnome.Builder`
281 : : *
282 : : * Check whether the flatpak app with the given @app_id is allowed to be run
283 : : * according to this app filter. This is a globbing match, matching @app_id
284 : : * against potentially multiple entries in the blocklist, as the blocklist
285 : : * contains flatpak refs (for example, `app/org.gnome.Builder/x86_64/master`)
286 : : * which contain architecture and branch information. App IDs (for example,
287 : : * `org.gnome.Builder`) do not contain architecture or branch information.
288 : : *
289 : : * Returns: %TRUE if the user this @filter corresponds to is allowed to run the
290 : : * flatpak called @app_id according to the @filter policy; %FALSE otherwise
291 : : * Since: 0.2.0
292 : : */
293 : : gboolean
294 : 53 : mct_app_filter_is_flatpak_app_allowed (MctAppFilter *filter,
295 : : const gchar *app_id)
296 : : {
297 : 53 : g_return_val_if_fail (filter != NULL, FALSE);
298 : 53 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
299 : 53 : g_return_val_if_fail (app_id != NULL, FALSE);
300 : :
301 : 53 : gsize app_id_len = strlen (app_id);
302 : :
303 : 53 : gboolean id_in_list = FALSE;
304 [ + + ]: 150 : for (gsize i = 0; filter->app_list[i] != NULL; i++)
305 : : {
306 [ + + + - ]: 156 : if (is_valid_flatpak_ref (filter->app_list[i]) &&
307 : 43 : g_str_has_prefix (filter->app_list[i], "app/") &&
308 [ + + ]: 43 : strncmp (filter->app_list[i] + strlen ("app/"), app_id, app_id_len) == 0 &&
309 [ + - ]: 16 : filter->app_list[i][strlen ("app/") + app_id_len] == '/')
310 : : {
311 : 16 : id_in_list = TRUE;
312 : 16 : break;
313 : : }
314 : : }
315 : :
316 [ + + - ]: 53 : switch (filter->app_list_type)
317 : : {
318 : 50 : case MCT_APP_FILTER_LIST_BLOCKLIST:
319 : 50 : return !id_in_list;
320 : 3 : case MCT_APP_FILTER_LIST_ALLOWLIST:
321 : 3 : return id_in_list;
322 : 0 : default:
323 : : g_assert_not_reached ();
324 : : }
325 : : }
326 : :
327 : : /**
328 : : * mct_app_filter_is_appinfo_allowed:
329 : : * @filter: an #MctAppFilter
330 : : * @app_info: (transfer none): application information
331 : : *
332 : : * Check whether the app with the given @app_info is allowed to be run
333 : : * according to this app filter. This matches on multiple keys potentially
334 : : * present in the #GAppInfo, including the path of the executable.
335 : : *
336 : : * Returns: %TRUE if the user this @filter corresponds to is allowed to run the
337 : : * app represented by @app_info according to the @filter policy; %FALSE
338 : : * otherwise
339 : : * Since: 0.2.0
340 : : */
341 : : gboolean
342 : 26 : mct_app_filter_is_appinfo_allowed (MctAppFilter *filter,
343 : : GAppInfo *app_info)
344 : : {
345 : 26 : g_autofree gchar *abs_path = NULL;
346 : 26 : const gchar * const *types = NULL;
347 : :
348 : 26 : g_return_val_if_fail (filter != NULL, FALSE);
349 : 26 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
350 : 26 : g_return_val_if_fail (G_IS_APP_INFO (app_info), FALSE);
351 : :
352 : 26 : abs_path = g_find_program_in_path (g_app_info_get_executable (app_info));
353 : :
354 [ + - + + ]: 52 : if (abs_path != NULL &&
355 : 26 : !mct_app_filter_is_path_allowed (filter, abs_path))
356 : 2 : return FALSE;
357 : :
358 : 24 : types = g_app_info_get_supported_types (app_info);
359 [ + + + + ]: 36 : for (gsize i = 0; types != NULL && types[i] != NULL; i++)
360 : : {
361 [ + + ]: 16 : if (!mct_app_filter_is_content_type_allowed (filter, types[i]))
362 : 4 : return FALSE;
363 : : }
364 : :
365 [ - + + - : 20 : if (G_IS_DESKTOP_APP_INFO (app_info))
+ - + - ]
366 : : {
367 [ + + ]: 20 : g_autofree gchar *flatpak_app = NULL;
368 [ + + ]: 20 : g_autofree gchar *old_flatpak_apps_str = NULL;
369 : :
370 : : /* This gives `org.gnome.Builder`. */
371 : 20 : flatpak_app = g_desktop_app_info_get_string (G_DESKTOP_APP_INFO (app_info), "X-Flatpak");
372 [ + + ]: 20 : if (flatpak_app != NULL)
373 : 10 : flatpak_app = g_strstrip (flatpak_app);
374 : :
375 [ + + + + ]: 30 : if (flatpak_app != NULL &&
376 : 10 : !mct_app_filter_is_flatpak_app_allowed (filter, flatpak_app))
377 : 2 : return FALSE;
378 : :
379 : : /* FIXME: This could do with the g_desktop_app_info_get_string_list() API
380 : : * from GLib 2.60. Gives `gimp.desktop;org.gimp.Gimp.desktop;`. */
381 : 18 : old_flatpak_apps_str = g_desktop_app_info_get_string (G_DESKTOP_APP_INFO (app_info), "X-Flatpak-RenamedFrom");
382 [ + + ]: 18 : if (old_flatpak_apps_str != NULL)
383 : : {
384 [ + + ]: 24 : g_auto(GStrv) old_flatpak_apps = g_strsplit (old_flatpak_apps_str, ";", -1);
385 : :
386 [ + + ]: 26 : for (gsize i = 0; old_flatpak_apps[i] != NULL; i++)
387 : : {
388 : 18 : gchar *old_flatpak_app = g_strstrip (old_flatpak_apps[i]);
389 : :
390 [ + + ]: 18 : if (g_str_has_suffix (old_flatpak_app, ".desktop"))
391 : 4 : old_flatpak_app[strlen (old_flatpak_app) - strlen (".desktop")] = '\0';
392 : 18 : old_flatpak_app = g_strstrip (old_flatpak_app);
393 : :
394 [ + + + + ]: 30 : if (*old_flatpak_app != '\0' &&
395 : 12 : !mct_app_filter_is_flatpak_app_allowed (filter, old_flatpak_app))
396 : 4 : return FALSE;
397 : : }
398 : : }
399 : : }
400 : :
401 : 14 : return TRUE;
402 : : }
403 : :
404 : : /* Check whether a given @content_type is valid.
405 : : *
406 : : * For simplicity this method will only check whether:
407 : : * - the @content_type contains exactly 1 slash char
408 : : * - the @content_type does not start with a slash char
409 : : * - the type and subtype components of the @content_type are not empty
410 : : */
411 : : static gboolean
412 : 58 : is_valid_content_type (const gchar *content_type)
413 : : {
414 : 58 : g_auto(GStrv) parts = NULL;
415 : :
416 [ - + ]: 58 : if (content_type == NULL)
417 : 0 : return FALSE;
418 : :
419 : 58 : parts = g_strsplit (content_type, "/", 0);
420 : 58 : return (g_strv_length (parts) == 2 &&
421 [ + - + - ]: 116 : *parts[0] != '\0' &&
422 [ + - ]: 58 : *parts[1] != '\0');
423 : : }
424 : :
425 : : /**
426 : : * mct_app_filter_is_content_type_allowed:
427 : : * @filter: an #MctAppFilter
428 : : * @content_type: content type to check
429 : : *
430 : : * Check whether apps handling the given @content_type are allowed to be run
431 : : * according to this app filter.
432 : : *
433 : : * Note that this method doesn’t match content subtypes. For example, if
434 : : * `application/xml` is added to the blocklist but `application/xspf+xml` is not,
435 : : * a check for whether `application/xspf+xml` is blocklisted would return false.
436 : : *
437 : : * Returns: %TRUE if the user this @filter corresponds to is allowed to run
438 : : * programs handling @content_type according to the @filter policy;
439 : : * %FALSE otherwise
440 : : * Since: 0.4.0
441 : : */
442 : : gboolean
443 : 44 : mct_app_filter_is_content_type_allowed (MctAppFilter *filter,
444 : : const gchar *content_type)
445 : : {
446 : 44 : g_return_val_if_fail (filter != NULL, FALSE);
447 : 44 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
448 : 44 : g_return_val_if_fail (content_type != NULL, FALSE);
449 : 44 : g_return_val_if_fail (is_valid_content_type (content_type), FALSE);
450 : :
451 : 44 : gboolean ref_in_list = g_strv_contains ((const gchar * const *) filter->app_list,
452 : : content_type);
453 : :
454 [ + + - ]: 44 : switch (filter->app_list_type)
455 : : {
456 : 42 : case MCT_APP_FILTER_LIST_BLOCKLIST:
457 : 42 : return !ref_in_list;
458 : 2 : case MCT_APP_FILTER_LIST_ALLOWLIST:
459 : 2 : return ref_in_list;
460 : 0 : default:
461 : : g_assert_not_reached ();
462 : : }
463 : : }
464 : :
465 : : static gint
466 : 6 : strcmp_cb (gconstpointer a,
467 : : gconstpointer b)
468 : : {
469 : 6 : const gchar *str_a = *((const gchar * const *) a);
470 : 6 : const gchar *str_b = *((const gchar * const *) b);
471 : :
472 : 6 : return g_strcmp0 (str_a, str_b);
473 : : }
474 : :
475 : : /**
476 : : * mct_app_filter_get_oars_sections:
477 : : * @filter: an #MctAppFilter
478 : : *
479 : : * List the OARS sections present in this app filter. The sections are returned
480 : : * in lexicographic order. A section will be listed even if its stored value is
481 : : * %MCT_APP_FILTER_OARS_VALUE_UNKNOWN. The returned list may be empty.
482 : : *
483 : : * Returns: (transfer container) (array zero-terminated=1): %NULL-terminated
484 : : * array of OARS sections
485 : : * Since: 0.2.0
486 : : */
487 : : const gchar **
488 : 13 : mct_app_filter_get_oars_sections (MctAppFilter *filter)
489 : : {
490 : 26 : g_autoptr(GPtrArray) sections = g_ptr_array_new_with_free_func (NULL);
491 : : GVariantIter iter;
492 : : const gchar *oars_section;
493 : :
494 : 13 : g_return_val_if_fail (filter != NULL, NULL);
495 : 13 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
496 : :
497 : 13 : g_variant_iter_init (&iter, filter->oars_ratings);
498 : :
499 [ + + ]: 25 : while (g_variant_iter_loop (&iter, "{&s&s}", &oars_section, NULL))
500 : 12 : g_ptr_array_add (sections, (gpointer) oars_section);
501 : :
502 : : /* Sort alphabetically for easier comparisons later. */
503 : 13 : g_ptr_array_sort (sections, strcmp_cb);
504 : :
505 : 13 : g_ptr_array_add (sections, NULL); /* NULL terminator */
506 : :
507 : 13 : return (const gchar **) g_ptr_array_free (g_steal_pointer (§ions), FALSE);
508 : : }
509 : :
510 : : /**
511 : : * mct_app_filter_get_oars_value:
512 : : * @filter: an #MctAppFilter
513 : : * @oars_section: name of the OARS section to get the value from
514 : : *
515 : : * Get the value assigned to the given @oars_section in the OARS filter stored
516 : : * within @filter. If that section has no value explicitly defined,
517 : : * %MCT_APP_FILTER_OARS_VALUE_UNKNOWN is returned.
518 : : *
519 : : * This value is the most intense value allowed for apps to have in this
520 : : * section, inclusive. Any app with a more intense value for this section must
521 : : * be hidden from the user whose @filter this is.
522 : : *
523 : : * This does not factor in mct_app_filter_is_system_installation_allowed().
524 : : *
525 : : * Returns: an #MctAppFilterOarsValue
526 : : * Since: 0.2.0
527 : : */
528 : : MctAppFilterOarsValue
529 : 43 : mct_app_filter_get_oars_value (MctAppFilter *filter,
530 : : const gchar *oars_section)
531 : : {
532 : : const gchar *value_str;
533 : :
534 : 43 : g_return_val_if_fail (filter != NULL, MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
535 : 43 : g_return_val_if_fail (filter->ref_count >= 1,
536 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
537 : 43 : g_return_val_if_fail (oars_section != NULL && *oars_section != '\0',
538 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
539 : :
540 [ + + ]: 43 : if (!g_variant_lookup (filter->oars_ratings, oars_section, "&s", &value_str))
541 : 26 : return MCT_APP_FILTER_OARS_VALUE_UNKNOWN;
542 : :
543 : 17 : return oars_str_to_enum (value_str);
544 : : }
545 : :
546 : : /**
547 : : * mct_app_filter_is_user_installation_allowed:
548 : : * @filter: an #MctAppFilter
549 : : *
550 : : * Get whether the user is allowed to install to their flatpak user repository.
551 : : * This should be queried in addition to the OARS values
552 : : * (mct_app_filter_get_oars_value()) — if it returns %FALSE, the OARS values
553 : : * should be ignored and app installation should be unconditionally disallowed.
554 : : *
555 : : * Returns: %TRUE if app installation is allowed to the user repository for
556 : : * this user; %FALSE if it is unconditionally disallowed for this user
557 : : * Since: 0.2.0
558 : : */
559 : : gboolean
560 : 17 : mct_app_filter_is_user_installation_allowed (MctAppFilter *filter)
561 : : {
562 : 17 : g_return_val_if_fail (filter != NULL, FALSE);
563 : 17 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
564 : :
565 : 17 : return filter->allow_user_installation;
566 : : }
567 : :
568 : : /**
569 : : * mct_app_filter_is_system_installation_allowed:
570 : : * @filter: an #MctAppFilter
571 : : *
572 : : * Get whether the user is allowed to install to the flatpak system repository.
573 : : * This should be queried in addition to the OARS values
574 : : * (mct_app_filter_get_oars_value()) — if it returns %FALSE, the OARS values
575 : : * should be ignored and app installation should be unconditionally disallowed.
576 : : *
577 : : * Returns: %TRUE if app installation is allowed to the system repository for
578 : : * this user; %FALSE if it is unconditionally disallowed for this user
579 : : * Since: 0.2.0
580 : : */
581 : : gboolean
582 : 17 : mct_app_filter_is_system_installation_allowed (MctAppFilter *filter)
583 : : {
584 : 17 : g_return_val_if_fail (filter != NULL, FALSE);
585 : 17 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
586 : :
587 : 17 : return filter->allow_system_installation;
588 : : }
589 : :
590 : : /**
591 : : * _mct_app_filter_build_app_filter_variant:
592 : : * @filter: an #MctAppFilter
593 : : *
594 : : * Build a #GVariant which contains the app filter from @filter, in the format
595 : : * used for storing it in AccountsService.
596 : : *
597 : : * Returns: (transfer floating): a new, floating #GVariant containing the app
598 : : * filter
599 : : */
600 : : static GVariant *
601 : 10 : _mct_app_filter_build_app_filter_variant (MctAppFilter *filter)
602 : : {
603 : 20 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(bas)"));
604 : :
605 : 10 : g_return_val_if_fail (filter != NULL, NULL);
606 : 10 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
607 : :
608 : 10 : g_variant_builder_add (&builder, "b",
609 : 10 : (filter->app_list_type == MCT_APP_FILTER_LIST_ALLOWLIST));
610 : 10 : g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
611 : :
612 [ + + ]: 18 : for (gsize i = 0; filter->app_list[i] != NULL; i++)
613 : 8 : g_variant_builder_add (&builder, "s", filter->app_list[i]);
614 : :
615 : 10 : g_variant_builder_close (&builder);
616 : :
617 : 10 : return g_variant_builder_end (&builder);
618 : : }
619 : :
620 : : /**
621 : : * mct_app_filter_serialize:
622 : : * @filter: an #MctAppFilter
623 : : *
624 : : * Build a #GVariant which contains the app filter from @filter, in an opaque
625 : : * variant format. This format may change in future, but
626 : : * mct_app_filter_deserialize() is guaranteed to always be able to load any
627 : : * variant produced by the current or any previous version of
628 : : * mct_app_filter_serialize().
629 : : *
630 : : * Returns: (transfer floating): a new, floating #GVariant containing the app
631 : : * filter
632 : : * Since: 0.7.0
633 : : */
634 : : GVariant *
635 : 10 : mct_app_filter_serialize (MctAppFilter *filter)
636 : : {
637 : 20 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{sv}"));
638 : :
639 : 10 : g_return_val_if_fail (filter != NULL, NULL);
640 : 10 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
641 : :
642 : : /* The serialisation format is exactly the
643 : : * `com.endlessm.ParentalControls.AppFilter` D-Bus interface. */
644 : 10 : g_variant_builder_add (&builder, "{sv}", "AppFilter",
645 : : _mct_app_filter_build_app_filter_variant (filter));
646 : 10 : g_variant_builder_add (&builder, "{sv}", "OarsFilter",
647 : : g_variant_new ("(s@a{ss})", "oars-1.1",
648 : : filter->oars_ratings));
649 : 10 : g_variant_builder_add (&builder, "{sv}", "AllowUserInstallation",
650 : : g_variant_new_boolean (filter->allow_user_installation));
651 : 10 : g_variant_builder_add (&builder, "{sv}", "AllowSystemInstallation",
652 : : g_variant_new_boolean (filter->allow_system_installation));
653 : :
654 : 10 : return g_variant_builder_end (&builder);
655 : : }
656 : :
657 : : /**
658 : : * mct_app_filter_deserialize:
659 : : * @variant: a serialized app filter variant
660 : : * @user_id: the ID of the user the app filter relates to
661 : : * @error: return location for a #GError, or %NULL
662 : : *
663 : : * Deserialize an app filter previously serialized with
664 : : * mct_app_filter_serialize(). This function guarantees to be able to
665 : : * deserialize any serialized form from this version or older versions of
666 : : * libmalcontent.
667 : : *
668 : : * If deserialization fails, %MCT_MANAGER_ERROR_INVALID_DATA will be returned.
669 : : *
670 : : * Returns: (transfer full): deserialized app filter
671 : : * Since: 0.7.0
672 : : */
673 : : MctAppFilter *
674 : 61 : mct_app_filter_deserialize (GVariant *variant,
675 : : uid_t user_id,
676 : : GError **error)
677 : : {
678 : : gboolean is_allowlist;
679 : 61 : g_auto(GStrv) app_list = NULL;
680 : : const gchar *content_rating_kind;
681 : 61 : g_autoptr(GVariant) oars_variant = NULL;
682 : : gboolean allow_user_installation;
683 : : gboolean allow_system_installation;
684 : 61 : g_autoptr(MctAppFilter) app_filter = NULL;
685 : :
686 : 61 : g_return_val_if_fail (variant != NULL, NULL);
687 : 61 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
688 : :
689 : : /* Check the overall type. */
690 [ + + ]: 61 : if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}")))
691 : : {
692 : 4 : g_set_error (error, MCT_MANAGER_ERROR,
693 : : MCT_MANAGER_ERROR_INVALID_DATA,
694 : : _("App filter for user %u was in an unrecognized format"),
695 : : (guint) user_id);
696 : 4 : return NULL;
697 : : }
698 : :
699 : : /* Extract the properties we care about. The default values here should be
700 : : * kept in sync with those in the `com.endlessm.ParentalControls.AppFilter`
701 : : * D-Bus interface. */
702 [ + + ]: 57 : if (!g_variant_lookup (variant, "AppFilter", "(b^as)",
703 : : &is_allowlist, &app_list))
704 : : {
705 : : /* Default value. */
706 : 40 : is_allowlist = FALSE;
707 : 40 : app_list = g_new0 (gchar *, 1);
708 : : }
709 : :
710 [ + + ]: 57 : if (!g_variant_lookup (variant, "OarsFilter", "(&s@a{ss})",
711 : : &content_rating_kind, &oars_variant))
712 : : {
713 : : /* Default value. */
714 : 33 : content_rating_kind = "oars-1.1";
715 : 33 : oars_variant = g_variant_new ("a{ss}", NULL);
716 : : }
717 : :
718 : : /* Check that the OARS filter is in a format we support. Currently, that’s
719 : : * only oars-1.0 and oars-1.1. */
720 [ + - + + ]: 114 : if (!g_str_equal (content_rating_kind, "oars-1.0") &&
721 : 57 : !g_str_equal (content_rating_kind, "oars-1.1"))
722 : : {
723 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
724 : : MCT_MANAGER_ERROR_INVALID_DATA,
725 : : _("OARS filter for user %u has an unrecognized kind ‘%s’"),
726 : : (guint) user_id, content_rating_kind);
727 : 2 : return NULL;
728 : : }
729 : :
730 [ + + ]: 55 : if (!g_variant_lookup (variant, "AllowUserInstallation", "b",
731 : : &allow_user_installation))
732 : : {
733 : : /* Default value. */
734 : 43 : allow_user_installation = TRUE;
735 : : }
736 : :
737 [ + + ]: 55 : if (!g_variant_lookup (variant, "AllowSystemInstallation", "b",
738 : : &allow_system_installation))
739 : : {
740 : : /* Default value. */
741 : 43 : allow_system_installation = FALSE;
742 : : }
743 : :
744 : : /* Success. Create an #MctAppFilter object to contain the results. */
745 : 55 : app_filter = g_new0 (MctAppFilter, 1);
746 : 55 : app_filter->ref_count = 1;
747 : 55 : app_filter->user_id = user_id;
748 : 55 : app_filter->app_list = g_steal_pointer (&app_list);
749 : 55 : app_filter->app_list_type =
750 : 55 : is_allowlist ? MCT_APP_FILTER_LIST_ALLOWLIST : MCT_APP_FILTER_LIST_BLOCKLIST;
751 : 55 : app_filter->oars_ratings = g_steal_pointer (&oars_variant);
752 : 55 : app_filter->allow_user_installation = allow_user_installation;
753 : 55 : app_filter->allow_system_installation = allow_system_installation;
754 : :
755 : 55 : return g_steal_pointer (&app_filter);
756 : : }
757 : :
758 : : /**
759 : : * mct_app_filter_equal:
760 : : * @a: (not nullable): an #MctAppFilter
761 : : * @b: (not nullable): an #MctAppFilter
762 : : *
763 : : * Check whether app filters @a and @b are equal.
764 : : *
765 : : * Returns: %TRUE if @a and @b are equal, %FALSE otherwise
766 : : * Since: 0.10.0
767 : : */
768 : : gboolean
769 : 78 : mct_app_filter_equal (MctAppFilter *a,
770 : : MctAppFilter *b)
771 : : {
772 : 78 : g_return_val_if_fail (a != NULL, FALSE);
773 : 78 : g_return_val_if_fail (a->ref_count >= 1, FALSE);
774 : 78 : g_return_val_if_fail (b != NULL, FALSE);
775 : 78 : g_return_val_if_fail (b->ref_count >= 1, FALSE);
776 : :
777 : 136 : return (a->user_id == b->user_id &&
778 [ + + ]: 58 : a->app_list_type == b->app_list_type &&
779 [ + - ]: 42 : a->allow_user_installation == b->allow_user_installation &&
780 [ + + + + ]: 72 : a->allow_system_installation == b->allow_system_installation &&
781 [ + + + + ]: 166 : g_strv_equal ((const gchar * const *) a->app_list, (const gchar * const *) b->app_list) &&
782 : 22 : g_variant_equal (a->oars_ratings, b->oars_ratings));
783 : : }
784 : :
785 : : /*
786 : : * Actual implementation of #MctAppFilterBuilder.
787 : : *
788 : : * All members are %NULL if un-initialised, cleared, or ended.
789 : : */
790 : : typedef struct
791 : : {
792 : : GPtrArray *blocklist; /* (nullable) (owned) (element-type utf8) */
793 : : GHashTable *oars; /* (nullable) (owned) (element-type utf8 MctAppFilterOarsValue) */
794 : : gboolean allow_user_installation;
795 : : gboolean allow_system_installation;
796 : :
797 : : /*< private >*/
798 : : gpointer padding[2];
799 : : } MctAppFilterBuilderReal;
800 : :
801 : : G_STATIC_ASSERT (sizeof (MctAppFilterBuilderReal) ==
802 : : sizeof (MctAppFilterBuilder));
803 : : G_STATIC_ASSERT (__alignof__ (MctAppFilterBuilderReal) ==
804 : : __alignof__ (MctAppFilterBuilder));
805 : :
806 [ + - + - : 6 : G_DEFINE_BOXED_TYPE (MctAppFilterBuilder, mct_app_filter_builder,
+ - ]
807 : : mct_app_filter_builder_copy, mct_app_filter_builder_free)
808 : :
809 : : /**
810 : : * mct_app_filter_builder_init:
811 : : * @builder: an uninitialised #MctAppFilterBuilder
812 : : *
813 : : * Initialise the given @builder so it can be used to construct a new
814 : : * #MctAppFilter. @builder must have been allocated on the stack, and must not
815 : : * already be initialised.
816 : : *
817 : : * Construct the #MctAppFilter by calling methods on @builder, followed by
818 : : * mct_app_filter_builder_end(). To abort construction, use
819 : : * mct_app_filter_builder_clear().
820 : : *
821 : : * Since: 0.2.0
822 : : */
823 : : void
824 : 20 : mct_app_filter_builder_init (MctAppFilterBuilder *builder)
825 : : {
826 : 20 : MctAppFilterBuilder local_builder = MCT_APP_FILTER_BUILDER_INIT ();
827 : 20 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
828 : :
829 : 20 : g_return_if_fail (_builder != NULL);
830 : 20 : g_return_if_fail (_builder->blocklist == NULL);
831 : 20 : g_return_if_fail (_builder->oars == NULL);
832 : :
833 : 20 : memcpy (builder, &local_builder, sizeof (local_builder));
834 : : }
835 : :
836 : : /**
837 : : * mct_app_filter_builder_clear:
838 : : * @builder: an #MctAppFilterBuilder
839 : : *
840 : : * Clear @builder, freeing any internal state in it. This will not free the
841 : : * top-level storage for @builder itself, which is assumed to be allocated on
842 : : * the stack.
843 : : *
844 : : * If called on an already-cleared #MctAppFilterBuilder, this function is
845 : : * idempotent.
846 : : *
847 : : * Since: 0.2.0
848 : : */
849 : : void
850 : 78 : mct_app_filter_builder_clear (MctAppFilterBuilder *builder)
851 : : {
852 : 78 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
853 : :
854 : 78 : g_return_if_fail (_builder != NULL);
855 : :
856 [ + + ]: 78 : g_clear_pointer (&_builder->blocklist, g_ptr_array_unref);
857 [ + + ]: 78 : g_clear_pointer (&_builder->oars, g_hash_table_unref);
858 : : }
859 : :
860 : : /**
861 : : * mct_app_filter_builder_new:
862 : : *
863 : : * Construct a new #MctAppFilterBuilder on the heap. This is intended for
864 : : * language bindings. The returned builder must eventually be freed with
865 : : * mct_app_filter_builder_free(), but can be cleared zero or more times with
866 : : * mct_app_filter_builder_clear() first.
867 : : *
868 : : * Returns: (transfer full): a new heap-allocated #MctAppFilterBuilder
869 : : * Since: 0.2.0
870 : : */
871 : : MctAppFilterBuilder *
872 : 12 : mct_app_filter_builder_new (void)
873 : : {
874 : 12 : g_autoptr(MctAppFilterBuilder) builder = NULL;
875 : :
876 : 12 : builder = g_new0 (MctAppFilterBuilder, 1);
877 : 12 : mct_app_filter_builder_init (builder);
878 : :
879 : 12 : return g_steal_pointer (&builder);
880 : : }
881 : :
882 : : /**
883 : : * mct_app_filter_builder_copy:
884 : : * @builder: an #MctAppFilterBuilder
885 : : *
886 : : * Copy the given @builder to a newly-allocated #MctAppFilterBuilder on the
887 : : * heap. This is safe to use with cleared, stack-allocated
888 : : * #MctAppFilterBuilders.
889 : : *
890 : : * Returns: (transfer full): a copy of @builder
891 : : * Since: 0.2.0
892 : : */
893 : : MctAppFilterBuilder *
894 : 4 : mct_app_filter_builder_copy (MctAppFilterBuilder *builder)
895 : : {
896 : 4 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
897 : 4 : g_autoptr(MctAppFilterBuilder) copy = NULL;
898 : : MctAppFilterBuilderReal *_copy;
899 : :
900 : 4 : g_return_val_if_fail (builder != NULL, NULL);
901 : :
902 : 4 : copy = mct_app_filter_builder_new ();
903 : 4 : _copy = (MctAppFilterBuilderReal *) copy;
904 : :
905 : 4 : mct_app_filter_builder_clear (copy);
906 [ + + ]: 4 : if (_builder->blocklist != NULL)
907 : 2 : _copy->blocklist = g_ptr_array_ref (_builder->blocklist);
908 [ + + ]: 4 : if (_builder->oars != NULL)
909 : 2 : _copy->oars = g_hash_table_ref (_builder->oars);
910 : 4 : _copy->allow_user_installation = _builder->allow_user_installation;
911 : 4 : _copy->allow_system_installation = _builder->allow_system_installation;
912 : :
913 : 4 : return g_steal_pointer (©);
914 : : }
915 : :
916 : : /**
917 : : * mct_app_filter_builder_free:
918 : : * @builder: a heap-allocated #MctAppFilterBuilder
919 : : *
920 : : * Free an #MctAppFilterBuilder originally allocated using
921 : : * mct_app_filter_builder_new(). This must not be called on stack-allocated
922 : : * builders initialised using mct_app_filter_builder_init().
923 : : *
924 : : * Since: 0.2.0
925 : : */
926 : : void
927 : 12 : mct_app_filter_builder_free (MctAppFilterBuilder *builder)
928 : : {
929 : 12 : g_return_if_fail (builder != NULL);
930 : :
931 : 12 : mct_app_filter_builder_clear (builder);
932 : 12 : g_free (builder);
933 : : }
934 : :
935 : : /**
936 : : * mct_app_filter_builder_end:
937 : : * @builder: an initialised #MctAppFilterBuilder
938 : : *
939 : : * Finish constructing an #MctAppFilter with the given @builder, and return it.
940 : : * The #MctAppFilterBuilder will be cleared as if mct_app_filter_builder_clear()
941 : : * had been called.
942 : : *
943 : : * Returns: (transfer full): a newly constructed #MctAppFilter
944 : : * Since: 0.2.0
945 : : */
946 : : MctAppFilter *
947 : 35 : mct_app_filter_builder_end (MctAppFilterBuilder *builder)
948 : : {
949 : 35 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
950 : 35 : g_autoptr(MctAppFilter) app_filter = NULL;
951 : 70 : g_auto(GVariantBuilder) oars_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
952 : : GHashTableIter iter;
953 : : gpointer key, value;
954 : 35 : g_autoptr(GVariant) oars_variant = NULL;
955 : :
956 : 35 : g_return_val_if_fail (_builder != NULL, NULL);
957 : 35 : g_return_val_if_fail (_builder->blocklist != NULL, NULL);
958 : 35 : g_return_val_if_fail (_builder->oars != NULL, NULL);
959 : :
960 : : /* Ensure the paths list is %NULL-terminated. */
961 : 35 : g_ptr_array_add (_builder->blocklist, NULL);
962 : :
963 : : /* Build the OARS variant. */
964 : 35 : g_hash_table_iter_init (&iter, _builder->oars);
965 [ + + ]: 49 : while (g_hash_table_iter_next (&iter, &key, &value))
966 : : {
967 : 14 : const gchar *oars_section = key;
968 : 14 : MctAppFilterOarsValue oars_value = GPOINTER_TO_INT (value);
969 : 14 : const gchar *oars_value_strs[] =
970 : : {
971 : : NULL, /* MCT_APP_FILTER_OARS_VALUE_UNKNOWN */
972 : : "none",
973 : : "mild",
974 : : "moderate",
975 : : "intense",
976 : : };
977 : :
978 : 14 : g_assert ((int) oars_value >= 0 &&
979 : : (int) oars_value < (int) G_N_ELEMENTS (oars_value_strs));
980 : :
981 [ + - ]: 14 : if (oars_value_strs[oars_value] != NULL)
982 : 14 : g_variant_builder_add (&oars_builder, "{ss}",
983 : : oars_section, oars_value_strs[oars_value]);
984 : : }
985 : :
986 : 35 : oars_variant = g_variant_ref_sink (g_variant_builder_end (&oars_builder));
987 : :
988 : : /* Build the #MctAppFilter. */
989 : 35 : app_filter = g_new0 (MctAppFilter, 1);
990 : 35 : app_filter->ref_count = 1;
991 : 35 : app_filter->user_id = -1;
992 : 35 : app_filter->app_list = (gchar **) g_ptr_array_free (g_steal_pointer (&_builder->blocklist), FALSE);
993 : 35 : app_filter->app_list_type = MCT_APP_FILTER_LIST_BLOCKLIST;
994 : 35 : app_filter->oars_ratings = g_steal_pointer (&oars_variant);
995 : 35 : app_filter->allow_user_installation = _builder->allow_user_installation;
996 : 35 : app_filter->allow_system_installation = _builder->allow_system_installation;
997 : :
998 : 35 : mct_app_filter_builder_clear (builder);
999 : :
1000 : 35 : return g_steal_pointer (&app_filter);
1001 : : }
1002 : :
1003 : : /**
1004 : : * mct_app_filter_builder_blocklist_path:
1005 : : * @builder: an initialised #MctAppFilterBuilder
1006 : : * @path: (type filename): an absolute path to blocklist
1007 : : *
1008 : : * Add @path to the blocklist of app paths in the filter under construction. It
1009 : : * will be canonicalised (without doing any I/O) before being added.
1010 : : * The canonicalised @path will not be added again if it’s already been added.
1011 : : *
1012 : : * Since: 0.2.0
1013 : : */
1014 : : void
1015 : 22 : mct_app_filter_builder_blocklist_path (MctAppFilterBuilder *builder,
1016 : : const gchar *path)
1017 : : {
1018 : 22 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1019 : :
1020 : 22 : g_return_if_fail (_builder != NULL);
1021 : 22 : g_return_if_fail (_builder->blocklist != NULL);
1022 : 22 : g_return_if_fail (path != NULL);
1023 : 22 : g_return_if_fail (g_path_is_absolute (path));
1024 : :
1025 [ + - ]: 44 : g_autofree gchar *canonical_path = g_canonicalize_filename (path, "/");
1026 [ + - ]: 44 : g_autofree gchar *canonical_path_utf8 = g_filename_to_utf8 (canonical_path, -1,
1027 : : NULL, NULL, NULL);
1028 : 22 : g_return_if_fail (canonical_path_utf8 != NULL);
1029 : :
1030 [ + - ]: 22 : if (!g_ptr_array_find_with_equal_func (_builder->blocklist,
1031 : : canonical_path_utf8, g_str_equal, NULL))
1032 : 22 : g_ptr_array_add (_builder->blocklist, g_steal_pointer (&canonical_path_utf8));
1033 : : }
1034 : :
1035 : : /**
1036 : : * mct_app_filter_builder_blocklist_flatpak_ref:
1037 : : * @builder: an initialised #MctAppFilterBuilder
1038 : : * @app_ref: a flatpak app ref to blocklist
1039 : : *
1040 : : * Add @app_ref to the blocklist of flatpak refs in the filter under
1041 : : * construction. The @app_ref will not be added again if it’s already been
1042 : : * added.
1043 : : *
1044 : : * Since: 0.2.0
1045 : : */
1046 : : void
1047 : 10 : mct_app_filter_builder_blocklist_flatpak_ref (MctAppFilterBuilder *builder,
1048 : : const gchar *app_ref)
1049 : : {
1050 : 10 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1051 : :
1052 : 10 : g_return_if_fail (_builder != NULL);
1053 : 10 : g_return_if_fail (_builder->blocklist != NULL);
1054 : 10 : g_return_if_fail (app_ref != NULL);
1055 : 10 : g_return_if_fail (is_valid_flatpak_ref (app_ref));
1056 : :
1057 [ + - ]: 10 : if (!g_ptr_array_find_with_equal_func (_builder->blocklist,
1058 : : app_ref, g_str_equal, NULL))
1059 : 10 : g_ptr_array_add (_builder->blocklist, g_strdup (app_ref));
1060 : : }
1061 : :
1062 : : /**
1063 : : * mct_app_filter_builder_blocklist_content_type:
1064 : : * @builder: an initialised #MctAppFilterBuilder
1065 : : * @content_type: a content type to blocklist
1066 : : *
1067 : : * Add @content_type to the blocklist of content types in the filter under
1068 : : * construction. The @content_type will not be added again if it’s already been
1069 : : * added.
1070 : : *
1071 : : * Note that this method doesn’t handle content subtypes. For example, if
1072 : : * `application/xml` is added to the blocklist but `application/xspf+xml` is not,
1073 : : * a check for whether `application/xspf+xml` is blocklisted would return false.
1074 : : *
1075 : : * Since: 0.4.0
1076 : : */
1077 : : void
1078 : 14 : mct_app_filter_builder_blocklist_content_type (MctAppFilterBuilder *builder,
1079 : : const gchar *content_type)
1080 : : {
1081 : 14 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1082 : :
1083 : 14 : g_return_if_fail (_builder != NULL);
1084 : 14 : g_return_if_fail (_builder->blocklist != NULL);
1085 : 14 : g_return_if_fail (content_type != NULL);
1086 : 14 : g_return_if_fail (is_valid_content_type (content_type));
1087 : :
1088 [ + - ]: 14 : if (!g_ptr_array_find_with_equal_func (_builder->blocklist,
1089 : : content_type, g_str_equal, NULL))
1090 : 14 : g_ptr_array_add (_builder->blocklist, g_strdup (content_type));
1091 : : }
1092 : :
1093 : : /**
1094 : : * mct_app_filter_builder_set_oars_value:
1095 : : * @builder: an initialised #MctAppFilterBuilder
1096 : : * @oars_section: name of the OARS section to set the value for
1097 : : * @value: value to set for the @oars_section
1098 : : *
1099 : : * Set the OARS value for the given @oars_section, indicating the intensity of
1100 : : * content covered by that section which the user is allowed to see (inclusive).
1101 : : * Any apps which have more intense content in this section should not be usable
1102 : : * by the user.
1103 : : *
1104 : : * Since: 0.2.0
1105 : : */
1106 : : void
1107 : 14 : mct_app_filter_builder_set_oars_value (MctAppFilterBuilder *builder,
1108 : : const gchar *oars_section,
1109 : : MctAppFilterOarsValue value)
1110 : : {
1111 : 14 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1112 : :
1113 : 14 : g_return_if_fail (_builder != NULL);
1114 : 14 : g_return_if_fail (_builder->oars != NULL);
1115 : 14 : g_return_if_fail (oars_section != NULL && *oars_section != '\0');
1116 : :
1117 : 14 : g_hash_table_insert (_builder->oars, g_strdup (oars_section),
1118 : 14 : GUINT_TO_POINTER (value));
1119 : : }
1120 : :
1121 : : /**
1122 : : * mct_app_filter_builder_set_allow_user_installation:
1123 : : * @builder: an initialised #MctAppFilterBuilder
1124 : : * @allow_user_installation: %TRUE to allow app installation; %FALSE to
1125 : : * unconditionally disallow it
1126 : : *
1127 : : * Set whether the user is allowed to install to their flatpak user repository.
1128 : : * If this is %TRUE, app installation is still subject to the OARS values
1129 : : * (mct_app_filter_builder_set_oars_value()). If it is %FALSE, app installation
1130 : : * is unconditionally disallowed for this user.
1131 : : *
1132 : : * Since: 0.2.0
1133 : : */
1134 : : void
1135 : 10 : mct_app_filter_builder_set_allow_user_installation (MctAppFilterBuilder *builder,
1136 : : gboolean allow_user_installation)
1137 : : {
1138 : 10 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1139 : :
1140 : 10 : g_return_if_fail (_builder != NULL);
1141 : :
1142 : 10 : _builder->allow_user_installation = allow_user_installation;
1143 : : }
1144 : :
1145 : : /**
1146 : : * mct_app_filter_builder_set_allow_system_installation:
1147 : : * @builder: an initialised #MctAppFilterBuilder
1148 : : * @allow_system_installation: %TRUE to allow app installation; %FALSE to
1149 : : * unconditionally disallow it
1150 : : *
1151 : : * Set whether the user is allowed to install to the flatpak system repository.
1152 : : * If this is %TRUE, app installation is still subject to the OARS values
1153 : : * (mct_app_filter_builder_set_oars_value()). If it is %FALSE, app installation
1154 : : * is unconditionally disallowed for this user.
1155 : : *
1156 : : * Since: 0.2.0
1157 : : */
1158 : : void
1159 : 10 : mct_app_filter_builder_set_allow_system_installation (MctAppFilterBuilder *builder,
1160 : : gboolean allow_system_installation)
1161 : : {
1162 : 10 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1163 : :
1164 : 10 : g_return_if_fail (_builder != NULL);
1165 : :
1166 : 10 : _builder->allow_system_installation = allow_system_installation;
1167 : : }
|