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 : : * See [func@Malcontent.WebFilter.validate_domain_name] for validating
336 : : * domain names instead. Domain names are entries in the DNS database, hostnames
337 : : * are website addresses. Every hostname is a domain name, but not vice-versa.
338 : : *
339 : : * Returns: true if @hostname is a valid hostname, false otherwise
340 : : * Since: 0.14.0
341 : : */
342 : : gboolean
343 : 23 : mct_web_filter_validate_hostname (const char *hostname)
344 : : {
345 : 23 : return mct_web_filter_validate_hostname_len (hostname, G_MAXSIZE);
346 : : }
347 : :
348 : : /**
349 : : * mct_web_filter_validate_hostname_len:
350 : : * @hostname: a potential hostname
351 : : * @max_len: length (in bytes) to check, or until the first nul byte is reached
352 : : *
353 : : * Validate a potential hostname.
354 : : *
355 : : * This checks against [RFC 1035](https://datatracker.ietf.org/doc/html/rfc1035).
356 : : *
357 : : * See [func@Malcontent.WebFilter.validate_domain_name_len] for validating
358 : : * domain names instead. Domain names are entries in the DNS database, hostnames
359 : : * are website addresses. Every hostname is a domain name, but not vice-versa.
360 : : *
361 : : * Returns: true if @hostname is a valid hostname, false otherwise
362 : : * Since: 0.14.0
363 : : */
364 : : gboolean
365 : 23 : mct_web_filter_validate_hostname_len (const char *hostname,
366 : : size_t max_len)
367 : : {
368 : : size_t i;
369 : 23 : size_t start_of_label = 0;
370 : :
371 : 23 : g_return_val_if_fail (hostname != NULL, FALSE);
372 : :
373 : : /* A valid hostname must:
374 : : * - Be ≤253 characters long (https://web.archive.org/web/20190518124533/https://devblogs.microsoft.com/oldnewthing/?p=7873)
375 : : * - Have labels 1≤x≤63 characters long
376 : : * - Only contain characters A-Z, a-z, 0-9, `.` and `-`
377 : : * - Not have a label which starts or ends with a hyphen
378 : : * - Be non-empty
379 : : *
380 : : * See https://datatracker.ietf.org/doc/html/rfc1035
381 : : */
382 [ + + + - ]: 629 : for (i = 0; hostname[i] != '\0' && i < max_len; i++)
383 : : {
384 [ + + ]: 618 : if (!g_ascii_isalnum (hostname[i]) &&
385 [ + + ]: 33 : hostname[i] != '.' &&
386 [ + + ]: 10 : hostname[i] != '-')
387 : 3 : return FALSE;
388 : :
389 [ + + ]: 615 : if (hostname[i] == '.')
390 : : {
391 [ + + ]: 23 : if (i - start_of_label == 0 ||
392 [ + + ]: 18 : i - start_of_label > 63)
393 : 7 : return FALSE;
394 : :
395 [ + + ]: 16 : if (hostname[start_of_label] == '-' ||
396 [ + + ]: 15 : hostname[i - 1] == '-')
397 : 2 : return FALSE;
398 : :
399 : 14 : start_of_label = i + 1;
400 : : }
401 : : }
402 : :
403 : : /* Do the checks for the final label, if the hostname didn’t end in a `.` */
404 [ + + + + ]: 11 : if (i > 0 && hostname[i - 1] != '.')
405 : : {
406 [ + - ]: 9 : if (i - start_of_label == 0 ||
407 [ + + ]: 9 : i - start_of_label > 63)
408 : 1 : return FALSE;
409 : :
410 [ + + ]: 8 : if (hostname[start_of_label] == '-' ||
411 [ + + ]: 5 : hostname[i - 1] == '-')
412 : 4 : return FALSE;
413 : : }
414 : :
415 [ + + + + ]: 6 : if (i > 0 && i <= 253)
416 : : {
417 : : /* Double check against domain name validity, as hostnames are meant to be
418 : : * a subset of domain names. */
419 : 4 : g_assert (mct_web_filter_validate_domain_name_len (hostname, max_len));
420 : 4 : return TRUE;
421 : : }
422 : :
423 : 2 : return FALSE;
424 : : }
425 : :
426 : : /**
427 : : * mct_web_filter_validate_domain_name:
428 : : * @domain_name: a potential domain name
429 : : *
430 : : * Validate a potential domain name.
431 : : *
432 : : * This checks against [RFC 2181](https://datatracker.ietf.org/doc/html/rfc2181).
433 : : *
434 : : * See [func@Malcontent.WebFilter.validate_hostname] for validating
435 : : * hostnames instead. Domain names are entries in the DNS database, hostnames
436 : : * are website addresses. Every hostname is a domain name, but not vice-versa.
437 : : *
438 : : * Returns: true if @domain_name is a valid domain name, false otherwise
439 : : * Since: 0.14.0
440 : : */
441 : : gboolean
442 : 18 : mct_web_filter_validate_domain_name (const char *domain_name)
443 : : {
444 : 18 : return mct_web_filter_validate_domain_name_len (domain_name, G_MAXSIZE);
445 : : }
446 : :
447 : : /**
448 : : * mct_web_filter_validate_domain_name_len:
449 : : * @domain_name: a potential domain_name
450 : : * @max_len: length (in bytes) to check, or until the first nul byte is reached
451 : : *
452 : : * Validate a potential domain name.
453 : : *
454 : : * This checks against [RFC 2181](https://datatracker.ietf.org/doc/html/rfc2181).
455 : : *
456 : : * See [func@Malcontent.WebFilter.validate_hostname_len] for validating
457 : : * hostnames instead. Domain names are entries in the DNS database, hostnames
458 : : * are website addresses. Every hostname is a domain name, but not vice-versa.
459 : : *
460 : : * Returns: true if @domain_name is a valid domain_name, false otherwise
461 : : * Since: 0.14.0
462 : : */
463 : : gboolean
464 : 22 : mct_web_filter_validate_domain_name_len (const char *domain_name,
465 : : size_t max_len)
466 : : {
467 : : size_t i;
468 : 22 : size_t start_of_label = 0;
469 : :
470 : 22 : g_return_val_if_fail (domain_name != NULL, FALSE);
471 : :
472 : : /* A valid domain_name must:
473 : : * - Be ≤253 characters long (https://web.archive.org/web/20190518124533/https://devblogs.microsoft.com/oldnewthing/?p=7873)
474 : : * - Have labels 1≤x≤63 characters long (https://datatracker.ietf.org/doc/html/rfc2181#section-11)
475 : : * - Be non-empty
476 : : *
477 : : * In addition, we impose the requirements that each octet is a valid ASCII
478 : : * character (i.e. non-nul and <128) because nobody reasonably does anything
479 : : * else, and dealing with the byte strings which result from allowing other
480 : : * octets would be a recipe for bugs.
481 : : *
482 : : * See https://datatracker.ietf.org/doc/html/rfc2181
483 : : */
484 [ + + + - ]: 655 : for (i = 0; domain_name[i] != '\0' && i < max_len; i++)
485 : : {
486 [ + - ]: 639 : if (domain_name[i] == 0 ||
487 [ + + ]: 639 : (unsigned char) domain_name[i] >= 128)
488 : 1 : return FALSE;
489 : :
490 [ + + ]: 638 : if (domain_name[i] == '.')
491 : : {
492 [ + + ]: 27 : if (i - start_of_label == 0 ||
493 [ + + ]: 24 : i - start_of_label > 63)
494 : 5 : return FALSE;
495 : :
496 : 22 : start_of_label = i + 1;
497 : : }
498 : : }
499 : :
500 : : /* Do the checks for the final label, if the domain_name didn’t end in a `.` */
501 [ + + + + ]: 16 : if (i > 0 && domain_name[i - 1] != '.')
502 : : {
503 [ + - ]: 12 : if (i - start_of_label == 0 ||
504 [ + + ]: 12 : i - start_of_label > 63)
505 : 1 : return FALSE;
506 : : }
507 : :
508 [ + + + + ]: 15 : return (i > 0 && i <= 253);
509 : : }
510 : :
511 : : static GHashTable *
512 : 4 : ass_to_hash_table (GVariant *ass,
513 : : uid_t user_id,
514 : : GError **error)
515 : : {
516 : : GVariantIter iter;
517 : : const char *filter_list_id, *filter_uri;
518 : 4 : g_autoptr(GHashTable) table = NULL;
519 : :
520 : 4 : g_variant_iter_init (&iter, ass);
521 : 4 : table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
522 : :
523 [ + + ]: 9 : while (g_variant_iter_loop (&iter, "{&s&s}", &filter_list_id, &filter_uri))
524 : : {
525 [ + - - + ]: 10 : if (!mct_web_filter_validate_filter_id (filter_list_id) ||
526 : 5 : !validate_filter_uri (filter_uri))
527 : : {
528 : 0 : g_set_error (error, MCT_MANAGER_ERROR,
529 : : MCT_MANAGER_ERROR_INVALID_DATA,
530 : : _("Web filter for user %u references an invalid filter list ‘%s’ at ‘%s’"),
531 : : (unsigned int) user_id, filter_list_id, filter_uri);
532 : 0 : return NULL;
533 : : }
534 : :
535 : 10 : g_hash_table_replace (table, g_strdup (filter_list_id), g_strdup (filter_uri));
536 : : }
537 : :
538 : 4 : return g_steal_pointer (&table);
539 : : }
540 : :
541 : : static GVariant *
542 : 0 : strv_to_as (const char * const *strv,
543 : : size_t strv_len)
544 : : {
545 : 0 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("as"));
546 : :
547 [ # # ]: 0 : for (size_t i = 0; i < strv_len; i++)
548 : 0 : g_variant_builder_add (&builder, "s", strv[i]);
549 : :
550 : 0 : return g_variant_builder_end (&builder);
551 : : }
552 : :
553 : : static char **
554 : 0 : as_to_strv (GVariant *as,
555 : : size_t *out_strv_len,
556 : : uid_t user_id,
557 : : GError **error)
558 : : {
559 : : GVariantIter iter;
560 : : const char *filter_list_entry;
561 : 0 : g_autoptr(GPtrArray) array = NULL;
562 : :
563 : 0 : g_variant_iter_init (&iter, as);
564 : 0 : array = g_ptr_array_new_null_terminated (g_variant_n_children (as), g_free, TRUE);
565 : :
566 [ # # ]: 0 : while (g_variant_iter_loop (&iter, "&s", &filter_list_entry))
567 : : {
568 [ # # ]: 0 : if (!mct_web_filter_validate_hostname (filter_list_entry))
569 : : {
570 : 0 : g_set_error (error, MCT_MANAGER_ERROR,
571 : : MCT_MANAGER_ERROR_INVALID_DATA,
572 : : _("Web filter for user %u contains an invalid entry ‘%s’"),
573 : : (unsigned int) user_id, filter_list_entry);
574 [ # # ]: 0 : if (out_strv_len != NULL)
575 : 0 : *out_strv_len = 0;
576 : 0 : return NULL;
577 : : }
578 : :
579 : 0 : g_ptr_array_add (array, g_strdup (filter_list_entry));
580 : : }
581 : :
582 : 0 : g_ptr_array_sort_values (array, (GCompareFunc) g_strcmp0);
583 : :
584 [ # # ]: 0 : if (out_strv_len != NULL)
585 : 0 : *out_strv_len = array->len;
586 : :
587 : 0 : return (char **) g_ptr_array_steal (array, out_strv_len);
588 : : }
589 : :
590 : : /**
591 : : * mct_web_filter_serialize:
592 : : * @filter: a web filter
593 : : *
594 : : * Build a #GVariant which contains the web filter from @filter, in an
595 : : * opaque variant format. This format may change in future, but
596 : : * mct_web_filter_deserialize() is guaranteed to always be able to load any
597 : : * variant produced by the current or any previous version of
598 : : * mct_web_filter_serialize().
599 : : *
600 : : * Returns: (transfer floating): a new, floating #GVariant containing the
601 : : * web filter
602 : : * Since: 0.14.0
603 : : */
604 : : GVariant *
605 : 1 : mct_web_filter_serialize (MctWebFilter *filter)
606 : : {
607 : 2 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{sv}"));
608 : :
609 : 1 : g_return_val_if_fail (filter != NULL, NULL);
610 : 1 : g_return_val_if_fail (filter->ref_count >= 1, NULL);
611 : :
612 : : /* The serialisation format is exactly the
613 : : * `org.freedesktop.Malcontent.WebFilter` D-Bus interface. */
614 : 1 : g_variant_builder_add (&builder, "{sv}", "FilterType",
615 : 1 : g_variant_new_uint32 (filter->filter_type));
616 [ - + ]: 1 : if (filter->block_lists != NULL)
617 : 0 : g_variant_builder_add (&builder, "{sv}", "BlockLists",
618 : : hash_table_to_ass (filter->block_lists));
619 [ - + ]: 1 : if (filter->custom_block_list != NULL)
620 : 0 : g_variant_builder_add (&builder, "{sv}", "CustomBlockList",
621 : 0 : strv_to_as ((const char * const *) filter->custom_block_list, filter->custom_block_list_len));
622 [ - + ]: 1 : if (filter->allow_lists != NULL)
623 : 0 : g_variant_builder_add (&builder, "{sv}", "AllowLists",
624 : : hash_table_to_ass (filter->allow_lists));
625 [ - + ]: 1 : if (filter->custom_allow_list != NULL)
626 : 0 : g_variant_builder_add (&builder, "{sv}", "CustomAllowList",
627 : 0 : strv_to_as ((const char * const *) filter->custom_allow_list, filter->custom_allow_list_len));
628 : 1 : g_variant_builder_add (&builder, "{sv}", "ForceSafeSearch",
629 : : g_variant_new_boolean (filter->force_safe_search));
630 : :
631 : 1 : return g_variant_builder_end (&builder);
632 : : }
633 : :
634 : : /**
635 : : * mct_web_filter_deserialize:
636 : : * @variant: a serialized web filter variant
637 : : * @user_id: the ID of the user the web filter relates to
638 : : * @error: return location for a #GError, or %NULL
639 : : *
640 : : * Deserialize a set of web filters previously serialized with
641 : : * mct_web_filter_serialize(). This function guarantees to be able to
642 : : * deserialize any serialized form from this version or older versions of
643 : : * libmalcontent.
644 : : *
645 : : * If deserialization fails, %MCT_MANAGER_ERROR_INVALID_DATA will be returned.
646 : : *
647 : : * Returns: (transfer full): deserialized web filter
648 : : * Since: 0.14.0
649 : : */
650 : : MctWebFilter *
651 : 12 : mct_web_filter_deserialize (GVariant *variant,
652 : : uid_t user_id,
653 : : GError **error)
654 : : {
655 : 12 : g_autoptr(MctWebFilter) web_filter = NULL;
656 : : guint32 filter_type;
657 : 12 : g_autoptr(GHashTable) block_lists = NULL, allow_lists = NULL; /* (element-type utf8 utf8) */
658 : 12 : g_auto(GStrv) custom_block_list = NULL, custom_allow_list = NULL;
659 : 12 : size_t custom_block_list_len = 0, custom_allow_list_len = 0;
660 : 12 : g_autoptr(GVariant) block_lists_variant = NULL;
661 : 12 : g_autoptr(GVariant) custom_block_list_variant = NULL;
662 : 12 : g_autoptr(GVariant) allow_lists_variant = NULL;
663 : 12 : g_autoptr(GVariant) custom_allow_list_variant = NULL;
664 : 12 : gboolean force_safe_search = FALSE;
665 : 12 : g_autoptr(GError) local_error = NULL;
666 : :
667 : 12 : g_return_val_if_fail (variant != NULL, NULL);
668 : 12 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
669 : :
670 : : /* Check the overall type. */
671 [ + + ]: 12 : if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}")))
672 : : {
673 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
674 : : MCT_MANAGER_ERROR_INVALID_DATA,
675 : : _("Web filter for user %u was in an unrecognized format"),
676 : : (unsigned int) user_id);
677 : 2 : return NULL;
678 : : }
679 : :
680 : : /* Extract the properties we care about. The default values here should be
681 : : * kept in sync with those in the `org.freedesktop.Malcontent.WebFilter`
682 : : * D-Bus interface. */
683 [ + + ]: 10 : if (!g_variant_lookup (variant, "FilterType", "u",
684 : : &filter_type))
685 : : {
686 : : /* Default value. */
687 : 4 : filter_type = MCT_WEB_FILTER_TYPE_NONE;
688 : : }
689 : :
690 : : /* Check that the filter type is something we support. */
691 : : G_STATIC_ASSERT (sizeof (filter_type) >= sizeof (MctWebFilterType));
692 : :
693 [ + + ]: 10 : if ((unsigned int) filter_type > MCT_WEB_FILTER_TYPE_ALLOWLIST)
694 : : {
695 : 1 : g_set_error (error, MCT_MANAGER_ERROR,
696 : : MCT_MANAGER_ERROR_INVALID_DATA,
697 : : _("Web filter for user %u has an unrecognized type ‘%u’"),
698 : : (unsigned int) user_id, filter_type);
699 : 1 : return NULL;
700 : : }
701 : :
702 [ + + ]: 9 : if (g_variant_lookup (variant, "BlockLists", "@a{ss}", &block_lists_variant))
703 : : {
704 : 3 : block_lists = ass_to_hash_table (block_lists_variant, user_id, error);
705 [ - + ]: 3 : if (block_lists == NULL)
706 : 0 : return NULL;
707 : : }
708 : : else
709 : : {
710 : : /* Default value. */
711 : 6 : block_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
712 : : }
713 : :
714 [ - + ]: 9 : if (g_variant_lookup (variant, "CustomBlockList", "@as", &custom_block_list_variant))
715 : : {
716 : 0 : custom_block_list = as_to_strv (custom_block_list_variant, &custom_block_list_len, user_id, &local_error);
717 [ # # ]: 0 : if (local_error != NULL)
718 : : {
719 : 0 : g_propagate_error (error, g_steal_pointer (&local_error));
720 : 0 : return NULL;
721 : : }
722 : : }
723 : : else
724 : : {
725 : : /* Default value. */
726 : 9 : custom_block_list = NULL;
727 : 9 : custom_block_list_len = 0;
728 : : }
729 : :
730 [ + + ]: 9 : if (g_variant_lookup (variant, "AllowLists", "@a{ss}", &allow_lists_variant))
731 : : {
732 : 1 : allow_lists = ass_to_hash_table (allow_lists_variant, user_id, error);
733 [ - + ]: 1 : if (allow_lists == NULL)
734 : 0 : return NULL;
735 : : }
736 : : else
737 : : {
738 : : /* Default value. */
739 : 8 : allow_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
740 : : }
741 : :
742 [ - + ]: 9 : if (g_variant_lookup (variant, "CustomAllowList", "@as", &custom_allow_list_variant))
743 : : {
744 : 0 : custom_allow_list = as_to_strv (custom_allow_list_variant, &custom_allow_list_len, user_id, &local_error);
745 [ # # ]: 0 : if (local_error != NULL)
746 : : {
747 : 0 : g_propagate_error (error, g_steal_pointer (&local_error));
748 : 0 : return NULL;
749 : : }
750 : : }
751 : : else
752 : : {
753 : : /* Default value. */
754 : 9 : custom_allow_list = NULL;
755 : 9 : custom_allow_list_len = 0;
756 : : }
757 : :
758 [ + + ]: 9 : if (!g_variant_lookup (variant, "ForceSafeSearch", "b", &force_safe_search))
759 : : {
760 : : /* Default value. */
761 : 7 : force_safe_search = FALSE;
762 : : }
763 : :
764 : : /* Success. Create an #MctWebFilter object to contain the results. */
765 : 9 : web_filter = g_new0 (MctWebFilter, 1);
766 : 9 : web_filter->ref_count = 1;
767 : 9 : web_filter->user_id = user_id;
768 : 9 : web_filter->filter_type = filter_type;
769 : 9 : web_filter->block_lists = g_steal_pointer (&block_lists);
770 : 9 : web_filter->custom_block_list = g_steal_pointer (&custom_block_list);
771 : 9 : web_filter->custom_block_list_len = custom_block_list_len;
772 : 9 : web_filter->allow_lists = g_steal_pointer (&allow_lists);
773 : 9 : web_filter->custom_allow_list = g_steal_pointer (&custom_allow_list);
774 : 9 : web_filter->custom_allow_list_len = custom_allow_list_len;
775 : 9 : web_filter->force_safe_search = force_safe_search;
776 : :
777 : 9 : return g_steal_pointer (&web_filter);
778 : : }
779 : :
780 : : static gboolean
781 : 28 : hash_table_equal0 (GHashTable *a,
782 : : GHashTable *b,
783 : : GEqualFunc value_equal_func)
784 : : {
785 : : GHashTableIter iter;
786 : : void *key, *a_value, *b_value;
787 : :
788 [ + + + - ]: 28 : if (a == NULL && b == NULL)
789 : 8 : return TRUE;
790 [ + - - + ]: 20 : else if (a == NULL || b == NULL)
791 : 0 : return FALSE;
792 : :
793 [ + + ]: 20 : if (g_hash_table_size (a) != g_hash_table_size (b))
794 : 6 : return FALSE;
795 : :
796 : 14 : g_hash_table_iter_init (&iter, a);
797 : :
798 [ + + ]: 18 : while (g_hash_table_iter_next (&iter, &key, &a_value))
799 : : {
800 [ - + ]: 4 : if (!g_hash_table_lookup_extended (b, key, NULL, &b_value))
801 : 0 : return FALSE;
802 : :
803 [ - + ]: 4 : if (!value_equal_func (a_value, b_value))
804 : 0 : return FALSE;
805 : : }
806 : :
807 : 14 : return TRUE;
808 : : }
809 : :
810 : : static gboolean
811 : 22 : strv_equal0 (const char * const *a,
812 : : const char * const *b)
813 : : {
814 [ + - + - ]: 22 : if (a == NULL && b == NULL)
815 : 22 : return TRUE;
816 [ # # # # ]: 0 : else if (a == NULL || b == NULL)
817 : 0 : return FALSE;
818 : :
819 : 0 : return g_strv_equal (a, b);
820 : : }
821 : :
822 : : /**
823 : : * mct_web_filter_equal:
824 : : * @a: (not nullable): a web filter
825 : : * @b: (not nullable): a web filter
826 : : *
827 : : * Check whether web filters @a and @b are equal.
828 : : *
829 : : * Returns: true if @a and @b are equal, false otherwise
830 : : * Since: 0.14.0
831 : : */
832 : : gboolean
833 : 39 : mct_web_filter_equal (MctWebFilter *a,
834 : : MctWebFilter *b)
835 : : {
836 : 39 : g_return_val_if_fail (a != NULL, FALSE);
837 : 39 : g_return_val_if_fail (a->ref_count >= 1, FALSE);
838 : 39 : g_return_val_if_fail (b != NULL, FALSE);
839 : 39 : g_return_val_if_fail (b->ref_count >= 1, FALSE);
840 : :
841 : 68 : return (a->user_id == b->user_id &&
842 [ + + + + ]: 46 : a->filter_type == b->filter_type &&
843 : 17 : hash_table_equal0 (a->block_lists, b->block_lists, g_str_equal) &&
844 [ + - + - ]: 22 : a->custom_block_list_len == b->custom_block_list_len &&
845 : 11 : strv_equal0 ((const char * const *) a->custom_block_list,
846 [ + - ]: 22 : (const char * const *) b->custom_block_list) &&
847 : 11 : hash_table_equal0 (a->allow_lists, b->allow_lists, g_str_equal) &&
848 [ + - + - ]: 22 : a->custom_allow_list_len == b->custom_allow_list_len &&
849 : 11 : strv_equal0 ((const char * const *) a->custom_allow_list,
850 [ + + ]: 79 : (const char * const *) b->custom_allow_list) &&
851 [ + + ]: 11 : a->force_safe_search == b->force_safe_search);
852 : : }
853 : :
854 : : /*
855 : : * Actual implementation of #MctWebFilterBuilder.
856 : : *
857 : : * All members are %NULL if un-initialised, cleared, or ended.
858 : : */
859 : : typedef struct
860 : : {
861 : : MctWebFilterType filter_type;
862 : :
863 : : GHashTable *block_lists; /* (nullable) (owned) */
864 : : GPtrArray *custom_block_list; /* (nullable) (owned) */
865 : : GHashTable *allow_lists; /* (nullable) (owned) */
866 : : GPtrArray *custom_allow_list; /* (nullable) (owned) */
867 : :
868 : : gboolean force_safe_search;
869 : :
870 : : /*< private >*/
871 : : gpointer padding[10];
872 : : } MctWebFilterBuilderReal;
873 : :
874 : : G_STATIC_ASSERT (sizeof (MctWebFilterBuilderReal) ==
875 : : sizeof (MctWebFilterBuilder));
876 : : G_STATIC_ASSERT (__alignof__ (MctWebFilterBuilderReal) ==
877 : : __alignof__ (MctWebFilterBuilder));
878 : :
879 [ + - + - : 4 : G_DEFINE_BOXED_TYPE (MctWebFilterBuilder, mct_web_filter_builder,
+ - ]
880 : : mct_web_filter_builder_copy, mct_web_filter_builder_free)
881 : :
882 : : /**
883 : : * mct_web_filter_builder_init:
884 : : * @builder: an uninitialised #MctWebFilterBuilder
885 : : *
886 : : * Initialise the given @builder so it can be used to construct a new
887 : : * #MctWebFilter. @builder must have been allocated on the stack, and must
888 : : * not already be initialised.
889 : : *
890 : : * Construct the #MctWebFilter by calling methods on @builder, followed by
891 : : * mct_web_filter_builder_end(). To abort construction, use
892 : : * mct_web_filter_builder_clear().
893 : : *
894 : : * Since: 0.14.0
895 : : */
896 : : void
897 : 1 : mct_web_filter_builder_init (MctWebFilterBuilder *builder)
898 : : {
899 : 1 : MctWebFilterBuilder local_builder = MCT_WEB_FILTER_BUILDER_INIT ();
900 : 1 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
901 : :
902 : 1 : g_return_if_fail (_builder != NULL);
903 : 1 : g_return_if_fail (_builder->filter_type == MCT_WEB_FILTER_TYPE_NONE);
904 : :
905 : 1 : memcpy (builder, &local_builder, sizeof (local_builder));
906 : : }
907 : :
908 : : /**
909 : : * mct_web_filter_builder_clear:
910 : : * @builder: a web filter builder
911 : : *
912 : : * Clear @builder, freeing any internal state in it. This will not free the
913 : : * top-level storage for @builder itself, which is assumed to be allocated on
914 : : * the stack.
915 : : *
916 : : * If called on an already-cleared #MctWebFilterBuilder, this function is
917 : : * idempotent.
918 : : *
919 : : * Since: 0.14.0
920 : : */
921 : : void
922 : 7 : mct_web_filter_builder_clear (MctWebFilterBuilder *builder)
923 : : {
924 : 7 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
925 : :
926 : 7 : g_return_if_fail (_builder != NULL);
927 : :
928 [ - + ]: 7 : g_clear_pointer (&_builder->block_lists, g_hash_table_unref);
929 [ - + ]: 7 : g_clear_pointer (&_builder->custom_block_list, g_ptr_array_unref);
930 [ - + ]: 7 : g_clear_pointer (&_builder->allow_lists, g_hash_table_unref);
931 [ - + ]: 7 : g_clear_pointer (&_builder->custom_allow_list, g_ptr_array_unref);
932 : :
933 : 7 : _builder->filter_type = MCT_WEB_FILTER_TYPE_NONE;
934 : 7 : _builder->force_safe_search = FALSE;
935 : : }
936 : :
937 : : /**
938 : : * mct_web_filter_builder_new:
939 : : *
940 : : * Construct a new #MctWebFilterBuilder on the heap. This is intended for
941 : : * language bindings. The returned builder must eventually be freed with
942 : : * mct_web_filter_builder_free(), but can be cleared zero or more times with
943 : : * mct_web_filter_builder_clear() first.
944 : : *
945 : : * Returns: (transfer full): a new heap-allocated #MctWebFilterBuilder
946 : : * Since: 0.14.0
947 : : */
948 : : MctWebFilterBuilder *
949 : 0 : mct_web_filter_builder_new (void)
950 : : {
951 : 0 : g_autoptr(MctWebFilterBuilder) builder = NULL;
952 : :
953 : 0 : builder = g_new0 (MctWebFilterBuilder, 1);
954 : 0 : mct_web_filter_builder_init (builder);
955 : :
956 : 0 : return g_steal_pointer (&builder);
957 : : }
958 : :
959 : : static void *
960 : 0 : identity_copy (const void *src,
961 : : void *user_data)
962 : : {
963 : 0 : return (void *) src;
964 : : }
965 : :
966 : : static GHashTable *
967 : 0 : hash_table_copy (GHashTable *src,
968 : : GCopyFunc key_copy_func,
969 : : void *key_copy_user_data,
970 : : GCopyFunc value_copy_func,
971 : : void *value_copy_user_data)
972 : : {
973 : : GHashTableIter iter;
974 : : void *key, *value;
975 : 0 : g_autoptr(GHashTable) dest = NULL;
976 : :
977 : 0 : g_assert (src != NULL);
978 : :
979 : 0 : g_hash_table_iter_init (&iter, src);
980 : 0 : dest = g_hash_table_new_similar (src);
981 : :
982 [ # # ]: 0 : if (key_copy_func == NULL)
983 : 0 : key_copy_func = identity_copy;
984 [ # # ]: 0 : if (value_copy_func == NULL)
985 : 0 : value_copy_func = identity_copy;
986 : :
987 [ # # ]: 0 : while (g_hash_table_iter_next (&iter, &key, &value))
988 : : {
989 : : void *key_copy, *value_copy;
990 : :
991 : 0 : key_copy = key_copy_func (key, key_copy_user_data);
992 : 0 : value_copy = value_copy_func (value, value_copy_user_data);
993 : :
994 : 0 : g_hash_table_insert (dest, g_steal_pointer (&key_copy), g_steal_pointer (&value_copy));
995 : : }
996 : :
997 : 0 : return g_steal_pointer (&dest);
998 : : }
999 : :
1000 : : static void *
1001 : 0 : str_copy (const void *str,
1002 : : void *user_data)
1003 : : {
1004 : 0 : return g_strdup (str);
1005 : : }
1006 : :
1007 : : /**
1008 : : * mct_web_filter_builder_copy:
1009 : : * @builder: a web filter builder
1010 : : *
1011 : : * Copy the given @builder to a newly-allocated #MctWebFilterBuilder on the
1012 : : * heap. This is safe to use with cleared, stack-allocated
1013 : : * #MctWebFilterBuilders.
1014 : : *
1015 : : * Returns: (transfer full): a copy of @builder
1016 : : * Since: 0.14.0
1017 : : */
1018 : : MctWebFilterBuilder *
1019 : 0 : mct_web_filter_builder_copy (MctWebFilterBuilder *builder)
1020 : : {
1021 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1022 : 0 : g_autoptr(MctWebFilterBuilder) copy = NULL;
1023 : : MctWebFilterBuilderReal *_copy;
1024 : :
1025 : 0 : g_return_val_if_fail (builder != NULL, NULL);
1026 : :
1027 : 0 : copy = mct_web_filter_builder_new ();
1028 : 0 : _copy = (MctWebFilterBuilderReal *) copy;
1029 : :
1030 : 0 : mct_web_filter_builder_clear (copy);
1031 : 0 : _copy->filter_type = _builder->filter_type;
1032 [ # # ]: 0 : _copy->block_lists = (_builder->block_lists != NULL) ? hash_table_copy (_builder->block_lists, str_copy, NULL, str_copy, NULL) : NULL;
1033 [ # # ]: 0 : _copy->custom_block_list = (_builder->custom_block_list != NULL) ? g_ptr_array_copy (_builder->custom_block_list, str_copy, NULL) : NULL;
1034 [ # # ]: 0 : _copy->allow_lists = (_builder->allow_lists != NULL) ? hash_table_copy (_builder->allow_lists, str_copy, NULL, str_copy, NULL) : NULL;
1035 [ # # ]: 0 : _copy->custom_block_list = (_builder->custom_block_list != NULL) ? g_ptr_array_copy (_builder->custom_block_list, str_copy, NULL) : NULL;
1036 : 0 : _copy->force_safe_search = _builder->force_safe_search;
1037 : :
1038 : 0 : return g_steal_pointer (©);
1039 : : }
1040 : :
1041 : : /**
1042 : : * mct_web_filter_builder_free:
1043 : : * @builder: a heap-allocated #MctWebFilterBuilder
1044 : : *
1045 : : * Free an #MctWebFilterBuilder originally allocated using
1046 : : * mct_web_filter_builder_new(). This must not be called on stack-allocated
1047 : : * builders initialised using mct_web_filter_builder_init().
1048 : : *
1049 : : * Since: 0.14.0
1050 : : */
1051 : : void
1052 : 0 : mct_web_filter_builder_free (MctWebFilterBuilder *builder)
1053 : : {
1054 : 0 : g_return_if_fail (builder != NULL);
1055 : :
1056 : 0 : mct_web_filter_builder_clear (builder);
1057 : 0 : g_free (builder);
1058 : : }
1059 : :
1060 : : /**
1061 : : * mct_web_filter_builder_end:
1062 : : * @builder: an initialised #MctWebFilterBuilder
1063 : : *
1064 : : * Finish constructing an #MctWebFilter with the given @builder, and return
1065 : : * it. The #MctWebFilterBuilder will be cleared as if
1066 : : * mct_web_filter_builder_clear() had been called.
1067 : : *
1068 : : * Returns: (transfer full): a newly constructed #MctWebFilter
1069 : : * Since: 0.14.0
1070 : : */
1071 : : MctWebFilter *
1072 : 4 : mct_web_filter_builder_end (MctWebFilterBuilder *builder)
1073 : : {
1074 : 4 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1075 : 4 : g_autoptr(MctWebFilter) web_filter = NULL;
1076 : :
1077 : 4 : g_return_val_if_fail (_builder != NULL, NULL);
1078 : :
1079 : : /* Build the #MctWebFilter. */
1080 : 4 : web_filter = g_new0 (MctWebFilter, 1);
1081 : 4 : web_filter->ref_count = 1;
1082 : 4 : web_filter->user_id = -1;
1083 : 4 : web_filter->filter_type = _builder->filter_type;
1084 : :
1085 [ - + ]: 4 : if (_builder->custom_block_list != NULL)
1086 : 0 : g_ptr_array_sort_values (_builder->custom_block_list, (GCompareFunc) g_strcmp0);
1087 [ - + ]: 4 : if (_builder->custom_allow_list != NULL)
1088 : 0 : g_ptr_array_sort_values (_builder->custom_allow_list, (GCompareFunc) g_strcmp0);
1089 : :
1090 [ - + ]: 4 : web_filter->block_lists = (_builder->block_lists != NULL) ? g_hash_table_ref (_builder->block_lists) : NULL;
1091 [ - + ]: 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;
1092 [ - + ]: 4 : web_filter->allow_lists = (_builder->allow_lists != NULL) ? g_hash_table_ref (_builder->allow_lists) : NULL;
1093 [ - + ]: 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;
1094 : :
1095 : 4 : web_filter->force_safe_search = _builder->force_safe_search;
1096 : :
1097 : 4 : mct_web_filter_builder_clear (builder);
1098 : :
1099 : 4 : return g_steal_pointer (&web_filter);
1100 : : }
1101 : :
1102 : : /**
1103 : : * mct_web_filter_builder_set_filter_type:
1104 : : * @builder: an initialised #MctWebFilterBuilder
1105 : : * @filter_type: type of web filter
1106 : : *
1107 : : * Set the type of web filter to apply to the user.
1108 : : *
1109 : : * Since: 0.14.0
1110 : : */
1111 : : void
1112 : 0 : mct_web_filter_builder_set_filter_type (MctWebFilterBuilder *builder,
1113 : : MctWebFilterType filter_type)
1114 : : {
1115 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1116 : :
1117 : 0 : g_return_if_fail (_builder != NULL);
1118 : :
1119 : 0 : _builder->filter_type = filter_type;
1120 : : }
1121 : :
1122 : : /**
1123 : : * mct_web_filter_builder_add_block_list:
1124 : : * @builder: a web filter builder
1125 : : * @id: (not nullable): ID of the filter
1126 : : * @filter_uri: (not nullable): URI of the filter to download
1127 : : *
1128 : : * Adds a block list to the [struct@Malcontent.WebFilter], mapping the given @id
1129 : : * to a filter list downloadable at @filter_uri.
1130 : : *
1131 : : * All the entries at @filter_uri will be blocked. They must all be hostnames;
1132 : : * see the top-level documentation for [struct@Malcontent.WebFilter] for details
1133 : : * of the allowed filter list and ID formats.
1134 : : *
1135 : : * The filter list will be downloaded when the user’s web filter is compiled,
1136 : : * not when this function is called.
1137 : : *
1138 : : * Since: 0.14.0
1139 : : */
1140 : : void
1141 : 0 : mct_web_filter_builder_add_block_list (MctWebFilterBuilder *builder,
1142 : : const char *id,
1143 : : const char *filter_uri)
1144 : : {
1145 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1146 : :
1147 : 0 : g_return_if_fail (_builder != NULL);
1148 : 0 : g_return_if_fail (mct_web_filter_validate_filter_id (id));
1149 : 0 : g_return_if_fail (validate_filter_uri (filter_uri));
1150 : :
1151 [ # # ]: 0 : if (_builder->block_lists == NULL)
1152 : 0 : _builder->block_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1153 : :
1154 : 0 : g_hash_table_replace (_builder->block_lists, g_strdup (id), g_strdup (filter_uri));
1155 : : }
1156 : :
1157 : : /**
1158 : : * mct_web_filter_builder_add_custom_block_list_entry:
1159 : : * @builder: a web filter builder
1160 : : * @hostname: (not nullable): hostname to block
1161 : : *
1162 : : * Adds a single hostname to the [struct@Malcontent.WebFilter], to be blocked.
1163 : : *
1164 : : * See the top-level documentation for [struct@Malcontent.WebFilter] for details
1165 : : * of the allowed filter list and ID formats.
1166 : : *
1167 : : * Since: 0.14.0
1168 : : */
1169 : : void
1170 : 0 : mct_web_filter_builder_add_custom_block_list_entry (MctWebFilterBuilder *builder,
1171 : : const char *hostname)
1172 : : {
1173 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1174 : :
1175 : 0 : g_return_if_fail (_builder != NULL);
1176 : 0 : g_return_if_fail (mct_web_filter_validate_hostname (hostname));
1177 : :
1178 [ # # ]: 0 : if (_builder->custom_block_list == NULL)
1179 : 0 : _builder->custom_block_list = g_ptr_array_new_null_terminated (0, g_free, TRUE);
1180 : :
1181 : 0 : g_ptr_array_add (_builder->custom_block_list, g_strdup (hostname));
1182 : : }
1183 : :
1184 : : /**
1185 : : * mct_web_filter_builder_add_allow_list:
1186 : : * @builder: a web filter builder
1187 : : * @id: (not nullable): ID of the filter
1188 : : * @filter_uri: (not nullable): URI of the filter to download
1189 : : *
1190 : : * Adds a allow list to the [struct@Malcontent.WebFilter], mapping the given @id
1191 : : * to a filter list downloadable at @filter_uri.
1192 : : *
1193 : : * All the entries at @filter_uri will be allowed. They must all be hostnames;
1194 : : * see the top-level documentation for [struct@Malcontent.WebFilter] for details
1195 : : * of the allowed filter list and ID formats.
1196 : : *
1197 : : * The filter list will be downloaded when the user’s web filter is compiled,
1198 : : * not when this function is called.
1199 : : *
1200 : : * Since: 0.14.0
1201 : : */
1202 : : void
1203 : 0 : mct_web_filter_builder_add_allow_list (MctWebFilterBuilder *builder,
1204 : : const char *id,
1205 : : const char *filter_uri)
1206 : : {
1207 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1208 : :
1209 : 0 : g_return_if_fail (_builder != NULL);
1210 : 0 : g_return_if_fail (mct_web_filter_validate_filter_id (id));
1211 : 0 : g_return_if_fail (validate_filter_uri (filter_uri));
1212 : :
1213 [ # # ]: 0 : if (_builder->allow_lists == NULL)
1214 : 0 : _builder->allow_lists = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1215 : :
1216 : 0 : g_hash_table_replace (_builder->allow_lists, g_strdup (id), g_strdup (filter_uri));
1217 : : }
1218 : :
1219 : : /**
1220 : : * mct_web_filter_builder_add_custom_allow_list_entry:
1221 : : * @builder: a web filter builder
1222 : : * @hostname: (not nullable): hostname to allow
1223 : : *
1224 : : * Adds a single hostname to the [struct@Malcontent.WebFilter], to be allowed.
1225 : : *
1226 : : * See the top-level documentation for [struct@Malcontent.WebFilter] for details
1227 : : * of the allowed filter list and ID formats.
1228 : : *
1229 : : * Since: 0.14.0
1230 : : */
1231 : : void
1232 : 0 : mct_web_filter_builder_add_custom_allow_list_entry (MctWebFilterBuilder *builder,
1233 : : const char *hostname)
1234 : : {
1235 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1236 : :
1237 : 0 : g_return_if_fail (_builder != NULL);
1238 : 0 : g_return_if_fail (mct_web_filter_validate_hostname (hostname));
1239 : :
1240 [ # # ]: 0 : if (_builder->custom_allow_list == NULL)
1241 : 0 : _builder->custom_allow_list = g_ptr_array_new_null_terminated (0, g_free, TRUE);
1242 : :
1243 : 0 : g_ptr_array_add (_builder->custom_allow_list, g_strdup (hostname));
1244 : : }
1245 : :
1246 : : /**
1247 : : * mct_web_filter_builder_set_force_safe_search:
1248 : : * @builder: a web filter builder
1249 : : * @force_safe_search: true to force safe search to be enabled, false otherwise
1250 : : *
1251 : : * Sets the safe search preference for the [struct@Malcontent.WebFilter].
1252 : : *
1253 : : * If enabled, search engines and other popular websites will be automatically
1254 : : * redirected to their ‘safe search’ variant, if supported.
1255 : : *
1256 : : * Since: 0.14.0
1257 : : */
1258 : : void
1259 : 0 : mct_web_filter_builder_set_force_safe_search (MctWebFilterBuilder *builder,
1260 : : gboolean force_safe_search)
1261 : : {
1262 : 0 : MctWebFilterBuilderReal *_builder = (MctWebFilterBuilderReal *) builder;
1263 : :
1264 : 0 : g_return_if_fail (_builder != NULL);
1265 : :
1266 : 0 : _builder->force_safe_search = force_safe_search;
1267 : : }
|