Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2025 GNOME Foundation, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this library; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : : *
21 : : * Authors:
22 : : * - Philip Withnall <pwithnall@gnome.org>
23 : : */
24 : :
25 : : #include "config.h"
26 : :
27 : : #include <glib.h>
28 : : #include <glib-object.h>
29 : : #include <glib/gi18n-lib.h>
30 : : #include <gio/gio.h>
31 : : #include <libmalcontent/manager.h>
32 : : #include <libmalcontent/web-filter.h>
33 : :
34 : : #include "libmalcontent/web-filter-private.h"
35 : :
36 : :
37 : : /* struct _MctWebFilter is defined in web-filter-private.h */
38 : :
39 [ + - + - : 4 : G_DEFINE_BOXED_TYPE (MctWebFilter, mct_web_filter,
+ - ]
40 : : mct_web_filter_ref, mct_web_filter_unref)
41 : :
42 : : /**
43 : : * mct_web_filter_ref:
44 : : * @filter: (transfer none): a web filter
45 : : *
46 : : * Increment the reference count of @filter, and return the same pointer to it.
47 : : *
48 : : * Returns: (transfer full): the same pointer as @filter
49 : : * Since: 0.14.0
50 : : */
51 : : MctWebFilter *
52 : 1 : mct_web_filter_ref (MctWebFilter *filter)
53 : : {
54 : 1 : g_return_val_if_fail (filter != NULL, NULL);
55 : 1 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
56 : 1 : g_return_val_if_fail (filter->ref_count <= G_MAXINT - 1, NULL);
57 : :
58 : 1 : filter->ref_count++;
59 : 1 : return filter;
60 : : }
61 : :
62 : : /**
63 : : * mct_web_filter_unref:
64 : : * @filter: (transfer full): a web filter
65 : : *
66 : : * Decrement the reference count of @filter. If the reference count reaches
67 : : * zero, free the @filter and all its resources.
68 : : *
69 : : * Since: 0.14.0
70 : : */
71 : : void
72 : 14 : mct_web_filter_unref (MctWebFilter *filter)
73 : : {
74 : 14 : g_return_if_fail (filter != NULL);
75 : 14 : g_return_if_fail (filter->ref_count >= 1);
76 : :
77 : 14 : filter->ref_count--;
78 : :
79 [ + + ]: 14 : if (filter->ref_count <= 0)
80 : : {
81 [ + + ]: 13 : g_clear_pointer (&filter->block_lists, g_hash_table_unref);
82 [ - + ]: 13 : g_clear_pointer (&filter->custom_block_list, g_strfreev);
83 [ + + ]: 13 : g_clear_pointer (&filter->allow_lists, g_hash_table_unref);
84 [ - + ]: 13 : g_clear_pointer (&filter->custom_allow_list, g_strfreev);
85 : :
86 : 13 : g_free (filter);
87 : : }
88 : : }
89 : :
90 : : /**
91 : : * mct_web_filter_get_user_id:
92 : : * @filter: a web filter
93 : : *
94 : : * Get the user ID of the user this #MctWebFilter is for.
95 : : *
96 : : * Returns: user ID of the relevant user, or `(uid_t) -1` if unknown
97 : : * Since: 0.14.0
98 : : */
99 : : uid_t
100 : 0 : mct_web_filter_get_user_id (MctWebFilter *filter)
101 : : {
102 : 0 : g_return_val_if_fail (filter != NULL, (uid_t) -1);
103 : 0 : g_return_val_if_fail (filter->ref_count >= 1, (uid_t) -1);
104 : :
105 : 0 : return filter->user_id;
106 : : }
107 : :
108 : : /**
109 : : * mct_web_filter_is_enabled:
110 : : * @filter: a web filter
111 : : *
112 : : * Check whether any web filtering is enabled and is going to impose at least
113 : : * one restriction on the user.
114 : : *
115 : : * This gives a high level view of whether web filter parental controls are
116 : : * ‘enabled’ for the given user.
117 : : *
118 : : * Returns: true if the web filter object contains at least one restrictive
119 : : * filter, false if there are no filters in place
120 : : * Since: 0.14.0
121 : : */
122 : : gboolean
123 : 0 : mct_web_filter_is_enabled (MctWebFilter *filter)
124 : : {
125 : 0 : g_return_val_if_fail (filter != NULL, FALSE);
126 : 0 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
127 : :
128 [ # # ]: 0 : return (filter->force_safe_search ||
129 [ # # ]: 0 : (filter->filter_type != MCT_WEB_FILTER_TYPE_NONE &&
130 [ # # ]: 0 : (filter->block_lists != NULL ||
131 [ # # ]: 0 : filter->custom_block_list_len > 0 ||
132 [ # # ]: 0 : filter->allow_lists != NULL ||
133 [ # # ]: 0 : filter->custom_allow_list_len > 0)));
134 : : }
135 : :
136 : : /**
137 : : * mct_web_filter_get_filter_type:
138 : : * @filter: a web filter
139 : : *
140 : : * Gets the type of web filter.
141 : : *
142 : : * Returns: the currently active filter type
143 : : * Since: 0.14.0
144 : : */
145 : : MctWebFilterType
146 : 0 : mct_web_filter_get_filter_type (MctWebFilter *filter)
147 : : {
148 : 0 : g_return_val_if_fail (filter != NULL, MCT_WEB_FILTER_TYPE_NONE);
149 : 0 : g_return_val_if_fail (filter->ref_count >= 1, MCT_WEB_FILTER_TYPE_NONE);
150 : :
151 : 0 : return filter->filter_type;
152 : : }
153 : :
154 : : /**
155 : : * mct_web_filter_get_block_lists:
156 : : * @filter: a web filter
157 : : *
158 : : * Gets the block lists configured on the filter.
159 : : *
160 : : * These are a mapping from ID to the URI of the block list. See the
161 : : * [struct@Malcontent.WebFilter] documentation for allowed ID values.
162 : : *
163 : : * A `NULL` return value is equivalent to an empty mapping.
164 : : *
165 : : * Returns: (element-type utf8 utf8) (nullable) (transfer none): mapping of ID
166 : : * to URI for block lists
167 : : * Since: 0.14.0
168 : : */
169 : : GHashTable *
170 : 0 : mct_web_filter_get_block_lists (MctWebFilter *filter)
171 : : {
172 : 0 : g_return_val_if_fail (filter != NULL, NULL);
173 : 0 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
174 : :
175 : 0 : return filter->block_lists;
176 : : }
177 : :
178 : : /**
179 : : * mct_web_filter_get_custom_block_list:
180 : : * @filter: a web filter
181 : : * @out_len: (out caller-allocates) (optional): return location for the array
182 : : * length, or `NULL` to ignore
183 : : *
184 : : * Gets the custom block list configured on the filter.
185 : : *
186 : : * This is an array of hostnames to block. Hostnames are plain strings, not
187 : : * globs or regexps.
188 : : *
189 : : * A `NULL` return value is equivalent to an empty array.
190 : : *
191 : : * Returns: (array length=out_len) (nullable) (transfer none): array of
192 : : * hostnames to block
193 : : * Since: 0.14.0
194 : : */
195 : : const char * const *
196 : 0 : mct_web_filter_get_custom_block_list (MctWebFilter *filter,
197 : : size_t *out_len)
198 : : {
199 : 0 : g_return_val_if_fail (filter != NULL, NULL);
200 : 0 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
201 : :
202 [ # # ]: 0 : if (out_len != NULL)
203 : 0 : *out_len = filter->custom_block_list_len;
204 : :
205 : 0 : return (const char * const *) filter->custom_block_list;
206 : : }
207 : :
208 : : /**
209 : : * mct_web_filter_get_allow_lists:
210 : : * @filter: a web filter
211 : : *
212 : : * Get the allow lists configured on the filter.
213 : : *
214 : : * These are a mapping from ID to the URI of the allow list. See the
215 : : * [struct@Malcontent.WebFilter] documentation for allowed ID values.
216 : : *
217 : : * A `NULL` return value is equivalent to an empty mapping.
218 : : *
219 : : * Returns: (element-type utf8 utf8) (nullable) (transfer none): mapping of ID
220 : : * to URI for allow lists
221 : : * Since: 0.14.0
222 : : */
223 : : GHashTable *
224 : 0 : mct_web_filter_get_allow_lists (MctWebFilter *filter)
225 : : {
226 : 0 : g_return_val_if_fail (filter != NULL, NULL);
227 : 0 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
228 : :
229 : 0 : return filter->allow_lists;
230 : : }
231 : :
232 : : /**
233 : : * mct_web_filter_get_custom_allow_list:
234 : : * @filter: a web filter
235 : : * @out_len: (out caller-allocates) (optional): return location for the array
236 : : * length, or `NULL` to ignore
237 : : *
238 : : * Gets the custom allow list configured on the filter.
239 : : *
240 : : * This is an array of hostnames to allow. Hostnames are plain strings, not
241 : : * globs or regexps.
242 : : *
243 : : * A `NULL` return value is equivalent to an empty array.
244 : : *
245 : : * Returns: (array length=out_len) (nullable) (transfer none): array of
246 : : * hostnames to allow
247 : : * Since: 0.14.0
248 : : */
249 : : const char * const *
250 : 0 : mct_web_filter_get_custom_allow_list (MctWebFilter *filter,
251 : : size_t *out_len)
252 : : {
253 : 0 : g_return_val_if_fail (filter != NULL, NULL);
254 : 0 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
255 : :
256 [ # # ]: 0 : if (out_len != NULL)
257 : 0 : *out_len = filter->custom_allow_list_len;
258 : :
259 : 0 : return (const char * const *) filter->custom_allow_list;
260 : : }
261 : :
262 : : /**
263 : : * mct_web_filter_get_force_safe_search:
264 : : * @filter: a web filter
265 : : *
266 : : * Gets the safe search preference for the filter.
267 : : *
268 : : * If enabled, search engines and other popular websites will be automatically
269 : : * redirected to their ‘safe search’ variant, if supported.
270 : : *
271 : : * Returns: true if safe search is force-enabled, false otherwise
272 : : * Since: 0.14.0
273 : : */
274 : : gboolean
275 : 3 : mct_web_filter_get_force_safe_search (MctWebFilter *filter)
276 : : {
277 : 3 : g_return_val_if_fail (filter != NULL, FALSE);
278 : 3 : g_return_val_if_fail (filter->ref_count >= 1, FALSE);
279 : :
280 : 3 : return filter->force_safe_search;
281 : : }
282 : :
283 : : static GVariant *
284 : 0 : hash_table_to_ass (GHashTable *table)
285 : : {
286 : 0 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
287 : : GHashTableIter iter;
288 : : void *key, *value;
289 : :
290 : 0 : g_hash_table_iter_init (&iter, table);
291 : :
292 [ # # ]: 0 : while (g_hash_table_iter_next (&iter, &key, &value))
293 : : {
294 : 0 : const char *key_str = key, *value_str = value;
295 : :
296 : 0 : g_variant_builder_add (&builder, "{ss}", key_str, value_str);
297 : : }
298 : :
299 : 0 : return g_variant_builder_end (&builder);
300 : : }
301 : :
302 : : /**
303 : : * mct_web_filter_validate_filter_id:
304 : : * @id: a potential filter list ID
305 : : *
306 : : * Validate a potential filter list ID.
307 : : *
308 : : * Filter list IDs must be non-empty UTF-8 strings.
309 : : *
310 : : * Returns: true if @id is a valid filter list ID, false otherwise
311 : : * Since: 0.14.0
312 : : */
313 : : gboolean
314 : 8 : mct_web_filter_validate_filter_id (const char *id)
315 : : {
316 : 8 : g_return_val_if_fail (id != NULL, FALSE);
317 : 8 : return (id[0] != '\0');
318 : : }
319 : :
320 : : static gboolean
321 : 5 : validate_filter_uri (const char *filter_uri)
322 : : {
323 : 5 : g_return_val_if_fail (filter_uri != NULL, FALSE);
324 : 5 : return g_uri_is_valid (filter_uri, G_URI_FLAGS_NONE, NULL);
325 : : }
326 : :
327 : : /**
328 : : * mct_web_filter_validate_hostname:
329 : : * @hostname: a potential hostname
330 : : *
331 : : * Validate a potential hostname.
332 : : *
333 : : * This checks against [RFC 1035](https://datatracker.ietf.org/doc/html/rfc1035).
334 : : *
335 : : * Returns: true if @hostname is a valid hostname, false otherwise
336 : : * Since: 0.14.0
337 : : */
338 : : gboolean
339 : 11 : mct_web_filter_validate_hostname (const char *hostname)
340 : : {
341 : 11 : return mct_web_filter_validate_hostname_len (hostname, G_MAXSIZE);
342 : : }
343 : :
344 : : /**
345 : : * mct_web_filter_validate_hostname_len:
346 : : * @hostname: a potential hostname
347 : : * @max_len: length (in bytes) to check, or until the first nul byte is reached
348 : : *
349 : : * Validate a potential hostname.
350 : : *
351 : : * This checks against [RFC 1035](https://datatracker.ietf.org/doc/html/rfc1035).
352 : : *
353 : : * Returns: true if @hostname is a valid hostname, false otherwise
354 : : * Since: 0.14.0
355 : : */
356 : : gboolean
357 : 11 : mct_web_filter_validate_hostname_len (const char *hostname,
358 : : size_t max_len)
359 : : {
360 : : size_t i;
361 : 11 : size_t start_of_label = 0;
362 : :
363 : 11 : g_return_val_if_fail (hostname != NULL, FALSE);
364 : :
365 : : /* A valid hostname must:
366 : : * - Be ≤253 characters long (https://web.archive.org/web/20190518124533/https://devblogs.microsoft.com/oldnewthing/?p=7873)
367 : : * - Have labels ≤63 characters long
368 : : * - Only contain characters A-Z, a-z, 0-9, `.` and `-`
369 : : * - Not have a label which starts or ends with a hyphen
370 : : * - Be non-empty
371 : : *
372 : : * See https://datatracker.ietf.org/doc/html/rfc1035
373 : : */
374 [ + + + - ]: 391 : for (i = 0; hostname[i] != '\0' && i < max_len; i++)
375 : : {
376 [ + + ]: 384 : if (!g_ascii_isalnum (hostname[i]) &&
377 [ + + ]: 14 : hostname[i] != '.' &&
378 [ + + ]: 6 : hostname[i] != '-')
379 : 2 : return FALSE;
380 : :
381 [ + + ]: 382 : if (hostname[i] == '.')
382 : : {
383 [ + - ]: 8 : if (i - start_of_label == 0 ||
384 [ + + ]: 8 : i - start_of_label > 63)
385 : 1 : return FALSE;
386 : :
387 [ + + ]: 7 : if (hostname[start_of_label] == '-' ||
388 [ - + ]: 6 : hostname[i - 1] == '-')
389 : 1 : return FALSE;
390 : :
391 : 6 : start_of_label = i + 1;
392 : : }
393 : : }
394 : :
395 : : /* Do the checks for the final label, if the hostname didn’t end in a `.` */
396 [ + + + - ]: 7 : if (i > 0 && hostname[i - 1] != '.')
397 : : {
398 [ + - ]: 6 : if (i - start_of_label == 0 ||
399 [ - + ]: 6 : i - start_of_label > 63)
400 : 0 : return FALSE;
401 : :
402 [ + + ]: 6 : if (hostname[start_of_label] == '-' ||
403 [ + + ]: 5 : hostname[i - 1] == '-')
404 : 2 : return FALSE;
405 : : }
406 : :
407 [ + + + + ]: 5 : return (i > 0 && i <= 253);
408 : : }
409 : :
410 : : static GHashTable *
411 : 4 : ass_to_hash_table (GVariant *ass,
412 : : uid_t user_id,
413 : : GError **error)
414 : : {
415 : : GVariantIter iter;
416 : : const char *filter_list_id, *filter_uri;
417 : 4 : g_autoptr(GHashTable) table = NULL;
418 : :
419 : 4 : g_variant_iter_init (&iter, ass);
420 : 4 : table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
421 : :
422 [ + + ]: 9 : while (g_variant_iter_loop (&iter, "{&s&s}", &filter_list_id, &filter_uri))
423 : : {
424 [ + - - + ]: 10 : if (!mct_web_filter_validate_filter_id (filter_list_id) ||
425 : 5 : !validate_filter_uri (filter_uri))
426 : : {
427 : 0 : g_set_error (error, MCT_MANAGER_ERROR,
428 : : MCT_MANAGER_ERROR_INVALID_DATA,
429 : : _("Web filter for user %u references an invalid filter list ‘%s’ at ‘%s’"),
430 : : (unsigned int) user_id, filter_list_id, filter_uri);
431 : 0 : return NULL;
432 : : }
433 : :
434 : 10 : g_hash_table_replace (table, g_strdup (filter_list_id), g_strdup (filter_uri));
435 : : }
436 : :
437 : 4 : return g_steal_pointer (&table);
438 : : }
439 : :
440 : : static GVariant *
441 : 0 : strv_to_as (const char * const *strv,
442 : : size_t strv_len)
443 : : {
444 : 0 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("as"));
445 : :
446 [ # # ]: 0 : for (size_t i = 0; i < strv_len; i++)
447 : 0 : g_variant_builder_add (&builder, "s", strv[i]);
448 : :
449 : 0 : return g_variant_builder_end (&builder);
450 : : }
451 : :
452 : : static char **
453 : 0 : as_to_strv (GVariant *as,
454 : : size_t *out_strv_len,
455 : : uid_t user_id,
456 : : GError **error)
457 : : {
458 : : GVariantIter iter;
459 : : const char *filter_list_entry;
460 : 0 : g_autoptr(GPtrArray) array = NULL;
461 : :
462 : 0 : g_variant_iter_init (&iter, as);
463 : 0 : array = g_ptr_array_new_null_terminated (g_variant_n_children (as), g_free, TRUE);
464 : :
465 [ # # ]: 0 : while (g_variant_iter_loop (&iter, "&s", &filter_list_entry))
466 : : {
467 [ # # ]: 0 : if (!mct_web_filter_validate_hostname (filter_list_entry))
468 : : {
469 : 0 : g_set_error (error, MCT_MANAGER_ERROR,
470 : : MCT_MANAGER_ERROR_INVALID_DATA,
471 : : _("Web filter for user %u contains an invalid entry ‘%s’"),
472 : : (unsigned int) user_id, filter_list_entry);
473 [ # # ]: 0 : if (out_strv_len != NULL)
474 : 0 : *out_strv_len = 0;
475 : 0 : return NULL;
476 : : }
477 : :
478 : 0 : g_ptr_array_add (array, g_strdup (filter_list_entry));
479 : : }
480 : :
481 : 0 : g_ptr_array_sort_values (array, (GCompareFunc) g_strcmp0);
482 : :
483 [ # # ]: 0 : if (out_strv_len != NULL)
484 : 0 : *out_strv_len = array->len;
485 : :
486 : 0 : return (char **) g_ptr_array_steal (array, out_strv_len);
487 : : }
488 : :
489 : : /**
490 : : * mct_web_filter_serialize:
491 : : * @filter: a web filter
492 : : *
493 : : * Build a #GVariant which contains the web filter from @filter, in an
494 : : * opaque variant format. This format may change in future, but
495 : : * mct_web_filter_deserialize() is guaranteed to always be able to load any
496 : : * variant produced by the current or any previous version of
497 : : * mct_web_filter_serialize().
498 : : *
499 : : * Returns: (transfer floating): a new, floating #GVariant containing the
500 : : * web filter
501 : : * Since: 0.14.0
502 : : */
503 : : GVariant *
504 : 1 : mct_web_filter_serialize (MctWebFilter *filter)
505 : : {
506 : 2 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{sv}"));
507 : :
508 : 1 : g_return_val_if_fail (filter != NULL, NULL);
509 : 1 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
510 : :
511 : : /* The serialisation format is exactly the
512 : : * `org.freedesktop.Malcontent.WebFilter` D-Bus interface. */
513 : 1 : g_variant_builder_add (&builder, "{sv}", "FilterType",
514 : 1 : g_variant_new_uint32 (filter->filter_type));
515 [ - + ]: 1 : if (filter->block_lists != NULL)
516 : 0 : g_variant_builder_add (&builder, "{sv}", "BlockLists",
517 : : hash_table_to_ass (filter->block_lists));
518 [ - + ]: 1 : if (filter->custom_block_list != NULL)
519 : 0 : g_variant_builder_add (&builder, "{sv}", "CustomBlockList",
520 : 0 : strv_to_as ((const char * const *) filter->custom_block_list, filter->custom_block_list_len));
521 [ - + ]: 1 : if (filter->allow_lists != NULL)
522 : 0 : g_variant_builder_add (&builder, "{sv}", "AllowLists",
523 : : hash_table_to_ass (filter->allow_lists));
524 [ - + ]: 1 : if (filter->custom_allow_list != NULL)
525 : 0 : g_variant_builder_add (&builder, "{sv}", "CustomAllowList",
526 : 0 : strv_to_as ((const char * const *) filter->custom_allow_list, filter->custom_allow_list_len));
527 : 1 : g_variant_builder_add (&builder, "{sv}", "ForceSafeSearch",
528 : : g_variant_new_boolean (filter->force_safe_search));
529 : :
530 : 1 : return g_variant_builder_end (&builder);
531 : : }
532 : :
533 : : /**
534 : : * mct_web_filter_deserialize:
535 : : * @variant: a serialized web filter variant
536 : : * @user_id: the ID of the user the web filter relates to
537 : : * @error: return location for a #GError, or %NULL
538 : : *
539 : : * Deserialize a set of web filters previously serialized with
540 : : * mct_web_filter_serialize(). This function guarantees to be able to
541 : : * deserialize any serialized form from this version or older versions of
542 : : * libmalcontent.
543 : : *
544 : : * If deserialization fails, %MCT_MANAGER_ERROR_INVALID_DATA will be returned.
545 : : *
546 : : * Returns: (transfer full): deserialized web filter
547 : : * Since: 0.14.0
548 : : */
549 : : MctWebFilter *
550 : 12 : mct_web_filter_deserialize (GVariant *variant,
551 : : uid_t user_id,
552 : : GError **error)
553 : : {
554 : 12 : g_autoptr(MctWebFilter) web_filter = NULL;
555 : : guint32 filter_type;
556 : 12 : g_autoptr(GHashTable) block_lists = NULL, allow_lists = NULL; /* (element-type utf8 utf8) */
557 : 12 : g_auto(GStrv) custom_block_list = NULL, custom_allow_list = NULL;
558 : 12 : size_t custom_block_list_len = 0, custom_allow_list_len = 0;
559 : 12 : g_autoptr(GVariant) block_lists_variant = NULL;
560 : 12 : g_autoptr(GVariant) custom_block_list_variant = NULL;
561 : 12 : g_autoptr(GVariant) allow_lists_variant = NULL;
562 : 12 : g_autoptr(GVariant) custom_allow_list_variant = NULL;
563 : 12 : gboolean force_safe_search = FALSE;
564 : 12 : g_autoptr(GError) local_error = NULL;
565 : :
566 : 12 : g_return_val_if_fail (variant != NULL, NULL);
567 : 12 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
568 : :
569 : : /* Check the overall type. */
570 [ + + ]: 12 : if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}")))
571 : : {
572 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
573 : : MCT_MANAGER_ERROR_INVALID_DATA,
574 : : _("Web filter for user %u was in an unrecognized format"),
575 : : (unsigned int) user_id);
576 : 2 : return NULL;
577 : : }
578 : :
579 : : /* Extract the properties we care about. The default values here should be
580 : : * kept in sync with those in the `org.freedesktop.Malcontent.WebFilter`
581 : : * D-Bus interface. */
582 [ + + ]: 10 : if (!g_variant_lookup (variant, "FilterType", "u",
583 : : &filter_type))
584 : : {
585 : : /* Default value. */
586 : 4 : filter_type = MCT_WEB_FILTER_TYPE_NONE;
587 : : }
588 : :
589 : : /* Check that the filter type is something we support. */
590 : : G_STATIC_ASSERT (sizeof (filter_type) >= sizeof (MctWebFilterType));
591 : :
592 [ + + ]: 10 : if ((unsigned int) filter_type > MCT_WEB_FILTER_TYPE_ALLOWLIST)
593 : : {
594 : 1 : g_set_error (error, MCT_MANAGER_ERROR,
595 : : MCT_MANAGER_ERROR_INVALID_DATA,
596 : : _("Web filter for user %u has an unrecognized type ‘%u’"),
597 : : (unsigned int) user_id, filter_type);
598 : 1 : return NULL;
599 : : }
600 : :
601 [ + + ]: 9 : if (g_variant_lookup (variant, "BlockLists", "@a{ss}", &block_lists_variant))
602 : : {
603 : 3 : block_lists = ass_to_hash_table (block_lists_variant, user_id, error);
604 [ - + ]: 3 : if (block_lists == NULL)
605 : 0 : return NULL;
606 : : }
607 : : else
608 : : {
609 : : /* Default value. */
610 : 6 : block_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
611 : : }
612 : :
613 [ - + ]: 9 : if (g_variant_lookup (variant, "CustomBlockList", "@as", &custom_block_list_variant))
614 : : {
615 : 0 : custom_block_list = as_to_strv (custom_block_list_variant, &custom_block_list_len, user_id, &local_error);
616 [ # # ]: 0 : if (local_error != NULL)
617 : : {
618 : 0 : g_propagate_error (error, g_steal_pointer (&local_error));
619 : 0 : return NULL;
620 : : }
621 : : }
622 : : else
623 : : {
624 : : /* Default value. */
625 : 9 : custom_block_list = NULL;
626 : 9 : custom_block_list_len = 0;
627 : : }
628 : :
629 [ + + ]: 9 : if (g_variant_lookup (variant, "AllowLists", "@a{ss}", &allow_lists_variant))
630 : : {
631 : 1 : allow_lists = ass_to_hash_table (allow_lists_variant, user_id, error);
632 [ - + ]: 1 : if (allow_lists == NULL)
633 : 0 : return NULL;
634 : : }
635 : : else
636 : : {
637 : : /* Default value. */
638 : 8 : allow_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
639 : : }
640 : :
641 [ - + ]: 9 : if (g_variant_lookup (variant, "CustomAllowList", "@as", &custom_allow_list_variant))
642 : : {
643 : 0 : custom_allow_list = as_to_strv (custom_allow_list_variant, &custom_allow_list_len, user_id, &local_error);
644 [ # # ]: 0 : if (local_error != NULL)
645 : : {
646 : 0 : g_propagate_error (error, g_steal_pointer (&local_error));
647 : 0 : return NULL;
648 : : }
649 : : }
650 : : else
651 : : {
652 : : /* Default value. */
653 : 9 : custom_allow_list = NULL;
654 : 9 : custom_allow_list_len = 0;
655 : : }
656 : :
657 [ + + ]: 9 : if (!g_variant_lookup (variant, "ForceSafeSearch", "b", &force_safe_search))
658 : : {
659 : : /* Default value. */
660 : 7 : force_safe_search = FALSE;
661 : : }
662 : :
663 : : /* Success. Create an #MctWebFilter object to contain the results. */
664 : 9 : web_filter = g_new0 (MctWebFilter, 1);
665 : 9 : web_filter->ref_count = 1;
666 : 9 : web_filter->user_id = user_id;
667 : 9 : web_filter->filter_type = filter_type;
668 : 9 : web_filter->block_lists = g_steal_pointer (&block_lists);
669 : 9 : web_filter->custom_block_list = g_steal_pointer (&custom_block_list);
670 : 9 : web_filter->custom_block_list_len = custom_block_list_len;
671 : 9 : web_filter->allow_lists = g_steal_pointer (&allow_lists);
672 : 9 : web_filter->custom_allow_list = g_steal_pointer (&custom_allow_list);
673 : 9 : web_filter->custom_allow_list_len = custom_allow_list_len;
674 : 9 : web_filter->force_safe_search = force_safe_search;
675 : :
676 : 9 : return g_steal_pointer (&web_filter);
677 : : }
678 : :
679 : : static gboolean
680 : 28 : hash_table_equal0 (GHashTable *a,
681 : : GHashTable *b,
682 : : GEqualFunc value_equal_func)
683 : : {
684 : : GHashTableIter iter;
685 : : void *key, *a_value, *b_value;
686 : :
687 [ + + + - ]: 28 : if (a == NULL && b == NULL)
688 : 8 : return TRUE;
689 [ + - - + ]: 20 : else if (a == NULL || b == NULL)
690 : 0 : return FALSE;
691 : :
692 [ + + ]: 20 : if (g_hash_table_size (a) != g_hash_table_size (b))
693 : 6 : return FALSE;
694 : :
695 : 14 : g_hash_table_iter_init (&iter, a);
696 : :
697 [ + + ]: 18 : while (g_hash_table_iter_next (&iter, &key, &a_value))
698 : : {
699 [ - + ]: 4 : if (!g_hash_table_lookup_extended (b, key, NULL, &b_value))
700 : 0 : return FALSE;
701 : :
702 [ - + ]: 4 : if (!value_equal_func (a_value, b_value))
703 : 0 : return FALSE;
704 : : }
705 : :
706 : 14 : return TRUE;
707 : : }
708 : :
709 : : static gboolean
710 : 22 : strv_equal0 (const char * const *a,
711 : : const char * const *b)
712 : : {
713 [ + - + - ]: 22 : if (a == NULL && b == NULL)
714 : 22 : return TRUE;
715 [ # # # # ]: 0 : else if (a == NULL || b == NULL)
716 : 0 : return FALSE;
717 : :
718 : 0 : return g_strv_equal (a, b);
719 : : }
720 : :
721 : : /**
722 : : * mct_web_filter_equal:
723 : : * @a: (not nullable): a web filter
724 : : * @b: (not nullable): a web filter
725 : : *
726 : : * Check whether web filters @a and @b are equal.
727 : : *
728 : : * Returns: true if @a and @b are equal, false otherwise
729 : : * Since: 0.14.0
730 : : */
731 : : gboolean
732 : 39 : mct_web_filter_equal (MctWebFilter *a,
733 : : MctWebFilter *b)
734 : : {
735 : 39 : g_return_val_if_fail (a != NULL, FALSE);
736 : 39 : g_return_val_if_fail (a->ref_count >= 1, FALSE);
737 : 39 : g_return_val_if_fail (b != NULL, FALSE);
738 : 39 : g_return_val_if_fail (b->ref_count >= 1, FALSE);
739 : :
740 : 68 : return (a->user_id == b->user_id &&
741 [ + + + + ]: 46 : a->filter_type == b->filter_type &&
742 : 17 : hash_table_equal0 (a->block_lists, b->block_lists, g_str_equal) &&
743 [ + - + - ]: 22 : a->custom_block_list_len == b->custom_block_list_len &&
744 : 11 : strv_equal0 ((const char * const *) a->custom_block_list,
745 [ + - ]: 22 : (const char * const *) b->custom_block_list) &&
746 : 11 : hash_table_equal0 (a->allow_lists, b->allow_lists, g_str_equal) &&
747 [ + - + - ]: 22 : a->custom_allow_list_len == b->custom_allow_list_len &&
748 : 11 : strv_equal0 ((const char * const *) a->custom_allow_list,
749 [ + + ]: 79 : (const char * const *) b->custom_allow_list) &&
750 [ + + ]: 11 : a->force_safe_search == b->force_safe_search);
751 : : }
752 : :
753 : : /*
754 : : * Actual implementation of #MctWebFilterBuilder.
755 : : *
756 : : * All members are %NULL if un-initialised, cleared, or ended.
757 : : */
758 : : typedef struct
759 : : {
760 : : MctWebFilterType filter_type;
761 : :
762 : : GHashTable *block_lists; /* (nullable) (owned) */
763 : : GPtrArray *custom_block_list; /* (nullable) (owned) */
764 : : GHashTable *allow_lists; /* (nullable) (owned) */
765 : : GPtrArray *custom_allow_list; /* (nullable) (owned) */
766 : :
767 : : gboolean force_safe_search;
768 : :
769 : : /*< private >*/
770 : : gpointer padding[10];
771 : : } MctWebFilterBuilderReal;
772 : :
773 : : G_STATIC_ASSERT (sizeof (MctWebFilterBuilderReal) ==
774 : : sizeof (MctWebFilterBuilder));
775 : : G_STATIC_ASSERT (__alignof__ (MctWebFilterBuilderReal) ==
776 : : __alignof__ (MctWebFilterBuilder));
777 : :
778 [ + - + - : 4 : G_DEFINE_BOXED_TYPE (MctWebFilterBuilder, mct_web_filter_builder,
+ - ]
779 : : mct_web_filter_builder_copy, mct_web_filter_builder_free)
780 : :
781 : : /**
782 : : * mct_web_filter_builder_init:
783 : : * @builder: an uninitialised #MctWebFilterBuilder
784 : : *
785 : : * Initialise the given @builder so it can be used to construct a new
786 : : * #MctWebFilter. @builder must have been allocated on the stack, and must
787 : : * not already be initialised.
788 : : *
789 : : * Construct the #MctWebFilter by calling methods on @builder, followed by
790 : : * mct_web_filter_builder_end(). To abort construction, use
791 : : * mct_web_filter_builder_clear().
792 : : *
793 : : * Since: 0.14.0
794 : : */
795 : : void
796 : 1 : mct_web_filter_builder_init (MctWebFilterBuilder *builder)
797 : : {
798 : 1 : MctWebFilterBuilder local_builder = MCT_WEB_FILTER_BUILDER_INIT ();
799 : 1 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
800 : :
801 : 1 : g_return_if_fail (_builder != NULL);
802 : 1 : g_return_if_fail (_builder->filter_type == MCT_WEB_FILTER_TYPE_NONE);
803 : :
804 : 1 : memcpy (builder, &local_builder, sizeof (local_builder));
805 : : }
806 : :
807 : : /**
808 : : * mct_web_filter_builder_clear:
809 : : * @builder: a web filter builder
810 : : *
811 : : * Clear @builder, freeing any internal state in it. This will not free the
812 : : * top-level storage for @builder itself, which is assumed to be allocated on
813 : : * the stack.
814 : : *
815 : : * If called on an already-cleared #MctWebFilterBuilder, this function is
816 : : * idempotent.
817 : : *
818 : : * Since: 0.14.0
819 : : */
820 : : void
821 : 7 : mct_web_filter_builder_clear (MctWebFilterBuilder *builder)
822 : : {
823 : 7 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
824 : :
825 : 7 : g_return_if_fail (_builder != NULL);
826 : :
827 [ - + ]: 7 : g_clear_pointer (&_builder->block_lists, g_hash_table_unref);
828 [ - + ]: 7 : g_clear_pointer (&_builder->custom_block_list, g_ptr_array_unref);
829 [ - + ]: 7 : g_clear_pointer (&_builder->allow_lists, g_hash_table_unref);
830 [ - + ]: 7 : g_clear_pointer (&_builder->custom_allow_list, g_ptr_array_unref);
831 : :
832 : 7 : _builder->filter_type = MCT_WEB_FILTER_TYPE_NONE;
833 : 7 : _builder->force_safe_search = FALSE;
834 : : }
835 : :
836 : : /**
837 : : * mct_web_filter_builder_new:
838 : : *
839 : : * Construct a new #MctWebFilterBuilder on the heap. This is intended for
840 : : * language bindings. The returned builder must eventually be freed with
841 : : * mct_web_filter_builder_free(), but can be cleared zero or more times with
842 : : * mct_web_filter_builder_clear() first.
843 : : *
844 : : * Returns: (transfer full): a new heap-allocated #MctWebFilterBuilder
845 : : * Since: 0.14.0
846 : : */
847 : : MctWebFilterBuilder *
848 : 0 : mct_web_filter_builder_new (void)
849 : : {
850 : 0 : g_autoptr(MctWebFilterBuilder) builder = NULL;
851 : :
852 : 0 : builder = g_new0 (MctWebFilterBuilder, 1);
853 : 0 : mct_web_filter_builder_init (builder);
854 : :
855 : 0 : return g_steal_pointer (&builder);
856 : : }
857 : :
858 : : static void *
859 : 0 : identity_copy (const void *src,
860 : : void *user_data)
861 : : {
862 : 0 : return (void *) src;
863 : : }
864 : :
865 : : static GHashTable *
866 : 0 : hash_table_copy (GHashTable *src,
867 : : GCopyFunc key_copy_func,
868 : : void *key_copy_user_data,
869 : : GCopyFunc value_copy_func,
870 : : void *value_copy_user_data)
871 : : {
872 : : GHashTableIter iter;
873 : : void *key, *value;
874 : 0 : g_autoptr(GHashTable) dest = NULL;
875 : :
876 : 0 : g_assert (src != NULL);
877 : :
878 : 0 : g_hash_table_iter_init (&iter, src);
879 : 0 : dest = g_hash_table_new_similar (src);
880 : :
881 [ # # ]: 0 : if (key_copy_func == NULL)
882 : 0 : key_copy_func = identity_copy;
883 [ # # ]: 0 : if (value_copy_func == NULL)
884 : 0 : value_copy_func = identity_copy;
885 : :
886 [ # # ]: 0 : while (g_hash_table_iter_next (&iter, &key, &value))
887 : : {
888 : : void *key_copy, *value_copy;
889 : :
890 : 0 : key_copy = key_copy_func (key, key_copy_user_data);
891 : 0 : value_copy = value_copy_func (value, value_copy_user_data);
892 : :
893 : 0 : g_hash_table_insert (dest, g_steal_pointer (&key_copy), g_steal_pointer (&value_copy));
894 : : }
895 : :
896 : 0 : return g_steal_pointer (&dest);
897 : : }
898 : :
899 : : static void *
900 : 0 : str_copy (const void *str,
901 : : void *user_data)
902 : : {
903 : 0 : return g_strdup (str);
904 : : }
905 : :
906 : : /**
907 : : * mct_web_filter_builder_copy:
908 : : * @builder: a web filter builder
909 : : *
910 : : * Copy the given @builder to a newly-allocated #MctWebFilterBuilder on the
911 : : * heap. This is safe to use with cleared, stack-allocated
912 : : * #MctWebFilterBuilders.
913 : : *
914 : : * Returns: (transfer full): a copy of @builder
915 : : * Since: 0.14.0
916 : : */
917 : : MctWebFilterBuilder *
918 : 0 : mct_web_filter_builder_copy (MctWebFilterBuilder *builder)
919 : : {
920 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
921 : 0 : g_autoptr(MctWebFilterBuilder) copy = NULL;
922 : : MctWebFilterBuilderReal *_copy;
923 : :
924 : 0 : g_return_val_if_fail (builder != NULL, NULL);
925 : :
926 : 0 : copy = mct_web_filter_builder_new ();
927 : 0 : _copy = (MctWebFilterBuilderReal *) copy;
928 : :
929 : 0 : mct_web_filter_builder_clear (copy);
930 : 0 : _copy->filter_type = _builder->filter_type;
931 [ # # ]: 0 : _copy->block_lists = (_builder->block_lists != NULL) ? hash_table_copy (_builder->block_lists, str_copy, NULL, str_copy, NULL) : NULL;
932 [ # # ]: 0 : _copy->custom_block_list = (_builder->custom_block_list != NULL) ? g_ptr_array_copy (_builder->custom_block_list, str_copy, NULL) : NULL;
933 [ # # ]: 0 : _copy->allow_lists = (_builder->allow_lists != NULL) ? hash_table_copy (_builder->allow_lists, str_copy, NULL, str_copy, NULL) : NULL;
934 [ # # ]: 0 : _copy->custom_block_list = (_builder->custom_block_list != NULL) ? g_ptr_array_copy (_builder->custom_block_list, str_copy, NULL) : NULL;
935 : 0 : _copy->force_safe_search = _builder->force_safe_search;
936 : :
937 : 0 : return g_steal_pointer (©);
938 : : }
939 : :
940 : : /**
941 : : * mct_web_filter_builder_free:
942 : : * @builder: a heap-allocated #MctWebFilterBuilder
943 : : *
944 : : * Free an #MctWebFilterBuilder originally allocated using
945 : : * mct_web_filter_builder_new(). This must not be called on stack-allocated
946 : : * builders initialised using mct_web_filter_builder_init().
947 : : *
948 : : * Since: 0.14.0
949 : : */
950 : : void
951 : 0 : mct_web_filter_builder_free (MctWebFilterBuilder *builder)
952 : : {
953 : 0 : g_return_if_fail (builder != NULL);
954 : :
955 : 0 : mct_web_filter_builder_clear (builder);
956 : 0 : g_free (builder);
957 : : }
958 : :
959 : : /**
960 : : * mct_web_filter_builder_end:
961 : : * @builder: an initialised #MctWebFilterBuilder
962 : : *
963 : : * Finish constructing an #MctWebFilter with the given @builder, and return
964 : : * it. The #MctWebFilterBuilder will be cleared as if
965 : : * mct_web_filter_builder_clear() had been called.
966 : : *
967 : : * Returns: (transfer full): a newly constructed #MctWebFilter
968 : : * Since: 0.14.0
969 : : */
970 : : MctWebFilter *
971 : 4 : mct_web_filter_builder_end (MctWebFilterBuilder *builder)
972 : : {
973 : 4 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
974 : 4 : g_autoptr(MctWebFilter) web_filter = NULL;
975 : :
976 : 4 : g_return_val_if_fail (_builder != NULL, NULL);
977 : :
978 : : /* Build the #MctWebFilter. */
979 : 4 : web_filter = g_new0 (MctWebFilter, 1);
980 : 4 : web_filter->ref_count = 1;
981 : 4 : web_filter->user_id = -1;
982 : 4 : web_filter->filter_type = _builder->filter_type;
983 : :
984 [ - + ]: 4 : if (_builder->custom_block_list != NULL)
985 : 0 : g_ptr_array_sort_values (_builder->custom_block_list, (GCompareFunc) g_strcmp0);
986 [ - + ]: 4 : if (_builder->custom_allow_list != NULL)
987 : 0 : g_ptr_array_sort_values (_builder->custom_allow_list, (GCompareFunc) g_strcmp0);
988 : :
989 [ - + ]: 4 : web_filter->block_lists = (_builder->block_lists != NULL) ? g_hash_table_ref (_builder->block_lists) : NULL;
990 [ - + ]: 4 : web_filter->custom_block_list = (_builder->custom_block_list != NULL) ? (char **) g_ptr_array_steal (_builder->custom_block_list, &web_filter->custom_block_list_len) : NULL;
991 [ - + ]: 4 : web_filter->allow_lists = (_builder->allow_lists != NULL) ? g_hash_table_ref (_builder->allow_lists) : NULL;
992 [ - + ]: 4 : web_filter->custom_allow_list = (_builder->custom_allow_list != NULL) ? (char **) g_ptr_array_steal (_builder->custom_allow_list, &web_filter->custom_allow_list_len) : NULL;
993 : :
994 : 4 : web_filter->force_safe_search = _builder->force_safe_search;
995 : :
996 : 4 : mct_web_filter_builder_clear (builder);
997 : :
998 : 4 : return g_steal_pointer (&web_filter);
999 : : }
1000 : :
1001 : : /**
1002 : : * mct_web_filter_builder_set_filter_type:
1003 : : * @builder: an initialised #MctWebFilterBuilder
1004 : : * @filter_type: type of web filter
1005 : : *
1006 : : * Set the type of web filter to apply to the user.
1007 : : *
1008 : : * Since: 0.14.0
1009 : : */
1010 : : void
1011 : 0 : mct_web_filter_builder_set_filter_type (MctWebFilterBuilder *builder,
1012 : : MctWebFilterType filter_type)
1013 : : {
1014 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1015 : :
1016 : 0 : g_return_if_fail (_builder != NULL);
1017 : :
1018 : 0 : _builder->filter_type = filter_type;
1019 : : }
1020 : :
1021 : : /**
1022 : : * mct_web_filter_builder_add_block_list:
1023 : : * @builder: a web filter builder
1024 : : * @id: (not nullable): ID of the filter
1025 : : * @filter_uri: (not nullable): URI of the filter to download
1026 : : *
1027 : : * Adds a block list to the [struct@Malcontent.WebFilter], mapping the given @id
1028 : : * to a filter list downloadable at @filter_uri.
1029 : : *
1030 : : * All the entries at @filter_uri will be blocked. They must all be hostnames;
1031 : : * see the top-level documentation for [struct@Malcontent.WebFilter] for details
1032 : : * of the allowed filter list and ID formats.
1033 : : *
1034 : : * The filter list will be downloaded when the user’s web filter is compiled,
1035 : : * not when this function is called.
1036 : : *
1037 : : * Since: 0.14.0
1038 : : */
1039 : : void
1040 : 0 : mct_web_filter_builder_add_block_list (MctWebFilterBuilder *builder,
1041 : : const char *id,
1042 : : const char *filter_uri)
1043 : : {
1044 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1045 : :
1046 : 0 : g_return_if_fail (_builder != NULL);
1047 : 0 : g_return_if_fail (mct_web_filter_validate_filter_id (id));
1048 : 0 : g_return_if_fail (validate_filter_uri (filter_uri));
1049 : :
1050 [ # # ]: 0 : if (_builder->block_lists == NULL)
1051 : 0 : _builder->block_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1052 : :
1053 : 0 : g_hash_table_replace (_builder->block_lists, g_strdup (id), g_strdup (filter_uri));
1054 : : }
1055 : :
1056 : : /**
1057 : : * mct_web_filter_builder_add_custom_block_list_entry:
1058 : : * @builder: a web filter builder
1059 : : * @hostname: (not nullable): hostname to block
1060 : : *
1061 : : * Adds a single hostname to the [struct@Malcontent.WebFilter], to be blocked.
1062 : : *
1063 : : * See the top-level documentation for [struct@Malcontent.WebFilter] for details
1064 : : * of the allowed filter list and ID formats.
1065 : : *
1066 : : * Since: 0.14.0
1067 : : */
1068 : : void
1069 : 0 : mct_web_filter_builder_add_custom_block_list_entry (MctWebFilterBuilder *builder,
1070 : : const char *hostname)
1071 : : {
1072 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1073 : :
1074 : 0 : g_return_if_fail (_builder != NULL);
1075 : 0 : g_return_if_fail (mct_web_filter_validate_hostname (hostname));
1076 : :
1077 [ # # ]: 0 : if (_builder->custom_block_list == NULL)
1078 : 0 : _builder->custom_block_list = g_ptr_array_new_null_terminated (0, g_free, TRUE);
1079 : :
1080 : 0 : g_ptr_array_add (_builder->custom_block_list, g_strdup (hostname));
1081 : : }
1082 : :
1083 : : /**
1084 : : * mct_web_filter_builder_add_allow_list:
1085 : : * @builder: a web filter builder
1086 : : * @id: (not nullable): ID of the filter
1087 : : * @filter_uri: (not nullable): URI of the filter to download
1088 : : *
1089 : : * Adds a allow list to the [struct@Malcontent.WebFilter], mapping the given @id
1090 : : * to a filter list downloadable at @filter_uri.
1091 : : *
1092 : : * All the entries at @filter_uri will be allowed. They must all be hostnames;
1093 : : * see the top-level documentation for [struct@Malcontent.WebFilter] for details
1094 : : * of the allowed filter list and ID formats.
1095 : : *
1096 : : * The filter list will be downloaded when the user’s web filter is compiled,
1097 : : * not when this function is called.
1098 : : *
1099 : : * Since: 0.14.0
1100 : : */
1101 : : void
1102 : 0 : mct_web_filter_builder_add_allow_list (MctWebFilterBuilder *builder,
1103 : : const char *id,
1104 : : const char *filter_uri)
1105 : : {
1106 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1107 : :
1108 : 0 : g_return_if_fail (_builder != NULL);
1109 : 0 : g_return_if_fail (mct_web_filter_validate_filter_id (id));
1110 : 0 : g_return_if_fail (validate_filter_uri (filter_uri));
1111 : :
1112 [ # # ]: 0 : if (_builder->allow_lists == NULL)
1113 : 0 : _builder->allow_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1114 : :
1115 : 0 : g_hash_table_replace (_builder->allow_lists, g_strdup (id), g_strdup (filter_uri));
1116 : : }
1117 : :
1118 : : /**
1119 : : * mct_web_filter_builder_add_custom_allow_list_entry:
1120 : : * @builder: a web filter builder
1121 : : * @hostname: (not nullable): hostname to allow
1122 : : *
1123 : : * Adds a single hostname to the [struct@Malcontent.WebFilter], to be allowed.
1124 : : *
1125 : : * See the top-level documentation for [struct@Malcontent.WebFilter] for details
1126 : : * of the allowed filter list and ID formats.
1127 : : *
1128 : : * Since: 0.14.0
1129 : : */
1130 : : void
1131 : 0 : mct_web_filter_builder_add_custom_allow_list_entry (MctWebFilterBuilder *builder,
1132 : : const char *hostname)
1133 : : {
1134 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1135 : :
1136 : 0 : g_return_if_fail (_builder != NULL);
1137 : 0 : g_return_if_fail (mct_web_filter_validate_hostname (hostname));
1138 : :
1139 [ # # ]: 0 : if (_builder->custom_allow_list == NULL)
1140 : 0 : _builder->custom_allow_list = g_ptr_array_new_null_terminated (0, g_free, TRUE);
1141 : :
1142 : 0 : g_ptr_array_add (_builder->custom_allow_list, g_strdup (hostname));
1143 : : }
1144 : :
1145 : : /**
1146 : : * mct_web_filter_builder_set_force_safe_search:
1147 : : * @builder: a web filter builder
1148 : : * @force_safe_search: true to force safe search to be enabled, false otherwise
1149 : : *
1150 : : * Sets the safe search preference for the [struct@Malcontent.WebFilter].
1151 : : *
1152 : : * If enabled, search engines and other popular websites will be automatically
1153 : : * redirected to their ‘safe search’ variant, if supported.
1154 : : *
1155 : : * Since: 0.14.0
1156 : : */
1157 : : void
1158 : 0 : mct_web_filter_builder_set_force_safe_search (MctWebFilterBuilder *builder,
1159 : : gboolean force_safe_search)
1160 : : {
1161 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1162 : :
1163 : 0 : g_return_if_fail (_builder != NULL);
1164 : :
1165 : 0 : _builder->force_safe_search = force_safe_search;
1166 : : }
|