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 [ + + ]: 113 : 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 : : const char *exec;
346 : 26 : g_autofree gchar *abs_path = NULL;
347 : 26 : const gchar * const *types = NULL;
348 : :
349 : 26 : g_return_val_if_fail (filter != NULL, FALSE);
350 : 26 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
351 : 26 : g_return_val_if_fail (G_IS_APP_INFO (app_info), FALSE);
352 : :
353 : 26 : exec = g_app_info_get_executable (app_info);
354 [ + - ]: 26 : abs_path = (exec != NULL) ? g_find_program_in_path (exec) : NULL;
355 : :
356 [ + - + + ]: 52 : if (abs_path != NULL &&
357 : 26 : !mct_app_filter_is_path_allowed (filter, abs_path))
358 : 2 : return FALSE;
359 : :
360 : 24 : types = g_app_info_get_supported_types (app_info);
361 [ + + + + ]: 36 : for (gsize i = 0; types != NULL && types[i] != NULL; i++)
362 : : {
363 [ + + ]: 16 : if (!mct_app_filter_is_content_type_allowed (filter, types[i]))
364 : 4 : return FALSE;
365 : : }
366 : :
367 [ - + + - : 20 : if (G_IS_DESKTOP_APP_INFO (app_info))
+ - + - ]
368 : : {
369 [ + + ]: 20 : g_autofree gchar *flatpak_app = NULL;
370 [ + + ]: 20 : g_autofree gchar *old_flatpak_apps_str = NULL;
371 : :
372 : : /* This gives `org.gnome.Builder`. */
373 : 20 : flatpak_app = g_desktop_app_info_get_string (G_DESKTOP_APP_INFO (app_info), "X-Flatpak");
374 [ + + ]: 20 : if (flatpak_app != NULL)
375 : 10 : flatpak_app = g_strstrip (flatpak_app);
376 : :
377 [ + + + + ]: 30 : if (flatpak_app != NULL &&
378 : 10 : !mct_app_filter_is_flatpak_app_allowed (filter, flatpak_app))
379 : 2 : return FALSE;
380 : :
381 : : /* FIXME: This could do with the g_desktop_app_info_get_string_list() API
382 : : * from GLib 2.60. Gives `gimp.desktop;org.gimp.Gimp.desktop;`. */
383 : 18 : old_flatpak_apps_str = g_desktop_app_info_get_string (G_DESKTOP_APP_INFO (app_info), "X-Flatpak-RenamedFrom");
384 [ + + ]: 18 : if (old_flatpak_apps_str != NULL)
385 : : {
386 [ + + ]: 24 : g_auto(GStrv) old_flatpak_apps = g_strsplit (old_flatpak_apps_str, ";", -1);
387 : :
388 [ + + ]: 26 : for (gsize i = 0; old_flatpak_apps[i] != NULL; i++)
389 : : {
390 : 18 : gchar *old_flatpak_app = g_strstrip (old_flatpak_apps[i]);
391 : :
392 [ + - - + : 18 : if (g_str_has_suffix (old_flatpak_app, ".desktop"))
+ + + + ]
393 : 4 : old_flatpak_app[strlen (old_flatpak_app) - strlen (".desktop")] = '\0';
394 : 18 : old_flatpak_app = g_strstrip (old_flatpak_app);
395 : :
396 [ + + + + ]: 30 : if (*old_flatpak_app != '\0' &&
397 : 12 : !mct_app_filter_is_flatpak_app_allowed (filter, old_flatpak_app))
398 : 4 : return FALSE;
399 : : }
400 : : }
401 : : }
402 : :
403 : 14 : return TRUE;
404 : : }
405 : :
406 : : /* Check whether a given @content_type is valid.
407 : : *
408 : : * For simplicity this method will only check whether:
409 : : * - the @content_type contains exactly 1 slash char
410 : : * - the @content_type does not start with a slash char
411 : : * - the type and subtype components of the @content_type are not empty
412 : : */
413 : : static gboolean
414 : 58 : is_valid_content_type (const gchar *content_type)
415 : : {
416 : 58 : g_auto(GStrv) parts = NULL;
417 : :
418 [ - + ]: 58 : if (content_type == NULL)
419 : 0 : return FALSE;
420 : :
421 : 58 : parts = g_strsplit (content_type, "/", 0);
422 : 58 : return (g_strv_length (parts) == 2 &&
423 [ + - + - ]: 116 : *parts[0] != '\0' &&
424 [ + - ]: 58 : *parts[1] != '\0');
425 : : }
426 : :
427 : : /**
428 : : * mct_app_filter_is_content_type_allowed:
429 : : * @filter: an #MctAppFilter
430 : : * @content_type: content type to check
431 : : *
432 : : * Check whether apps handling the given @content_type are allowed to be run
433 : : * according to this app filter.
434 : : *
435 : : * Note that this method doesn’t match content subtypes. For example, if
436 : : * `application/xml` is added to the blocklist but `application/xspf+xml` is not,
437 : : * a check for whether `application/xspf+xml` is blocklisted would return false.
438 : : *
439 : : * Returns: %TRUE if the user this @filter corresponds to is allowed to run
440 : : * programs handling @content_type according to the @filter policy;
441 : : * %FALSE otherwise
442 : : * Since: 0.4.0
443 : : */
444 : : gboolean
445 : 44 : mct_app_filter_is_content_type_allowed (MctAppFilter *filter,
446 : : const gchar *content_type)
447 : : {
448 : 44 : g_return_val_if_fail (filter != NULL, FALSE);
449 : 44 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
450 : 44 : g_return_val_if_fail (content_type != NULL, FALSE);
451 : 44 : g_return_val_if_fail (is_valid_content_type (content_type), FALSE);
452 : :
453 : 44 : gboolean ref_in_list = g_strv_contains ((const gchar * const *) filter->app_list,
454 : : content_type);
455 : :
456 [ + + - ]: 44 : switch (filter->app_list_type)
457 : : {
458 : 42 : case MCT_APP_FILTER_LIST_BLOCKLIST:
459 : 42 : return !ref_in_list;
460 : 2 : case MCT_APP_FILTER_LIST_ALLOWLIST:
461 : 2 : return ref_in_list;
462 : 0 : default:
463 : : g_assert_not_reached ();
464 : : }
465 : : }
466 : :
467 : : static gint
468 : 6 : strcmp_cb (gconstpointer a,
469 : : gconstpointer b)
470 : : {
471 : 6 : const gchar *str_a = *((const gchar * const *) a);
472 : 6 : const gchar *str_b = *((const gchar * const *) b);
473 : :
474 : 6 : return g_strcmp0 (str_a, str_b);
475 : : }
476 : :
477 : : /**
478 : : * mct_app_filter_get_oars_sections:
479 : : * @filter: an #MctAppFilter
480 : : *
481 : : * List the OARS sections present in this app filter. The sections are returned
482 : : * in lexicographic order. A section will be listed even if its stored value is
483 : : * %MCT_APP_FILTER_OARS_VALUE_UNKNOWN. The returned list may be empty.
484 : : *
485 : : * Returns: (transfer container) (array zero-terminated=1): %NULL-terminated
486 : : * array of OARS sections
487 : : * Since: 0.2.0
488 : : */
489 : : const gchar **
490 : 13 : mct_app_filter_get_oars_sections (MctAppFilter *filter)
491 : : {
492 : 26 : g_autoptr(GPtrArray) sections = g_ptr_array_new_with_free_func (NULL);
493 : : GVariantIter iter;
494 : : const gchar *oars_section;
495 : :
496 : 13 : g_return_val_if_fail (filter != NULL, NULL);
497 : 13 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
498 : :
499 : 13 : g_variant_iter_init (&iter, filter->oars_ratings);
500 : :
501 [ + + ]: 25 : while (g_variant_iter_loop (&iter, "{&s&s}", &oars_section, NULL))
502 : 12 : g_ptr_array_add (sections, (gpointer) oars_section);
503 : :
504 : : /* Sort alphabetically for easier comparisons later. */
505 : 13 : g_ptr_array_sort (sections, strcmp_cb);
506 : :
507 : 13 : g_ptr_array_add (sections, NULL); /* NULL terminator */
508 : :
509 : 13 : return (const gchar **) g_ptr_array_free (g_steal_pointer (§ions), FALSE);
510 : : }
511 : :
512 : : /**
513 : : * mct_app_filter_get_oars_value:
514 : : * @filter: an #MctAppFilter
515 : : * @oars_section: name of the OARS section to get the value from
516 : : *
517 : : * Get the value assigned to the given @oars_section in the OARS filter stored
518 : : * within @filter. If that section has no value explicitly defined,
519 : : * %MCT_APP_FILTER_OARS_VALUE_UNKNOWN is returned.
520 : : *
521 : : * This value is the most intense value allowed for apps to have in this
522 : : * section, inclusive. Any app with a more intense value for this section must
523 : : * be hidden from the user whose @filter this is.
524 : : *
525 : : * This does not factor in mct_app_filter_is_system_installation_allowed().
526 : : *
527 : : * Returns: an #MctAppFilterOarsValue
528 : : * Since: 0.2.0
529 : : */
530 : : MctAppFilterOarsValue
531 : 43 : mct_app_filter_get_oars_value (MctAppFilter *filter,
532 : : const gchar *oars_section)
533 : : {
534 : : const gchar *value_str;
535 : :
536 : 43 : g_return_val_if_fail (filter != NULL, MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
537 : 43 : g_return_val_if_fail (filter->ref_count >= 1,
538 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
539 : 43 : g_return_val_if_fail (oars_section != NULL && *oars_section != '\0',
540 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
541 : :
542 [ + + ]: 43 : if (!g_variant_lookup (filter->oars_ratings, oars_section, "&s", &value_str))
543 : 26 : return MCT_APP_FILTER_OARS_VALUE_UNKNOWN;
544 : :
545 : 17 : return oars_str_to_enum (value_str);
546 : : }
547 : :
548 : : /**
549 : : * mct_app_filter_is_user_installation_allowed:
550 : : * @filter: an #MctAppFilter
551 : : *
552 : : * Get whether the user is allowed to install to their flatpak user repository.
553 : : * This should be queried in addition to the OARS values
554 : : * (mct_app_filter_get_oars_value()) — if it returns %FALSE, the OARS values
555 : : * should be ignored and app installation should be unconditionally disallowed.
556 : : *
557 : : * Returns: %TRUE if app installation is allowed to the user repository for
558 : : * this user; %FALSE if it is unconditionally disallowed for this user
559 : : * Since: 0.2.0
560 : : */
561 : : gboolean
562 : 17 : mct_app_filter_is_user_installation_allowed (MctAppFilter *filter)
563 : : {
564 : 17 : g_return_val_if_fail (filter != NULL, FALSE);
565 : 17 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
566 : :
567 : 17 : return filter->allow_user_installation;
568 : : }
569 : :
570 : : /**
571 : : * mct_app_filter_is_system_installation_allowed:
572 : : * @filter: an #MctAppFilter
573 : : *
574 : : * Get whether the user is allowed to install to the flatpak system repository.
575 : : * This should be queried in addition to the OARS values
576 : : * (mct_app_filter_get_oars_value()) — if it returns %FALSE, the OARS values
577 : : * should be ignored and app installation should be unconditionally disallowed.
578 : : *
579 : : * Returns: %TRUE if app installation is allowed to the system repository for
580 : : * this user; %FALSE if it is unconditionally disallowed for this user
581 : : * Since: 0.2.0
582 : : */
583 : : gboolean
584 : 17 : mct_app_filter_is_system_installation_allowed (MctAppFilter *filter)
585 : : {
586 : 17 : g_return_val_if_fail (filter != NULL, FALSE);
587 : 17 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
588 : :
589 : 17 : return filter->allow_system_installation;
590 : : }
591 : :
592 : : /**
593 : : * _mct_app_filter_build_app_filter_variant:
594 : : * @filter: an #MctAppFilter
595 : : *
596 : : * Build a #GVariant which contains the app filter from @filter, in the format
597 : : * used for storing it in AccountsService.
598 : : *
599 : : * Returns: (transfer floating): a new, floating #GVariant containing the app
600 : : * filter
601 : : */
602 : : static GVariant *
603 : 10 : _mct_app_filter_build_app_filter_variant (MctAppFilter *filter)
604 : : {
605 : 20 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(bas)"));
606 : :
607 : 10 : g_return_val_if_fail (filter != NULL, NULL);
608 : 10 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
609 : :
610 : 10 : g_variant_builder_add (&builder, "b",
611 : 10 : (filter->app_list_type == MCT_APP_FILTER_LIST_ALLOWLIST));
612 : 10 : g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
613 : :
614 [ + + ]: 18 : for (gsize i = 0; filter->app_list[i] != NULL; i++)
615 : 8 : g_variant_builder_add (&builder, "s", filter->app_list[i]);
616 : :
617 : 10 : g_variant_builder_close (&builder);
618 : :
619 : 10 : return g_variant_builder_end (&builder);
620 : : }
621 : :
622 : : /**
623 : : * mct_app_filter_serialize:
624 : : * @filter: an #MctAppFilter
625 : : *
626 : : * Build a #GVariant which contains the app filter from @filter, in an opaque
627 : : * variant format. This format may change in future, but
628 : : * mct_app_filter_deserialize() is guaranteed to always be able to load any
629 : : * variant produced by the current or any previous version of
630 : : * mct_app_filter_serialize().
631 : : *
632 : : * Returns: (transfer floating): a new, floating #GVariant containing the app
633 : : * filter
634 : : * Since: 0.7.0
635 : : */
636 : : GVariant *
637 : 10 : mct_app_filter_serialize (MctAppFilter *filter)
638 : : {
639 : 20 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{sv}"));
640 : :
641 : 10 : g_return_val_if_fail (filter != NULL, NULL);
642 : 10 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
643 : :
644 : : /* The serialisation format is exactly the
645 : : * `com.endlessm.ParentalControls.AppFilter` D-Bus interface. */
646 : 10 : g_variant_builder_add (&builder, "{sv}", "AppFilter",
647 : : _mct_app_filter_build_app_filter_variant (filter));
648 : 10 : g_variant_builder_add (&builder, "{sv}", "OarsFilter",
649 : : g_variant_new ("(s@a{ss})", "oars-1.1",
650 : : filter->oars_ratings));
651 : 10 : g_variant_builder_add (&builder, "{sv}", "AllowUserInstallation",
652 : : g_variant_new_boolean (filter->allow_user_installation));
653 : 10 : g_variant_builder_add (&builder, "{sv}", "AllowSystemInstallation",
654 : : g_variant_new_boolean (filter->allow_system_installation));
655 : :
656 : 10 : return g_variant_builder_end (&builder);
657 : : }
658 : :
659 : : /**
660 : : * mct_app_filter_deserialize:
661 : : * @variant: a serialized app filter variant
662 : : * @user_id: the ID of the user the app filter relates to
663 : : * @error: return location for a #GError, or %NULL
664 : : *
665 : : * Deserialize an app filter previously serialized with
666 : : * mct_app_filter_serialize(). This function guarantees to be able to
667 : : * deserialize any serialized form from this version or older versions of
668 : : * libmalcontent.
669 : : *
670 : : * If deserialization fails, %MCT_MANAGER_ERROR_INVALID_DATA will be returned.
671 : : *
672 : : * Returns: (transfer full): deserialized app filter
673 : : * Since: 0.7.0
674 : : */
675 : : MctAppFilter *
676 : 61 : mct_app_filter_deserialize (GVariant *variant,
677 : : uid_t user_id,
678 : : GError **error)
679 : : {
680 : : gboolean is_allowlist;
681 : 61 : g_auto(GStrv) app_list = NULL;
682 : : const gchar *content_rating_kind;
683 : 61 : g_autoptr(GVariant) oars_variant = NULL;
684 : : gboolean allow_user_installation;
685 : : gboolean allow_system_installation;
686 : 61 : g_autoptr(MctAppFilter) app_filter = NULL;
687 : :
688 : 61 : g_return_val_if_fail (variant != NULL, NULL);
689 : 61 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
690 : :
691 : : /* Check the overall type. */
692 [ + + ]: 61 : if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}")))
693 : : {
694 : 4 : g_set_error (error, MCT_MANAGER_ERROR,
695 : : MCT_MANAGER_ERROR_INVALID_DATA,
696 : : _("App filter for user %u was in an unrecognized format"),
697 : : (guint) user_id);
698 : 4 : return NULL;
699 : : }
700 : :
701 : : /* Extract the properties we care about. The default values here should be
702 : : * kept in sync with those in the `com.endlessm.ParentalControls.AppFilter`
703 : : * D-Bus interface. */
704 [ + + ]: 57 : if (!g_variant_lookup (variant, "AppFilter", "(b^as)",
705 : : &is_allowlist, &app_list))
706 : : {
707 : : /* Default value. */
708 : 40 : is_allowlist = FALSE;
709 : 40 : app_list = g_new0 (gchar *, 1);
710 : : }
711 : :
712 [ + + ]: 57 : if (!g_variant_lookup (variant, "OarsFilter", "(&s@a{ss})",
713 : : &content_rating_kind, &oars_variant))
714 : : {
715 : : /* Default value. */
716 : 33 : content_rating_kind = "oars-1.1";
717 : 33 : oars_variant = g_variant_new ("a{ss}", NULL);
718 : : }
719 : :
720 : : /* Check that the OARS filter is in a format we support. Currently, that’s
721 : : * only oars-1.0 and oars-1.1. */
722 [ + - ]: 57 : if (!g_str_equal (content_rating_kind, "oars-1.0") &&
723 [ + + ]: 57 : !g_str_equal (content_rating_kind, "oars-1.1"))
724 : : {
725 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
726 : : MCT_MANAGER_ERROR_INVALID_DATA,
727 : : _("OARS filter for user %u has an unrecognized kind ‘%s’"),
728 : : (guint) user_id, content_rating_kind);
729 : 2 : return NULL;
730 : : }
731 : :
732 [ + + ]: 55 : if (!g_variant_lookup (variant, "AllowUserInstallation", "b",
733 : : &allow_user_installation))
734 : : {
735 : : /* Default value. */
736 : 43 : allow_user_installation = TRUE;
737 : : }
738 : :
739 [ + + ]: 55 : if (!g_variant_lookup (variant, "AllowSystemInstallation", "b",
740 : : &allow_system_installation))
741 : : {
742 : : /* Default value. */
743 : 43 : allow_system_installation = FALSE;
744 : : }
745 : :
746 : : /* Success. Create an #MctAppFilter object to contain the results. */
747 : 55 : app_filter = g_new0 (MctAppFilter, 1);
748 : 55 : app_filter->ref_count = 1;
749 : 55 : app_filter->user_id = user_id;
750 : 55 : app_filter->app_list = g_steal_pointer (&app_list);
751 : 55 : app_filter->app_list_type =
752 : 55 : is_allowlist ? MCT_APP_FILTER_LIST_ALLOWLIST : MCT_APP_FILTER_LIST_BLOCKLIST;
753 : 55 : app_filter->oars_ratings = g_steal_pointer (&oars_variant);
754 : 55 : app_filter->allow_user_installation = allow_user_installation;
755 : 55 : app_filter->allow_system_installation = allow_system_installation;
756 : :
757 : 55 : return g_steal_pointer (&app_filter);
758 : : }
759 : :
760 : : /**
761 : : * mct_app_filter_equal:
762 : : * @a: (not nullable): an #MctAppFilter
763 : : * @b: (not nullable): an #MctAppFilter
764 : : *
765 : : * Check whether app filters @a and @b are equal.
766 : : *
767 : : * Returns: %TRUE if @a and @b are equal, %FALSE otherwise
768 : : * Since: 0.10.0
769 : : */
770 : : gboolean
771 : 78 : mct_app_filter_equal (MctAppFilter *a,
772 : : MctAppFilter *b)
773 : : {
774 : 78 : g_return_val_if_fail (a != NULL, FALSE);
775 : 78 : g_return_val_if_fail (a->ref_count >= 1, FALSE);
776 : 78 : g_return_val_if_fail (b != NULL, FALSE);
777 : 78 : g_return_val_if_fail (b->ref_count >= 1, FALSE);
778 : :
779 : 136 : return (a->user_id == b->user_id &&
780 [ + + ]: 58 : a->app_list_type == b->app_list_type &&
781 [ + - ]: 42 : a->allow_user_installation == b->allow_user_installation &&
782 [ + + + + ]: 72 : a->allow_system_installation == b->allow_system_installation &&
783 [ + + + + ]: 166 : g_strv_equal ((const gchar * const *) a->app_list, (const gchar * const *) b->app_list) &&
784 : 22 : g_variant_equal (a->oars_ratings, b->oars_ratings));
785 : : }
786 : :
787 : : /*
788 : : * Actual implementation of #MctAppFilterBuilder.
789 : : *
790 : : * All members are %NULL if un-initialised, cleared, or ended.
791 : : */
792 : : typedef struct
793 : : {
794 : : GPtrArray *blocklist; /* (nullable) (owned) (element-type utf8) */
795 : : GHashTable *oars; /* (nullable) (owned) (element-type utf8 MctAppFilterOarsValue) */
796 : : gboolean allow_user_installation;
797 : : gboolean allow_system_installation;
798 : :
799 : : /*< private >*/
800 : : gpointer padding[2];
801 : : } MctAppFilterBuilderReal;
802 : :
803 : : G_STATIC_ASSERT (sizeof (MctAppFilterBuilderReal) ==
804 : : sizeof (MctAppFilterBuilder));
805 : : G_STATIC_ASSERT (__alignof__ (MctAppFilterBuilderReal) ==
806 : : __alignof__ (MctAppFilterBuilder));
807 : :
808 [ + - + - : 6 : G_DEFINE_BOXED_TYPE (MctAppFilterBuilder, mct_app_filter_builder,
+ - ]
809 : : mct_app_filter_builder_copy, mct_app_filter_builder_free)
810 : :
811 : : /**
812 : : * mct_app_filter_builder_init:
813 : : * @builder: an uninitialised #MctAppFilterBuilder
814 : : *
815 : : * Initialise the given @builder so it can be used to construct a new
816 : : * #MctAppFilter. @builder must have been allocated on the stack, and must not
817 : : * already be initialised.
818 : : *
819 : : * Construct the #MctAppFilter by calling methods on @builder, followed by
820 : : * mct_app_filter_builder_end(). To abort construction, use
821 : : * mct_app_filter_builder_clear().
822 : : *
823 : : * Since: 0.2.0
824 : : */
825 : : void
826 : 20 : mct_app_filter_builder_init (MctAppFilterBuilder *builder)
827 : : {
828 : 20 : MctAppFilterBuilder local_builder = MCT_APP_FILTER_BUILDER_INIT ();
829 : 20 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
830 : :
831 : 20 : g_return_if_fail (_builder != NULL);
832 : 20 : g_return_if_fail (_builder->blocklist == NULL);
833 : 20 : g_return_if_fail (_builder->oars == NULL);
834 : :
835 : 20 : memcpy (builder, &local_builder, sizeof (local_builder));
836 : : }
837 : :
838 : : /**
839 : : * mct_app_filter_builder_clear:
840 : : * @builder: an #MctAppFilterBuilder
841 : : *
842 : : * Clear @builder, freeing any internal state in it. This will not free the
843 : : * top-level storage for @builder itself, which is assumed to be allocated on
844 : : * the stack.
845 : : *
846 : : * If called on an already-cleared #MctAppFilterBuilder, this function is
847 : : * idempotent.
848 : : *
849 : : * Since: 0.2.0
850 : : */
851 : : void
852 : 78 : mct_app_filter_builder_clear (MctAppFilterBuilder *builder)
853 : : {
854 : 78 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
855 : :
856 : 78 : g_return_if_fail (_builder != NULL);
857 : :
858 [ + + ]: 78 : g_clear_pointer (&_builder->blocklist, g_ptr_array_unref);
859 [ + + ]: 78 : g_clear_pointer (&_builder->oars, g_hash_table_unref);
860 : : }
861 : :
862 : : /**
863 : : * mct_app_filter_builder_new:
864 : : *
865 : : * Construct a new #MctAppFilterBuilder on the heap. This is intended for
866 : : * language bindings. The returned builder must eventually be freed with
867 : : * mct_app_filter_builder_free(), but can be cleared zero or more times with
868 : : * mct_app_filter_builder_clear() first.
869 : : *
870 : : * Returns: (transfer full): a new heap-allocated #MctAppFilterBuilder
871 : : * Since: 0.2.0
872 : : */
873 : : MctAppFilterBuilder *
874 : 12 : mct_app_filter_builder_new (void)
875 : : {
876 : 12 : g_autoptr(MctAppFilterBuilder) builder = NULL;
877 : :
878 : 12 : builder = g_new0 (MctAppFilterBuilder, 1);
879 : 12 : mct_app_filter_builder_init (builder);
880 : :
881 : 12 : return g_steal_pointer (&builder);
882 : : }
883 : :
884 : : /**
885 : : * mct_app_filter_builder_copy:
886 : : * @builder: an #MctAppFilterBuilder
887 : : *
888 : : * Copy the given @builder to a newly-allocated #MctAppFilterBuilder on the
889 : : * heap. This is safe to use with cleared, stack-allocated
890 : : * #MctAppFilterBuilders.
891 : : *
892 : : * Returns: (transfer full): a copy of @builder
893 : : * Since: 0.2.0
894 : : */
895 : : MctAppFilterBuilder *
896 : 4 : mct_app_filter_builder_copy (MctAppFilterBuilder *builder)
897 : : {
898 : 4 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
899 : 4 : g_autoptr(MctAppFilterBuilder) copy = NULL;
900 : : MctAppFilterBuilderReal *_copy;
901 : :
902 : 4 : g_return_val_if_fail (builder != NULL, NULL);
903 : :
904 : 4 : copy = mct_app_filter_builder_new ();
905 : 4 : _copy = (MctAppFilterBuilderReal *) copy;
906 : :
907 : 4 : mct_app_filter_builder_clear (copy);
908 [ + + ]: 4 : if (_builder->blocklist != NULL)
909 : 2 : _copy->blocklist = g_ptr_array_ref (_builder->blocklist);
910 [ + + ]: 4 : if (_builder->oars != NULL)
911 : 2 : _copy->oars = g_hash_table_ref (_builder->oars);
912 : 4 : _copy->allow_user_installation = _builder->allow_user_installation;
913 : 4 : _copy->allow_system_installation = _builder->allow_system_installation;
914 : :
915 : 4 : return g_steal_pointer (©);
916 : : }
917 : :
918 : : /**
919 : : * mct_app_filter_builder_free:
920 : : * @builder: a heap-allocated #MctAppFilterBuilder
921 : : *
922 : : * Free an #MctAppFilterBuilder originally allocated using
923 : : * mct_app_filter_builder_new(). This must not be called on stack-allocated
924 : : * builders initialised using mct_app_filter_builder_init().
925 : : *
926 : : * Since: 0.2.0
927 : : */
928 : : void
929 : 12 : mct_app_filter_builder_free (MctAppFilterBuilder *builder)
930 : : {
931 : 12 : g_return_if_fail (builder != NULL);
932 : :
933 : 12 : mct_app_filter_builder_clear (builder);
934 : 12 : g_free (builder);
935 : : }
936 : :
937 : : /**
938 : : * mct_app_filter_builder_end:
939 : : * @builder: an initialised #MctAppFilterBuilder
940 : : *
941 : : * Finish constructing an #MctAppFilter with the given @builder, and return it.
942 : : * The #MctAppFilterBuilder will be cleared as if mct_app_filter_builder_clear()
943 : : * had been called.
944 : : *
945 : : * Returns: (transfer full): a newly constructed #MctAppFilter
946 : : * Since: 0.2.0
947 : : */
948 : : MctAppFilter *
949 : 35 : mct_app_filter_builder_end (MctAppFilterBuilder *builder)
950 : : {
951 : 35 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
952 : 35 : g_autoptr(MctAppFilter) app_filter = NULL;
953 : 70 : g_auto(GVariantBuilder) oars_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
954 : : GHashTableIter iter;
955 : : gpointer key, value;
956 : 35 : g_autoptr(GVariant) oars_variant = NULL;
957 : :
958 : 35 : g_return_val_if_fail (_builder != NULL, NULL);
959 : 35 : g_return_val_if_fail (_builder->blocklist != NULL, NULL);
960 : 35 : g_return_val_if_fail (_builder->oars != NULL, NULL);
961 : :
962 : : /* Ensure the paths list is %NULL-terminated. */
963 : 35 : g_ptr_array_add (_builder->blocklist, NULL);
964 : :
965 : : /* Build the OARS variant. */
966 : 35 : g_hash_table_iter_init (&iter, _builder->oars);
967 [ + + ]: 49 : while (g_hash_table_iter_next (&iter, &key, &value))
968 : : {
969 : 14 : const gchar *oars_section = key;
970 : 14 : MctAppFilterOarsValue oars_value = GPOINTER_TO_INT (value);
971 : 14 : const gchar *oars_value_strs[] =
972 : : {
973 : : NULL, /* MCT_APP_FILTER_OARS_VALUE_UNKNOWN */
974 : : "none",
975 : : "mild",
976 : : "moderate",
977 : : "intense",
978 : : };
979 : :
980 : 14 : g_assert ((int) oars_value >= 0 &&
981 : : (int) oars_value < (int) G_N_ELEMENTS (oars_value_strs));
982 : :
983 [ + - ]: 14 : if (oars_value_strs[oars_value] != NULL)
984 : 14 : g_variant_builder_add (&oars_builder, "{ss}",
985 : : oars_section, oars_value_strs[oars_value]);
986 : : }
987 : :
988 : 35 : oars_variant = g_variant_ref_sink (g_variant_builder_end (&oars_builder));
989 : :
990 : : /* Build the #MctAppFilter. */
991 : 35 : app_filter = g_new0 (MctAppFilter, 1);
992 : 35 : app_filter->ref_count = 1;
993 : 35 : app_filter->user_id = -1;
994 : 35 : app_filter->app_list = (gchar **) g_ptr_array_free (g_steal_pointer (&_builder->blocklist), FALSE);
995 : 35 : app_filter->app_list_type = MCT_APP_FILTER_LIST_BLOCKLIST;
996 : 35 : app_filter->oars_ratings = g_steal_pointer (&oars_variant);
997 : 35 : app_filter->allow_user_installation = _builder->allow_user_installation;
998 : 35 : app_filter->allow_system_installation = _builder->allow_system_installation;
999 : :
1000 : 35 : mct_app_filter_builder_clear (builder);
1001 : :
1002 : 35 : return g_steal_pointer (&app_filter);
1003 : : }
1004 : :
1005 : : /**
1006 : : * mct_app_filter_builder_blocklist_path:
1007 : : * @builder: an initialised #MctAppFilterBuilder
1008 : : * @path: (type filename): an absolute path to blocklist
1009 : : *
1010 : : * Add @path to the blocklist of app paths in the filter under construction. It
1011 : : * will be canonicalised (without doing any I/O) before being added.
1012 : : * The canonicalised @path will not be added again if it’s already been added.
1013 : : *
1014 : : * Since: 0.2.0
1015 : : */
1016 : : void
1017 : 22 : mct_app_filter_builder_blocklist_path (MctAppFilterBuilder *builder,
1018 : : const gchar *path)
1019 : : {
1020 : 22 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1021 : :
1022 : 22 : g_return_if_fail (_builder != NULL);
1023 : 22 : g_return_if_fail (_builder->blocklist != NULL);
1024 : 22 : g_return_if_fail (path != NULL);
1025 : 22 : g_return_if_fail (g_path_is_absolute (path));
1026 : :
1027 [ + - ]: 44 : g_autofree gchar *canonical_path = g_canonicalize_filename (path, "/");
1028 [ + - ]: 44 : g_autofree gchar *canonical_path_utf8 = g_filename_to_utf8 (canonical_path, -1,
1029 : : NULL, NULL, NULL);
1030 : 22 : g_return_if_fail (canonical_path_utf8 != NULL);
1031 : :
1032 [ + - ]: 22 : if (!g_ptr_array_find_with_equal_func (_builder->blocklist,
1033 : : canonical_path_utf8, g_str_equal, NULL))
1034 : 22 : g_ptr_array_add (_builder->blocklist, g_steal_pointer (&canonical_path_utf8));
1035 : : }
1036 : :
1037 : : /**
1038 : : * mct_app_filter_builder_blocklist_flatpak_ref:
1039 : : * @builder: an initialised #MctAppFilterBuilder
1040 : : * @app_ref: a flatpak app ref to blocklist
1041 : : *
1042 : : * Add @app_ref to the blocklist of flatpak refs in the filter under
1043 : : * construction. The @app_ref will not be added again if it’s already been
1044 : : * added.
1045 : : *
1046 : : * Since: 0.2.0
1047 : : */
1048 : : void
1049 : 10 : mct_app_filter_builder_blocklist_flatpak_ref (MctAppFilterBuilder *builder,
1050 : : const gchar *app_ref)
1051 : : {
1052 : 10 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1053 : :
1054 : 10 : g_return_if_fail (_builder != NULL);
1055 : 10 : g_return_if_fail (_builder->blocklist != NULL);
1056 : 10 : g_return_if_fail (app_ref != NULL);
1057 : 10 : g_return_if_fail (is_valid_flatpak_ref (app_ref));
1058 : :
1059 [ + - ]: 10 : if (!g_ptr_array_find_with_equal_func (_builder->blocklist,
1060 : : app_ref, g_str_equal, NULL))
1061 : 10 : g_ptr_array_add (_builder->blocklist, g_strdup (app_ref));
1062 : : }
1063 : :
1064 : : /**
1065 : : * mct_app_filter_builder_blocklist_content_type:
1066 : : * @builder: an initialised #MctAppFilterBuilder
1067 : : * @content_type: a content type to blocklist
1068 : : *
1069 : : * Add @content_type to the blocklist of content types in the filter under
1070 : : * construction. The @content_type will not be added again if it’s already been
1071 : : * added.
1072 : : *
1073 : : * Note that this method doesn’t handle content subtypes. For example, if
1074 : : * `application/xml` is added to the blocklist but `application/xspf+xml` is not,
1075 : : * a check for whether `application/xspf+xml` is blocklisted would return false.
1076 : : *
1077 : : * Since: 0.4.0
1078 : : */
1079 : : void
1080 : 14 : mct_app_filter_builder_blocklist_content_type (MctAppFilterBuilder *builder,
1081 : : const gchar *content_type)
1082 : : {
1083 : 14 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1084 : :
1085 : 14 : g_return_if_fail (_builder != NULL);
1086 : 14 : g_return_if_fail (_builder->blocklist != NULL);
1087 : 14 : g_return_if_fail (content_type != NULL);
1088 : 14 : g_return_if_fail (is_valid_content_type (content_type));
1089 : :
1090 [ + - ]: 14 : if (!g_ptr_array_find_with_equal_func (_builder->blocklist,
1091 : : content_type, g_str_equal, NULL))
1092 : 14 : g_ptr_array_add (_builder->blocklist, g_strdup (content_type));
1093 : : }
1094 : :
1095 : : /**
1096 : : * mct_app_filter_builder_set_oars_value:
1097 : : * @builder: an initialised #MctAppFilterBuilder
1098 : : * @oars_section: name of the OARS section to set the value for
1099 : : * @value: value to set for the @oars_section
1100 : : *
1101 : : * Set the OARS value for the given @oars_section, indicating the intensity of
1102 : : * content covered by that section which the user is allowed to see (inclusive).
1103 : : * Any apps which have more intense content in this section should not be usable
1104 : : * by the user.
1105 : : *
1106 : : * Since: 0.2.0
1107 : : */
1108 : : void
1109 : 14 : mct_app_filter_builder_set_oars_value (MctAppFilterBuilder *builder,
1110 : : const gchar *oars_section,
1111 : : MctAppFilterOarsValue value)
1112 : : {
1113 : 14 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1114 : :
1115 : 14 : g_return_if_fail (_builder != NULL);
1116 : 14 : g_return_if_fail (_builder->oars != NULL);
1117 : 14 : g_return_if_fail (oars_section != NULL && *oars_section != '\0');
1118 : :
1119 : 14 : g_hash_table_insert (_builder->oars, g_strdup (oars_section),
1120 : 14 : GUINT_TO_POINTER (value));
1121 : : }
1122 : :
1123 : : /**
1124 : : * mct_app_filter_builder_set_allow_user_installation:
1125 : : * @builder: an initialised #MctAppFilterBuilder
1126 : : * @allow_user_installation: %TRUE to allow app installation; %FALSE to
1127 : : * unconditionally disallow it
1128 : : *
1129 : : * Set whether the user is allowed to install to their flatpak user repository.
1130 : : * If this is %TRUE, app installation is still subject to the OARS values
1131 : : * (mct_app_filter_builder_set_oars_value()). If it is %FALSE, app installation
1132 : : * is unconditionally disallowed for this user.
1133 : : *
1134 : : * Since: 0.2.0
1135 : : */
1136 : : void
1137 : 10 : mct_app_filter_builder_set_allow_user_installation (MctAppFilterBuilder *builder,
1138 : : gboolean allow_user_installation)
1139 : : {
1140 : 10 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1141 : :
1142 : 10 : g_return_if_fail (_builder != NULL);
1143 : :
1144 : 10 : _builder->allow_user_installation = allow_user_installation;
1145 : : }
1146 : :
1147 : : /**
1148 : : * mct_app_filter_builder_set_allow_system_installation:
1149 : : * @builder: an initialised #MctAppFilterBuilder
1150 : : * @allow_system_installation: %TRUE to allow app installation; %FALSE to
1151 : : * unconditionally disallow it
1152 : : *
1153 : : * Set whether the user is allowed to install to the flatpak system repository.
1154 : : * If this is %TRUE, app installation is still subject to the OARS values
1155 : : * (mct_app_filter_builder_set_oars_value()). If it is %FALSE, app installation
1156 : : * is unconditionally disallowed for this user.
1157 : : *
1158 : : * Since: 0.2.0
1159 : : */
1160 : : void
1161 : 10 : mct_app_filter_builder_set_allow_system_installation (MctAppFilterBuilder *builder,
1162 : : gboolean allow_system_installation)
1163 : : {
1164 : 10 : MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
1165 : :
1166 : 10 : g_return_if_fail (_builder != NULL);
1167 : :
1168 : 10 : _builder->allow_system_installation = allow_system_installation;
1169 : : }
|