Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2019 Endless Mobile, 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 <withnall@endlessm.com>
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/app-filter.h>
32 : : #include <libmalcontent/manager.h>
33 : : #include <libmalcontent/session-limits.h>
34 : : #include <libmalcontent/web-filter.h>
35 : :
36 : : #include "libmalcontent/app-filter-private.h"
37 : : #include "libmalcontent/session-limits-private.h"
38 : : #include "libmalcontent/web-filter-private.h"
39 : :
40 : :
41 : : /**
42 : : * mct_manager_error_quark:
43 : : *
44 : : * Error quark for [error@Malcontent.ManagerError].
45 : : *
46 : : * Returns: identifier for error domain
47 : : * Since: 0.5.0
48 : : */
49 [ + + ]: 72 : G_DEFINE_QUARK (MctManagerError, mct_manager_error)
50 : :
51 : : /**
52 : : * MctManager:
53 : : *
54 : : * A top-level policy management object which is used to query, set and monitor
55 : : * parental controls policy for different users.
56 : : *
57 : : * Since: 0.3.0
58 : : */
59 : : struct _MctManager
60 : : {
61 : : GObject parent_instance;
62 : :
63 : : GDBusConnection *connection; /* (owned) */
64 : : unsigned int user_properties_changed_old_id;
65 : : unsigned int user_properties_changed_id;
66 : : };
67 : :
68 [ + + + - : 283 : G_DEFINE_TYPE (MctManager, mct_manager, G_TYPE_OBJECT)
+ + ]
69 : :
70 : : typedef enum
71 : : {
72 : : PROP_CONNECTION = 1,
73 : : } MctManagerProperty;
74 : :
75 : : static GParamSpec *props[PROP_CONNECTION + 1] = { NULL, };
76 : :
77 : : typedef enum
78 : : {
79 : : SIGNAL_APP_FILTER_CHANGED,
80 : : SIGNAL_SESSION_LIMITS_CHANGED,
81 : : SIGNAL_WEB_FILTER_CHANGED,
82 : : } MctManagerSignal;
83 : :
84 : : static unsigned int signals[SIGNAL_WEB_FILTER_CHANGED + 1] = { 0, };
85 : :
86 : : static void
87 : 37 : mct_manager_init (MctManager *self)
88 : : {
89 : : /* Nothing to do here. */
90 : 37 : }
91 : :
92 : : static void
93 : 0 : mct_manager_get_property (GObject *object,
94 : : guint property_id,
95 : : GValue *value,
96 : : GParamSpec *spec)
97 : : {
98 : 0 : MctManager *self = MCT_MANAGER (object);
99 : :
100 [ # # ]: 0 : switch ((MctManagerProperty) property_id)
101 : : {
102 : 0 : case PROP_CONNECTION:
103 : 0 : g_value_set_object (value, self->connection);
104 : 0 : break;
105 : :
106 : 0 : default:
107 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
108 : 0 : break;
109 : : }
110 : 0 : }
111 : :
112 : : static void
113 : 37 : mct_manager_set_property (GObject *object,
114 : : guint property_id,
115 : : const GValue *value,
116 : : GParamSpec *spec)
117 : : {
118 : 37 : MctManager *self = MCT_MANAGER (object);
119 : :
120 [ + - ]: 37 : switch ((MctManagerProperty) property_id)
121 : : {
122 : 37 : case PROP_CONNECTION:
123 : : /* Construct-only. May not be `NULL`. */
124 : 37 : g_assert (self->connection == NULL);
125 : 37 : self->connection = g_value_dup_object (value);
126 : 37 : g_assert (self->connection != NULL);
127 : 37 : break;
128 : :
129 : 0 : default:
130 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
131 : 0 : break;
132 : : }
133 : 37 : }
134 : :
135 : : static void _mct_manager_user_properties_changed_cb (GDBusConnection *connection,
136 : : const char *sender_name,
137 : : const char *object_path,
138 : : const char *interface_name,
139 : : const char *signal_name,
140 : : GVariant *parameters,
141 : : void *user_data);
142 : :
143 : : static void
144 : 37 : mct_manager_constructed (GObject *object)
145 : : {
146 : 37 : MctManager *self = MCT_MANAGER (object);
147 : :
148 : : /* Chain up. */
149 : 37 : G_OBJECT_CLASS (mct_manager_parent_class)->constructed (object);
150 : :
151 : : /* Connect to notifications from AccountsService. */
152 : 37 : g_assert (self->connection != NULL);
153 : 37 : self->user_properties_changed_old_id =
154 : 37 : g_dbus_connection_signal_subscribe (self->connection,
155 : : "org.freedesktop.Accounts", /* sender */
156 : : "org.freedesktop.DBus.Properties", /* interface name */
157 : : "PropertiesChanged", /* signal name */
158 : : NULL, /* object path */
159 : : "com.endlessm.ParentalControls", /* arg0 namespace prefix */
160 : : G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE,
161 : : _mct_manager_user_properties_changed_cb,
162 : : self, NULL);
163 : 37 : self->user_properties_changed_id =
164 : 37 : g_dbus_connection_signal_subscribe (self->connection,
165 : : "org.freedesktop.Accounts", /* sender */
166 : : "org.freedesktop.DBus.Properties", /* interface name */
167 : : "PropertiesChanged", /* signal name */
168 : : NULL, /* object path */
169 : : "org.freedesktop.Malcontent", /* arg0 namespace prefix */
170 : : G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE,
171 : : _mct_manager_user_properties_changed_cb,
172 : : self, NULL);
173 : 37 : }
174 : :
175 : : static void
176 : 35 : mct_manager_dispose (GObject *object)
177 : : {
178 : 35 : MctManager *self = MCT_MANAGER (object);
179 : :
180 [ + - + - ]: 35 : if (self->user_properties_changed_id != 0 && self->connection != NULL)
181 : 35 : g_dbus_connection_signal_unsubscribe (self->connection,
182 : : g_steal_handle_id (&self->user_properties_changed_id));
183 [ + - + - ]: 35 : if (self->user_properties_changed_old_id != 0 && self->connection != NULL)
184 : 35 : g_dbus_connection_signal_unsubscribe (self->connection,
185 : : g_steal_handle_id (&self->user_properties_changed_old_id));
186 [ + - ]: 35 : g_clear_object (&self->connection);
187 : :
188 : 35 : G_OBJECT_CLASS (mct_manager_parent_class)->dispose (object);
189 : 35 : }
190 : :
191 : : static void
192 : 5 : mct_manager_class_init (MctManagerClass *klass)
193 : : {
194 : 5 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
195 : :
196 : 5 : object_class->constructed = mct_manager_constructed;
197 : 5 : object_class->dispose = mct_manager_dispose;
198 : 5 : object_class->get_property = mct_manager_get_property;
199 : 5 : object_class->set_property = mct_manager_set_property;
200 : :
201 : : /**
202 : : * MctManager:connection: (not nullable)
203 : : *
204 : : * A connection to the system bus, where accounts-service runs.
205 : : *
206 : : * It’s provided mostly for testing purposes, or to allow an existing
207 : : * connection to be re-used.
208 : : *
209 : : * Since: 0.3.0
210 : : */
211 : 5 : props[PROP_CONNECTION] = g_param_spec_object ("connection", NULL, NULL,
212 : : G_TYPE_DBUS_CONNECTION,
213 : : G_PARAM_READWRITE |
214 : : G_PARAM_CONSTRUCT_ONLY |
215 : : G_PARAM_STATIC_STRINGS);
216 : :
217 : 5 : g_object_class_install_properties (object_class,
218 : : G_N_ELEMENTS (props),
219 : : props);
220 : :
221 : : /**
222 : : * MctManager::app-filter-changed:
223 : : * @self: a policy manager
224 : : * @user_id: UID of the user whose app filter has changed
225 : : *
226 : : * Emitted when the app filter stored for a user changes.
227 : : *
228 : : * The new app filter for the user should be requested again from
229 : : * the [class@Malcontent.Manager] instance.
230 : : *
231 : : * Since: 0.3.0
232 : : */
233 : 5 : signals[SIGNAL_APP_FILTER_CHANGED] =
234 : 5 : g_signal_new ("app-filter-changed", G_TYPE_FROM_CLASS (klass),
235 : : G_SIGNAL_RUN_LAST,
236 : : 0, NULL, NULL, NULL,
237 : : G_TYPE_NONE, 1,
238 : : G_TYPE_UINT64);
239 : :
240 : : /**
241 : : * MctManager::session-limits-changed:
242 : : * @self: a policy manager
243 : : * @user_id: UID of the user whose session limits have changed
244 : : *
245 : : * Emitted when the session limits stored for a user change.
246 : : *
247 : : * The new session limits for the user should be requested again from
248 : : * the [class@Malcontent.Manager] instance.
249 : : *
250 : : * Since: 0.14.0
251 : : */
252 : 5 : signals[SIGNAL_SESSION_LIMITS_CHANGED] =
253 : 5 : g_signal_new ("session-limits-changed", G_TYPE_FROM_CLASS (klass),
254 : : G_SIGNAL_RUN_LAST,
255 : : 0, NULL, NULL, NULL,
256 : : G_TYPE_NONE, 1,
257 : : G_TYPE_UINT64);
258 : :
259 : : /**
260 : : * MctManager::web-filter-changed:
261 : : * @self: a policy manager
262 : : * @user_id: UID of the user whose web filter has changed
263 : : *
264 : : * Emitted when the web filter stored for a user changes.
265 : : *
266 : : * The new web filter for the user should be requested again from
267 : : * the [class@Malcontent.Manager] instance.
268 : : *
269 : : * Since: 0.14.0
270 : : */
271 : 5 : signals[SIGNAL_WEB_FILTER_CHANGED] =
272 : 5 : g_signal_new ("web-filter-changed", G_TYPE_FROM_CLASS (klass),
273 : : G_SIGNAL_RUN_LAST,
274 : : 0, NULL, NULL, NULL,
275 : : G_TYPE_NONE, 1,
276 : : G_TYPE_UINT64);
277 : 5 : }
278 : :
279 : : /**
280 : : * mct_manager_new:
281 : : * @connection: (transfer none): a D-Bus connection to use
282 : : *
283 : : * Create a new [class@Malcontent.Manager].
284 : : *
285 : : * Returns: (transfer full): a new [class@Malcontent.Manager]
286 : : * Since: 0.3.0
287 : : */
288 : : MctManager *
289 : 37 : mct_manager_new (GDBusConnection *connection)
290 : : {
291 : 37 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
292 : :
293 : 37 : return g_object_new (MCT_TYPE_MANAGER,
294 : : "connection", connection,
295 : : NULL);
296 : : }
297 : :
298 : : static void
299 : 0 : _mct_manager_user_properties_changed_cb (GDBusConnection *connection,
300 : : const char *sender_name,
301 : : const char *object_path,
302 : : const char *interface_name,
303 : : const char *signal_name,
304 : : GVariant *parameters,
305 : : void *user_data)
306 : : {
307 : 0 : MctManager *manager = MCT_MANAGER (user_data);
308 [ # # ]: 0 : g_autoptr(GError) local_error = NULL;
309 : : const gchar *uid_str;
310 : : guint64 uid;
311 : : const char *properties_interface_name;
312 : :
313 : 0 : g_assert (g_str_equal (interface_name, "org.freedesktop.DBus.Properties"));
314 : 0 : g_assert (g_str_equal (signal_name, "PropertiesChanged"));
315 : :
316 : : /* Extract the UID from the object path. This is a bit hacky, but probably
317 : : * better than depending on libaccountsservice just for this. */
318 [ # # # # : 0 : if (!g_str_has_prefix (object_path, "/org/freedesktop/Accounts/User"))
# # # # ]
319 : 0 : return;
320 : :
321 : 0 : uid_str = object_path + strlen ("/org/freedesktop/Accounts/User");
322 [ # # ]: 0 : if (!g_ascii_string_to_unsigned (uid_str, 10, 0, G_MAXUINT64, &uid, &local_error))
323 : : {
324 : 0 : g_warning ("Error converting object path ‘%s’ to user ID: %s",
325 : : object_path, local_error->message);
326 : 0 : g_clear_error (&local_error);
327 : 0 : return;
328 : : }
329 : :
330 [ # # ]: 0 : if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
331 : : {
332 : 0 : g_warning ("Unexpected PropertiesChanged parameter type ‘%s’",
333 : : g_variant_get_type_string (parameters));
334 : 0 : return;
335 : : }
336 : :
337 : 0 : g_variant_get (parameters, "(&sa{sv}as)", &properties_interface_name, NULL, NULL);
338 : :
339 [ # # ]: 0 : if (g_str_equal (properties_interface_name, "com.endlessm.ParentalControls.AppFilter"))
340 : 0 : g_signal_emit (manager, signals[SIGNAL_APP_FILTER_CHANGED], 0, uid);
341 [ # # ]: 0 : else if (g_str_equal (properties_interface_name, "com.endlessm.ParentalControls.SessionLimits"))
342 : 0 : g_signal_emit (manager, signals[SIGNAL_SESSION_LIMITS_CHANGED], 0, uid);
343 [ # # ]: 0 : else if (g_str_equal (properties_interface_name, "org.freedesktop.Malcontent.WebFilter"))
344 : 0 : g_signal_emit (manager, signals[SIGNAL_WEB_FILTER_CHANGED], 0, uid);
345 : : else
346 : 0 : g_debug ("PropertiesChanged on unknown interface ‘%s’, ignoring", properties_interface_name);
347 : : }
348 : :
349 : : /* Check if @error is a D-Bus remote error matching @expected_error_name. */
350 : : static gboolean
351 : 32 : bus_remote_error_matches (const GError *error,
352 : : const gchar *expected_error_name)
353 : : {
354 : 32 : g_autofree gchar *error_name = NULL;
355 : :
356 [ - + ]: 32 : if (!g_dbus_error_is_remote_error (error))
357 : 0 : return FALSE;
358 : :
359 : 32 : error_name = g_dbus_error_get_remote_error (error);
360 : :
361 : 32 : return g_str_equal (error_name, expected_error_name);
362 : : }
363 : :
364 : : /* Convert a `GDBusError` into a `MctManagerError`. */
365 : : static GError *
366 : 18 : bus_error_to_manager_error (const GError *bus_error,
367 : : uid_t user_id)
368 : : {
369 [ + - + + ]: 36 : if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED) ||
370 : 18 : bus_remote_error_matches (bus_error, "org.freedesktop.Accounts.Error.PermissionDenied"))
371 : 4 : return g_error_new (MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED,
372 : : _("Not allowed to query parental controls data for user %u"),
373 : : (guint) user_id);
374 [ + - + + ]: 28 : else if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD) ||
375 : 14 : bus_remote_error_matches (bus_error, "org.freedesktop.Accounts.Error.Failed"))
376 : 4 : return g_error_new (MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_USER,
377 : : _("User %u does not exist"), (guint) user_id);
378 [ + - - + ]: 20 : else if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) ||
379 : 10 : g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
380 : : /* If accountsservice is not available on the system bus, then the
381 : : * com.endlessm.ParentalControls.AppFilter extension interface
382 : : * certainly can't be available. */
383 : 0 : return g_error_new_literal (MCT_MANAGER_ERROR,
384 : : MCT_MANAGER_ERROR_DISABLED,
385 : : _("System accounts service not available"));
386 : : else
387 : 10 : return g_error_copy (bus_error);
388 : : }
389 : :
390 : : /* Find the object path for the given @user_id on the accountsservice D-Bus
391 : : * interface, by calling its FindUserById() method. This is a synchronous,
392 : : * blocking function. */
393 : : static gchar *
394 : 35 : accounts_find_user_by_id (GDBusConnection *connection,
395 : : uid_t user_id,
396 : : gboolean allow_interactive_authorization,
397 : : GCancellable *cancellable,
398 : : GError **error)
399 : : {
400 : 35 : g_autofree gchar *object_path = NULL;
401 : 35 : g_autoptr(GVariant) result_variant = NULL;
402 : 35 : g_autoptr(GError) local_error = NULL;
403 : :
404 : 35 : result_variant =
405 [ - + ]: 35 : g_dbus_connection_call_sync (connection,
406 : : "org.freedesktop.Accounts",
407 : : "/org/freedesktop/Accounts",
408 : : "org.freedesktop.Accounts",
409 : : "FindUserById",
410 : : g_variant_new ("(x)", (gint64) user_id),
411 : : G_VARIANT_TYPE ("(o)"),
412 : : allow_interactive_authorization
413 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
414 : : : G_DBUS_CALL_FLAGS_NONE,
415 : : -1, /* timeout, ms */
416 : : cancellable,
417 : : &local_error);
418 [ + + ]: 35 : if (local_error != NULL)
419 : : {
420 : 6 : g_autoptr(GError) app_filter_error = bus_error_to_manager_error (local_error,
421 : : user_id);
422 : 6 : g_propagate_error (error, g_steal_pointer (&app_filter_error));
423 : 6 : return NULL;
424 : : }
425 : :
426 : 29 : g_variant_get (result_variant, "(o)", &object_path);
427 : :
428 : 29 : return g_steal_pointer (&object_path);
429 : : }
430 : :
431 : : static GVariant *
432 : 19 : account_get_interface_properties (MctManager *self,
433 : : uid_t user_id,
434 : : const char *interface_name,
435 : : const char *canary_key,
436 : : const GVariantType *canary_type,
437 : : const char *disabled_error_message,
438 : : MctManagerGetValueFlags flags,
439 : : GCancellable *cancellable,
440 : : GError **error)
441 : : {
442 : 19 : g_autofree gchar *object_path = NULL;
443 : 19 : g_autoptr(GVariant) result_variant = NULL;
444 : 19 : g_autoptr(GVariant) properties = NULL;
445 : 19 : g_autoptr(GVariant) canary_value = NULL;
446 : 19 : g_autoptr(GError) local_error = NULL;
447 : :
448 : 19 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
449 : 19 : g_return_val_if_fail (interface_name != NULL, NULL);
450 : 19 : g_return_val_if_fail (canary_key != NULL, NULL);
451 : 19 : g_return_val_if_fail (canary_type != NULL, NULL);
452 : 19 : g_return_val_if_fail (disabled_error_message != NULL, NULL);
453 : 19 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
454 : 19 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
455 : :
456 : 38 : object_path = accounts_find_user_by_id (self->connection, user_id,
457 : 19 : (flags & MCT_MANAGER_GET_VALUE_FLAGS_INTERACTIVE),
458 : : cancellable, error);
459 [ + + ]: 19 : if (object_path == NULL)
460 : 4 : return NULL;
461 : :
462 : 15 : result_variant =
463 : 15 : g_dbus_connection_call_sync (self->connection,
464 : : "org.freedesktop.Accounts",
465 : : object_path,
466 : : "org.freedesktop.DBus.Properties",
467 : : "GetAll",
468 : : g_variant_new ("(s)", interface_name),
469 : : G_VARIANT_TYPE ("(a{sv})"),
470 : : (flags & MCT_MANAGER_GET_VALUE_FLAGS_INTERACTIVE)
471 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
472 : 15 : : G_DBUS_CALL_FLAGS_NONE,
473 : : -1, /* timeout, ms */
474 : : cancellable,
475 : : &local_error);
476 [ + + ]: 15 : if (local_error != NULL)
477 : : {
478 : 5 : g_autoptr(GError) manager_error = NULL;
479 : 5 : g_autofree char *remote_dbus_error = g_dbus_error_get_remote_error (local_error);
480 : :
481 [ + + + + ]: 8 : if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS) ||
482 : 3 : g_strcmp0 (remote_dbus_error, "org.freedesktop.Accounts.User.UnknownInterface") == 0)
483 : : {
484 : : /* o.fd.D.GetAll() will return InvalidArgs errors if
485 : : * accountsservice doesn’t have the `interface_name`
486 : : * extension interface installed. */
487 : 3 : manager_error = g_error_new_literal (MCT_MANAGER_ERROR,
488 : : MCT_MANAGER_ERROR_DISABLED,
489 : : disabled_error_message);
490 : : }
491 : : else
492 : : {
493 : 2 : manager_error = bus_error_to_manager_error (local_error, user_id);
494 : : }
495 : :
496 : 5 : g_propagate_error (error, g_steal_pointer (&manager_error));
497 : 5 : return NULL;
498 : : }
499 : :
500 : : /* Extract the properties we care about. They may be silently omitted from the
501 : : * results if we don’t have permission to access them. */
502 : 10 : properties = g_variant_get_child_value (result_variant, 0);
503 : 10 : canary_value = g_variant_lookup_value (properties, canary_key, canary_type);
504 [ + + ]: 10 : if (canary_value == NULL)
505 : : {
506 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
507 : : MCT_MANAGER_ERROR_PERMISSION_DENIED,
508 : : _("Not allowed to query parental controls data for user %u"),
509 : : (guint) user_id);
510 : 2 : return NULL;
511 : : }
512 : :
513 : 8 : return g_steal_pointer (&properties);
514 : : }
515 : :
516 : : static gboolean
517 : 16 : account_set_interface_properties (MctManager *self,
518 : : uid_t user_id,
519 : : const char *interface_name,
520 : : GVariant *properties_variant,
521 : : const char *final_key,
522 : : MctManagerSetValueFlags flags,
523 : : GCancellable *cancellable,
524 : : GError **error)
525 : : {
526 : 16 : g_autofree gchar *object_path = NULL;
527 : 16 : g_autoptr(GVariant) properties_value = NULL;
528 : 16 : const gchar *properties_key = NULL;
529 : : GVariantIter iter;
530 : 16 : g_autoptr(GVariant) final_key_variant = NULL;
531 : 16 : g_autoptr(GVariant) final_key_result_variant = NULL;
532 : 16 : g_autoptr(GError) local_error = NULL;
533 : :
534 : 16 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
535 : 16 : g_return_val_if_fail (interface_name != NULL, FALSE);
536 : 16 : g_return_val_if_fail (properties_variant != NULL, FALSE);
537 : 16 : g_return_val_if_fail (final_key != NULL, FALSE);
538 : 16 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
539 : 16 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
540 : :
541 : 32 : object_path = accounts_find_user_by_id (self->connection, user_id,
542 : 16 : (flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE),
543 : : cancellable, error);
544 [ + + ]: 16 : if (object_path == NULL)
545 : 2 : return FALSE;
546 : :
547 : 14 : g_variant_iter_init (&iter, properties_variant);
548 [ + + ]: 42 : while (g_variant_iter_loop (&iter, "{&sv}", &properties_key, &properties_value))
549 : : {
550 [ + + + ]: 34 : g_autoptr(GVariant) result_variant = NULL;
551 : :
552 : : /* Change the ‘final key’ last. This is the key which will trigger an
553 : : * atomic update in clients. */
554 [ + + ]: 34 : if (g_str_equal (properties_key, final_key))
555 : : {
556 : 13 : final_key_variant = g_steal_pointer (&properties_value);
557 : 13 : continue;
558 : : }
559 : :
560 : 21 : result_variant =
561 : 21 : g_dbus_connection_call_sync (self->connection,
562 : : "org.freedesktop.Accounts",
563 : : object_path,
564 : : "org.freedesktop.DBus.Properties",
565 : : "Set",
566 : : g_variant_new ("(ssv)",
567 : : interface_name,
568 : : properties_key,
569 : : properties_value),
570 : : G_VARIANT_TYPE ("()"),
571 : : (flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE)
572 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
573 : 21 : : G_DBUS_CALL_FLAGS_NONE,
574 : : -1, /* timeout, ms */
575 : : cancellable,
576 : : &local_error);
577 [ + + ]: 21 : if (local_error != NULL)
578 : : {
579 : 6 : g_propagate_error (error, bus_error_to_manager_error (local_error, user_id));
580 : 6 : return FALSE;
581 : : }
582 : : }
583 : :
584 : 8 : final_key_result_variant =
585 : 8 : g_dbus_connection_call_sync (self->connection,
586 : : "org.freedesktop.Accounts",
587 : : object_path,
588 : : "org.freedesktop.DBus.Properties",
589 : : "Set",
590 : : g_variant_new ("(ssv)",
591 : : interface_name,
592 : : final_key,
593 : : final_key_variant),
594 : : G_VARIANT_TYPE ("()"),
595 : : (flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE)
596 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
597 : 8 : : G_DBUS_CALL_FLAGS_NONE,
598 : : -1, /* timeout, ms */
599 : : cancellable,
600 : : &local_error);
601 [ + + ]: 8 : if (local_error != NULL)
602 : : {
603 : 4 : g_propagate_error (error, bus_error_to_manager_error (local_error, user_id));
604 : 4 : return FALSE;
605 : : }
606 : :
607 : 4 : return TRUE;
608 : : }
609 : :
610 : : /**
611 : : * mct_manager_get_app_filter:
612 : : * @self: a policy manager
613 : : * @user_id: ID of the user to query, typically coming from `getuid()`
614 : : * @flags: flags to affect the behaviour of the call
615 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
616 : : * @error: return location for a [type@GLib.Error], or `NULL`
617 : : *
618 : : * Synchronous version of [method@Malcontent.Manager.get_app_filter_async].
619 : : *
620 : : * Returns: (transfer full): app filter for the queried user
621 : : * Since: 0.3.0
622 : : */
623 : : MctAppFilter *
624 : 10 : mct_manager_get_app_filter (MctManager *self,
625 : : uid_t user_id,
626 : : MctManagerGetValueFlags flags,
627 : : GCancellable *cancellable,
628 : : GError **error)
629 : : {
630 : 10 : g_autoptr(GVariant) properties = NULL;
631 : :
632 : 10 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
633 : 10 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
634 : 10 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
635 : :
636 : 10 : properties = account_get_interface_properties (self,
637 : : user_id,
638 : : "com.endlessm.ParentalControls.AppFilter",
639 : : "AppFilter",
640 : : G_VARIANT_TYPE ("(bas)"),
641 : 10 : _("App filtering is globally disabled"),
642 : : flags,
643 : : cancellable,
644 : : error);
645 [ + + ]: 10 : if (properties == NULL)
646 : 5 : return NULL;
647 : :
648 : 5 : return mct_app_filter_deserialize (properties, user_id, error);
649 : : }
650 : :
651 : : static void get_app_filter_thread_cb (GTask *task,
652 : : gpointer source_object,
653 : : gpointer task_data,
654 : : GCancellable *cancellable);
655 : :
656 : : typedef struct
657 : : {
658 : : uid_t user_id;
659 : : MctManagerGetValueFlags flags;
660 : : } GetAppFilterData;
661 : :
662 : : static void
663 : 6 : get_app_filter_data_free (GetAppFilterData *data)
664 : : {
665 : 6 : g_free (data);
666 : 6 : }
667 : :
668 [ - + ]: 12 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetAppFilterData, get_app_filter_data_free)
669 : :
670 : : /**
671 : : * mct_manager_get_app_filter_async:
672 : : * @self: a policy manager
673 : : * @user_id: ID of the user to query, typically coming from `getuid()`
674 : : * @flags: flags to affect the behaviour of the call
675 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
676 : : * @callback: an asynchronous operation result callback
677 : : * @user_data: user data to pass to @callback
678 : : *
679 : : * Asynchronously get a snapshot of the app filter settings for the given
680 : : * @user_id.
681 : : *
682 : : * On failure, a [error@Malcontent.ManagerError], a [error@Gio.DBusError] or a
683 : : * [error@Gio.IOErrorEnum] will be returned.
684 : : *
685 : : * Since: 0.3.0
686 : : */
687 : : void
688 : 6 : mct_manager_get_app_filter_async (MctManager *self,
689 : : uid_t user_id,
690 : : MctManagerGetValueFlags flags,
691 : : GCancellable *cancellable,
692 : : GAsyncReadyCallback callback,
693 : : gpointer user_data)
694 : : {
695 [ + - ]: 6 : g_autoptr(GTask) task = NULL;
696 [ + - ]: 6 : g_autoptr(GetAppFilterData) data = NULL;
697 : :
698 : 6 : g_return_if_fail (MCT_IS_MANAGER (self));
699 : 6 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
700 : :
701 : 6 : task = g_task_new (self, cancellable, callback, user_data);
702 [ + - ]: 6 : g_task_set_source_tag (task, mct_manager_get_app_filter_async);
703 : :
704 : 6 : data = g_new0 (GetAppFilterData, 1);
705 : 6 : data->user_id = user_id;
706 : 6 : data->flags = flags;
707 : 6 : g_task_set_task_data (task, g_steal_pointer (&data),
708 : : (GDestroyNotify) get_app_filter_data_free);
709 : :
710 : 6 : g_task_run_in_thread (task, get_app_filter_thread_cb);
711 : : }
712 : :
713 : : static void
714 : 6 : get_app_filter_thread_cb (GTask *task,
715 : : gpointer source_object,
716 : : gpointer task_data,
717 : : GCancellable *cancellable)
718 : : {
719 : 6 : g_autoptr(MctAppFilter) filter = NULL;
720 : 6 : MctManager *manager = MCT_MANAGER (source_object);
721 : 6 : GetAppFilterData *data = task_data;
722 : 6 : g_autoptr(GError) local_error = NULL;
723 : :
724 : 6 : filter = mct_manager_get_app_filter (manager, data->user_id,
725 : : data->flags,
726 : : cancellable, &local_error);
727 : :
728 [ + + ]: 6 : if (local_error != NULL)
729 : 5 : g_task_return_error (task, g_steal_pointer (&local_error));
730 : : else
731 : 1 : g_task_return_pointer (task, g_steal_pointer (&filter),
732 : : (GDestroyNotify) mct_app_filter_unref);
733 : 6 : }
734 : :
735 : : /**
736 : : * mct_manager_get_app_filter_finish:
737 : : * @self: a policy manager
738 : : * @result: the asynchronous operation result
739 : : * @error: return location for a [type@GLib.Error], or `NULL`
740 : : *
741 : : * Finish an asynchronous operation to get the app filter for a user, started
742 : : * with [method@Malcontent.Manager.get_app_filter_async].
743 : : *
744 : : * Returns: (transfer full): app filter for the queried user
745 : : * Since: 0.3.0
746 : : */
747 : : MctAppFilter *
748 : 6 : mct_manager_get_app_filter_finish (MctManager *self,
749 : : GAsyncResult *result,
750 : : GError **error)
751 : : {
752 : 6 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
753 : 6 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
754 : 6 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
755 : :
756 : 6 : return g_task_propagate_pointer (G_TASK (result), error);
757 : : }
758 : :
759 : : /**
760 : : * mct_manager_set_app_filter:
761 : : * @self: a policy manager
762 : : * @user_id: ID of the user to set the filter for, typically coming from
763 : : * `getuid()`
764 : : * @app_filter: (transfer none): the app filter to set for the user
765 : : * @flags: flags to affect the behaviour of the call
766 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
767 : : * @error: return location for a [type@GLib.Error], or `NULL`
768 : : *
769 : : * Synchronous version of [method@Malcontent.Manager.set_app_filter_async].
770 : : *
771 : : * Returns: true on success, false otherwise
772 : : * Since: 0.3.0
773 : : */
774 : : gboolean
775 : 9 : mct_manager_set_app_filter (MctManager *self,
776 : : uid_t user_id,
777 : : MctAppFilter *app_filter,
778 : : MctManagerSetValueFlags flags,
779 : : GCancellable *cancellable,
780 : : GError **error)
781 : : {
782 : 9 : g_autoptr(GVariant) properties_variant = NULL;
783 : :
784 : 9 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
785 : 9 : g_return_val_if_fail (app_filter != NULL, FALSE);
786 : 9 : g_return_val_if_fail (app_filter->ref_count >= 1, FALSE);
787 : 9 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
788 : 9 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
789 : :
790 : 9 : properties_variant = mct_app_filter_serialize (app_filter);
791 : :
792 : 9 : return account_set_interface_properties (self,
793 : : user_id,
794 : : "com.endlessm.ParentalControls.AppFilter",
795 : : properties_variant,
796 : : "AppFilter",
797 : : flags,
798 : : cancellable,
799 : : error);
800 : : }
801 : :
802 : : static void set_app_filter_thread_cb (GTask *task,
803 : : gpointer source_object,
804 : : gpointer task_data,
805 : : GCancellable *cancellable);
806 : :
807 : : typedef struct
808 : : {
809 : : uid_t user_id;
810 : : MctAppFilter *app_filter; /* (owned) */
811 : : MctManagerSetValueFlags flags;
812 : : } SetAppFilterData;
813 : :
814 : : static void
815 : 2 : set_app_filter_data_free (SetAppFilterData *data)
816 : : {
817 : 2 : mct_app_filter_unref (data->app_filter);
818 : 2 : g_free (data);
819 : 2 : }
820 : :
821 [ - + ]: 4 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (SetAppFilterData, set_app_filter_data_free)
822 : :
823 : : /**
824 : : * mct_manager_set_app_filter_async:
825 : : * @self: a policy manager
826 : : * @user_id: ID of the user to set the filter for, typically coming from
827 : : * `getuid()`
828 : : * @app_filter: (transfer none): the app filter to set for the user
829 : : * @flags: flags to affect the behaviour of the call
830 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
831 : : * @callback: an asynchronous operation result callback
832 : : * @user_data: user data to pass to @callback
833 : : *
834 : : * Asynchronously set the app filter settings for the given @user_id to the
835 : : * given @app_filter instance.
836 : : *
837 : : * This will set all fields of the app filter.
838 : : *
839 : : * On failure, a [error@Malcontent.ManagerError], a [error@Gio.DBusError] or a
840 : : * [error@Gio.IOErrorEnum] will be returned. The user’s app filter settings will be
841 : : * left in an undefined state.
842 : : *
843 : : * Since: 0.3.0
844 : : */
845 : : void
846 : 2 : mct_manager_set_app_filter_async (MctManager *self,
847 : : uid_t user_id,
848 : : MctAppFilter *app_filter,
849 : : MctManagerSetValueFlags flags,
850 : : GCancellable *cancellable,
851 : : GAsyncReadyCallback callback,
852 : : gpointer user_data)
853 : : {
854 [ + - ]: 2 : g_autoptr(GTask) task = NULL;
855 [ + - ]: 2 : g_autoptr(SetAppFilterData) data = NULL;
856 : :
857 : 2 : g_return_if_fail (MCT_IS_MANAGER (self));
858 : 2 : g_return_if_fail (app_filter != NULL);
859 : 2 : g_return_if_fail (app_filter->ref_count >= 1);
860 : 2 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
861 : :
862 : 2 : task = g_task_new (self, cancellable, callback, user_data);
863 [ + - ]: 2 : g_task_set_source_tag (task, mct_manager_set_app_filter_async);
864 : :
865 : 2 : data = g_new0 (SetAppFilterData, 1);
866 : 2 : data->user_id = user_id;
867 : 2 : data->app_filter = mct_app_filter_ref (app_filter);
868 : 2 : data->flags = flags;
869 : 2 : g_task_set_task_data (task, g_steal_pointer (&data),
870 : : (GDestroyNotify) set_app_filter_data_free);
871 : :
872 : 2 : g_task_run_in_thread (task, set_app_filter_thread_cb);
873 : : }
874 : :
875 : : static void
876 : 2 : set_app_filter_thread_cb (GTask *task,
877 : : gpointer source_object,
878 : : gpointer task_data,
879 : : GCancellable *cancellable)
880 : : {
881 : : gboolean success;
882 : 2 : MctManager *manager = MCT_MANAGER (source_object);
883 : 2 : SetAppFilterData *data = task_data;
884 : 2 : g_autoptr(GError) local_error = NULL;
885 : :
886 : 2 : success = mct_manager_set_app_filter (manager, data->user_id,
887 : : data->app_filter, data->flags,
888 : : cancellable, &local_error);
889 : :
890 [ + + ]: 2 : if (local_error != NULL)
891 : 1 : g_task_return_error (task, g_steal_pointer (&local_error));
892 : : else
893 : 1 : g_task_return_boolean (task, success);
894 : 2 : }
895 : :
896 : : /**
897 : : * mct_manager_set_app_filter_finish:
898 : : * @self: a policy manager
899 : : * @result: the asynchronous operation result
900 : : * @error: return location for a [type@GLib.Error], or `NULL`
901 : : *
902 : : * Finish an asynchronous operation to set the app filter for a user, started
903 : : * with [method@Malcontent.Manager.set_app_filter_async].
904 : : *
905 : : * Returns: true on success, false otherwise
906 : : * Since: 0.3.0
907 : : */
908 : : gboolean
909 : 2 : mct_manager_set_app_filter_finish (MctManager *self,
910 : : GAsyncResult *result,
911 : : GError **error)
912 : : {
913 : 2 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
914 : 2 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
915 : 2 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
916 : :
917 : 2 : return g_task_propagate_boolean (G_TASK (result), error);
918 : : }
919 : :
920 : : /**
921 : : * mct_manager_get_session_limits:
922 : : * @self: a policy manager
923 : : * @user_id: ID of the user to query, typically coming from `getuid()`
924 : : * @flags: flags to affect the behaviour of the call
925 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
926 : : * @error: return location for a [type@GLib.Error], or `NULL`
927 : : *
928 : : * Synchronous version of [method@Malcontent.Manager.get_session_limits_async].
929 : : *
930 : : * Returns: (transfer full): session limits for the queried user
931 : : * Since: 0.5.0
932 : : */
933 : : MctSessionLimits *
934 : 9 : mct_manager_get_session_limits (MctManager *self,
935 : : uid_t user_id,
936 : : MctManagerGetValueFlags flags,
937 : : GCancellable *cancellable,
938 : : GError **error)
939 : : {
940 : 9 : g_autoptr(GVariant) properties = NULL;
941 : :
942 : 9 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
943 : 9 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
944 : 9 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
945 : :
946 : 9 : properties = account_get_interface_properties (self,
947 : : user_id,
948 : : "com.endlessm.ParentalControls.SessionLimits",
949 : : "LimitType",
950 : : G_VARIANT_TYPE ("u"),
951 : 9 : _("Session limits are globally disabled"),
952 : : flags,
953 : : cancellable,
954 : : error);
955 [ + + ]: 9 : if (properties == NULL)
956 : 6 : return NULL;
957 : :
958 : 3 : return mct_session_limits_deserialize (properties, user_id, error);
959 : : }
960 : :
961 : : static void get_session_limits_thread_cb (GTask *task,
962 : : gpointer source_object,
963 : : gpointer task_data,
964 : : GCancellable *cancellable);
965 : :
966 : : typedef struct
967 : : {
968 : : uid_t user_id;
969 : : MctManagerGetValueFlags flags;
970 : : } GetSessionLimitsData;
971 : :
972 : : static void
973 : 7 : get_session_limits_data_free (GetSessionLimitsData *data)
974 : : {
975 : 7 : g_free (data);
976 : 7 : }
977 : :
978 [ - + ]: 14 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetSessionLimitsData, get_session_limits_data_free)
979 : :
980 : : /**
981 : : * mct_manager_get_session_limits_async:
982 : : * @self: a policy manager
983 : : * @user_id: ID of the user to query, typically coming from `getuid()`
984 : : * @flags: flags to affect the behaviour of the call
985 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
986 : : * @callback: an asynchronous operation result callback
987 : : * @user_data: user data to pass to @callback
988 : : *
989 : : * Asynchronously get a snapshot of the session limit settings for the given
990 : : * @user_id.
991 : : *
992 : : * On failure, a [error@Malcontent.ManagerError], a [error@Gio.DBusError] or a
993 : : * [error@Gio.IOErrorEnum] will be returned via
994 : : * [method@Malcontent.Manager.get_session_limits_finish].
995 : : *
996 : : * Since: 0.5.0
997 : : */
998 : : void
999 : 7 : mct_manager_get_session_limits_async (MctManager *self,
1000 : : uid_t user_id,
1001 : : MctManagerGetValueFlags flags,
1002 : : GCancellable *cancellable,
1003 : : GAsyncReadyCallback callback,
1004 : : gpointer user_data)
1005 : : {
1006 [ + - ]: 7 : g_autoptr(GTask) task = NULL;
1007 [ + - ]: 7 : g_autoptr(GetSessionLimitsData) data = NULL;
1008 : :
1009 : 7 : g_return_if_fail (MCT_IS_MANAGER (self));
1010 : 7 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1011 : :
1012 : 7 : task = g_task_new (self, cancellable, callback, user_data);
1013 [ + - ]: 7 : g_task_set_source_tag (task, mct_manager_get_session_limits_async);
1014 : :
1015 : 7 : data = g_new0 (GetSessionLimitsData, 1);
1016 : 7 : data->user_id = user_id;
1017 : 7 : data->flags = flags;
1018 : 7 : g_task_set_task_data (task, g_steal_pointer (&data),
1019 : : (GDestroyNotify) get_session_limits_data_free);
1020 : :
1021 : 7 : g_task_run_in_thread (task, get_session_limits_thread_cb);
1022 : : }
1023 : :
1024 : : static void
1025 : 7 : get_session_limits_thread_cb (GTask *task,
1026 : : gpointer source_object,
1027 : : gpointer task_data,
1028 : : GCancellable *cancellable)
1029 : : {
1030 : 7 : g_autoptr(MctSessionLimits) limits = NULL;
1031 : 7 : MctManager *manager = MCT_MANAGER (source_object);
1032 : 7 : GetSessionLimitsData *data = task_data;
1033 : 7 : g_autoptr(GError) local_error = NULL;
1034 : :
1035 : 7 : limits = mct_manager_get_session_limits (manager, data->user_id,
1036 : : data->flags,
1037 : : cancellable, &local_error);
1038 : :
1039 [ + + ]: 7 : if (local_error != NULL)
1040 : 6 : g_task_return_error (task, g_steal_pointer (&local_error));
1041 : : else
1042 : 1 : g_task_return_pointer (task, g_steal_pointer (&limits),
1043 : : (GDestroyNotify) mct_session_limits_unref);
1044 : 7 : }
1045 : :
1046 : : /**
1047 : : * mct_manager_get_session_limits_finish:
1048 : : * @self: a policy manager
1049 : : * @result: the asynchronous operation result
1050 : : * @error: return location for a [type@GLib.Error], or `NULL`
1051 : : *
1052 : : * Finish an asynchronous operation to get the session limits for a user,
1053 : : * started with [method@Malcontent.Manager.get_session_limits_async].
1054 : : *
1055 : : * Returns: (transfer full): session limits for the queried user
1056 : : * Since: 0.5.0
1057 : : */
1058 : : MctSessionLimits *
1059 : 7 : mct_manager_get_session_limits_finish (MctManager *self,
1060 : : GAsyncResult *result,
1061 : : GError **error)
1062 : : {
1063 : 7 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
1064 : 7 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
1065 : 7 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1066 : :
1067 : 7 : return g_task_propagate_pointer (G_TASK (result), error);
1068 : : }
1069 : :
1070 : : /**
1071 : : * mct_manager_set_session_limits:
1072 : : * @self: a policy manager
1073 : : * @user_id: ID of the user to set the limits for, typically coming from
1074 : : * `getuid()`
1075 : : * @session_limits: (transfer none): the session limits to set for the user
1076 : : * @flags: flags to affect the behaviour of the call
1077 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
1078 : : * @error: return location for a [type@GLib.Error], or `NULL`
1079 : : *
1080 : : * Synchronous version of [method@Malcontent.Manager.set_session_limits_async].
1081 : : *
1082 : : * Returns: true on success, false otherwise
1083 : : * Since: 0.5.0
1084 : : */
1085 : : gboolean
1086 : 7 : mct_manager_set_session_limits (MctManager *self,
1087 : : uid_t user_id,
1088 : : MctSessionLimits *session_limits,
1089 : : MctManagerSetValueFlags flags,
1090 : : GCancellable *cancellable,
1091 : : GError **error)
1092 : : {
1093 : 7 : g_autoptr(GVariant) properties_variant = NULL;
1094 : :
1095 : 7 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
1096 : 7 : g_return_val_if_fail (session_limits != NULL, FALSE);
1097 : 7 : g_return_val_if_fail (session_limits->ref_count >= 1, FALSE);
1098 : 7 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1099 : 7 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1100 : :
1101 : 7 : properties_variant = mct_session_limits_serialize (session_limits);
1102 : :
1103 : 7 : return account_set_interface_properties (self,
1104 : : user_id,
1105 : : "com.endlessm.ParentalControls.SessionLimits",
1106 : : properties_variant,
1107 : : "LimitType",
1108 : : flags,
1109 : : cancellable,
1110 : : error);
1111 : : }
1112 : :
1113 : : static void set_session_limits_thread_cb (GTask *task,
1114 : : gpointer source_object,
1115 : : gpointer task_data,
1116 : : GCancellable *cancellable);
1117 : :
1118 : : typedef struct
1119 : : {
1120 : : uid_t user_id;
1121 : : MctSessionLimits *session_limits; /* (owned) */
1122 : : MctManagerSetValueFlags flags;
1123 : : } SetSessionLimitsData;
1124 : :
1125 : : static void
1126 : 2 : set_session_limits_data_free (SetSessionLimitsData *data)
1127 : : {
1128 : 2 : mct_session_limits_unref (data->session_limits);
1129 : 2 : g_free (data);
1130 : 2 : }
1131 : :
1132 [ - + ]: 4 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (SetSessionLimitsData, set_session_limits_data_free)
1133 : :
1134 : : /**
1135 : : * mct_manager_set_session_limits_async:
1136 : : * @self: a policy manager
1137 : : * @user_id: ID of the user to set the limits for, typically coming from
1138 : : * `getuid()`
1139 : : * @session_limits: (transfer none): the session limits to set for the user
1140 : : * @flags: flags to affect the behaviour of the call
1141 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
1142 : : * @callback: an asynchronous operation result callback
1143 : : * @user_data: user data to pass to @callback
1144 : : *
1145 : : * Asynchronously set the session limits settings for the given @user_id to the
1146 : : * given @session_limits instance.
1147 : : *
1148 : : * On failure, a [error@Malcontent.ManagerError], a [error@Gio.DBusError] or a
1149 : : * [error@Gio.IOErrorEnum] will be returned via
1150 : : * [method@Malcontent.Manager.set_session_limits_finish]. The user’s session
1151 : : * limits settings will be left in an undefined state.
1152 : : *
1153 : : * Since: 0.5.0
1154 : : */
1155 : : void
1156 : 2 : mct_manager_set_session_limits_async (MctManager *self,
1157 : : uid_t user_id,
1158 : : MctSessionLimits *session_limits,
1159 : : MctManagerSetValueFlags flags,
1160 : : GCancellable *cancellable,
1161 : : GAsyncReadyCallback callback,
1162 : : gpointer user_data)
1163 : : {
1164 [ + - ]: 2 : g_autoptr(GTask) task = NULL;
1165 [ + - ]: 2 : g_autoptr(SetSessionLimitsData) data = NULL;
1166 : :
1167 : 2 : g_return_if_fail (MCT_IS_MANAGER (self));
1168 : 2 : g_return_if_fail (session_limits != NULL);
1169 : 2 : g_return_if_fail (session_limits->ref_count >= 1);
1170 : 2 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1171 : :
1172 : 2 : task = g_task_new (self, cancellable, callback, user_data);
1173 [ + - ]: 2 : g_task_set_source_tag (task, mct_manager_set_session_limits_async);
1174 : :
1175 : 2 : data = g_new0 (SetSessionLimitsData, 1);
1176 : 2 : data->user_id = user_id;
1177 : 2 : data->session_limits = mct_session_limits_ref (session_limits);
1178 : 2 : data->flags = flags;
1179 : 2 : g_task_set_task_data (task, g_steal_pointer (&data),
1180 : : (GDestroyNotify) set_session_limits_data_free);
1181 : :
1182 : 2 : g_task_run_in_thread (task, set_session_limits_thread_cb);
1183 : : }
1184 : :
1185 : : static void
1186 : 2 : set_session_limits_thread_cb (GTask *task,
1187 : : gpointer source_object,
1188 : : gpointer task_data,
1189 : : GCancellable *cancellable)
1190 : : {
1191 : : gboolean success;
1192 : 2 : MctManager *manager = MCT_MANAGER (source_object);
1193 : 2 : SetSessionLimitsData *data = task_data;
1194 : 2 : g_autoptr(GError) local_error = NULL;
1195 : :
1196 : 2 : success = mct_manager_set_session_limits (manager, data->user_id,
1197 : : data->session_limits, data->flags,
1198 : : cancellable, &local_error);
1199 : :
1200 [ + + ]: 2 : if (local_error != NULL)
1201 : 1 : g_task_return_error (task, g_steal_pointer (&local_error));
1202 : : else
1203 : 1 : g_task_return_boolean (task, success);
1204 : 2 : }
1205 : :
1206 : : /**
1207 : : * mct_manager_set_session_limits_finish:
1208 : : * @self: a policy manager
1209 : : * @result: the asynchronous operation result
1210 : : * @error: return location for a [type@GLib.Error], or `NULL`
1211 : : *
1212 : : * Finish an asynchronous operation to set the session limits for a user,
1213 : : * started with [method@Malcontent.Manager.set_session_limits_async].
1214 : : *
1215 : : * Returns: true on success, false otherwise
1216 : : * Since: 0.5.0
1217 : : */
1218 : : gboolean
1219 : 2 : mct_manager_set_session_limits_finish (MctManager *self,
1220 : : GAsyncResult *result,
1221 : : GError **error)
1222 : : {
1223 : 2 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
1224 : 2 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
1225 : 2 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1226 : :
1227 : 2 : return g_task_propagate_boolean (G_TASK (result), error);
1228 : : }
1229 : :
1230 : : /**
1231 : : * mct_manager_get_web_filter:
1232 : : * @self: a policy manager
1233 : : * @user_id: ID of the user to query, typically coming from `getuid()`
1234 : : * @flags: flags to affect the behaviour of the call
1235 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
1236 : : * @error: return location for a [type@GLib.Error], or `NULL`
1237 : : *
1238 : : * Synchronous version of [method@Malcontent.Manager.get_web_filter_async].
1239 : : *
1240 : : * Returns: (transfer full): web filter for the queried user
1241 : : * Since: 0.14.0
1242 : : */
1243 : : MctWebFilter *
1244 : 0 : mct_manager_get_web_filter (MctManager *self,
1245 : : uid_t user_id,
1246 : : MctManagerGetValueFlags flags,
1247 : : GCancellable *cancellable,
1248 : : GError **error)
1249 : : {
1250 : 0 : g_autoptr(GVariant) properties = NULL;
1251 : :
1252 : 0 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
1253 : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
1254 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1255 : :
1256 : 0 : properties = account_get_interface_properties (self,
1257 : : user_id,
1258 : : "org.freedesktop.Malcontent.WebFilter",
1259 : : "FilterType",
1260 : : G_VARIANT_TYPE ("u"),
1261 : 0 : _("Web filtering is globally disabled"),
1262 : : flags,
1263 : : cancellable,
1264 : : error);
1265 [ # # ]: 0 : if (properties == NULL)
1266 : 0 : return NULL;
1267 : :
1268 : 0 : return mct_web_filter_deserialize (properties, user_id, error);
1269 : : }
1270 : :
1271 : : static void get_web_filter_thread_cb (GTask *task,
1272 : : gpointer source_object,
1273 : : gpointer task_data,
1274 : : GCancellable *cancellable);
1275 : :
1276 : : typedef struct
1277 : : {
1278 : : uid_t user_id;
1279 : : MctManagerGetValueFlags flags;
1280 : : } GetWebFilterData;
1281 : :
1282 : : static void
1283 : 0 : get_web_filter_data_free (GetWebFilterData *data)
1284 : : {
1285 : 0 : g_free (data);
1286 : 0 : }
1287 : :
1288 [ # # ]: 0 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetWebFilterData, get_web_filter_data_free)
1289 : :
1290 : : /**
1291 : : * mct_manager_get_web_filter_async:
1292 : : * @self: a policy manager
1293 : : * @user_id: ID of the user to query, typically coming from `getuid()`
1294 : : * @flags: flags to affect the behaviour of the call
1295 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
1296 : : * @callback: an asynchronous operation result callback
1297 : : * @user_data: user data to pass to @callback
1298 : : *
1299 : : * Asynchronously get a snapshot of the web filter settings for the given
1300 : : * @user_id.
1301 : : *
1302 : : * On failure, a [error@Malcontent.ManagerError], a [error@Gio.DBusError] or a
1303 : : * [error@Gio.IOErrorEnum] will be returned via
1304 : : * [method@Malcontent.Manager.get_web_filter_finish].
1305 : : *
1306 : : * Since: 0.14.0
1307 : : */
1308 : : void
1309 : 0 : mct_manager_get_web_filter_async (MctManager *self,
1310 : : uid_t user_id,
1311 : : MctManagerGetValueFlags flags,
1312 : : GCancellable *cancellable,
1313 : : GAsyncReadyCallback callback,
1314 : : void *user_data)
1315 : : {
1316 [ # # ]: 0 : g_autoptr(GTask) task = NULL;
1317 [ # # ]: 0 : g_autoptr(GetWebFilterData) data = NULL;
1318 : :
1319 : 0 : g_return_if_fail (MCT_IS_MANAGER (self));
1320 : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1321 : :
1322 : 0 : task = g_task_new (self, cancellable, callback, user_data);
1323 [ # # ]: 0 : g_task_set_source_tag (task, mct_manager_get_web_filter_async);
1324 : :
1325 : 0 : data = g_new0 (GetWebFilterData, 1);
1326 : 0 : data->user_id = user_id;
1327 : 0 : data->flags = flags;
1328 : 0 : g_task_set_task_data (task, g_steal_pointer (&data),
1329 : : (GDestroyNotify) get_web_filter_data_free);
1330 : :
1331 : 0 : g_task_run_in_thread (task, get_web_filter_thread_cb);
1332 : : }
1333 : :
1334 : : static void
1335 : 0 : get_web_filter_thread_cb (GTask *task,
1336 : : gpointer source_object,
1337 : : gpointer task_data,
1338 : : GCancellable *cancellable)
1339 : : {
1340 : 0 : g_autoptr(MctWebFilter) web_filter = NULL;
1341 : 0 : MctManager *manager = MCT_MANAGER (source_object);
1342 : 0 : GetWebFilterData *data = task_data;
1343 : 0 : g_autoptr(GError) local_error = NULL;
1344 : :
1345 : 0 : web_filter = mct_manager_get_web_filter (manager, data->user_id,
1346 : : data->flags,
1347 : : cancellable, &local_error);
1348 : :
1349 [ # # ]: 0 : if (local_error != NULL)
1350 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
1351 : : else
1352 : 0 : g_task_return_pointer (task, g_steal_pointer (&web_filter),
1353 : : (GDestroyNotify) mct_web_filter_unref);
1354 : 0 : }
1355 : :
1356 : : /**
1357 : : * mct_manager_get_web_filter_finish:
1358 : : * @self: a policy manager
1359 : : * @result: the asynchronous operation result
1360 : : * @error: return location for a [type@GLib.Error], or `NULL`
1361 : : *
1362 : : * Finish an asynchronous operation to get the web filter for a user,
1363 : : * started with [method@Malcontent.Manager.get_web_filter_async].
1364 : : *
1365 : : * Returns: (transfer full): web filter for the queried user
1366 : : * Since: 0.14.0
1367 : : */
1368 : : MctWebFilter *
1369 : 0 : mct_manager_get_web_filter_finish (MctManager *self,
1370 : : GAsyncResult *result,
1371 : : GError **error)
1372 : : {
1373 : 0 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
1374 : 0 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
1375 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1376 : :
1377 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
1378 : : }
1379 : :
1380 : : /**
1381 : : * mct_manager_set_web_filter:
1382 : : * @self: a policy manager
1383 : : * @user_id: ID of the user to set the web_filter for, typically coming from
1384 : : * `getuid()`
1385 : : * @web_filter: (transfer none): the web filter to set for the user
1386 : : * @flags: flags to affect the behaviour of the call
1387 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
1388 : : * @error: return location for a [type@GLib.Error], or `NULL`
1389 : : *
1390 : : * Synchronous version of [method@Malcontent.Manager.set_web_filter_async].
1391 : : *
1392 : : * Returns: true on success, false otherwise
1393 : : * Since: 0.14.0
1394 : : */
1395 : : gboolean
1396 : 0 : mct_manager_set_web_filter (MctManager *self,
1397 : : uid_t user_id,
1398 : : MctWebFilter *web_filter,
1399 : : MctManagerSetValueFlags flags,
1400 : : GCancellable *cancellable,
1401 : : GError **error)
1402 : : {
1403 : 0 : g_autoptr(GVariant) properties_variant = NULL;
1404 : :
1405 : 0 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
1406 : 0 : g_return_val_if_fail (web_filter != NULL, FALSE);
1407 : 0 : g_return_val_if_fail (web_filter->ref_count >= 1, FALSE);
1408 : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1409 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1410 : :
1411 : 0 : properties_variant = mct_web_filter_serialize (web_filter);
1412 : :
1413 : 0 : return account_set_interface_properties (self,
1414 : : user_id,
1415 : : "org.freedesktop.Malcontent.WebFilter",
1416 : : properties_variant,
1417 : : "FilterType",
1418 : : flags,
1419 : : cancellable,
1420 : : error);
1421 : :
1422 : : /* FIXME: Also call org.freedesktop.MalcontentWeb1.Filtering.UpdateFilters(uid)? */
1423 : : }
1424 : :
1425 : : static void set_web_filter_thread_cb (GTask *task,
1426 : : gpointer source_object,
1427 : : gpointer task_data,
1428 : : GCancellable *cancellable);
1429 : :
1430 : : typedef struct
1431 : : {
1432 : : uid_t user_id;
1433 : : MctWebFilter *web_filter; /* (owned) */
1434 : : MctManagerSetValueFlags flags;
1435 : : } SetWebFilterData;
1436 : :
1437 : : static void
1438 : 0 : set_web_filter_data_free (SetWebFilterData *data)
1439 : : {
1440 : 0 : mct_web_filter_unref (data->web_filter);
1441 : 0 : g_free (data);
1442 : 0 : }
1443 : :
1444 [ # # ]: 0 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (SetWebFilterData, set_web_filter_data_free)
1445 : :
1446 : : /**
1447 : : * mct_manager_set_web_filter_async:
1448 : : * @self: a policy manager
1449 : : * @user_id: ID of the user to set the web filter for, typically coming from
1450 : : * `getuid()`
1451 : : * @web_filter: (transfer none): the web filter to set for the user
1452 : : * @flags: flags to affect the behaviour of the call
1453 : : * @cancellable: (nullable): a [class@Gio.Cancellable], or `NULL`
1454 : : * @callback: an asynchronous operation result callback
1455 : : * @user_data: user data to pass to @callback
1456 : : *
1457 : : * Asynchronously set the web filter settings for the given @user_id to the
1458 : : * given @web_filter instance.
1459 : : *
1460 : : * On failure, a [error@Malcontent.ManagerError], a [error@Gio.DBusError] or a
1461 : : * [error@Gio.IOErrorEnum] will be returned via
1462 : : * [method@Malcontent.Manager.set_web_filter_finish]. The user’s web filter
1463 : : * settings will be left in an undefined state.
1464 : : *
1465 : : * Since: 0.14.0
1466 : : */
1467 : : void
1468 : 0 : mct_manager_set_web_filter_async (MctManager *self,
1469 : : uid_t user_id,
1470 : : MctWebFilter *web_filter,
1471 : : MctManagerSetValueFlags flags,
1472 : : GCancellable *cancellable,
1473 : : GAsyncReadyCallback callback,
1474 : : gpointer user_data)
1475 : : {
1476 [ # # ]: 0 : g_autoptr(GTask) task = NULL;
1477 [ # # ]: 0 : g_autoptr(SetWebFilterData) data = NULL;
1478 : :
1479 : 0 : g_return_if_fail (MCT_IS_MANAGER (self));
1480 : 0 : g_return_if_fail (web_filter != NULL);
1481 : 0 : g_return_if_fail (web_filter->ref_count >= 1);
1482 : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1483 : :
1484 : 0 : task = g_task_new (self, cancellable, callback, user_data);
1485 [ # # ]: 0 : g_task_set_source_tag (task, mct_manager_set_web_filter_async);
1486 : :
1487 : 0 : data = g_new0 (SetWebFilterData, 1);
1488 : 0 : data->user_id = user_id;
1489 : 0 : data->web_filter = mct_web_filter_ref (web_filter);
1490 : 0 : data->flags = flags;
1491 : 0 : g_task_set_task_data (task, g_steal_pointer (&data),
1492 : : (GDestroyNotify) set_web_filter_data_free);
1493 : :
1494 : 0 : g_task_run_in_thread (task, set_web_filter_thread_cb);
1495 : : }
1496 : :
1497 : : static void
1498 : 0 : set_web_filter_thread_cb (GTask *task,
1499 : : gpointer source_object,
1500 : : gpointer task_data,
1501 : : GCancellable *cancellable)
1502 : : {
1503 : : gboolean success;
1504 : 0 : MctManager *manager = MCT_MANAGER (source_object);
1505 : 0 : SetWebFilterData *data = task_data;
1506 : 0 : g_autoptr(GError) local_error = NULL;
1507 : :
1508 : 0 : success = mct_manager_set_web_filter (manager, data->user_id,
1509 : : data->web_filter, data->flags,
1510 : : cancellable, &local_error);
1511 : :
1512 [ # # ]: 0 : if (local_error != NULL)
1513 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
1514 : : else
1515 : 0 : g_task_return_boolean (task, success);
1516 : 0 : }
1517 : :
1518 : : /**
1519 : : * mct_manager_set_web_filter_finish:
1520 : : * @self: a policy manager
1521 : : * @result: the asynchronous operation result
1522 : : * @error: return location for a [type@GLib.Error], or `NULL`
1523 : : *
1524 : : * Finish an asynchronous operation to set the web filter for a user,
1525 : : * started with [method@Malcontent.Manager.set_web_filter_async].
1526 : : *
1527 : : * Returns: true on success, false otherwise
1528 : : * Since: 0.14.0
1529 : : */
1530 : : gboolean
1531 : 0 : mct_manager_set_web_filter_finish (MctManager *self,
1532 : : GAsyncResult *result,
1533 : : GError **error)
1534 : : {
1535 : 0 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
1536 : 0 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
1537 : 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1538 : :
1539 : 0 : return g_task_propagate_boolean (G_TASK (result), error);
1540 : : }
|