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