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 : : * This library is free software; you can redistribute it and/or
6 : : * modify it under the terms of the GNU Lesser General Public
7 : : * License as published by the Free Software Foundation; either
8 : : * version 2.1 of the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General Public
16 : : * License along with this library; if not, write to the Free Software
17 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : : *
19 : : * Authors:
20 : : * - Philip Withnall <withnall@endlessm.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <glib.h>
26 : : #include <glib-object.h>
27 : : #include <glib/gi18n-lib.h>
28 : : #include <gio/gio.h>
29 : : #include <libmalcontent/app-filter.h>
30 : : #include <libmalcontent/manager.h>
31 : : #include <libmalcontent/session-limits.h>
32 : :
33 : : #include "libmalcontent/app-filter-private.h"
34 : : #include "libmalcontent/session-limits-private.h"
35 : :
36 : :
37 [ + + ]: 60 : G_DEFINE_QUARK (MctManagerError, mct_manager_error)
38 : :
39 : : /**
40 : : * MctManager:
41 : : *
42 : : * #MctManager is a top-level management object which is used to query and
43 : : * monitor #MctAppFilters for different users.
44 : : *
45 : : * Since: 0.3.0
46 : : */
47 : : struct _MctManager
48 : : {
49 : : GObject parent_instance;
50 : :
51 : : GDBusConnection *connection; /* (owned) */
52 : : guint user_changed_id;
53 : : };
54 : :
55 [ + + + - : 225 : G_DEFINE_TYPE (MctManager, mct_manager, G_TYPE_OBJECT)
+ + ]
56 : :
57 : : typedef enum
58 : : {
59 : : PROP_CONNECTION = 1,
60 : : } MctManagerProperty;
61 : :
62 : : static GParamSpec *props[PROP_CONNECTION + 1] = { NULL, };
63 : :
64 : : static void
65 : 34 : mct_manager_init (MctManager *self)
66 : : {
67 : : /* Nothing to do here. */
68 : 34 : }
69 : :
70 : : static void
71 : 0 : mct_manager_get_property (GObject *object,
72 : : guint property_id,
73 : : GValue *value,
74 : : GParamSpec *spec)
75 : : {
76 : 0 : MctManager *self = MCT_MANAGER (object);
77 : :
78 [ # # ]: 0 : switch ((MctManagerProperty) property_id)
79 : : {
80 : 0 : case PROP_CONNECTION:
81 : 0 : g_value_set_object (value, self->connection);
82 : 0 : break;
83 : :
84 : 0 : default:
85 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
86 : 0 : break;
87 : : }
88 : 0 : }
89 : :
90 : : static void
91 : 34 : mct_manager_set_property (GObject *object,
92 : : guint property_id,
93 : : const GValue *value,
94 : : GParamSpec *spec)
95 : : {
96 : 34 : MctManager *self = MCT_MANAGER (object);
97 : :
98 [ + - ]: 34 : switch ((MctManagerProperty) property_id)
99 : : {
100 : 34 : case PROP_CONNECTION:
101 : : /* Construct-only. May not be %NULL. */
102 : 34 : g_assert (self->connection == NULL);
103 : 34 : self->connection = g_value_dup_object (value);
104 : 34 : g_assert (self->connection != NULL);
105 : 34 : break;
106 : :
107 : 0 : default:
108 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
109 : 0 : break;
110 : : }
111 : 34 : }
112 : :
113 : : static void _mct_manager_user_changed_cb (GDBusConnection *connection,
114 : : const gchar *sender_name,
115 : : const gchar *object_path,
116 : : const gchar *interface_name,
117 : : const gchar *signal_name,
118 : : GVariant *parameters,
119 : : gpointer user_data);
120 : :
121 : : static void
122 : 34 : mct_manager_constructed (GObject *object)
123 : : {
124 : 34 : MctManager *self = MCT_MANAGER (object);
125 : :
126 : : /* Chain up. */
127 : 34 : G_OBJECT_CLASS (mct_manager_parent_class)->constructed (object);
128 : :
129 : : /* Connect to notifications from AccountsService. */
130 : 34 : g_assert (self->connection != NULL);
131 : 34 : self->user_changed_id =
132 : 34 : g_dbus_connection_signal_subscribe (self->connection,
133 : : "org.freedesktop.Accounts", /* sender */
134 : : "org.freedesktop.Accounts.User", /* interface name */
135 : : "Changed", /* signal name */
136 : : NULL, /* object path */
137 : : NULL, /* arg0 */
138 : : G_DBUS_SIGNAL_FLAGS_NONE,
139 : : _mct_manager_user_changed_cb,
140 : : self, NULL);
141 : 34 : }
142 : :
143 : : static void
144 : 34 : mct_manager_dispose (GObject *object)
145 : : {
146 : 34 : MctManager *self = MCT_MANAGER (object);
147 : :
148 [ + - + - ]: 34 : if (self->user_changed_id != 0 && self->connection != NULL)
149 : : {
150 : 34 : g_dbus_connection_signal_unsubscribe (self->connection,
151 : : self->user_changed_id);
152 : 34 : self->user_changed_id = 0;
153 : : }
154 [ + - ]: 34 : g_clear_object (&self->connection);
155 : :
156 : 34 : G_OBJECT_CLASS (mct_manager_parent_class)->dispose (object);
157 : 34 : }
158 : :
159 : : static void
160 : 3 : mct_manager_class_init (MctManagerClass *klass)
161 : : {
162 : 3 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
163 : :
164 : 3 : object_class->constructed = mct_manager_constructed;
165 : 3 : object_class->dispose = mct_manager_dispose;
166 : 3 : object_class->get_property = mct_manager_get_property;
167 : 3 : object_class->set_property = mct_manager_set_property;
168 : :
169 : : /**
170 : : * MctManager:connection: (not nullable)
171 : : *
172 : : * A connection to the system bus, where accounts-service runs. It’s provided
173 : : * mostly for testing purposes, or to allow an existing connection to be
174 : : * re-used.
175 : : *
176 : : * Since: 0.3.0
177 : : */
178 : 3 : props[PROP_CONNECTION] = g_param_spec_object ("connection",
179 : : "D-Bus Connection",
180 : : "A connection to the system bus.",
181 : : G_TYPE_DBUS_CONNECTION,
182 : : G_PARAM_READWRITE |
183 : : G_PARAM_CONSTRUCT_ONLY |
184 : : G_PARAM_STATIC_STRINGS);
185 : :
186 : 3 : g_object_class_install_properties (object_class,
187 : : G_N_ELEMENTS (props),
188 : : props);
189 : :
190 : : /**
191 : : * MctManager::app-filter-changed:
192 : : * @self: a #MctManager
193 : : * @user_id: UID of the user whose app filter has changed
194 : : *
195 : : * Emitted when the app filter stored for a user changes.
196 : : * The new app filter for the user should be requested again from
197 : : * the #MctManager instance.
198 : : *
199 : : * Since: 0.3.0
200 : : */
201 : 3 : g_signal_new ("app-filter-changed", G_TYPE_FROM_CLASS (klass),
202 : : G_SIGNAL_RUN_LAST,
203 : : 0, NULL, NULL, NULL,
204 : : G_TYPE_NONE, 1,
205 : : G_TYPE_UINT64);
206 : 3 : }
207 : :
208 : : /**
209 : : * mct_manager_new:
210 : : * @connection: (transfer none): a #GDBusConnection to use
211 : : *
212 : : * Create a new #MctManager.
213 : : *
214 : : * Returns: (transfer full): a new #MctManager
215 : : * Since: 0.3.0
216 : : */
217 : : MctManager *
218 : 34 : mct_manager_new (GDBusConnection *connection)
219 : : {
220 : 34 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
221 : :
222 : 34 : return g_object_new (MCT_TYPE_MANAGER,
223 : : "connection", connection,
224 : : NULL);
225 : : }
226 : :
227 : : static void
228 : 0 : _mct_manager_user_changed_cb (GDBusConnection *connection,
229 : : const gchar *sender_name,
230 : : const gchar *object_path,
231 : : const gchar *interface_name,
232 : : const gchar *signal_name,
233 : : GVariant *parameters,
234 : : gpointer user_data)
235 : : {
236 : 0 : MctManager *manager = MCT_MANAGER (user_data);
237 [ # # ]: 0 : g_autoptr(GError) local_error = NULL;
238 : : const gchar *uid_str;
239 : : guint64 uid;
240 : :
241 : 0 : g_assert (g_str_equal (interface_name, "org.freedesktop.Accounts.User"));
242 : 0 : g_assert (g_str_equal (signal_name, "Changed"));
243 : :
244 : : /* Extract the UID from the object path. This is a bit hacky, but probably
245 : : * better than depending on libaccountsservice just for this. */
246 [ # # ]: 0 : if (!g_str_has_prefix (object_path, "/org/freedesktop/Accounts/User"))
247 : 0 : return;
248 : :
249 : 0 : uid_str = object_path + strlen ("/org/freedesktop/Accounts/User");
250 [ # # ]: 0 : if (!g_ascii_string_to_unsigned (uid_str, 10, 0, G_MAXUINT64, &uid, &local_error))
251 : : {
252 : 0 : g_warning ("Error converting object path ‘%s’ to user ID: %s",
253 : : object_path, local_error->message);
254 : 0 : g_clear_error (&local_error);
255 : : }
256 : :
257 : 0 : g_signal_emit_by_name (manager, "app-filter-changed", uid);
258 : : }
259 : :
260 : : /* Check if @error is a D-Bus remote error matching @expected_error_name. */
261 : : static gboolean
262 : 32 : bus_remote_error_matches (const GError *error,
263 : : const gchar *expected_error_name)
264 : : {
265 : 32 : g_autofree gchar *error_name = NULL;
266 : :
267 [ - + ]: 32 : if (!g_dbus_error_is_remote_error (error))
268 : 0 : return FALSE;
269 : :
270 : 32 : error_name = g_dbus_error_get_remote_error (error);
271 : :
272 : 32 : return g_str_equal (error_name, expected_error_name);
273 : : }
274 : :
275 : : /* Convert a #GDBusError into a #MctManagerError. */
276 : : static GError *
277 : 18 : bus_error_to_manager_error (const GError *bus_error,
278 : : uid_t user_id)
279 : : {
280 [ + - + + ]: 36 : if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED) ||
281 : 18 : bus_remote_error_matches (bus_error, "org.freedesktop.Accounts.Error.PermissionDenied"))
282 : 4 : return g_error_new (MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED,
283 : : _("Not allowed to query parental controls data for user %u"),
284 : : (guint) user_id);
285 [ + - + + ]: 28 : else if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD) ||
286 : 14 : bus_remote_error_matches (bus_error, "org.freedesktop.Accounts.Error.Failed"))
287 : 4 : return g_error_new (MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_USER,
288 : : _("User %u does not exist"), (guint) user_id);
289 [ + - - + ]: 20 : else if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) ||
290 : 10 : g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
291 : : /* If accountsservice is not available on the system bus, then the
292 : : * com.endlessm.ParentalControls.AppFilter extension interface
293 : : * certainly can't be available. */
294 : 0 : return g_error_new_literal (MCT_MANAGER_ERROR,
295 : : MCT_MANAGER_ERROR_DISABLED,
296 : : _("System accounts service not available"));
297 : : else
298 : 10 : return g_error_copy (bus_error);
299 : : }
300 : :
301 : : /* Find the object path for the given @user_id on the accountsservice D-Bus
302 : : * interface, by calling its FindUserById() method. This is a synchronous,
303 : : * blocking function. */
304 : : static gchar *
305 : 34 : accounts_find_user_by_id (GDBusConnection *connection,
306 : : uid_t user_id,
307 : : gboolean allow_interactive_authorization,
308 : : GCancellable *cancellable,
309 : : GError **error)
310 : : {
311 : 34 : g_autofree gchar *object_path = NULL;
312 : 34 : g_autoptr(GVariant) result_variant = NULL;
313 : 34 : g_autoptr(GError) local_error = NULL;
314 : :
315 : 34 : result_variant =
316 [ - + ]: 34 : g_dbus_connection_call_sync (connection,
317 : : "org.freedesktop.Accounts",
318 : : "/org/freedesktop/Accounts",
319 : : "org.freedesktop.Accounts",
320 : : "FindUserById",
321 : : g_variant_new ("(x)", (gint64) user_id),
322 : : G_VARIANT_TYPE ("(o)"),
323 : : allow_interactive_authorization
324 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
325 : : : G_DBUS_CALL_FLAGS_NONE,
326 : : -1, /* timeout, ms */
327 : : cancellable,
328 : : &local_error);
329 [ + + ]: 34 : if (local_error != NULL)
330 : : {
331 : 6 : g_autoptr(GError) app_filter_error = bus_error_to_manager_error (local_error,
332 : : user_id);
333 : 6 : g_propagate_error (error, g_steal_pointer (&app_filter_error));
334 : 6 : return NULL;
335 : : }
336 : :
337 : 28 : g_variant_get (result_variant, "(o)", &object_path);
338 : :
339 : 28 : return g_steal_pointer (&object_path);
340 : : }
341 : :
342 : : /**
343 : : * mct_manager_get_app_filter:
344 : : * @self: a #MctManager
345 : : * @user_id: ID of the user to query, typically coming from getuid()
346 : : * @flags: flags to affect the behaviour of the call
347 : : * @cancellable: (nullable): a #GCancellable, or %NULL
348 : : * @error: return location for a #GError, or %NULL
349 : : *
350 : : * Synchronous version of mct_manager_get_app_filter_async().
351 : : *
352 : : * Returns: (transfer full): app filter for the queried user
353 : : * Since: 0.3.0
354 : : */
355 : : MctAppFilter *
356 : 10 : mct_manager_get_app_filter (MctManager *self,
357 : : uid_t user_id,
358 : : MctManagerGetValueFlags flags,
359 : : GCancellable *cancellable,
360 : : GError **error)
361 : : {
362 : 10 : g_autofree gchar *object_path = NULL;
363 : 10 : g_autoptr(GVariant) result_variant = NULL;
364 : 10 : g_autoptr(GVariant) properties = NULL;
365 : 10 : g_autoptr(GError) local_error = NULL;
366 : :
367 : 10 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
368 : 10 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
369 : 10 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
370 : :
371 : 20 : object_path = accounts_find_user_by_id (self->connection, user_id,
372 : 10 : (flags & MCT_MANAGER_GET_VALUE_FLAGS_INTERACTIVE),
373 : : cancellable, error);
374 [ + + ]: 10 : if (object_path == NULL)
375 : 2 : return NULL;
376 : :
377 : 8 : result_variant =
378 : 8 : g_dbus_connection_call_sync (self->connection,
379 : : "org.freedesktop.Accounts",
380 : : object_path,
381 : : "org.freedesktop.DBus.Properties",
382 : : "GetAll",
383 : : g_variant_new ("(s)", "com.endlessm.ParentalControls.AppFilter"),
384 : : G_VARIANT_TYPE ("(a{sv})"),
385 : : (flags & MCT_MANAGER_GET_VALUE_FLAGS_INTERACTIVE)
386 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
387 : 8 : : G_DBUS_CALL_FLAGS_NONE,
388 : : -1, /* timeout, ms */
389 : : cancellable,
390 : : &local_error);
391 [ + + ]: 8 : if (local_error != NULL)
392 : : {
393 : 4 : g_autoptr(GError) manager_error = NULL;
394 : :
395 [ + + ]: 2 : if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS))
396 : : {
397 : : /* o.fd.D.GetAll() will return InvalidArgs errors if
398 : : * accountsservice doesn’t have the com.endlessm.ParentalControls.AppFilter
399 : : * extension interface installed. */
400 : 1 : manager_error = g_error_new_literal (MCT_MANAGER_ERROR,
401 : : MCT_MANAGER_ERROR_DISABLED,
402 : : _("App filtering is globally disabled"));
403 : : }
404 : : else
405 : : {
406 : 1 : manager_error = bus_error_to_manager_error (local_error, user_id);
407 : : }
408 : :
409 : 2 : g_propagate_error (error, g_steal_pointer (&manager_error));
410 : 2 : return NULL;
411 : : }
412 : :
413 : : /* Extract the properties we care about. They may be silently omitted from the
414 : : * results if we don’t have permission to access them. */
415 : 6 : properties = g_variant_get_child_value (result_variant, 0);
416 [ + + ]: 6 : if (!g_variant_lookup (properties, "AppFilter", "(b^as)", NULL, NULL))
417 : : {
418 : 1 : g_set_error (error, MCT_MANAGER_ERROR,
419 : : MCT_MANAGER_ERROR_PERMISSION_DENIED,
420 : : _("Not allowed to query parental controls data for user %u"),
421 : : (guint) user_id);
422 : 1 : return NULL;
423 : : }
424 : :
425 : 5 : return mct_app_filter_deserialize (properties, user_id, error);
426 : : }
427 : :
428 : : static void get_app_filter_thread_cb (GTask *task,
429 : : gpointer source_object,
430 : : gpointer task_data,
431 : : GCancellable *cancellable);
432 : :
433 : : typedef struct
434 : : {
435 : : uid_t user_id;
436 : : MctManagerGetValueFlags flags;
437 : : } GetAppFilterData;
438 : :
439 : : static void
440 : 6 : get_app_filter_data_free (GetAppFilterData *data)
441 : : {
442 : 6 : g_free (data);
443 : 6 : }
444 : :
445 [ - + ]: 12 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetAppFilterData, get_app_filter_data_free)
446 : :
447 : : /**
448 : : * mct_manager_get_app_filter_async:
449 : : * @self: a #MctManager
450 : : * @user_id: ID of the user to query, typically coming from getuid()
451 : : * @flags: flags to affect the behaviour of the call
452 : : * @cancellable: (nullable): a #GCancellable, or %NULL
453 : : * @callback: a #GAsyncReadyCallback
454 : : * @user_data: user data to pass to @callback
455 : : *
456 : : * Asynchronously get a snapshot of the app filter settings for the given
457 : : * @user_id.
458 : : *
459 : : * On failure, an #MctManagerError, a #GDBusError or a #GIOError will be
460 : : * returned.
461 : : *
462 : : * Since: 0.3.0
463 : : */
464 : : void
465 : 6 : mct_manager_get_app_filter_async (MctManager *self,
466 : : uid_t user_id,
467 : : MctManagerGetValueFlags flags,
468 : : GCancellable *cancellable,
469 : : GAsyncReadyCallback callback,
470 : : gpointer user_data)
471 : : {
472 [ + - ]: 6 : g_autoptr(GTask) task = NULL;
473 [ + - ]: 6 : g_autoptr(GetAppFilterData) data = NULL;
474 : :
475 : 6 : g_return_if_fail (MCT_IS_MANAGER (self));
476 : 6 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
477 : :
478 : 6 : task = g_task_new (self, cancellable, callback, user_data);
479 : 6 : g_task_set_source_tag (task, mct_manager_get_app_filter_async);
480 : :
481 : 6 : data = g_new0 (GetAppFilterData, 1);
482 : 6 : data->user_id = user_id;
483 : 6 : data->flags = flags;
484 : 6 : g_task_set_task_data (task, g_steal_pointer (&data),
485 : : (GDestroyNotify) get_app_filter_data_free);
486 : :
487 : 6 : g_task_run_in_thread (task, get_app_filter_thread_cb);
488 : : }
489 : :
490 : : static void
491 : 6 : get_app_filter_thread_cb (GTask *task,
492 : : gpointer source_object,
493 : : gpointer task_data,
494 : : GCancellable *cancellable)
495 : : {
496 : 6 : g_autoptr(MctAppFilter) filter = NULL;
497 : 6 : MctManager *manager = MCT_MANAGER (source_object);
498 : 6 : GetAppFilterData *data = task_data;
499 : 6 : g_autoptr(GError) local_error = NULL;
500 : :
501 : 6 : filter = mct_manager_get_app_filter (manager, data->user_id,
502 : : data->flags,
503 : : cancellable, &local_error);
504 : :
505 [ + + ]: 6 : if (local_error != NULL)
506 : 5 : g_task_return_error (task, g_steal_pointer (&local_error));
507 : : else
508 : 1 : g_task_return_pointer (task, g_steal_pointer (&filter),
509 : : (GDestroyNotify) mct_app_filter_unref);
510 : 6 : }
511 : :
512 : : /**
513 : : * mct_manager_get_app_filter_finish:
514 : : * @self: a #MctManager
515 : : * @result: a #GAsyncResult
516 : : * @error: return location for a #GError, or %NULL
517 : : *
518 : : * Finish an asynchronous operation to get the app filter for a user, started
519 : : * with mct_manager_get_app_filter_async().
520 : : *
521 : : * Returns: (transfer full): app filter for the queried user
522 : : * Since: 0.3.0
523 : : */
524 : : MctAppFilter *
525 : 6 : mct_manager_get_app_filter_finish (MctManager *self,
526 : : GAsyncResult *result,
527 : : GError **error)
528 : : {
529 : 6 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
530 : 6 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
531 : 6 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
532 : :
533 : 6 : return g_task_propagate_pointer (G_TASK (result), error);
534 : : }
535 : :
536 : : /**
537 : : * mct_manager_set_app_filter:
538 : : * @self: a #MctManager
539 : : * @user_id: ID of the user to set the filter for, typically coming from getuid()
540 : : * @app_filter: (transfer none): the app filter to set for the user
541 : : * @flags: flags to affect the behaviour of the call
542 : : * @cancellable: (nullable): a #GCancellable, or %NULL
543 : : * @error: return location for a #GError, or %NULL
544 : : *
545 : : * Synchronous version of mct_manager_set_app_filter_async().
546 : : *
547 : : * Returns: %TRUE on success, %FALSE otherwise
548 : : * Since: 0.3.0
549 : : */
550 : : gboolean
551 : 9 : mct_manager_set_app_filter (MctManager *self,
552 : : uid_t user_id,
553 : : MctAppFilter *app_filter,
554 : : MctManagerSetValueFlags flags,
555 : : GCancellable *cancellable,
556 : : GError **error)
557 : : {
558 : 9 : g_autofree gchar *object_path = NULL;
559 : 9 : g_autoptr(GVariant) properties_variant = NULL;
560 : 9 : g_autoptr(GVariant) properties_value = NULL;
561 : 9 : const gchar *properties_key = NULL;
562 : : GVariantIter iter;
563 : 9 : g_autoptr(GError) local_error = NULL;
564 : :
565 : 9 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
566 : 9 : g_return_val_if_fail (app_filter != NULL, FALSE);
567 : 9 : g_return_val_if_fail (app_filter->ref_count >= 1, FALSE);
568 : 9 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
569 : 9 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
570 : :
571 : 18 : object_path = accounts_find_user_by_id (self->connection, user_id,
572 : 9 : (flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE),
573 : : cancellable, error);
574 [ + + ]: 9 : if (object_path == NULL)
575 : 1 : return FALSE;
576 : :
577 : 8 : properties_variant = mct_app_filter_serialize (app_filter);
578 : :
579 : 8 : g_variant_iter_init (&iter, properties_variant);
580 [ + + ]: 22 : while (g_variant_iter_loop (&iter, "{&sv}", &properties_key, &properties_value))
581 : : {
582 [ + + ]: 20 : g_autoptr(GVariant) result_variant = NULL;
583 : :
584 : 20 : result_variant =
585 : 20 : 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 : : "com.endlessm.ParentalControls.AppFilter",
592 : : properties_key,
593 : : properties_value),
594 : : G_VARIANT_TYPE ("()"),
595 : : (flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE)
596 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
597 : 20 : : G_DBUS_CALL_FLAGS_NONE,
598 : : -1, /* timeout, ms */
599 : : cancellable,
600 : : &local_error);
601 [ + + ]: 20 : if (local_error != NULL)
602 : : {
603 : 6 : g_propagate_error (error, bus_error_to_manager_error (local_error, user_id));
604 : 6 : return FALSE;
605 : : }
606 : : }
607 : :
608 : 2 : return TRUE;
609 : : }
610 : :
611 : : static void set_app_filter_thread_cb (GTask *task,
612 : : gpointer source_object,
613 : : gpointer task_data,
614 : : GCancellable *cancellable);
615 : :
616 : : typedef struct
617 : : {
618 : : uid_t user_id;
619 : : MctAppFilter *app_filter; /* (owned) */
620 : : MctManagerSetValueFlags flags;
621 : : } SetAppFilterData;
622 : :
623 : : static void
624 : 2 : set_app_filter_data_free (SetAppFilterData *data)
625 : : {
626 : 2 : mct_app_filter_unref (data->app_filter);
627 : 2 : g_free (data);
628 : 2 : }
629 : :
630 [ - + ]: 4 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (SetAppFilterData, set_app_filter_data_free)
631 : :
632 : : /**
633 : : * mct_manager_set_app_filter_async:
634 : : * @self: a #MctManager
635 : : * @user_id: ID of the user to set the filter for, typically coming from getuid()
636 : : * @app_filter: (transfer none): the app filter to set for the user
637 : : * @flags: flags to affect the behaviour of the call
638 : : * @cancellable: (nullable): a #GCancellable, or %NULL
639 : : * @callback: a #GAsyncReadyCallback
640 : : * @user_data: user data to pass to @callback
641 : : *
642 : : * Asynchronously set the app filter settings for the given @user_id to the
643 : : * given @app_filter instance. This will set all fields of the app filter.
644 : : *
645 : : * On failure, an #MctManagerError, a #GDBusError or a #GIOError will be
646 : : * returned. The user’s app filter settings will be left in an undefined state.
647 : : *
648 : : * Since: 0.3.0
649 : : */
650 : : void
651 : 2 : mct_manager_set_app_filter_async (MctManager *self,
652 : : uid_t user_id,
653 : : MctAppFilter *app_filter,
654 : : MctManagerSetValueFlags flags,
655 : : GCancellable *cancellable,
656 : : GAsyncReadyCallback callback,
657 : : gpointer user_data)
658 : : {
659 [ + - ]: 2 : g_autoptr(GTask) task = NULL;
660 [ + - ]: 2 : g_autoptr(SetAppFilterData) data = NULL;
661 : :
662 : 2 : g_return_if_fail (MCT_IS_MANAGER (self));
663 : 2 : g_return_if_fail (app_filter != NULL);
664 : 2 : g_return_if_fail (app_filter->ref_count >= 1);
665 : 2 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
666 : :
667 : 2 : task = g_task_new (self, cancellable, callback, user_data);
668 : 2 : g_task_set_source_tag (task, mct_manager_set_app_filter_async);
669 : :
670 : 2 : data = g_new0 (SetAppFilterData, 1);
671 : 2 : data->user_id = user_id;
672 : 2 : data->app_filter = mct_app_filter_ref (app_filter);
673 : 2 : data->flags = flags;
674 : 2 : g_task_set_task_data (task, g_steal_pointer (&data),
675 : : (GDestroyNotify) set_app_filter_data_free);
676 : :
677 : 2 : g_task_run_in_thread (task, set_app_filter_thread_cb);
678 : : }
679 : :
680 : : static void
681 : 2 : set_app_filter_thread_cb (GTask *task,
682 : : gpointer source_object,
683 : : gpointer task_data,
684 : : GCancellable *cancellable)
685 : : {
686 : : gboolean success;
687 : 2 : MctManager *manager = MCT_MANAGER (source_object);
688 : 2 : SetAppFilterData *data = task_data;
689 : 2 : g_autoptr(GError) local_error = NULL;
690 : :
691 : 2 : success = mct_manager_set_app_filter (manager, data->user_id,
692 : : data->app_filter, data->flags,
693 : : cancellable, &local_error);
694 : :
695 [ + + ]: 2 : if (local_error != NULL)
696 : 1 : g_task_return_error (task, g_steal_pointer (&local_error));
697 : : else
698 : 1 : g_task_return_boolean (task, success);
699 : 2 : }
700 : :
701 : : /**
702 : : * mct_manager_set_app_filter_finish:
703 : : * @self: a #MctManager
704 : : * @result: a #GAsyncResult
705 : : * @error: return location for a #GError, or %NULL
706 : : *
707 : : * Finish an asynchronous operation to set the app filter for a user, started
708 : : * with mct_manager_set_app_filter_async().
709 : : *
710 : : * Returns: %TRUE on success, %FALSE otherwise
711 : : * Since: 0.3.0
712 : : */
713 : : gboolean
714 : 2 : mct_manager_set_app_filter_finish (MctManager *self,
715 : : GAsyncResult *result,
716 : : GError **error)
717 : : {
718 : 2 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
719 : 2 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
720 : 2 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
721 : :
722 : 2 : return g_task_propagate_boolean (G_TASK (result), error);
723 : : }
724 : :
725 : : /**
726 : : * mct_manager_get_session_limits:
727 : : * @self: a #MctManager
728 : : * @user_id: ID of the user to query, typically coming from getuid()
729 : : * @flags: flags to affect the behaviour of the call
730 : : * @cancellable: (nullable): a #GCancellable, or %NULL
731 : : * @error: return location for a #GError, or %NULL
732 : : *
733 : : * Synchronous version of mct_manager_get_session_limits_async().
734 : : *
735 : : * Returns: (transfer full): session limits for the queried user
736 : : * Since: 0.5.0
737 : : */
738 : : MctSessionLimits *
739 : 8 : mct_manager_get_session_limits (MctManager *self,
740 : : uid_t user_id,
741 : : MctManagerGetValueFlags flags,
742 : : GCancellable *cancellable,
743 : : GError **error)
744 : : {
745 : 8 : g_autofree gchar *object_path = NULL;
746 : 8 : g_autoptr(GVariant) result_variant = NULL;
747 : 8 : g_autoptr(GVariant) properties = NULL;
748 : 8 : g_autoptr(GError) local_error = NULL;
749 : :
750 : 8 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
751 : 8 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
752 : 8 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
753 : :
754 : 16 : object_path = accounts_find_user_by_id (self->connection, user_id,
755 : 8 : (flags & MCT_MANAGER_GET_VALUE_FLAGS_INTERACTIVE),
756 : : cancellable, error);
757 [ + + ]: 8 : if (object_path == NULL)
758 : 2 : return NULL;
759 : :
760 : 6 : result_variant =
761 : 6 : g_dbus_connection_call_sync (self->connection,
762 : : "org.freedesktop.Accounts",
763 : : object_path,
764 : : "org.freedesktop.DBus.Properties",
765 : : "GetAll",
766 : : g_variant_new ("(s)", "com.endlessm.ParentalControls.SessionLimits"),
767 : : G_VARIANT_TYPE ("(a{sv})"),
768 : : (flags & MCT_MANAGER_GET_VALUE_FLAGS_INTERACTIVE)
769 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
770 : 6 : : G_DBUS_CALL_FLAGS_NONE,
771 : : -1, /* timeout, ms */
772 : : cancellable,
773 : : &local_error);
774 [ + + ]: 6 : if (local_error != NULL)
775 : : {
776 : 4 : g_autoptr(GError) manager_error = NULL;
777 : :
778 [ + + ]: 2 : if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS))
779 : : {
780 : : /* o.fd.D.GetAll() will return InvalidArgs errors if
781 : : * accountsservice doesn’t have the com.endlessm.ParentalControls.SessionLimits
782 : : * extension interface installed. */
783 : 1 : manager_error = g_error_new_literal (MCT_MANAGER_ERROR,
784 : : MCT_MANAGER_ERROR_DISABLED,
785 : : _("Session limits are globally disabled"));
786 : : }
787 : : else
788 : : {
789 : 1 : manager_error = bus_error_to_manager_error (local_error, user_id);
790 : : }
791 : :
792 : 2 : g_propagate_error (error, g_steal_pointer (&manager_error));
793 : 2 : return NULL;
794 : : }
795 : :
796 : : /* Extract the properties we care about. They may be silently omitted from the
797 : : * results if we don’t have permission to access them. */
798 : 4 : properties = g_variant_get_child_value (result_variant, 0);
799 [ + + ]: 4 : if (!g_variant_lookup (properties, "LimitType", "u", NULL))
800 : : {
801 : 1 : g_set_error (error, MCT_MANAGER_ERROR,
802 : : MCT_MANAGER_ERROR_PERMISSION_DENIED,
803 : : _("Not allowed to query parental controls data for user %u"),
804 : : (guint) user_id);
805 : 1 : return NULL;
806 : : }
807 : :
808 : 3 : return mct_session_limits_deserialize (properties, user_id, error);
809 : : }
810 : :
811 : : static void get_session_limits_thread_cb (GTask *task,
812 : : gpointer source_object,
813 : : gpointer task_data,
814 : : GCancellable *cancellable);
815 : :
816 : : typedef struct
817 : : {
818 : : uid_t user_id;
819 : : MctManagerGetValueFlags flags;
820 : : } GetSessionLimitsData;
821 : :
822 : : static void
823 : 6 : get_session_limits_data_free (GetSessionLimitsData *data)
824 : : {
825 : 6 : g_free (data);
826 : 6 : }
827 : :
828 [ - + ]: 12 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetSessionLimitsData, get_session_limits_data_free)
829 : :
830 : : /**
831 : : * mct_manager_get_session_limits_async:
832 : : * @self: a #MctManager
833 : : * @user_id: ID of the user to query, typically coming from getuid()
834 : : * @flags: flags to affect the behaviour of the call
835 : : * @cancellable: (nullable): a #GCancellable, or %NULL
836 : : * @callback: a #GAsyncReadyCallback
837 : : * @user_data: user data to pass to @callback
838 : : *
839 : : * Asynchronously get a snapshot of the session limit settings for the given
840 : : * @user_id.
841 : : *
842 : : * On failure, an #MctManagerError, a #GDBusError or a #GIOError will be
843 : : * returned via mct_manager_get_session_limits_finish().
844 : : *
845 : : * Since: 0.5.0
846 : : */
847 : : void
848 : 6 : mct_manager_get_session_limits_async (MctManager *self,
849 : : uid_t user_id,
850 : : MctManagerGetValueFlags flags,
851 : : GCancellable *cancellable,
852 : : GAsyncReadyCallback callback,
853 : : gpointer user_data)
854 : : {
855 [ + - ]: 6 : g_autoptr(GTask) task = NULL;
856 [ + - ]: 6 : g_autoptr(GetSessionLimitsData) data = NULL;
857 : :
858 : 6 : g_return_if_fail (MCT_IS_MANAGER (self));
859 : 6 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
860 : :
861 : 6 : task = g_task_new (self, cancellable, callback, user_data);
862 : 6 : g_task_set_source_tag (task, mct_manager_get_session_limits_async);
863 : :
864 : 6 : data = g_new0 (GetSessionLimitsData, 1);
865 : 6 : data->user_id = user_id;
866 : 6 : data->flags = flags;
867 : 6 : g_task_set_task_data (task, g_steal_pointer (&data),
868 : : (GDestroyNotify) get_session_limits_data_free);
869 : :
870 : 6 : g_task_run_in_thread (task, get_session_limits_thread_cb);
871 : : }
872 : :
873 : : static void
874 : 6 : get_session_limits_thread_cb (GTask *task,
875 : : gpointer source_object,
876 : : gpointer task_data,
877 : : GCancellable *cancellable)
878 : : {
879 : 6 : g_autoptr(MctSessionLimits) limits = NULL;
880 : 6 : MctManager *manager = MCT_MANAGER (source_object);
881 : 6 : GetSessionLimitsData *data = task_data;
882 : 6 : g_autoptr(GError) local_error = NULL;
883 : :
884 : 6 : limits = mct_manager_get_session_limits (manager, data->user_id,
885 : : data->flags,
886 : : cancellable, &local_error);
887 : :
888 [ + + ]: 6 : if (local_error != NULL)
889 : 5 : g_task_return_error (task, g_steal_pointer (&local_error));
890 : : else
891 : 1 : g_task_return_pointer (task, g_steal_pointer (&limits),
892 : : (GDestroyNotify) mct_session_limits_unref);
893 : 6 : }
894 : :
895 : : /**
896 : : * mct_manager_get_session_limits_finish:
897 : : * @self: a #MctManager
898 : : * @result: a #GAsyncResult
899 : : * @error: return location for a #GError, or %NULL
900 : : *
901 : : * Finish an asynchronous operation to get the session limits for a user,
902 : : * started with mct_manager_get_session_limits_async().
903 : : *
904 : : * Returns: (transfer full): session limits for the queried user
905 : : * Since: 0.5.0
906 : : */
907 : : MctSessionLimits *
908 : 6 : mct_manager_get_session_limits_finish (MctManager *self,
909 : : GAsyncResult *result,
910 : : GError **error)
911 : : {
912 : 6 : g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
913 : 6 : g_return_val_if_fail (g_task_is_valid (result, self), NULL);
914 : 6 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
915 : :
916 : 6 : return g_task_propagate_pointer (G_TASK (result), error);
917 : : }
918 : :
919 : : /**
920 : : * mct_manager_set_session_limits:
921 : : * @self: a #MctManager
922 : : * @user_id: ID of the user to set the limits for, typically coming from getuid()
923 : : * @session_limits: (transfer none): the session limits to set for the user
924 : : * @flags: flags to affect the behaviour of the call
925 : : * @cancellable: (nullable): a #GCancellable, or %NULL
926 : : * @error: return location for a #GError, or %NULL
927 : : *
928 : : * Synchronous version of mct_manager_set_session_limits_async().
929 : : *
930 : : * Returns: %TRUE on success, %FALSE otherwise
931 : : * Since: 0.5.0
932 : : */
933 : : gboolean
934 : 7 : mct_manager_set_session_limits (MctManager *self,
935 : : uid_t user_id,
936 : : MctSessionLimits *session_limits,
937 : : MctManagerSetValueFlags flags,
938 : : GCancellable *cancellable,
939 : : GError **error)
940 : : {
941 : 7 : g_autofree gchar *object_path = NULL;
942 : 7 : g_autoptr(GVariant) limit_type_variant = NULL;
943 : 7 : g_autoptr(GVariant) limit_type_result_variant = NULL;
944 : 7 : g_autoptr(GVariant) properties_variant = NULL;
945 : 7 : g_autoptr(GVariant) properties_value = NULL;
946 : 7 : const gchar *properties_key = NULL;
947 : : GVariantIter iter;
948 : 7 : g_autoptr(GError) local_error = NULL;
949 : :
950 : 7 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
951 : 7 : g_return_val_if_fail (session_limits != NULL, FALSE);
952 : 7 : g_return_val_if_fail (session_limits->ref_count >= 1, FALSE);
953 : 7 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
954 : 7 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
955 : :
956 : 14 : object_path = accounts_find_user_by_id (self->connection, user_id,
957 : 7 : (flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE),
958 : : cancellable, error);
959 [ + + ]: 7 : if (object_path == NULL)
960 : 1 : return FALSE;
961 : :
962 : 6 : properties_variant = mct_session_limits_serialize (session_limits);
963 : :
964 : 6 : g_variant_iter_init (&iter, properties_variant);
965 [ + + ]: 14 : while (g_variant_iter_loop (&iter, "{&sv}", &properties_key, &properties_value))
966 : : {
967 [ + + + ]: 9 : g_autoptr(GVariant) result_variant = NULL;
968 : :
969 : : /* Change the limit type last, so all the details of the new limit are
970 : : * correct by the time it’s changed over. */
971 [ + + ]: 9 : if (g_str_equal (properties_key, "LimitType"))
972 : : {
973 : 5 : limit_type_variant = g_steal_pointer (&properties_value);
974 : 5 : continue;
975 : : }
976 : :
977 : 4 : result_variant =
978 : 4 : g_dbus_connection_call_sync (self->connection,
979 : : "org.freedesktop.Accounts",
980 : : object_path,
981 : : "org.freedesktop.DBus.Properties",
982 : : "Set",
983 : : g_variant_new ("(ssv)",
984 : : "com.endlessm.ParentalControls.SessionLimits",
985 : : properties_key,
986 : : properties_value),
987 : : G_VARIANT_TYPE ("()"),
988 : : (flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE)
989 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
990 : 4 : : G_DBUS_CALL_FLAGS_NONE,
991 : : -1, /* timeout, ms */
992 : : cancellable,
993 : : &local_error);
994 [ + + ]: 4 : if (local_error != NULL)
995 : : {
996 : 1 : g_propagate_error (error, bus_error_to_manager_error (local_error, user_id));
997 : 1 : return FALSE;
998 : : }
999 : : }
1000 : :
1001 : 5 : limit_type_result_variant =
1002 : 5 : g_dbus_connection_call_sync (self->connection,
1003 : : "org.freedesktop.Accounts",
1004 : : object_path,
1005 : : "org.freedesktop.DBus.Properties",
1006 : : "Set",
1007 : : g_variant_new ("(ssv)",
1008 : : "com.endlessm.ParentalControls.SessionLimits",
1009 : : "LimitType",
1010 : : limit_type_variant),
1011 : : G_VARIANT_TYPE ("()"),
1012 : : (flags & MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE)
1013 : : ? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
1014 : 5 : : G_DBUS_CALL_FLAGS_NONE,
1015 : : -1, /* timeout, ms */
1016 : : cancellable,
1017 : : &local_error);
1018 [ + + ]: 5 : if (local_error != NULL)
1019 : : {
1020 : 3 : g_propagate_error (error, bus_error_to_manager_error (local_error, user_id));
1021 : 3 : return FALSE;
1022 : : }
1023 : :
1024 : 2 : return TRUE;
1025 : : }
1026 : :
1027 : : static void set_session_limits_thread_cb (GTask *task,
1028 : : gpointer source_object,
1029 : : gpointer task_data,
1030 : : GCancellable *cancellable);
1031 : :
1032 : : typedef struct
1033 : : {
1034 : : uid_t user_id;
1035 : : MctSessionLimits *session_limits; /* (owned) */
1036 : : MctManagerSetValueFlags flags;
1037 : : } SetSessionLimitsData;
1038 : :
1039 : : static void
1040 : 2 : set_session_limits_data_free (SetSessionLimitsData *data)
1041 : : {
1042 : 2 : mct_session_limits_unref (data->session_limits);
1043 : 2 : g_free (data);
1044 : 2 : }
1045 : :
1046 [ - + ]: 4 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (SetSessionLimitsData, set_session_limits_data_free)
1047 : :
1048 : : /**
1049 : : * mct_manager_set_session_limits_async:
1050 : : * @self: a #MctManager
1051 : : * @user_id: ID of the user to set the limits for, typically coming from getuid()
1052 : : * @session_limits: (transfer none): the session limits to set for the user
1053 : : * @flags: flags to affect the behaviour of the call
1054 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1055 : : * @callback: a #GAsyncReadyCallback
1056 : : * @user_data: user data to pass to @callback
1057 : : *
1058 : : * Asynchronously set the session limits settings for the given @user_id to the
1059 : : * given @session_limits instance.
1060 : : *
1061 : : * On failure, an #MctManagerError, a #GDBusError or a #GIOError will be
1062 : : * returned via mct_manager_set_session_limits_finish(). The user’s session
1063 : : * limits settings will be left in an undefined state.
1064 : : *
1065 : : * Since: 0.5.0
1066 : : */
1067 : : void
1068 : 2 : mct_manager_set_session_limits_async (MctManager *self,
1069 : : uid_t user_id,
1070 : : MctSessionLimits *session_limits,
1071 : : MctManagerSetValueFlags flags,
1072 : : GCancellable *cancellable,
1073 : : GAsyncReadyCallback callback,
1074 : : gpointer user_data)
1075 : : {
1076 [ + - ]: 2 : g_autoptr(GTask) task = NULL;
1077 [ + - ]: 2 : g_autoptr(SetSessionLimitsData) data = NULL;
1078 : :
1079 : 2 : g_return_if_fail (MCT_IS_MANAGER (self));
1080 : 2 : g_return_if_fail (session_limits != NULL);
1081 : 2 : g_return_if_fail (session_limits->ref_count >= 1);
1082 : 2 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1083 : :
1084 : 2 : task = g_task_new (self, cancellable, callback, user_data);
1085 : 2 : g_task_set_source_tag (task, mct_manager_set_session_limits_async);
1086 : :
1087 : 2 : data = g_new0 (SetSessionLimitsData, 1);
1088 : 2 : data->user_id = user_id;
1089 : 2 : data->session_limits = mct_session_limits_ref (session_limits);
1090 : 2 : data->flags = flags;
1091 : 2 : g_task_set_task_data (task, g_steal_pointer (&data),
1092 : : (GDestroyNotify) set_session_limits_data_free);
1093 : :
1094 : 2 : g_task_run_in_thread (task, set_session_limits_thread_cb);
1095 : : }
1096 : :
1097 : : static void
1098 : 2 : set_session_limits_thread_cb (GTask *task,
1099 : : gpointer source_object,
1100 : : gpointer task_data,
1101 : : GCancellable *cancellable)
1102 : : {
1103 : : gboolean success;
1104 : 2 : MctManager *manager = MCT_MANAGER (source_object);
1105 : 2 : SetSessionLimitsData *data = task_data;
1106 : 2 : g_autoptr(GError) local_error = NULL;
1107 : :
1108 : 2 : success = mct_manager_set_session_limits (manager, data->user_id,
1109 : : data->session_limits, data->flags,
1110 : : cancellable, &local_error);
1111 : :
1112 [ + + ]: 2 : if (local_error != NULL)
1113 : 1 : g_task_return_error (task, g_steal_pointer (&local_error));
1114 : : else
1115 : 1 : g_task_return_boolean (task, success);
1116 : 2 : }
1117 : :
1118 : : /**
1119 : : * mct_manager_set_session_limits_finish:
1120 : : * @self: a #MctManager
1121 : : * @result: a #GAsyncResult
1122 : : * @error: return location for a #GError, or %NULL
1123 : : *
1124 : : * Finish an asynchronous operation to set the session limits for a user,
1125 : : * started with mct_manager_set_session_limits_async().
1126 : : *
1127 : : * Returns: %TRUE on success, %FALSE otherwise
1128 : : * Since: 0.5.0
1129 : : */
1130 : : gboolean
1131 : 2 : mct_manager_set_session_limits_finish (MctManager *self,
1132 : : GAsyncResult *result,
1133 : : GError **error)
1134 : : {
1135 : 2 : g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
1136 : 2 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
1137 : 2 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1138 : :
1139 : 2 : return g_task_propagate_boolean (G_TASK (result), error);
1140 : : }
|