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 : : * Copyright 2025 GNOME Foundation, Inc.
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General Public
19 : : * License along with this library; if not, write to the Free Software
20 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 : : *
22 : : * Authors:
23 : : * - Philip Withnall <withnall@endlessm.com>
24 : : */
25 : :
26 : : #include "config.h"
27 : :
28 : : #include <glib.h>
29 : : #include <glib-object.h>
30 : : #include <glib/gi18n-lib.h>
31 : : #include <gio/gio.h>
32 : : #include <libmalcontent/manager.h>
33 : : #include <libmalcontent/session-limits.h>
34 : :
35 : : #include "libmalcontent/session-limits-private.h"
36 : :
37 : :
38 : : /* struct _MctSessionLimits is defined in session-limits-private.h */
39 : :
40 [ + - + - : 6 : G_DEFINE_BOXED_TYPE (MctSessionLimits, mct_session_limits,
+ - ]
41 : : mct_session_limits_ref, mct_session_limits_unref)
42 : :
43 : : /**
44 : : * mct_session_limits_ref:
45 : : * @limits: (transfer none): a [struct@Malcontent.SessionLimits]
46 : : *
47 : : * Increment the reference count of @limits, and return the same pointer to it.
48 : : *
49 : : * Returns: (transfer full): the same pointer as @limits
50 : : * Since: 0.5.0
51 : : */
52 : : MctSessionLimits *
53 : 4 : mct_session_limits_ref (MctSessionLimits *limits)
54 : : {
55 : 4 : g_return_val_if_fail (limits != NULL, NULL);
56 : 4 : g_return_val_if_fail (limits->ref_count >= 1, NULL);
57 : 4 : g_return_val_if_fail (limits->ref_count <= G_MAXINT - 1, NULL);
58 : :
59 : 4 : limits->ref_count++;
60 : 4 : return limits;
61 : : }
62 : :
63 : : /**
64 : : * mct_session_limits_unref:
65 : : * @limits: (transfer full): a [struct@Malcontent.SessionLimits]
66 : : *
67 : : * Decrement the reference count of @limits. If the reference count reaches
68 : : * zero, free the @limits and all its resources.
69 : : *
70 : : * Since: 0.5.0
71 : : */
72 : : void
73 : 159 : mct_session_limits_unref (MctSessionLimits *limits)
74 : : {
75 : 159 : g_return_if_fail (limits != NULL);
76 : 159 : g_return_if_fail (limits->ref_count >= 1);
77 : :
78 : 159 : limits->ref_count--;
79 : :
80 [ + + ]: 159 : if (limits->ref_count <= 0)
81 : : {
82 : 155 : g_free (limits);
83 : : }
84 : : }
85 : :
86 : : /**
87 : : * mct_session_limits_get_user_id:
88 : : * @limits: a [struct@Malcontent.SessionLimits]
89 : : *
90 : : * Get the user ID of the user this [struct@Malcontent.SessionLimits] is for.
91 : : *
92 : : * Returns: user ID of the relevant user, or `(uid_t) -1` if unknown
93 : : * Since: 0.5.0
94 : : */
95 : : uid_t
96 : 3 : mct_session_limits_get_user_id (MctSessionLimits *limits)
97 : : {
98 : 3 : g_return_val_if_fail (limits != NULL, (uid_t) -1);
99 : 3 : g_return_val_if_fail (limits->ref_count >= 1, (uid_t) -1);
100 : :
101 : 3 : return limits->user_id;
102 : : }
103 : :
104 : : /**
105 : : * mct_session_limits_is_enabled:
106 : : * @limits: a [struct@Malcontent.SessionLimits]
107 : : *
108 : : * Check whether any session limits are enabled and are going to impose at least
109 : : * one restriction on the user.
110 : : *
111 : : * This gives a high level view of whether session limit parental controls are
112 : : * ‘enabled’ for the given user.
113 : : *
114 : : * This function is equivalent to the value returned by the
115 : : * `time_limit_enabled_out` argument of
116 : : * [method@Malcontent.SessionLimits.check_time_remaining].
117 : : *
118 : : * Returns: true if the session limits object contains at least one restrictive
119 : : * session limit, false if there are no limits in place
120 : : * Since: 0.7.0
121 : : */
122 : : gboolean
123 : 7 : mct_session_limits_is_enabled (MctSessionLimits *limits)
124 : : {
125 : 7 : g_return_val_if_fail (limits != NULL, FALSE);
126 : 7 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
127 : :
128 : 7 : return (limits->limit_type != MCT_SESSION_LIMITS_TYPE_NONE);
129 : : }
130 : :
131 : : /**
132 : : * mct_session_limits_get_daily_schedule:
133 : : * @limits: a [struct@Malcontent.SessionLimits]
134 : : * @out_start_time_secs: (out) (optional): return location for the earliest
135 : : * allowable session start time for the user, in seconds since midnight
136 : : * @out_end_time_secs: (out) (optional): return location for the latest
137 : : * allowable session end time for the user, in seconds since midnight
138 : : *
139 : : * Get the daily schedule session limits, if set.
140 : : *
141 : : * If set, sessions are allowed between the given start and end time every day.
142 : : * The times are given as offsets from the start of the day, in seconds.
143 : : *
144 : : * @out_end_time_secs is guaranteed to be greater than @out_start_time_secs, if
145 : : * they are set. @out_end_time_secs is guaranteed to be at most `24 * 60 * 60`
146 : : * if set.
147 : : *
148 : : * Returns: true if a daily schedule is set, false otherwise
149 : : * Since: 0.14.0
150 : : */
151 : : gboolean
152 : 18 : mct_session_limits_get_daily_schedule (MctSessionLimits *limits,
153 : : unsigned int *out_start_time_secs,
154 : : unsigned int *out_end_time_secs)
155 : : {
156 : 18 : g_return_val_if_fail (limits != NULL, FALSE);
157 : 18 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
158 : :
159 [ + - ]: 18 : if (out_start_time_secs != NULL)
160 [ + + ]: 18 : *out_start_time_secs = (limits->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE) ? limits->daily_start_time : 0;
161 [ + - ]: 18 : if (out_end_time_secs != NULL)
162 [ + + ]: 18 : *out_end_time_secs = (limits->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE) ? limits->daily_end_time : 0;
163 : :
164 : 18 : return (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE);
165 : : }
166 : :
167 : : /**
168 : : * mct_session_limits_get_daily_limit:
169 : : * @limits: a [struct@Malcontent.SessionLimits]
170 : : * @out_daily_limit_secs: (out) (optional): return location for the maximum
171 : : * amount of active session time allowed per day for the user, in seconds
172 : : *
173 : : * Get the daily limit, if set.
174 : : *
175 : : * If set, sessions are allowed to be up to the given limit in length every day.
176 : : *
177 : : * @out_daily_limit_secs is guaranteed to be at most `24 * 60 * 60` if set.
178 : : *
179 : : * Returns: true if a daily limit is set, false otherwise
180 : : * Since: 0.14.0
181 : : */
182 : : gboolean
183 : 8 : mct_session_limits_get_daily_limit (MctSessionLimits *limits,
184 : : unsigned int *out_daily_limit_secs)
185 : : {
186 : 8 : g_return_val_if_fail (limits != NULL, FALSE);
187 : 8 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
188 : :
189 [ + - ]: 8 : if (out_daily_limit_secs != NULL)
190 [ + - ]: 8 : *out_daily_limit_secs = (limits->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT) ? limits->daily_limit_secs : 0;
191 : :
192 : 8 : return (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT);
193 : : }
194 : :
195 : : /**
196 : : * mct_session_limits_get_active_extension:
197 : : * @limits: a [struct@Malcontent.SessionLimits]
198 : : * @now_dt: current time in the user’s timezone, typically queried using
199 : : * `g_date_time_new_now_local()`
200 : : * @out_start_time_secs: (out) (optional): return location for the start time of
201 : : * the extension, in seconds since the Unix epoch; or zero if there is no
202 : : * active extension
203 : : * @out_duration_secs: (out) (optional): return location for the duration of the
204 : : * extension, in seconds; or zero if there is no active extension
205 : : *
206 : : * Check whether there is an active screen time limit extension, and get its
207 : : * bounds.
208 : : *
209 : : * If a screen time limit extension has been set on @limits using
210 : : * [method@Malcontent.SessionLimitsBuilder.set_active_extension], and @limits
211 : : * has at least one limit enabled (see
212 : : * [method@Malcontent.SessionLimits.is_enabled]), and the bounds of the
213 : : * extension contain @now_dt (i.e. the extension isn’t entirely in the past or
214 : : * the future), then the out arguments will be set to its bounds and true will
215 : : * be returned.
216 : : *
217 : : * Otherwise, there is no screen time limit extension active, so false will be
218 : : * returned and the out arguments will be set to zero.
219 : : *
220 : : * Returns: true if @limits has a screen time limit extension active; false
221 : : * otherwise
222 : : * Since: 0.14.0
223 : : */
224 : : gboolean
225 : 213 : mct_session_limits_get_active_extension (MctSessionLimits *limits,
226 : : GDateTime *now_dt,
227 : : uint64_t *out_start_time_secs,
228 : : unsigned int *out_duration_secs)
229 : : {
230 : : gboolean extension_is_active;
231 : : uint64_t now_secs;
232 : : uint64_t start_time_secs;
233 : : unsigned int duration_secs;
234 : :
235 : 213 : g_return_val_if_fail (limits != NULL, FALSE);
236 : 213 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
237 : 213 : g_return_val_if_fail (now_dt != NULL, FALSE);
238 : :
239 : : /* We check for this being very far in the future in the `if` block below. */
240 : 213 : now_secs = g_date_time_to_unix (now_dt);
241 : :
242 [ + + + - ]: 386 : if (limits->set_limit_type == MCT_SESSION_LIMITS_TYPE_NONE ||
243 : 173 : g_date_time_to_unix (now_dt) < 0 ||
244 [ + + ]: 173 : limits->active_extension_duration_secs == 0 ||
245 [ + + ]: 62 : limits->active_extension_start_time_secs > now_secs ||
246 [ + + ]: 34 : limits->active_extension_start_time_secs + limits->active_extension_duration_secs <= now_secs)
247 : : {
248 : 189 : extension_is_active = FALSE;
249 : 189 : start_time_secs = 0;
250 : 189 : duration_secs = 0;
251 : : }
252 : : else
253 : : {
254 : 24 : extension_is_active = TRUE;
255 : 24 : start_time_secs = limits->active_extension_start_time_secs;
256 : 24 : duration_secs = limits->active_extension_duration_secs;
257 : : }
258 : :
259 [ + + ]: 213 : if (out_start_time_secs != NULL)
260 : 211 : *out_start_time_secs = start_time_secs;
261 [ + + ]: 213 : if (out_duration_secs != NULL)
262 : 211 : *out_duration_secs = duration_secs;
263 : :
264 : 213 : return extension_is_active;
265 : : }
266 : :
267 : : /**
268 : : * mct_session_limits_check_time_remaining:
269 : : * @limits: a [struct@Malcontent.SessionLimits]
270 : : * @now_dt: current time in the user’s timezone, typically queried using
271 : : * `g_date_time_new_now_local()`
272 : : * @active_session_time_today_secs: total time the user has spent in an active
273 : : * session so far today, in seconds
274 : : * @time_remaining_secs_out: (out) (optional): return location for the number
275 : : * of seconds remaining before the user’s session has to end, if limits are
276 : : * in force
277 : : * @time_limit_enabled_out: (out) (optional): return location for whether time
278 : : * limits are enabled for this user
279 : : * @extension_active_out: (out) (optional): return location for whether @limits
280 : : * has a screen time limit extension active (see
281 : : * [method@Malcontent.SessionLimits.get_active_extension])
282 : : *
283 : : * Check whether the user has time remaining in which they are allowed to use
284 : : * the computer.
285 : : *
286 : : * This assumes that @now_dt is the current time and
287 : : * @active_session_time_today_secs is the total amount of time the user has
288 : : * spent in an active session up to that point today, and applies the
289 : : * session limit policy from @limits to them.
290 : : *
291 : : * This will return whether the user is allowed to use the computer now; further
292 : : * information about the policy and remaining time is provided in
293 : : * @time_remaining_secs_out and @time_limit_enabled_out.
294 : : *
295 : : * Returns: true if the user this @limits corresponds to is allowed to be in
296 : : * an active session at the given time; false otherwise
297 : : * Since: 0.14.0
298 : : */
299 : : gboolean
300 : 189 : mct_session_limits_check_time_remaining (MctSessionLimits *limits,
301 : : GDateTime *now_dt,
302 : : uint64_t active_session_time_today_secs,
303 : : guint64 *time_remaining_secs_out,
304 : : gboolean *time_limit_enabled_out,
305 : : gboolean *extension_active_out)
306 : : {
307 : : guint64 time_remaining_secs;
308 : 189 : gboolean time_limit_enabled = FALSE;
309 : 189 : gboolean user_allowed_now = TRUE;
310 : : uint64_t now_secs;
311 : : guint64 now_time_of_day_secs;
312 : : gboolean extension_active;
313 : : uint64_t active_extension_start_time_secs, active_extension_end_time_secs;
314 : : uint64_t active_extension_start_time_of_day_secs, active_extension_end_time_of_day_secs;
315 : : unsigned int active_extension_duration_secs;
316 : 189 : g_autoptr(GDateTime) start_of_today = NULL, start_of_tomorrow = NULL;
317 : : uint64_t start_of_today_secs, start_of_tomorrow_secs;
318 : :
319 : 189 : g_return_val_if_fail (limits != NULL, FALSE);
320 : 189 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
321 : 189 : g_return_val_if_fail (now_dt != NULL, FALSE);
322 : :
323 : : /* Helper calculations. If we end up with a @now_dt before the UNIX epoch,
324 : : * the caller has provided a date very far in the future which we don’t yet
325 : : * support.
326 : : *
327 : : * This needs to be in the user’s local timezone, because `DAILY_SCHEDULE`
328 : : * limits are essentially in the local timezone by virtue of being wall clock
329 : : * times. */
330 [ + + ]: 189 : if (g_date_time_to_unix (now_dt) < 0)
331 : : {
332 : 2 : time_remaining_secs = 0;
333 : 2 : time_limit_enabled = TRUE;
334 : 2 : user_allowed_now = FALSE;
335 : 2 : extension_active = FALSE;
336 : 2 : goto out;
337 : : }
338 : :
339 : 187 : extension_active = mct_session_limits_get_active_extension (limits,
340 : : now_dt,
341 : : &active_extension_start_time_secs,
342 : : &active_extension_duration_secs);
343 [ + + ]: 187 : if (extension_active)
344 : 16 : g_debug ("%s: Extension active from %" G_GUINT64_FORMAT " for %u seconds",
345 : : G_STRFUNC, active_extension_start_time_secs, active_extension_duration_secs);
346 : : else
347 : 171 : g_debug ("%s: No extension active", G_STRFUNC);
348 : :
349 : : /* Work out some times */
350 : 187 : now_time_of_day_secs = ((g_date_time_get_hour (now_dt) * 60 +
351 : 187 : g_date_time_get_minute (now_dt)) * 60 +
352 : 187 : g_date_time_get_second (now_dt));
353 : 187 : start_of_today = g_date_time_new_local (g_date_time_get_year (now_dt),
354 : : g_date_time_get_month (now_dt),
355 : : g_date_time_get_day_of_month (now_dt),
356 : : 0, 0, 0);
357 : 187 : start_of_tomorrow = g_date_time_add_days (start_of_today, 1);
358 : 187 : g_assert (start_of_today != NULL);
359 : 187 : g_assert (start_of_tomorrow != NULL);
360 : 187 : start_of_today_secs = g_date_time_to_unix (start_of_today);
361 : 187 : start_of_tomorrow_secs = g_date_time_to_unix (start_of_tomorrow);
362 : 187 : active_extension_end_time_secs = active_extension_start_time_secs + active_extension_duration_secs;
363 : 187 : active_extension_start_time_of_day_secs = active_extension_start_time_secs - start_of_today_secs;
364 : 187 : active_extension_end_time_of_day_secs = active_extension_end_time_secs - start_of_today_secs;
365 : 187 : now_secs = g_date_time_to_unix (now_dt);
366 : 187 : time_remaining_secs = start_of_tomorrow_secs - now_secs;
367 : :
368 : 187 : g_debug ("%s:\n"
369 : : "\tstart_of_today_secs %" G_GUINT64_FORMAT "\n"
370 : : "\tstart_of_tomorrow_secs %" G_GUINT64_FORMAT "\n"
371 : : "\tactive_extension_end_time_secs %" G_GUINT64_FORMAT "\n"
372 : : "\tactive_extension_start_time_of_day_secs %" G_GUINT64_FORMAT "\n"
373 : : "\tactive_extension_end_time_of_day_secs %" G_GUINT64_FORMAT "\n"
374 : : "\tnow_secs %" G_GUINT64_FORMAT "\n"
375 : : "\ttime_remaining_secs %" G_GUINT64_FORMAT,
376 : : G_STRFUNC,
377 : : start_of_today_secs, start_of_tomorrow_secs, active_extension_end_time_secs,
378 : : active_extension_start_time_of_day_secs, active_extension_end_time_of_day_secs,
379 : : now_secs, time_remaining_secs);
380 : :
381 : : /* This is guaranteed by mct_session_limits_get_active_extension() */
382 [ + + ]: 187 : if (extension_active)
383 : : {
384 : 16 : g_assert (active_extension_start_time_secs <= now_secs);
385 : 16 : g_assert (active_extension_end_time_secs > now_secs);
386 : : }
387 : :
388 : : /* Work out the limits. */
389 [ + + ]: 187 : if (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
390 : : {
391 : 135 : uint64_t effective_daily_start_time = limits->daily_start_time;
392 : 135 : uint64_t effective_daily_end_time = limits->daily_end_time;
393 : :
394 : : /* Apply the time limit extension, if applicable */
395 [ + + + + ]: 135 : if (extension_active &&
396 : : active_extension_start_time_of_day_secs < effective_daily_start_time)
397 : 2 : effective_daily_start_time = active_extension_start_time_of_day_secs;
398 [ + + + + ]: 135 : if (extension_active &&
399 : : active_extension_end_time_of_day_secs > effective_daily_end_time)
400 : 4 : effective_daily_end_time = active_extension_end_time_of_day_secs;
401 : :
402 [ + - + + ]: 234 : user_allowed_now = user_allowed_now &&
403 [ + + ]: 99 : (now_time_of_day_secs >= effective_daily_start_time &&
404 : : now_time_of_day_secs < effective_daily_end_time);
405 [ + + ]: 135 : time_remaining_secs = user_allowed_now ? MIN (time_remaining_secs, effective_daily_end_time - now_time_of_day_secs) : 0;
406 : 135 : time_limit_enabled = TRUE;
407 : :
408 : 135 : g_debug ("%s: Daily schedule limit allowed in %" G_GUINT64_FORMAT "–%"
409 : : G_GUINT64_FORMAT " (configured %u–%u; now is %"
410 : : G_GUINT64_FORMAT "); %" G_GUINT64_FORMAT " seconds remaining",
411 : : G_STRFUNC, effective_daily_start_time, effective_daily_end_time,
412 : : limits->daily_start_time, limits->daily_end_time,
413 : : now_time_of_day_secs, time_remaining_secs);
414 : : }
415 : :
416 [ + + ]: 187 : if (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
417 : : {
418 : 80 : unsigned int effective_daily_limit_secs = limits->daily_limit_secs;
419 : :
420 : : /* Apply the time limit extension, if applicable */
421 [ + + ]: 80 : if (extension_active &&
422 [ + + ]: 14 : (effective_daily_limit_secs < active_session_time_today_secs ||
423 [ + + ]: 12 : active_extension_end_time_secs - now_secs > effective_daily_limit_secs - active_session_time_today_secs))
424 : 8 : effective_daily_limit_secs = active_extension_end_time_secs - now_secs + active_session_time_today_secs;
425 : :
426 [ + + ]: 138 : user_allowed_now = (user_allowed_now &&
427 [ + + ]: 58 : active_session_time_today_secs < effective_daily_limit_secs);
428 [ + + ]: 80 : time_remaining_secs = user_allowed_now ? MIN (time_remaining_secs, effective_daily_limit_secs - active_session_time_today_secs) : 0;
429 : 80 : time_limit_enabled = TRUE;
430 : :
431 : 80 : g_debug ("%s: Daily limit allowed up to %u (configured %u; currently used %"
432 : : G_GUINT64_FORMAT "); %" G_GUINT64_FORMAT " seconds remaining",
433 : : G_STRFUNC, effective_daily_limit_secs, limits->daily_limit_secs,
434 : : active_session_time_today_secs, time_remaining_secs);
435 : : }
436 : :
437 [ + + ]: 187 : if (limits->limit_type == MCT_SESSION_LIMITS_TYPE_NONE)
438 : : {
439 : 40 : user_allowed_now = TRUE;
440 : 40 : time_remaining_secs = G_MAXUINT64;
441 : 40 : time_limit_enabled = FALSE;
442 : :
443 : 40 : g_debug ("%s: No limit enabled", G_STRFUNC);
444 : : }
445 : :
446 : 147 : out:
447 : : /* Postconditions. */
448 : 189 : g_assert (!user_allowed_now || time_remaining_secs > 0);
449 : 189 : g_assert (user_allowed_now || time_remaining_secs == 0);
450 : 189 : g_assert (time_limit_enabled || time_remaining_secs == G_MAXUINT64);
451 : 189 : g_assert (time_limit_enabled || !extension_active);
452 : :
453 : : /* Output. */
454 [ + + ]: 189 : if (time_remaining_secs_out != NULL)
455 : 75 : *time_remaining_secs_out = time_remaining_secs;
456 [ + + ]: 189 : if (time_limit_enabled_out != NULL)
457 : 75 : *time_limit_enabled_out = time_limit_enabled;
458 [ + + ]: 189 : if (extension_active_out != NULL)
459 : 75 : *extension_active_out = extension_active;
460 : :
461 : 189 : return user_allowed_now;
462 : : }
463 : :
464 : : /**
465 : : * mct_session_limits_serialize:
466 : : * @limits: a [struct@Malcontent.SessionLimits]
467 : : *
468 : : * Build a [struct@GLib.Variant] which contains the session limits from @limits,
469 : : * in an opaque variant format.
470 : : *
471 : : * This format may change in future, but
472 : : * [func@Malcontent.SessionLimits.deserialize] is guaranteed to always be able
473 : : * to load any variant produced by the current or any previous version of
474 : : * [method@Malcontent.SessionLimits.serialize].
475 : : *
476 : : * Returns: (transfer floating): a new, floating [struct@GLib.Variant]
477 : : * containing the session limits
478 : : * Since: 0.7.0
479 : : */
480 : : GVariant *
481 : 11 : mct_session_limits_serialize (MctSessionLimits *limits)
482 : : {
483 : 22 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{sv}"));
484 : :
485 : 11 : g_return_val_if_fail (limits != NULL, NULL);
486 : 11 : g_return_val_if_fail (limits->ref_count >= 1, NULL);
487 : :
488 : : /* The serialisation format is exactly the
489 : : * `com.endlessm.ParentalControls.SessionLimits` D-Bus interface. */
490 [ + + ]: 11 : if (limits->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
491 : 6 : g_variant_builder_add (&builder, "{sv}", "DailySchedule",
492 : : g_variant_new ("(uu)",
493 : : limits->daily_start_time,
494 : : limits->daily_end_time));
495 : :
496 [ + + ]: 11 : if (limits->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
497 : 2 : g_variant_builder_add (&builder, "{sv}", "DailyLimit",
498 : : g_variant_new ("u", limits->daily_limit_secs));
499 : :
500 [ + + ]: 11 : if (limits->set_limit_type != MCT_SESSION_LIMITS_TYPE_NONE)
501 : 6 : g_variant_builder_add (&builder, "{sv}", "ActiveExtension",
502 : : g_variant_new ("(tu)",
503 : : limits->active_extension_start_time_secs,
504 : : limits->active_extension_duration_secs));
505 : :
506 : 11 : g_variant_builder_add (&builder, "{sv}", "LimitType",
507 : 11 : g_variant_new_uint32 (limits->limit_type));
508 : :
509 : 11 : return g_variant_builder_end (&builder);
510 : : }
511 : :
512 : : /**
513 : : * mct_session_limits_deserialize:
514 : : * @variant: a serialized session limits variant
515 : : * @user_id: the ID of the user the session limits relate to
516 : : * @error: return location for a [type@GLib.Error], or `NULL`
517 : : *
518 : : * Deserialize a set of session limits previously serialized with
519 : : * [method@Malcontent.SessionLimits.serialize].
520 : : *
521 : : * This function guarantees to be able to deserialize any serialized form from
522 : : * this version or older versions of libmalcontent.
523 : : *
524 : : * If deserialization fails, [error@Malcontent.ManagerError.INVALID_DATA] will
525 : : * be returned.
526 : : *
527 : : * Returns: (transfer full): deserialized session limits
528 : : * Since: 0.7.0
529 : : */
530 : : MctSessionLimits *
531 : 77 : mct_session_limits_deserialize (GVariant *variant,
532 : : uid_t user_id,
533 : : GError **error)
534 : : {
535 : 77 : g_autoptr(MctSessionLimits) session_limits = NULL;
536 : : guint32 limit_type;
537 : 77 : MctSessionLimitsType set_limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
538 : : guint32 daily_start_time, daily_end_time;
539 : : uint32_t daily_limit_secs;
540 : : uint64_t active_extension_start_time_secs;
541 : : unsigned int active_extension_duration_secs;
542 : :
543 : 77 : g_return_val_if_fail (variant != NULL, NULL);
544 : 77 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
545 : :
546 : : /* Check the overall type. */
547 [ + + ]: 77 : if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}")))
548 : : {
549 : 4 : g_set_error (error, MCT_MANAGER_ERROR,
550 : : MCT_MANAGER_ERROR_INVALID_DATA,
551 : : _("Session limit for user %u was in an unrecognized format"),
552 : : (guint) user_id);
553 : 4 : return NULL;
554 : : }
555 : :
556 : : /* Extract the properties we care about. The default values here should be
557 : : * kept in sync with those in the `com.endlessm.ParentalControls.SessionLimits`
558 : : * D-Bus interface. */
559 [ + + ]: 73 : if (!g_variant_lookup (variant, "LimitType", "u",
560 : : &limit_type))
561 : : {
562 : : /* Default value. */
563 : 26 : limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
564 : : }
565 : :
566 : : /* Check that the limit type is something we support. */
567 : : G_STATIC_ASSERT (sizeof (limit_type) >= sizeof (MctSessionLimitsType));
568 : :
569 [ + + ]: 73 : if (((guint) limit_type & ~MCT_SESSION_LIMITS_TYPE_MASK) != 0)
570 : : {
571 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
572 : : MCT_MANAGER_ERROR_INVALID_DATA,
573 : : _("Session limit for user %u has an unrecognized type ‘%u’"),
574 : : (guint) user_id, limit_type);
575 : 2 : return NULL;
576 : : }
577 : :
578 : : /* Daily schedule */
579 [ + + ]: 71 : if (!g_variant_lookup (variant, "DailySchedule", "(uu)",
580 : : &daily_start_time, &daily_end_time))
581 : : {
582 : : /* Default value. */
583 : 26 : daily_start_time = 0;
584 : 26 : daily_end_time = 24 * 60 * 60;
585 : : }
586 : : else
587 : : {
588 : 45 : set_limit_type |= MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE;
589 : : }
590 : :
591 [ + + ]: 71 : if (daily_start_time >= daily_end_time ||
592 [ + + ]: 69 : daily_end_time > 24 * 60 * 60)
593 : : {
594 : 4 : g_set_error (error, MCT_MANAGER_ERROR,
595 : : MCT_MANAGER_ERROR_INVALID_DATA,
596 : : _("Session limit for user %u has invalid daily schedule %u–%u"),
597 : : (guint) user_id, daily_start_time, daily_end_time);
598 : 4 : return NULL;
599 : : }
600 : :
601 : : /* Daily limit */
602 [ + + ]: 67 : if (!g_variant_lookup (variant, "DailyLimit", "u", &daily_limit_secs))
603 : : {
604 : : /* Default value. */
605 : 43 : daily_limit_secs = 24 * 60 * 60;
606 : : }
607 : : else
608 : : {
609 : 24 : set_limit_type |= MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT;
610 : : }
611 : :
612 [ + + ]: 67 : if (daily_limit_secs > 24 * 60 * 60)
613 : : {
614 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
615 : : MCT_MANAGER_ERROR_INVALID_DATA,
616 : : _("Session limit for user %u has invalid daily limit %u"),
617 : : (guint) user_id, daily_limit_secs);
618 : 2 : return NULL;
619 : : }
620 : :
621 : : /* Active extension */
622 [ + + ]: 65 : if (!g_variant_lookup (variant, "ActiveExtension", "(tu)",
623 : : &active_extension_start_time_secs,
624 : : &active_extension_duration_secs))
625 : : {
626 : : /* Default value. */
627 : 41 : active_extension_start_time_secs = 0;
628 : 41 : active_extension_duration_secs = 0;
629 : : }
630 : :
631 [ + + ]: 65 : if (active_extension_duration_secs > UINT64_MAX - active_extension_start_time_secs)
632 : : {
633 : 2 : g_autofree char *start_time_str = g_strdup_printf ("%" G_GUINT64_FORMAT, active_extension_start_time_secs);
634 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
635 : : MCT_MANAGER_ERROR_INVALID_DATA,
636 : : _("Session limit for user %u has invalid active extension %s+%u"),
637 : : (guint) user_id, start_time_str,
638 : : active_extension_duration_secs);
639 : 2 : return NULL;
640 : : }
641 : :
642 : : /* Ensure @set_limit_type is equal to, or a superset of, @limit_type. */
643 : 63 : set_limit_type |= limit_type;
644 : :
645 : : /* Success. Create an `MctSessionLimits` object to contain the results. */
646 : 63 : session_limits = g_new0 (MctSessionLimits, 1);
647 : 63 : session_limits->ref_count = 1;
648 : 63 : session_limits->user_id = user_id;
649 : 63 : session_limits->limit_type = limit_type;
650 : 63 : session_limits->set_limit_type = set_limit_type;
651 : 63 : session_limits->daily_start_time = daily_start_time;
652 : 63 : session_limits->daily_end_time = daily_end_time;
653 : 63 : session_limits->daily_limit_secs = daily_limit_secs;
654 : 63 : session_limits->active_extension_start_time_secs = active_extension_start_time_secs;
655 : 63 : session_limits->active_extension_duration_secs = active_extension_duration_secs;
656 : :
657 : 63 : return g_steal_pointer (&session_limits);
658 : : }
659 : :
660 : : /**
661 : : * mct_session_limits_equal:
662 : : * @a: (not nullable): a session limits configuration
663 : : * @b: (not nullable): a session limits configuration
664 : : *
665 : : * Check whether session limits configurations @a and @b are equal.
666 : : *
667 : : * Returns: true if @a and @b are equal, false otherwise
668 : : * Since: 0.14.0
669 : : */
670 : : gboolean
671 : 298 : mct_session_limits_equal (MctSessionLimits *a,
672 : : MctSessionLimits *b)
673 : : {
674 : 298 : g_return_val_if_fail (a != NULL, FALSE);
675 : 298 : g_return_val_if_fail (a->ref_count >= 1, FALSE);
676 : 298 : g_return_val_if_fail (b != NULL, FALSE);
677 : 298 : g_return_val_if_fail (b->ref_count >= 1, FALSE);
678 : :
679 : 552 : return (a->user_id == b->user_id &&
680 [ + + ]: 254 : a->limit_type == b->limit_type &&
681 [ + + ]: 110 : a->set_limit_type == b->set_limit_type &&
682 [ + + ]: 66 : a->daily_start_time == b->daily_start_time &&
683 [ + + ]: 58 : a->daily_end_time == b->daily_end_time &&
684 [ + + ]: 50 : a->daily_limit_secs == b->daily_limit_secs &&
685 [ + + + + ]: 590 : a->active_extension_start_time_secs == b->active_extension_start_time_secs &&
686 [ + + ]: 38 : a->active_extension_duration_secs == b->active_extension_duration_secs);
687 : : }
688 : :
689 : : /*
690 : : * Actual implementation of `MctSessionLimitsBuilder`.
691 : : *
692 : : * All members are `NULL` if un-initialised, cleared, or ended.
693 : : */
694 : : typedef struct
695 : : {
696 : : MctSessionLimitsType limit_type;
697 : : MctSessionLimitsType set_limit_type;
698 : :
699 : : struct
700 : : {
701 : : guint start_time; /* seconds since midnight */
702 : : guint end_time; /* seconds since midnight */
703 : : } daily_schedule;
704 : :
705 : : unsigned int daily_limit_secs;
706 : :
707 : : uint64_t active_extension_start_time_secs;
708 : : unsigned int active_extension_duration_secs;
709 : :
710 : : /*< private >*/
711 : : gpointer padding[9];
712 : : } MctSessionLimitsBuilderReal;
713 : :
714 : : G_STATIC_ASSERT (sizeof (MctSessionLimitsBuilderReal) ==
715 : : sizeof (MctSessionLimitsBuilder));
716 : : G_STATIC_ASSERT (__alignof__ (MctSessionLimitsBuilderReal) ==
717 : : __alignof__ (MctSessionLimitsBuilder));
718 : :
719 [ + - + - : 6 : G_DEFINE_BOXED_TYPE (MctSessionLimitsBuilder, mct_session_limits_builder,
+ - ]
720 : : mct_session_limits_builder_copy, mct_session_limits_builder_free)
721 : :
722 : : /**
723 : : * mct_session_limits_builder_init:
724 : : * @builder: an uninitialised [struct@Malcontent.SessionLimitsBuilder]
725 : : *
726 : : * Initialise the given @builder so it can be used to construct a new
727 : : * [struct@Malcontent.SessionLimits].
728 : : *
729 : : * @builder must have been allocated on the stack, and must not already be
730 : : * initialised.
731 : : *
732 : : * Construct the [struct@Malcontent.SessionLimits] by calling methods on
733 : : * @builder, followed by [method@Malcontent.SessionLimitsBuilder.end]. To abort
734 : : * construction, use [method@Malcontent.SessionLimitsBuilder.clear].
735 : : *
736 : : * Since: 0.5.0
737 : : */
738 : : void
739 : 70 : mct_session_limits_builder_init (MctSessionLimitsBuilder *builder)
740 : : {
741 : 70 : MctSessionLimitsBuilder local_builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
742 : 70 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
743 : :
744 : 70 : g_return_if_fail (_builder != NULL);
745 : 70 : g_return_if_fail (_builder->limit_type == MCT_SESSION_LIMITS_TYPE_NONE);
746 : 70 : g_return_if_fail (_builder->set_limit_type == MCT_SESSION_LIMITS_TYPE_NONE);
747 : :
748 : 70 : memcpy (builder, &local_builder, sizeof (local_builder));
749 : : }
750 : :
751 : : /**
752 : : * mct_session_limits_builder_clear:
753 : : * @builder: a [struct@Malcontent.SessionLimitsBuilder]
754 : : *
755 : : * Clear @builder, freeing any internal state in it.
756 : : *
757 : : * This will not free the top-level storage for @builder itself, which is
758 : : * assumed to be allocated on the stack.
759 : : *
760 : : * If called on an already-cleared [struct@Malcontent.SessionLimitsBuilder],
761 : : * this function is idempotent.
762 : : *
763 : : * Since: 0.5.0
764 : : */
765 : : void
766 : 234 : mct_session_limits_builder_clear (MctSessionLimitsBuilder *builder)
767 : : {
768 : 234 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
769 : :
770 : 234 : g_return_if_fail (_builder != NULL);
771 : :
772 : : /* Nothing to free here for now. */
773 : 234 : _builder->limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
774 : 234 : _builder->set_limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
775 : : }
776 : :
777 : : /**
778 : : * mct_session_limits_builder_new:
779 : : *
780 : : * Construct a new [struct@Malcontent.SessionLimitsBuilder] on the heap.
781 : : *
782 : : * This is intended for language bindings. The returned builder must eventually
783 : : * be freed with [method@Malcontent.SessionLimitsBuilder.free], but can be
784 : : * cleared zero or more times with
785 : : * [method@Malcontent.SessionLimitsBuilder.clear] first.
786 : : *
787 : : * Returns: (transfer full): a new heap-allocated
788 : : * [struct@Malcontent.SessionLimitsBuilder]
789 : : * Since: 0.5.0
790 : : */
791 : : MctSessionLimitsBuilder *
792 : 62 : mct_session_limits_builder_new (void)
793 : : {
794 : 62 : g_autoptr(MctSessionLimitsBuilder) builder = NULL;
795 : :
796 : 62 : builder = g_new0 (MctSessionLimitsBuilder, 1);
797 : 62 : mct_session_limits_builder_init (builder);
798 : :
799 : 62 : return g_steal_pointer (&builder);
800 : : }
801 : :
802 : : /**
803 : : * mct_session_limits_builder_copy:
804 : : * @builder: a [struct@Malcontent.SessionLimitsBuilder]
805 : : *
806 : : * Copy the given @builder to a newly-allocated
807 : : * [struct@Malcontent.SessionLimitsBuilder] on the heap.
808 : : *
809 : : * This is safe to use with cleared, stack-allocated
810 : : * [struct@Malcontent.SessionLimitsBuilder]s.
811 : : *
812 : : * Returns: (transfer full): a copy of @builder
813 : : * Since: 0.5.0
814 : : */
815 : : MctSessionLimitsBuilder *
816 : 38 : mct_session_limits_builder_copy (MctSessionLimitsBuilder *builder)
817 : : {
818 : 38 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
819 : 38 : g_autoptr(MctSessionLimitsBuilder) copy = NULL;
820 : : MctSessionLimitsBuilderReal *_copy;
821 : :
822 : 38 : g_return_val_if_fail (builder != NULL, NULL);
823 : :
824 : 38 : copy = mct_session_limits_builder_new ();
825 : 38 : _copy = (MctSessionLimitsBuilderReal *) copy;
826 : :
827 : 38 : mct_session_limits_builder_clear (copy);
828 : 38 : _copy->limit_type = _builder->limit_type;
829 : 38 : _copy->set_limit_type = _builder->set_limit_type;
830 : :
831 [ + + ]: 38 : if (_builder->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
832 : : {
833 : 36 : _copy->daily_schedule.start_time = _builder->daily_schedule.start_time;
834 : 36 : _copy->daily_schedule.end_time = _builder->daily_schedule.end_time;
835 : : }
836 : :
837 [ + + ]: 38 : if (_builder->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
838 : 34 : _copy->daily_limit_secs = _builder->daily_limit_secs;
839 : :
840 : 38 : _copy->active_extension_start_time_secs = _builder->active_extension_start_time_secs;
841 : 38 : _copy->active_extension_duration_secs = _builder->active_extension_duration_secs;
842 : :
843 : 38 : return g_steal_pointer (©);
844 : : }
845 : :
846 : : /**
847 : : * mct_session_limits_builder_free:
848 : : * @builder: a heap-allocated [struct@Malcontent.SessionLimitsBuilder]
849 : : *
850 : : * Free an [struct@Malcontent.SessionLimitsBuilder] originally allocated using
851 : : * [ctor@Malcontent.SessionLimitsBuilder.new].
852 : : *
853 : : * This must not be called on stack-allocated builders initialised using
854 : : * [method@Malcontent.SessionLimitsBuilder.init].
855 : : *
856 : : * Since: 0.5.0
857 : : */
858 : : void
859 : 62 : mct_session_limits_builder_free (MctSessionLimitsBuilder *builder)
860 : : {
861 : 62 : g_return_if_fail (builder != NULL);
862 : :
863 : 62 : mct_session_limits_builder_clear (builder);
864 : 62 : g_free (builder);
865 : : }
866 : :
867 : : /**
868 : : * mct_session_limits_builder_end:
869 : : * @builder: an initialised [struct@Malcontent.SessionLimitsBuilder]
870 : : *
871 : : * Finish constructing an [struct@Malcontent.SessionLimits] with the given
872 : : * @builder, and return it.
873 : : *
874 : : * The [struct@Malcontent.SessionLimitsBuilder] will be cleared as if
875 : : * [method@Malcontent.SessionLimitsBuilder.clear] had been called.
876 : : *
877 : : * Returns: (transfer full): a newly constructed
878 : : * [struct@Malcontent.SessionLimits]
879 : : * Since: 0.5.0
880 : : */
881 : : MctSessionLimits *
882 : 92 : mct_session_limits_builder_end (MctSessionLimitsBuilder *builder)
883 : : {
884 : 92 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
885 : 92 : g_autoptr(MctSessionLimits) session_limits = NULL;
886 : :
887 : 92 : g_return_val_if_fail (_builder != NULL, NULL);
888 : :
889 : : /* Build the `MctSessionLimits`. */
890 : 92 : session_limits = g_new0 (MctSessionLimits, 1);
891 : 92 : session_limits->ref_count = 1;
892 : 92 : session_limits->user_id = -1;
893 : 92 : session_limits->limit_type = _builder->limit_type;
894 : 92 : session_limits->set_limit_type = _builder->limit_type;
895 : :
896 [ + + ]: 92 : if (_builder->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
897 : : {
898 : 67 : session_limits->daily_start_time = _builder->daily_schedule.start_time;
899 : 67 : session_limits->daily_end_time = _builder->daily_schedule.end_time;
900 : : }
901 : : else
902 : : {
903 : : /* Defaults: */
904 : 25 : session_limits->daily_start_time = 0;
905 : 25 : session_limits->daily_end_time = 24 * 60 * 60;
906 : : }
907 : :
908 [ + + ]: 92 : if (_builder->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
909 : 46 : session_limits->daily_limit_secs = _builder->daily_limit_secs;
910 : : else
911 : 46 : session_limits->daily_limit_secs = 24 * 60 * 60; /* default */
912 : :
913 : 92 : session_limits->active_extension_start_time_secs = _builder->active_extension_start_time_secs;
914 : 92 : session_limits->active_extension_duration_secs = _builder->active_extension_duration_secs;
915 : :
916 : 92 : mct_session_limits_builder_clear (builder);
917 : :
918 : 92 : return g_steal_pointer (&session_limits);
919 : : }
920 : :
921 : : /**
922 : : * mct_session_limits_builder_set_from_instance:
923 : : * @builder: an initialised [struct@Malcontent.SessionLimitsBuilder]
924 : : * @limits: a [struct@Malcontent.SessionLimits] to copy into the builder
925 : : *
926 : : * Set all the options in @builder to copies of the options in @limits.
927 : : *
928 : : * Use this to set up a @builder as a clone of @limits, ready to have its
929 : : * options modified further.
930 : : *
931 : : * Since: 0.14.0
932 : : */
933 : : void
934 : 4 : mct_session_limits_builder_set_from_instance (MctSessionLimitsBuilder *builder,
935 : : MctSessionLimits *limits)
936 : : {
937 : 4 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
938 : :
939 : 4 : g_return_if_fail (_builder != NULL);
940 : 4 : g_return_if_fail (limits != NULL);
941 : 4 : g_return_if_fail (limits->ref_count >= 1);
942 : :
943 : 4 : _builder->limit_type = limits->limit_type;
944 : 4 : _builder->set_limit_type = limits->set_limit_type;
945 : :
946 [ + + ]: 4 : if (limits->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
947 : : {
948 : 2 : _builder->daily_schedule.start_time = limits->daily_start_time;
949 : 2 : _builder->daily_schedule.end_time = limits->daily_end_time;
950 : : }
951 : :
952 [ + + ]: 4 : if (limits->set_limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
953 : 2 : _builder->daily_limit_secs = limits->daily_limit_secs;
954 : :
955 : 4 : _builder->active_extension_start_time_secs = limits->active_extension_start_time_secs;
956 : 4 : _builder->active_extension_duration_secs = limits->active_extension_duration_secs;
957 : : }
958 : :
959 : : /**
960 : : * mct_session_limits_builder_set_none:
961 : : * @builder: an initialised [struct@Malcontent.SessionLimitsBuilder]
962 : : *
963 : : * Unset any session limits currently set in the @builder.
964 : : *
965 : : * Since: 0.5.0
966 : : */
967 : : void
968 : 2 : mct_session_limits_builder_set_none (MctSessionLimitsBuilder *builder)
969 : : {
970 : 2 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
971 : :
972 : 2 : g_return_if_fail (_builder != NULL);
973 : :
974 : : /* This will need to free other limit types’ data first in future. */
975 : 2 : _builder->limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
976 : : }
977 : :
978 : : /**
979 : : * mct_session_limits_builder_set_daily_schedule:
980 : : * @builder: an initialised [struct@Malcontent.SessionLimitsBuilder]
981 : : * @enforced: true if the limit type is to be enforced, false if the value is
982 : : * just being stored for future use without being enforced
983 : : * @start_time_secs: number of seconds since midnight when the user’s session
984 : : * can first start
985 : : * @end_time_secs: number of seconds since midnight when the user’s session can
986 : : * last end
987 : : *
988 : : * Set the session limits in @builder to be a daily schedule, where sessions are
989 : : * allowed between @start_time_secs and @end_time_secs every day.
990 : : *
991 : : * @start_time_secs and @end_time_secs are given as offsets from the start of
992 : : * the day, in seconds. @end_time_secs must be greater than @start_time_secs.
993 : : * @end_time_secs must be at most `24 * 60 * 60`.
994 : : *
995 : : * This will act in addition to any other session limits.
996 : : *
997 : : * Since: 0.14.0
998 : : */
999 : : void
1000 : 35 : mct_session_limits_builder_set_daily_schedule (MctSessionLimitsBuilder *builder,
1001 : : gboolean enforced,
1002 : : guint start_time_secs,
1003 : : guint end_time_secs)
1004 : : {
1005 : 35 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
1006 : :
1007 : 35 : g_return_if_fail (_builder != NULL);
1008 : 35 : g_return_if_fail (start_time_secs < end_time_secs);
1009 : 35 : g_return_if_fail (end_time_secs <= 24 * 60 * 60);
1010 : :
1011 : 35 : _builder->set_limit_type |= MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE;
1012 [ + - ]: 35 : if (enforced)
1013 : 35 : _builder->limit_type |= MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE;
1014 : 35 : _builder->daily_schedule.start_time = start_time_secs;
1015 : 35 : _builder->daily_schedule.end_time = end_time_secs;
1016 : : }
1017 : :
1018 : : /**
1019 : : * mct_session_limits_builder_set_daily_limit:
1020 : : * @builder: an initialised [struct@Malcontent.SessionLimitsBuilder]
1021 : : * @enforced: true if the limit type is to be enforced, false if the value is
1022 : : * just being stored for future use without being enforced
1023 : : * @daily_limit_secs: maximum length for the user’s active session time each
1024 : : * day, in seconds
1025 : : *
1026 : : * Set the session limits in @builder to be a daily limit, where the total
1027 : : * active session time for the user has a given limit each day.
1028 : : *
1029 : : * @daily_limit_secs must be at most `24 * 60 * 60`.
1030 : : *
1031 : : * This will act in addition to any other session limits.
1032 : : *
1033 : : * Since: 0.14.0
1034 : : */
1035 : : void
1036 : 14 : mct_session_limits_builder_set_daily_limit (MctSessionLimitsBuilder *builder,
1037 : : gboolean enforced,
1038 : : unsigned int daily_limit_secs)
1039 : : {
1040 : 14 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
1041 : :
1042 : 14 : g_return_if_fail (_builder != NULL);
1043 : 14 : g_return_if_fail (daily_limit_secs <= 24 * 60 * 60);
1044 : :
1045 : 14 : _builder->set_limit_type |= MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT;
1046 [ + - ]: 14 : if (enforced)
1047 : 14 : _builder->limit_type |= MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT;
1048 : 14 : _builder->daily_limit_secs = daily_limit_secs;
1049 : : }
1050 : :
1051 : : /**
1052 : : * mct_session_limits_builder_set_active_extension:
1053 : : * @builder: an initialised [struct@Malcontent.SessionLimitsBuilder]
1054 : : * @start_time_secs: start time of the extension, in seconds since the Unix
1055 : : * epoch
1056 : : * @duration_secs: duration of the extension, in seconds; or zero if there is no
1057 : : * active extension
1058 : : *
1059 : : * Set the screen time extension in @builder to the given period.
1060 : : *
1061 : : * This is used for recording a screen time extension request which has been
1062 : : * granted.
1063 : : *
1064 : : * If both @start_time_secs and @duration_secs are zero, the screen time
1065 : : * extension will be cleared. @duration_secs must otherwise be non-zero.
1066 : : *
1067 : : * This will act on top of any session limits. If no session limits are set, any
1068 : : * extension will be ignored.
1069 : : *
1070 : : * Since: 0.14.0
1071 : : */
1072 : : void
1073 : 44 : mct_session_limits_builder_set_active_extension (MctSessionLimitsBuilder *builder,
1074 : : uint64_t start_time_secs,
1075 : : unsigned int duration_secs)
1076 : : {
1077 : 44 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
1078 : :
1079 : 44 : g_return_if_fail (_builder != NULL);
1080 : 44 : g_return_if_fail (duration_secs <= UINT64_MAX - start_time_secs);
1081 : 44 : g_return_if_fail (duration_secs != 0 || start_time_secs == 0);
1082 : :
1083 : 44 : _builder->active_extension_start_time_secs = start_time_secs;
1084 : 44 : _builder->active_extension_duration_secs = duration_secs;
1085 : : }
|