Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright 2024, 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 <errno.h>
28 : : #include <glib.h>
29 : : #include <glib-unix.h>
30 : : #include <glib/gi18n-lib.h>
31 : : #include <gio/gio.h>
32 : : #include <libmalcontent/manager.h>
33 : : #include <libmalcontent-web/filtering-dbus-service.h>
34 : : #include <libmalcontent-web/filter-updater.h>
35 : : #include <pwd.h>
36 : : #include <string.h>
37 : : #include <sys/types.h>
38 : :
39 : : #include "filtering-iface.h"
40 : : #include "enums.h"
41 : :
42 : :
43 : : static void mct_filtering_dbus_service_constructed (GObject *object);
44 : : static void mct_filtering_dbus_service_dispose (GObject *object);
45 : : static void mct_filtering_dbus_service_get_property (GObject *object,
46 : : guint property_id,
47 : : GValue *value,
48 : : GParamSpec *pspec);
49 : : static void mct_filtering_dbus_service_set_property (GObject *object,
50 : : guint property_id,
51 : : const GValue *value,
52 : : GParamSpec *pspec);
53 : :
54 : : static void mct_filtering_dbus_service_method_call (GDBusConnection *connection,
55 : : const char *sender,
56 : : const char *object_path,
57 : : const char *interface_name,
58 : : const char *method_name,
59 : : GVariant *parameters,
60 : : GDBusMethodInvocation *invocation,
61 : : void *user_data);
62 : : static void mct_filtering_dbus_service_properties_get (MctFilteringDBusService *self,
63 : : GDBusConnection *connection,
64 : : const char *sender,
65 : : GVariant *parameters,
66 : : GDBusMethodInvocation *invocation);
67 : : static void mct_filtering_dbus_service_properties_set (MctFilteringDBusService *self,
68 : : GDBusConnection *connection,
69 : : const char *sender,
70 : : GVariant *parameters,
71 : : GDBusMethodInvocation *invocation);
72 : : static void mct_filtering_dbus_service_properties_get_all (MctFilteringDBusService *self,
73 : : GDBusConnection *connection,
74 : : const char *sender,
75 : : GVariant *parameters,
76 : : GDBusMethodInvocation *invocation);
77 : :
78 : : static void mct_filtering_dbus_service_update_filters (MctFilteringDBusService *self,
79 : : GDBusConnection *connection,
80 : : const char *sender,
81 : : GVariant *parameters,
82 : : GDBusMethodInvocation *invocation);
83 : :
84 : : /* These errors do go over the bus, and are registered in mct_filtering_dbus_service_class_init(). */
85 : : static const gchar *filtering_dbus_service_errors[] =
86 : : {
87 : : "org.freedesktop.MalcontentWeb1.Filtering.Error.Busy",
88 : : "org.freedesktop.MalcontentWeb1.Filtering.Error.Disabled",
89 : : "org.freedesktop.MalcontentWeb1.Filtering.Error.QueryingPolicy",
90 : : "org.freedesktop.MalcontentWeb1.Filtering.Error.InvalidFilterFormat",
91 : : "org.freedesktop.MalcontentWeb1.Filtering.Error.FileSystem",
92 : : "org.freedesktop.MalcontentWeb1.Filtering.Error.Downloading",
93 : : };
94 : : static const GDBusErrorEntry filtering_dbus_service_error_map[] =
95 : : {
96 : : { MCT_FILTER_UPDATER_ERROR_BUSY, "org.freedesktop.MalcontentWeb1.Filtering.Error.Busy" },
97 : : { MCT_FILTER_UPDATER_ERROR_DISABLED, "org.freedesktop.MalcontentWeb1.Filtering.Error.Disabled" },
98 : : { MCT_FILTER_UPDATER_ERROR_QUERYING_POLICY, "org.freedesktop.MalcontentWeb1.Filtering.Error.QueryingPolicy" },
99 : : { MCT_FILTER_UPDATER_ERROR_INVALID_FILTER_FORMAT, "org.freedesktop.MalcontentWeb1.Filtering.Error.InvalidFilterFormat" },
100 : : { MCT_FILTER_UPDATER_ERROR_FILE_SYSTEM, "org.freedesktop.MalcontentWeb1.Filtering.Error.FileSystem" },
101 : : { MCT_FILTER_UPDATER_ERROR_DOWNLOADING, "org.freedesktop.MalcontentWeb1.Filtering.Error.Downloading" },
102 : :
103 : : };
104 : : G_STATIC_ASSERT (G_N_ELEMENTS (filtering_dbus_service_error_map) == MCT_FILTER_UPDATER_N_ERRORS);
105 : : G_STATIC_ASSERT (G_N_ELEMENTS (filtering_dbus_service_error_map) == G_N_ELEMENTS (filtering_dbus_service_errors));
106 : :
107 : : /**
108 : : * MctFilteringDBusService:
109 : : *
110 : : * An implementation of the `org.freedesktop.MalcontentWeb1.Filtering` D-Bus
111 : : * interface, providing a way for unprivileged processes to request that the
112 : : * compiled web filters for one or more child users are updated.
113 : : *
114 : : * This will expose all the necessary objects on the bus for peers to interact
115 : : * with them, and hooks them up to internal state management using
116 : : * [property@Malcontent.FilteringDBusService:filter-updater].
117 : : *
118 : : * Since: 0.14.0
119 : : */
120 : : struct _MctFilteringDBusService
121 : : {
122 : : GObject parent;
123 : :
124 : : GDBusConnection *connection; /* (owned) */
125 : : char *object_path; /* (owned) */
126 : : unsigned int object_id;
127 : :
128 : : /* Used to cancel any pending operations when the object is unregistered. */
129 : : GCancellable *cancellable; /* (owned) */
130 : :
131 : : MctFilterUpdater *filter_updater; /* (owned) */
132 : : unsigned int n_pending_operations;
133 : : };
134 : :
135 : : typedef enum
136 : : {
137 : : PROP_CONNECTION = 1,
138 : : PROP_OBJECT_PATH,
139 : : PROP_FILTER_UPDATER,
140 : : PROP_BUSY,
141 : : } MctFilteringDBusServiceProperty;
142 : :
143 : : static GParamSpec *props[PROP_BUSY + 1] = { NULL, };
144 : :
145 [ + + + - : 12 : G_DEFINE_TYPE (MctFilteringDBusService, mct_filtering_dbus_service, G_TYPE_OBJECT)
+ + ]
146 : :
147 : : static void
148 : 1 : mct_filtering_dbus_service_class_init (MctFilteringDBusServiceClass *klass)
149 : : {
150 : 1 : GObjectClass *object_class = (GObjectClass *) klass;
151 : :
152 : 1 : object_class->constructed = mct_filtering_dbus_service_constructed;
153 : 1 : object_class->dispose = mct_filtering_dbus_service_dispose;
154 : 1 : object_class->get_property = mct_filtering_dbus_service_get_property;
155 : 1 : object_class->set_property = mct_filtering_dbus_service_set_property;
156 : :
157 : : /**
158 : : * MctFilteringDBusService:connection:
159 : : *
160 : : * D-Bus connection to export objects on.
161 : : *
162 : : * Since: 0.14.0
163 : : */
164 : 1 : props[PROP_CONNECTION] =
165 : 1 : g_param_spec_object ("connection", NULL, NULL,
166 : : G_TYPE_DBUS_CONNECTION,
167 : : G_PARAM_READWRITE |
168 : : G_PARAM_CONSTRUCT_ONLY |
169 : : G_PARAM_STATIC_STRINGS);
170 : :
171 : : /**
172 : : * MctFilteringDBusService:object-path:
173 : : *
174 : : * Object path to root all exported objects at. If this does not end in a
175 : : * slash, one will be added.
176 : : *
177 : : * Since: 0.14.0
178 : : */
179 : 1 : props[PROP_OBJECT_PATH] =
180 : 1 : g_param_spec_string ("object-path", NULL, NULL,
181 : : "/",
182 : : G_PARAM_READWRITE |
183 : : G_PARAM_CONSTRUCT_ONLY |
184 : : G_PARAM_STATIC_STRINGS);
185 : :
186 : : /**
187 : : * MctFilteringDBusService:filter-updater:
188 : : *
189 : : * Helper object to update cached and compiled filters.
190 : : *
191 : : * Since: 0.14.0
192 : : */
193 : 1 : props[PROP_FILTER_UPDATER] =
194 : 1 : g_param_spec_object ("filter-updater", NULL, NULL,
195 : : MCT_TYPE_FILTER_UPDATER,
196 : : G_PARAM_READWRITE |
197 : : G_PARAM_CONSTRUCT_ONLY |
198 : : G_PARAM_STATIC_STRINGS);
199 : :
200 : : /**
201 : : * MctFilteringDBusService:busy:
202 : : *
203 : : * True if the D-Bus API is busy.
204 : : *
205 : : * For example, if there are any outstanding method calls which haven’t been
206 : : * replied to yet.
207 : : *
208 : : * Since: 0.14.0
209 : : */
210 : 1 : props[PROP_BUSY] =
211 : 1 : g_param_spec_boolean ("busy", NULL, NULL,
212 : : FALSE,
213 : : G_PARAM_READABLE |
214 : : G_PARAM_STATIC_STRINGS);
215 : :
216 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
217 : :
218 : : /* Error domain registration for D-Bus. We do this here, rather than in a
219 : : * #GOnce section in mct_filtering_dbus_service_error_quark(), to avoid spreading the
220 : : * D-Bus code outside this file.
221 : : *
222 : : * For the moment, all the errors are mapped directly from
223 : : * `MctFilterUpdaterError`.*/
224 [ + + ]: 7 : for (size_t i = 0; i < G_N_ELEMENTS (filtering_dbus_service_error_map); i++)
225 : 6 : g_dbus_error_register_error (MCT_FILTER_UPDATER_ERROR,
226 : 6 : filtering_dbus_service_error_map[i].error_code,
227 : 6 : filtering_dbus_service_error_map[i].dbus_error_name);
228 : 1 : }
229 : :
230 : : static void
231 : 1 : mct_filtering_dbus_service_init (MctFilteringDBusService *self)
232 : : {
233 : 1 : self->cancellable = g_cancellable_new ();
234 : 1 : }
235 : :
236 : : static void
237 : 1 : mct_filtering_dbus_service_constructed (GObject *object)
238 : : {
239 : 1 : MctFilteringDBusService *self = MCT_FILTERING_DBUS_SERVICE (object);
240 : :
241 : : /* Chain up. */
242 : 1 : G_OBJECT_CLASS (mct_filtering_dbus_service_parent_class)->constructed (object);
243 : :
244 : : /* Check our construct properties. */
245 : 1 : g_assert (G_IS_DBUS_CONNECTION (self->connection));
246 : 1 : g_assert (g_variant_is_object_path (self->object_path));
247 : 1 : g_assert (MCT_IS_FILTER_UPDATER (self->filter_updater));
248 : 1 : }
249 : :
250 : : static void
251 : 0 : mct_filtering_dbus_service_dispose (GObject *object)
252 : : {
253 : 0 : MctFilteringDBusService *self = MCT_FILTERING_DBUS_SERVICE (object);
254 : :
255 : 0 : g_assert (self->object_id == 0);
256 : 0 : g_assert (self->n_pending_operations == 0);
257 : :
258 [ # # ]: 0 : g_clear_object (&self->filter_updater);
259 : :
260 [ # # ]: 0 : g_clear_object (&self->connection);
261 [ # # ]: 0 : g_clear_pointer (&self->object_path, g_free);
262 [ # # ]: 0 : g_clear_object (&self->cancellable);
263 : :
264 : : /* Chain up to the parent class */
265 : 0 : G_OBJECT_CLASS (mct_filtering_dbus_service_parent_class)->dispose (object);
266 : 0 : }
267 : :
268 : : static void
269 : 0 : mct_filtering_dbus_service_get_property (GObject *object,
270 : : guint property_id,
271 : : GValue *value,
272 : : GParamSpec *pspec)
273 : : {
274 : 0 : MctFilteringDBusService *self = MCT_FILTERING_DBUS_SERVICE (object);
275 : :
276 [ # # # # : 0 : switch ((MctFilteringDBusServiceProperty) property_id)
# ]
277 : : {
278 : 0 : case PROP_CONNECTION:
279 : 0 : g_value_set_object (value, self->connection);
280 : 0 : break;
281 : 0 : case PROP_OBJECT_PATH:
282 : 0 : g_value_set_string (value, self->object_path);
283 : 0 : break;
284 : 0 : case PROP_FILTER_UPDATER:
285 : 0 : g_value_set_object (value, self->filter_updater);
286 : 0 : break;
287 : 0 : case PROP_BUSY:
288 : 0 : g_value_set_boolean (value, mct_filtering_dbus_service_get_busy (self));
289 : 0 : break;
290 : 0 : default:
291 : : g_assert_not_reached ();
292 : : }
293 : 0 : }
294 : :
295 : : static void
296 : 3 : mct_filtering_dbus_service_set_property (GObject *object,
297 : : guint property_id,
298 : : const GValue *value,
299 : : GParamSpec *pspec)
300 : : {
301 : 3 : MctFilteringDBusService *self = MCT_FILTERING_DBUS_SERVICE (object);
302 : :
303 [ + + + - ]: 3 : switch ((MctFilteringDBusServiceProperty) property_id)
304 : : {
305 : 1 : case PROP_CONNECTION:
306 : : /* Construct only. */
307 : 1 : g_assert (self->connection == NULL);
308 : 1 : self->connection = g_value_dup_object (value);
309 : 1 : break;
310 : 1 : case PROP_OBJECT_PATH:
311 : : /* Construct only. */
312 : 1 : g_assert (self->object_path == NULL);
313 : 1 : g_assert (g_variant_is_object_path (g_value_get_string (value)));
314 : 1 : self->object_path = g_value_dup_string (value);
315 : 1 : break;
316 : 1 : case PROP_FILTER_UPDATER:
317 : : /* Construct only. */
318 : 1 : g_assert (self->filter_updater == NULL);
319 : 1 : self->filter_updater = g_value_dup_object (value);
320 : 1 : break;
321 : 0 : case PROP_BUSY:
322 : : /* Read only. Fall through. */
323 : : G_GNUC_FALLTHROUGH;
324 : : default:
325 : : g_assert_not_reached ();
326 : : }
327 : 3 : }
328 : :
329 : : /**
330 : : * mct_filtering_dbus_service_register:
331 : : * @self: a filtering service
332 : : * @error: return location for a [type@GLib.Error]
333 : : *
334 : : * Register the filtering service objects on D-Bus using the connection details
335 : : * given in [property@Malcontent.FilteringDBusService.connection] and
336 : : * [property@Malcontent.FilteringDBusService.object-path].
337 : : *
338 : : * Use [method@Malcontent.FilteringDBusService.unregister] to unregister them.
339 : : * Calls to these two functions must be well paired.
340 : : *
341 : : * Returns: true on success, false otherwise
342 : : * Since: 0.14.0
343 : : */
344 : : gboolean
345 : 1 : mct_filtering_dbus_service_register (MctFilteringDBusService *self,
346 : : GError **error)
347 : : {
348 : 1 : g_return_val_if_fail (MCT_IS_FILTERING_DBUS_SERVICE (self), FALSE);
349 : 1 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
350 : :
351 : 1 : const GDBusInterfaceVTable interface_vtable =
352 : : {
353 : : mct_filtering_dbus_service_method_call,
354 : : NULL, /* handled in mct_filtering_dbus_service_method_call() */
355 : : NULL, /* handled in mct_filtering_dbus_service_method_call() */
356 : : { NULL, }, /* padding */
357 : : };
358 : :
359 : 1 : guint id = g_dbus_connection_register_object (self->connection,
360 : 1 : self->object_path,
361 : : (GDBusInterfaceInfo *) &org_freedesktop_malcontent_web1_filtering_interface,
362 : : &interface_vtable,
363 : : g_object_ref (self),
364 : : g_object_unref,
365 : : error);
366 : :
367 [ - + ]: 1 : if (id == 0)
368 : 0 : return FALSE;
369 : :
370 : 1 : self->object_id = id;
371 : :
372 : : /* This has potentially changed. */
373 : 1 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_BUSY]);
374 : :
375 : 1 : return TRUE;
376 : : }
377 : :
378 : : /**
379 : : * mct_filtering_dbus_service_unregister:
380 : : * @self: a filtering service
381 : : *
382 : : * Unregister objects from D-Bus which were previously registered using
383 : : * [method@Malcontent.FilteringDBusService.register].
384 : : *
385 : : * Calls to these two functions must be well paired.
386 : : *
387 : : * Since: 0.14.0
388 : : */
389 : : void
390 : 1 : mct_filtering_dbus_service_unregister (MctFilteringDBusService *self)
391 : : {
392 : 1 : g_return_if_fail (MCT_IS_FILTERING_DBUS_SERVICE (self));
393 : :
394 : 1 : g_dbus_connection_unregister_object (self->connection, self->object_id);
395 : 1 : self->object_id = 0;
396 : :
397 : : /* This has potentially changed. */
398 : 1 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_BUSY]);
399 : : }
400 : :
401 : : static gboolean
402 : 0 : validate_dbus_interface_name (GDBusMethodInvocation *invocation,
403 : : const gchar *interface_name)
404 : : {
405 [ # # ]: 0 : if (!g_dbus_is_interface_name (interface_name))
406 : : {
407 : 0 : g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
408 : : G_DBUS_ERROR_UNKNOWN_INTERFACE,
409 : : _("Invalid interface name ‘%s’."),
410 : : interface_name);
411 : 0 : return FALSE;
412 : : }
413 : :
414 : 0 : return TRUE;
415 : : }
416 : :
417 : : typedef void (*ChildMethodCallFunc) (MctFilteringDBusService *self,
418 : : GDBusConnection *connection,
419 : : const char *sender,
420 : : GVariant *parameters,
421 : : GDBusMethodInvocation *invocation);
422 : :
423 : : static const struct
424 : : {
425 : : const char *interface_name;
426 : : const char *method_name;
427 : : ChildMethodCallFunc func;
428 : : }
429 : : filtering_methods[] =
430 : : {
431 : : /* Handle properties. */
432 : : { "org.freedesktop.DBus.Properties", "Get",
433 : : mct_filtering_dbus_service_properties_get },
434 : : { "org.freedesktop.DBus.Properties", "Set",
435 : : mct_filtering_dbus_service_properties_set },
436 : : { "org.freedesktop.DBus.Properties", "GetAll",
437 : : mct_filtering_dbus_service_properties_get_all },
438 : :
439 : : /* Filtering methods. */
440 : : { "org.freedesktop.MalcontentWeb1.Filtering", "UpdateFilters",
441 : : mct_filtering_dbus_service_update_filters },
442 : : };
443 : :
444 : : static void
445 : 0 : mct_filtering_dbus_service_method_call (GDBusConnection *connection,
446 : : const char *sender,
447 : : const char *object_path,
448 : : const char *interface_name,
449 : : const char *method_name,
450 : : GVariant *parameters,
451 : : GDBusMethodInvocation *invocation,
452 : : void *user_data)
453 : : {
454 : 0 : MctFilteringDBusService *self = MCT_FILTERING_DBUS_SERVICE (user_data);
455 : :
456 : : /* Check we’ve implemented all the methods. Unfortunately this can’t be a
457 : : * compile time check because the method array is declared in a separate
458 : : * compilation unit. */
459 : 0 : size_t n_filtering_interface_methods = 0;
460 [ # # ]: 0 : for (size_t i = 0; org_freedesktop_malcontent_web1_filtering_interface.methods[i] != NULL; i++)
461 : 0 : n_filtering_interface_methods++;
462 : :
463 : 0 : g_assert (G_N_ELEMENTS (filtering_methods) ==
464 : : n_filtering_interface_methods +
465 : : 3 /* o.fdo.DBus.Properties */);
466 : :
467 : : /* Remove the service prefix from the path. */
468 : 0 : g_assert (g_str_equal (object_path, self->object_path));
469 : :
470 : : /* Work out which method to call. */
471 [ # # ]: 0 : for (gsize i = 0; i < G_N_ELEMENTS (filtering_methods); i++)
472 : : {
473 [ # # ]: 0 : if (g_str_equal (filtering_methods[i].interface_name, interface_name) &&
474 [ # # ]: 0 : g_str_equal (filtering_methods[i].method_name, method_name))
475 : : {
476 : 0 : filtering_methods[i].func (self, connection, sender, parameters, invocation);
477 : 0 : return;
478 : : }
479 : : }
480 : :
481 : : /* Make sure we actually called a method implementation. GIO guarantees that
482 : : * this function is only called with methods we’ve declared in the interface
483 : : * info, so this should never fail. */
484 : : g_assert_not_reached ();
485 : : }
486 : :
487 : : static void
488 : 0 : mct_filtering_dbus_service_properties_get (MctFilteringDBusService *self,
489 : : GDBusConnection *connection,
490 : : const char *sender,
491 : : GVariant *parameters,
492 : : GDBusMethodInvocation *invocation)
493 : : {
494 : : const char *interface_name, *property_name;
495 : 0 : g_variant_get (parameters, "(&s&s)", &interface_name, &property_name);
496 : :
497 : : /* D-Bus property names can be anything. */
498 [ # # ]: 0 : if (!validate_dbus_interface_name (invocation, interface_name))
499 : 0 : return;
500 : :
501 : : /* No properties exposed. */
502 : 0 : g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
503 : : G_DBUS_ERROR_UNKNOWN_PROPERTY,
504 : : _("Unknown property ‘%s.%s’."),
505 : : interface_name, property_name);
506 : : }
507 : :
508 : : static void
509 : 0 : mct_filtering_dbus_service_properties_set (MctFilteringDBusService *self,
510 : : GDBusConnection *connection,
511 : : const char *sender,
512 : : GVariant *parameters,
513 : : GDBusMethodInvocation *invocation)
514 : : {
515 : : const char *interface_name, *property_name;
516 : 0 : g_variant_get (parameters, "(&s&sv)", &interface_name, &property_name, NULL);
517 : :
518 : : /* D-Bus property names can be anything. */
519 [ # # ]: 0 : if (!validate_dbus_interface_name (invocation, interface_name))
520 : 0 : return;
521 : :
522 : : /* No properties exposed. */
523 : 0 : g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
524 : : G_DBUS_ERROR_UNKNOWN_PROPERTY,
525 : : _("Unknown property ‘%s.%s’."),
526 : : interface_name, property_name);
527 : : }
528 : :
529 : : static void
530 : 0 : mct_filtering_dbus_service_properties_get_all (MctFilteringDBusService *self,
531 : : GDBusConnection *connection,
532 : : const char *sender,
533 : : GVariant *parameters,
534 : : GDBusMethodInvocation *invocation)
535 : : {
536 : : const char *interface_name;
537 : 0 : g_variant_get (parameters, "(&s)", &interface_name);
538 : :
539 [ # # ]: 0 : if (!validate_dbus_interface_name (invocation, interface_name))
540 : 0 : return;
541 : :
542 : : /* Try the interface. */
543 [ # # ]: 0 : if (g_str_equal (interface_name, "org.freedesktop.MalcontentWeb1.Filtering"))
544 : 0 : g_dbus_method_invocation_return_value (invocation,
545 : : g_variant_new_parsed ("(@a{sv} {},)"));
546 : : else
547 : 0 : g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
548 : : G_DBUS_ERROR_UNKNOWN_INTERFACE,
549 : : _("Unknown interface ‘%s’."),
550 : : interface_name);
551 : : }
552 : :
553 : : typedef struct
554 : : {
555 : : MctFilteringDBusService *service; /* (not owned) (not nullable) */
556 : : GDBusMethodInvocation *invocation; /* (owned) (not nullable) */
557 : : } UpdateFiltersData;
558 : :
559 : : static void
560 : 0 : update_filters_data_free (UpdateFiltersData *data)
561 : : {
562 [ # # ]: 0 : g_clear_object (&data->invocation);
563 : 0 : g_free (data);
564 : 0 : }
565 : :
566 [ # # ]: 0 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (UpdateFiltersData, update_filters_data_free)
567 : :
568 : : static void update_filters_cb (GObject *object,
569 : : GAsyncResult *result,
570 : : void *user_data);
571 : :
572 : : static void
573 : 0 : mct_filtering_dbus_service_update_filters (MctFilteringDBusService *self,
574 : : GDBusConnection *connection,
575 : : const char *sender,
576 : : GVariant *parameters,
577 : : GDBusMethodInvocation *invocation)
578 : : {
579 : 0 : g_autoptr(UpdateFiltersData) data_owned = NULL;
580 : : UpdateFiltersData *data;
581 : : uid_t uid;
582 : :
583 : : /* Validate the parameters. */
584 : 0 : g_variant_get (parameters, "(u)", &uid);
585 : :
586 : : /* We don’t check the credentials of the caller because there’s no need — this
587 : : * method is called on a systemd timer for all users anyway, so preventing any
588 : : * user from calling it would just delay an update of the filters rather than
589 : : * stop it. */
590 : :
591 : 0 : self->n_pending_operations++;
592 [ # # ]: 0 : if (self->n_pending_operations == 1)
593 : 0 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_BUSY]);
594 : :
595 : : /* Update the filters. */
596 : 0 : data = data_owned = g_new0 (UpdateFiltersData, 1);
597 : 0 : data->service = self;
598 : 0 : data->invocation = g_object_ref (invocation);
599 : :
600 : 0 : mct_filter_updater_update_filters_async (self->filter_updater, uid,
601 : : self->cancellable, update_filters_cb,
602 : 0 : g_steal_pointer (&data_owned));
603 : 0 : }
604 : :
605 : : static void
606 : 0 : update_filters_cb (GObject *object,
607 : : GAsyncResult *result,
608 : : void *user_data)
609 : : {
610 : 0 : g_autoptr(UpdateFiltersData) data = g_steal_pointer (&user_data);
611 : 0 : MctFilteringDBusService *self = data->service;
612 : 0 : MctFilterUpdater *filter_updater = MCT_FILTER_UPDATER (object);
613 : 0 : g_autoptr(GError) local_error = NULL;
614 : :
615 : 0 : self->n_pending_operations--;
616 [ # # ]: 0 : if (self->n_pending_operations == 0)
617 : 0 : g_object_notify_by_pspec (G_OBJECT (self), props[PROP_BUSY]);
618 : :
619 [ # # ]: 0 : if (!mct_filter_updater_update_filters_finish (filter_updater, result, &local_error))
620 : : {
621 : : /* This will always be a MctFilterUpdaterError, and as we’ve mapped those
622 : : * to D-Bus error names, then we can just prefix the message. */
623 : 0 : g_assert (local_error->domain == MCT_FILTER_UPDATER_ERROR);
624 : 0 : g_dbus_method_invocation_return_error (data->invocation, local_error->domain,
625 : 0 : local_error->code,
626 : 0 : _("Error updating filters: %s"), local_error->message);
627 : : }
628 : : else
629 : : {
630 : 0 : g_dbus_method_invocation_return_value (data->invocation, NULL);
631 : : }
632 : 0 : }
633 : :
634 : : /**
635 : : * mct_filtering_dbus_service_new:
636 : : * @connection: (transfer none): D-Bus connection to export objects on
637 : : * @object_path: root path to export objects below; must be a valid D-Bus object
638 : : * path
639 : : * @filter_updater: (transfer none): filter updater object
640 : : *
641 : : * Create a new [class@Malcontent.FilteringDBusService] instance which is set up
642 : : * to run as a service.
643 : : *
644 : : * Returns: (transfer full): a new [class@Malcontent.FilteringDBusService]
645 : : * Since: 0.14.0
646 : : */
647 : : MctFilteringDBusService *
648 : 1 : mct_filtering_dbus_service_new (GDBusConnection *connection,
649 : : const char *object_path,
650 : : MctFilterUpdater *filter_updater)
651 : : {
652 : 1 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
653 : 1 : g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
654 : 1 : g_return_val_if_fail (MCT_IS_FILTER_UPDATER (filter_updater), NULL);
655 : :
656 : 1 : return g_object_new (MCT_TYPE_FILTERING_DBUS_SERVICE,
657 : : "connection", connection,
658 : : "object-path", object_path,
659 : : "filter-updater", filter_updater,
660 : : NULL);
661 : : }
662 : :
663 : : /**
664 : : * mct_filtering_dbus_service_get_busy:
665 : : * @self: a filtering service
666 : : *
667 : : * Get the value of [property@Malcontent.FilteringDBusService.busy].
668 : : *
669 : : * Returns: true if the service is busy, false otherwise
670 : : * Since: 0.14.0
671 : : */
672 : : gboolean
673 : 3 : mct_filtering_dbus_service_get_busy (MctFilteringDBusService *self)
674 : : {
675 : 3 : g_return_val_if_fail (MCT_IS_FILTERING_DBUS_SERVICE (self), FALSE);
676 : :
677 [ + + - + ]: 3 : return (self->object_id != 0 && self->n_pending_operations > 0);
678 : : }
679 : :
|