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): an #MctSessionLimits
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): an #MctSessionLimits
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 : 79 : mct_session_limits_unref (MctSessionLimits *limits)
74 : : {
75 : 79 : g_return_if_fail (limits != NULL);
76 : 79 : g_return_if_fail (limits->ref_count >= 1);
77 : :
78 : 79 : limits->ref_count--;
79 : :
80 [ + + ]: 79 : if (limits->ref_count <= 0)
81 : : {
82 : 75 : g_free (limits);
83 : : }
84 : : }
85 : :
86 : : /**
87 : : * mct_session_limits_get_user_id:
88 : : * @limits: an #MctSessionLimits
89 : : *
90 : : * Get the user ID of the user this #MctSessionLimits 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: an #MctSessionLimits
107 : : *
108 : : * Check whether any session limits are enabled and are going to impose at least
109 : : * one restriction on the user. This gives a high level view of whether session
110 : : * limit parental controls are ‘enabled’ for the given user.
111 : : *
112 : : * This function is equivalent to the value returned by the
113 : : * `time_limit_enabled_out` argument of
114 : : * mct_session_limits_check_time_remaining().
115 : : *
116 : : * Returns: %TRUE if the session limits object contains at least one restrictive
117 : : * session limit, %FALSE if there are no limits in place
118 : : * Since: 0.7.0
119 : : */
120 : : gboolean
121 : 3 : mct_session_limits_is_enabled (MctSessionLimits *limits)
122 : : {
123 : 3 : g_return_val_if_fail (limits != NULL, FALSE);
124 : 3 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
125 : :
126 : 3 : return (limits->limit_type != MCT_SESSION_LIMITS_TYPE_NONE);
127 : : }
128 : :
129 : : /**
130 : : * mct_session_limits_get_daily_schedule:
131 : : * @limits: an #MctSessionLimits
132 : : * @out_start_time_secs: (out) (optional): return location for the earliest
133 : : * allowable session start time for the user, in seconds since midnight
134 : : * @out_end_time_secs: (out) (optional): return location for the latest
135 : : * allowable session end time for the user, in seconds since midnight
136 : : *
137 : : * Get the daily schedule session limits, if set.
138 : : *
139 : : * If set, sessions are allowed between the given start and end time every day.
140 : : * The times are given as offsets from the start of the day, in seconds.
141 : : *
142 : : * @out_end_time_secs is guaranteed to be greater than @out_start_time_secs, if
143 : : * they are set. @out_end_time_secs is guaranteed to be at most `24 * 60 * 60`
144 : : * if set.
145 : : *
146 : : * Returns: true if a daily schedule is set, false otherwise
147 : : * Since: 0.14.0
148 : : */
149 : : gboolean
150 : 14 : mct_session_limits_get_daily_schedule (MctSessionLimits *limits,
151 : : unsigned int *out_start_time_secs,
152 : : unsigned int *out_end_time_secs)
153 : : {
154 : 14 : g_return_val_if_fail (limits != NULL, FALSE);
155 : 14 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
156 : :
157 [ + - ]: 14 : if (out_start_time_secs != NULL)
158 [ + + ]: 14 : *out_start_time_secs = (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE) ? limits->daily_start_time : 0;
159 [ + - ]: 14 : if (out_end_time_secs != NULL)
160 [ + + ]: 14 : *out_end_time_secs = (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE) ? limits->daily_end_time : 0;
161 : :
162 : 14 : return (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE);
163 : : }
164 : :
165 : : /**
166 : : * mct_session_limits_get_daily_limit:
167 : : * @limits: an #MctSessionLimits
168 : : * @out_daily_limit_secs: (out) (optional): return location for the maximum
169 : : * amount of active session time allowed per day for the user, in seconds
170 : : *
171 : : * Get the daily limit, if set.
172 : : *
173 : : * If set, sessions are allowed to be up to the given limit in length every day.
174 : : *
175 : : * @out_daily_limit_secs is guaranteed to be at most `24 * 60 * 60` if set.
176 : : *
177 : : * Returns: true if a daily limit is set, false otherwise
178 : : * Since: 0.14.0
179 : : */
180 : : gboolean
181 : 2 : mct_session_limits_get_daily_limit (MctSessionLimits *limits,
182 : : unsigned int *out_daily_limit_secs)
183 : : {
184 : 2 : g_return_val_if_fail (limits != NULL, FALSE);
185 : 2 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
186 : :
187 [ + - ]: 2 : if (out_daily_limit_secs != NULL)
188 [ + - ]: 2 : *out_daily_limit_secs = (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT) ? limits->daily_limit_secs : 0;
189 : :
190 : 2 : return (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT);
191 : : }
192 : :
193 : : /**
194 : : * mct_session_limits_check_time_remaining:
195 : : * @limits: an #MctSessionLimits
196 : : * @now_dt: current time in the user’s timezone, typically queried using
197 : : * `g_date_time_new_now_local()`
198 : : * @active_session_time_today_secs: total time the user has spent in an active
199 : : * session so far today, in seconds
200 : : * @time_remaining_secs_out: (out) (optional): return location for the number
201 : : * of seconds remaining before the user’s session has to end, if limits are
202 : : * in force
203 : : * @time_limit_enabled_out: (out) (optional): return location for whether time
204 : : * limits are enabled for this user
205 : : *
206 : : * Check whether the user has time remaining in which they are allowed to use
207 : : * the computer, assuming that @now_dt is the current time and
208 : : * @active_session_time_today_secs is the total amount of time the user has
209 : : * spent in an active session up to that point today, and applying the
210 : : * session limit policy from @limits to them.
211 : : *
212 : : * This will return whether the user is allowed to use the computer now; further
213 : : * information about the policy and remaining time is provided in
214 : : * @time_remaining_secs_out and @time_limit_enabled_out.
215 : : *
216 : : * Returns: %TRUE if the user this @limits corresponds to is allowed to be in
217 : : * an active session at the given time; %FALSE otherwise
218 : : * Since: 0.14.0
219 : : */
220 : : gboolean
221 : 143 : mct_session_limits_check_time_remaining (MctSessionLimits *limits,
222 : : GDateTime *now_dt,
223 : : uint64_t active_session_time_today_secs,
224 : : guint64 *time_remaining_secs_out,
225 : : gboolean *time_limit_enabled_out)
226 : : {
227 : : guint64 time_remaining_secs;
228 : 143 : gboolean time_limit_enabled = FALSE;
229 : 143 : gboolean user_allowed_now = TRUE;
230 : : guint64 now_time_of_day_secs;
231 : :
232 : 143 : g_return_val_if_fail (limits != NULL, FALSE);
233 : 143 : g_return_val_if_fail (limits->ref_count >= 1, FALSE);
234 : 143 : g_return_val_if_fail (now_dt != NULL, FALSE);
235 : :
236 : : /* Helper calculations. If we end up with a @now_dt before the UNIX epoch,
237 : : * the caller has provided a date very far in the future which we don’t yet
238 : : * support.
239 : : *
240 : : * This needs to be in the user’s local timezone, because `DAILY_SCHEDULE`
241 : : * limits are essentially in the local timezone by virtue of being wall clock
242 : : * times. */
243 [ + + ]: 143 : if (g_date_time_to_unix (now_dt) < 0)
244 : : {
245 : 2 : time_remaining_secs = 0;
246 : 2 : time_limit_enabled = TRUE;
247 : 2 : user_allowed_now = FALSE;
248 : 2 : goto out;
249 : : }
250 : :
251 : 141 : now_time_of_day_secs = ((g_date_time_get_hour (now_dt) * 60 +
252 : 141 : g_date_time_get_minute (now_dt)) * 60 +
253 : 141 : g_date_time_get_second (now_dt));
254 : 141 : time_remaining_secs = 24 * 60 * 60 - now_time_of_day_secs;
255 : :
256 : : /* Work out the limits. */
257 [ + + ]: 141 : if (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
258 : : {
259 [ + - ]: 178 : user_allowed_now = user_allowed_now &&
260 [ + + ]: 89 : (now_time_of_day_secs >= limits->daily_start_time &&
261 [ + + ]: 59 : now_time_of_day_secs < limits->daily_end_time);
262 [ + + ]: 89 : time_remaining_secs = user_allowed_now ? MIN (time_remaining_secs, limits->daily_end_time - now_time_of_day_secs) : 0;
263 : 89 : time_limit_enabled = TRUE;
264 : :
265 : 89 : g_debug ("%s: Daily schedule limit allowed in %u–%u (now is %"
266 : : G_GUINT64_FORMAT "); %" G_GUINT64_FORMAT " seconds remaining",
267 : : G_STRFUNC, limits->daily_start_time, limits->daily_end_time,
268 : : now_time_of_day_secs, time_remaining_secs);
269 : : }
270 : :
271 [ + + ]: 141 : if (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
272 : : {
273 [ + + ]: 52 : user_allowed_now = (user_allowed_now &&
274 [ + + ]: 24 : active_session_time_today_secs < limits->daily_limit_secs);
275 [ + + ]: 28 : time_remaining_secs = user_allowed_now ? MIN (time_remaining_secs, limits->daily_limit_secs - active_session_time_today_secs) : 0;
276 : 28 : time_limit_enabled = TRUE;
277 : :
278 : 28 : g_debug ("%s: Daily limit allowed up to %u (currently used %"
279 : : G_GUINT64_FORMAT "); %" G_GUINT64_FORMAT " seconds remaining",
280 : : G_STRFUNC, limits->daily_limit_secs,
281 : : active_session_time_today_secs, time_remaining_secs);
282 : : }
283 : :
284 [ + + ]: 141 : if (limits->limit_type == MCT_SESSION_LIMITS_TYPE_NONE)
285 : : {
286 : 40 : user_allowed_now = TRUE;
287 : 40 : time_remaining_secs = G_MAXUINT64;
288 : 40 : time_limit_enabled = FALSE;
289 : :
290 : 40 : g_debug ("%s: No limit enabled", G_STRFUNC);
291 : : }
292 : :
293 : 101 : out:
294 : : /* Postconditions. */
295 : 143 : g_assert (!user_allowed_now || time_remaining_secs > 0);
296 : 143 : g_assert (user_allowed_now || time_remaining_secs == 0);
297 : 143 : g_assert (time_limit_enabled || time_remaining_secs == G_MAXUINT64);
298 : :
299 : : /* Output. */
300 [ + + ]: 143 : if (time_remaining_secs_out != NULL)
301 : 43 : *time_remaining_secs_out = time_remaining_secs;
302 [ + + ]: 143 : if (time_limit_enabled_out != NULL)
303 : 43 : *time_limit_enabled_out = time_limit_enabled;
304 : :
305 : 143 : return user_allowed_now;
306 : : }
307 : :
308 : : /**
309 : : * mct_session_limits_serialize:
310 : : * @limits: an #MctSessionLimits
311 : : *
312 : : * Build a #GVariant which contains the session limits from @limits, in an
313 : : * opaque variant format. This format may change in future, but
314 : : * mct_session_limits_deserialize() is guaranteed to always be able to load any
315 : : * variant produced by the current or any previous version of
316 : : * mct_session_limits_serialize().
317 : : *
318 : : * Returns: (transfer floating): a new, floating #GVariant containing the
319 : : * session limits
320 : : * Since: 0.7.0
321 : : */
322 : : GVariant *
323 : 9 : mct_session_limits_serialize (MctSessionLimits *limits)
324 : : {
325 : 18 : g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{sv}"));
326 : :
327 : 9 : g_return_val_if_fail (limits != NULL, NULL);
328 : 9 : g_return_val_if_fail (limits->ref_count >= 1, NULL);
329 : :
330 : : /* The serialisation format is exactly the
331 : : * `com.endlessm.ParentalControls.SessionLimits` D-Bus interface. */
332 [ + + ]: 9 : if (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
333 : 4 : g_variant_builder_add (&builder, "{sv}", "DailySchedule",
334 : : g_variant_new ("(uu)",
335 : : limits->daily_start_time,
336 : : limits->daily_end_time));
337 : :
338 [ - + ]: 9 : if (limits->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
339 : 0 : g_variant_builder_add (&builder, "{sv}", "DailyLimit",
340 : : g_variant_new ("u", limits->daily_limit_secs));
341 : :
342 : 9 : g_variant_builder_add (&builder, "{sv}", "LimitType",
343 : 9 : g_variant_new_uint32 (limits->limit_type));
344 : :
345 : 9 : return g_variant_builder_end (&builder);
346 : : }
347 : :
348 : : /**
349 : : * mct_session_limits_deserialize:
350 : : * @variant: a serialized session limits variant
351 : : * @user_id: the ID of the user the session limits relate to
352 : : * @error: return location for a #GError, or %NULL
353 : : *
354 : : * Deserialize a set of session limits previously serialized with
355 : : * mct_session_limits_serialize(). This function guarantees to be able to
356 : : * deserialize any serialized form from this version or older versions of
357 : : * libmalcontent.
358 : : *
359 : : * If deserialization fails, %MCT_MANAGER_ERROR_INVALID_DATA will be returned.
360 : : *
361 : : * Returns: (transfer full): deserialized session limits
362 : : * Since: 0.7.0
363 : : */
364 : : MctSessionLimits *
365 : 41 : mct_session_limits_deserialize (GVariant *variant,
366 : : uid_t user_id,
367 : : GError **error)
368 : : {
369 : 41 : g_autoptr(MctSessionLimits) session_limits = NULL;
370 : : guint32 limit_type;
371 : : guint32 daily_start_time, daily_end_time;
372 : : uint32_t daily_limit_secs;
373 : :
374 : 41 : g_return_val_if_fail (variant != NULL, NULL);
375 : 41 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
376 : :
377 : : /* Check the overall type. */
378 [ + + ]: 41 : if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}")))
379 : : {
380 : 4 : g_set_error (error, MCT_MANAGER_ERROR,
381 : : MCT_MANAGER_ERROR_INVALID_DATA,
382 : : _("Session limit for user %u was in an unrecognized format"),
383 : : (guint) user_id);
384 : 4 : return NULL;
385 : : }
386 : :
387 : : /* Extract the properties we care about. The default values here should be
388 : : * kept in sync with those in the `com.endlessm.ParentalControls.SessionLimits`
389 : : * D-Bus interface. */
390 [ + + ]: 37 : if (!g_variant_lookup (variant, "LimitType", "u",
391 : : &limit_type))
392 : : {
393 : : /* Default value. */
394 : 20 : limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
395 : : }
396 : :
397 : : /* Check that the limit type is something we support. */
398 : : G_STATIC_ASSERT (sizeof (limit_type) >= sizeof (MctSessionLimitsType));
399 : :
400 [ + + ]: 37 : if (((guint) limit_type & ~MCT_SESSION_LIMITS_TYPE_MASK) != 0)
401 : : {
402 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
403 : : MCT_MANAGER_ERROR_INVALID_DATA,
404 : : _("Session limit for user %u has an unrecognized type ‘%u’"),
405 : : (guint) user_id, limit_type);
406 : 2 : return NULL;
407 : : }
408 : :
409 : : /* Daily schedule */
410 [ + + ]: 35 : if (!g_variant_lookup (variant, "DailySchedule", "(uu)",
411 : : &daily_start_time, &daily_end_time))
412 : : {
413 : : /* Default value. */
414 : 16 : daily_start_time = 0;
415 : 16 : daily_end_time = 24 * 60 * 60;
416 : : }
417 : :
418 [ + + ]: 35 : if (daily_start_time >= daily_end_time ||
419 [ + + ]: 33 : daily_end_time > 24 * 60 * 60)
420 : : {
421 : 4 : g_set_error (error, MCT_MANAGER_ERROR,
422 : : MCT_MANAGER_ERROR_INVALID_DATA,
423 : : _("Session limit for user %u has invalid daily schedule %u–%u"),
424 : : (guint) user_id, daily_start_time, daily_end_time);
425 : 4 : return NULL;
426 : : }
427 : :
428 : : /* Daily limit */
429 [ + + ]: 31 : if (!g_variant_lookup (variant, "DailyLimit", "u", &daily_limit_secs))
430 : : {
431 : : /* Default value. */
432 : 21 : daily_limit_secs = 24 * 60 * 60;
433 : : }
434 : :
435 [ + + ]: 31 : if (daily_limit_secs > 24 * 60 * 60)
436 : : {
437 : 2 : g_set_error (error, MCT_MANAGER_ERROR,
438 : : MCT_MANAGER_ERROR_INVALID_DATA,
439 : : _("Session limit for user %u has invalid daily limit %u"),
440 : : (guint) user_id, daily_limit_secs);
441 : 2 : return NULL;
442 : : }
443 : :
444 : : /* Success. Create an #MctSessionLimits object to contain the results. */
445 : 29 : session_limits = g_new0 (MctSessionLimits, 1);
446 : 29 : session_limits->ref_count = 1;
447 : 29 : session_limits->user_id = user_id;
448 : 29 : session_limits->limit_type = limit_type;
449 : 29 : session_limits->daily_start_time = daily_start_time;
450 : 29 : session_limits->daily_end_time = daily_end_time;
451 : 29 : session_limits->daily_limit_secs = daily_limit_secs;
452 : :
453 : 29 : return g_steal_pointer (&session_limits);
454 : : }
455 : :
456 : : /**
457 : : * mct_session_limits_equal:
458 : : * @a: (not nullable): a session limits configuration
459 : : * @b: (not nullable): a session limits configuration
460 : : *
461 : : * Check whether session limits configurations @a and @b are equal.
462 : : *
463 : : * Returns: true if @a and @b are equal, false otherwise
464 : : * Since: 0.14.0
465 : : */
466 : : gboolean
467 : 134 : mct_session_limits_equal (MctSessionLimits *a,
468 : : MctSessionLimits *b)
469 : : {
470 : 134 : g_return_val_if_fail (a != NULL, FALSE);
471 : 134 : g_return_val_if_fail (a->ref_count >= 1, FALSE);
472 : 134 : g_return_val_if_fail (b != NULL, FALSE);
473 : 134 : g_return_val_if_fail (b->ref_count >= 1, FALSE);
474 : :
475 : 240 : return (a->user_id == b->user_id &&
476 [ + + ]: 106 : a->limit_type == b->limit_type &&
477 [ + - ]: 50 : a->daily_start_time == b->daily_start_time &&
478 [ + + + + ]: 266 : a->daily_end_time == b->daily_end_time &&
479 [ + + ]: 26 : a->daily_limit_secs == b->daily_limit_secs);
480 : : }
481 : :
482 : : /*
483 : : * Actual implementation of #MctSessionLimitsBuilder.
484 : : *
485 : : * All members are %NULL if un-initialised, cleared, or ended.
486 : : */
487 : : typedef struct
488 : : {
489 : : MctSessionLimitsType limit_type;
490 : :
491 : : struct
492 : : {
493 : : guint start_time; /* seconds since midnight */
494 : : guint end_time; /* seconds since midnight */
495 : : } daily_schedule;
496 : :
497 : : unsigned int daily_limit_secs;
498 : :
499 : : /*< private >*/
500 : : gpointer padding[9];
501 : : } MctSessionLimitsBuilderReal;
502 : :
503 : : G_STATIC_ASSERT (sizeof (MctSessionLimitsBuilderReal) ==
504 : : sizeof (MctSessionLimitsBuilder));
505 : : G_STATIC_ASSERT (__alignof__ (MctSessionLimitsBuilderReal) ==
506 : : __alignof__ (MctSessionLimitsBuilder));
507 : :
508 [ + - + - : 6 : G_DEFINE_BOXED_TYPE (MctSessionLimitsBuilder, mct_session_limits_builder,
+ - ]
509 : : mct_session_limits_builder_copy, mct_session_limits_builder_free)
510 : :
511 : : /**
512 : : * mct_session_limits_builder_init:
513 : : * @builder: an uninitialised #MctSessionLimitsBuilder
514 : : *
515 : : * Initialise the given @builder so it can be used to construct a new
516 : : * #MctSessionLimits. @builder must have been allocated on the stack, and must
517 : : * not already be initialised.
518 : : *
519 : : * Construct the #MctSessionLimits by calling methods on @builder, followed by
520 : : * mct_session_limits_builder_end(). To abort construction, use
521 : : * mct_session_limits_builder_clear().
522 : : *
523 : : * Since: 0.5.0
524 : : */
525 : : void
526 : 26 : mct_session_limits_builder_init (MctSessionLimitsBuilder *builder)
527 : : {
528 : 26 : MctSessionLimitsBuilder local_builder = MCT_SESSION_LIMITS_BUILDER_INIT ();
529 : 26 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
530 : :
531 : 26 : g_return_if_fail (_builder != NULL);
532 : 26 : g_return_if_fail (_builder->limit_type == MCT_SESSION_LIMITS_TYPE_NONE);
533 : :
534 : 26 : memcpy (builder, &local_builder, sizeof (local_builder));
535 : : }
536 : :
537 : : /**
538 : : * mct_session_limits_builder_clear:
539 : : * @builder: an #MctSessionLimitsBuilder
540 : : *
541 : : * Clear @builder, freeing any internal state in it. This will not free the
542 : : * top-level storage for @builder itself, which is assumed to be allocated on
543 : : * the stack.
544 : : *
545 : : * If called on an already-cleared #MctSessionLimitsBuilder, this function is
546 : : * idempotent.
547 : : *
548 : : * Since: 0.5.0
549 : : */
550 : : void
551 : 100 : mct_session_limits_builder_clear (MctSessionLimitsBuilder *builder)
552 : : {
553 : 100 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
554 : :
555 : 100 : g_return_if_fail (_builder != NULL);
556 : :
557 : : /* Nothing to free here for now. */
558 : 100 : _builder->limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
559 : : }
560 : :
561 : : /**
562 : : * mct_session_limits_builder_new:
563 : : *
564 : : * Construct a new #MctSessionLimitsBuilder on the heap. This is intended for
565 : : * language bindings. The returned builder must eventually be freed with
566 : : * mct_session_limits_builder_free(), but can be cleared zero or more times with
567 : : * mct_session_limits_builder_clear() first.
568 : : *
569 : : * Returns: (transfer full): a new heap-allocated #MctSessionLimitsBuilder
570 : : * Since: 0.5.0
571 : : */
572 : : MctSessionLimitsBuilder *
573 : 18 : mct_session_limits_builder_new (void)
574 : : {
575 : 18 : g_autoptr(MctSessionLimitsBuilder) builder = NULL;
576 : :
577 : 18 : builder = g_new0 (MctSessionLimitsBuilder, 1);
578 : 18 : mct_session_limits_builder_init (builder);
579 : :
580 : 18 : return g_steal_pointer (&builder);
581 : : }
582 : :
583 : : /**
584 : : * mct_session_limits_builder_copy:
585 : : * @builder: an #MctSessionLimitsBuilder
586 : : *
587 : : * Copy the given @builder to a newly-allocated #MctSessionLimitsBuilder on the
588 : : * heap. This is safe to use with cleared, stack-allocated
589 : : * #MctSessionLimitsBuilders.
590 : : *
591 : : * Returns: (transfer full): a copy of @builder
592 : : * Since: 0.5.0
593 : : */
594 : : MctSessionLimitsBuilder *
595 : 4 : mct_session_limits_builder_copy (MctSessionLimitsBuilder *builder)
596 : : {
597 : 4 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
598 : 4 : g_autoptr(MctSessionLimitsBuilder) copy = NULL;
599 : : MctSessionLimitsBuilderReal *_copy;
600 : :
601 : 4 : g_return_val_if_fail (builder != NULL, NULL);
602 : :
603 : 4 : copy = mct_session_limits_builder_new ();
604 : 4 : _copy = (MctSessionLimitsBuilderReal *) copy;
605 : :
606 : 4 : mct_session_limits_builder_clear (copy);
607 : 4 : _copy->limit_type = _builder->limit_type;
608 : :
609 [ + + ]: 4 : if (_builder->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
610 : : {
611 : 2 : _copy->daily_schedule.start_time = _builder->daily_schedule.start_time;
612 : 2 : _copy->daily_schedule.end_time = _builder->daily_schedule.end_time;
613 : : }
614 : :
615 [ - + ]: 4 : if (_builder->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
616 : 0 : _copy->daily_limit_secs = _builder->daily_limit_secs;
617 : :
618 : 4 : return g_steal_pointer (©);
619 : : }
620 : :
621 : : /**
622 : : * mct_session_limits_builder_free:
623 : : * @builder: a heap-allocated #MctSessionLimitsBuilder
624 : : *
625 : : * Free an #MctSessionLimitsBuilder originally allocated using
626 : : * mct_session_limits_builder_new(). This must not be called on stack-allocated
627 : : * builders initialised using mct_session_limits_builder_init().
628 : : *
629 : : * Since: 0.5.0
630 : : */
631 : : void
632 : 18 : mct_session_limits_builder_free (MctSessionLimitsBuilder *builder)
633 : : {
634 : 18 : g_return_if_fail (builder != NULL);
635 : :
636 : 18 : mct_session_limits_builder_clear (builder);
637 : 18 : g_free (builder);
638 : : }
639 : :
640 : : /**
641 : : * mct_session_limits_builder_end:
642 : : * @builder: an initialised #MctSessionLimitsBuilder
643 : : *
644 : : * Finish constructing an #MctSessionLimits with the given @builder, and return
645 : : * it. The #MctSessionLimitsBuilder will be cleared as if
646 : : * mct_session_limits_builder_clear() had been called.
647 : : *
648 : : * Returns: (transfer full): a newly constructed #MctSessionLimits
649 : : * Since: 0.5.0
650 : : */
651 : : MctSessionLimits *
652 : 46 : mct_session_limits_builder_end (MctSessionLimitsBuilder *builder)
653 : : {
654 : 46 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
655 : 46 : g_autoptr(MctSessionLimits) session_limits = NULL;
656 : :
657 : 46 : g_return_val_if_fail (_builder != NULL, NULL);
658 : :
659 : : /* Build the #MctSessionLimits. */
660 : 46 : session_limits = g_new0 (MctSessionLimits, 1);
661 : 46 : session_limits->ref_count = 1;
662 : 46 : session_limits->user_id = -1;
663 : 46 : session_limits->limit_type = _builder->limit_type;
664 : :
665 [ + + ]: 46 : if (_builder->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
666 : : {
667 : 23 : session_limits->daily_start_time = _builder->daily_schedule.start_time;
668 : 23 : session_limits->daily_end_time = _builder->daily_schedule.end_time;
669 : : }
670 : :
671 [ + + ]: 46 : if (_builder->limit_type & MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT)
672 : 6 : session_limits->daily_limit_secs = _builder->daily_limit_secs;
673 : :
674 [ + + ]: 46 : if (_builder->limit_type == MCT_SESSION_LIMITS_TYPE_NONE)
675 : : {
676 : : /* Defaults: */
677 : 21 : session_limits->daily_start_time = 0;
678 : 21 : session_limits->daily_end_time = 24 * 60 * 60;
679 : 21 : session_limits->daily_limit_secs = 24 * 60 * 60;
680 : : }
681 : :
682 : 46 : mct_session_limits_builder_clear (builder);
683 : :
684 : 46 : return g_steal_pointer (&session_limits);
685 : : }
686 : :
687 : : /**
688 : : * mct_session_limits_builder_set_none:
689 : : * @builder: an initialised #MctSessionLimitsBuilder
690 : : *
691 : : * Unset any session limits currently set in the @builder.
692 : : *
693 : : * Since: 0.5.0
694 : : */
695 : : void
696 : 2 : mct_session_limits_builder_set_none (MctSessionLimitsBuilder *builder)
697 : : {
698 : 2 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
699 : :
700 : 2 : g_return_if_fail (_builder != NULL);
701 : :
702 : : /* This will need to free other limit types’ data first in future. */
703 : 2 : _builder->limit_type = MCT_SESSION_LIMITS_TYPE_NONE;
704 : : }
705 : :
706 : : /**
707 : : * mct_session_limits_builder_set_daily_schedule:
708 : : * @builder: an initialised #MctSessionLimitsBuilder
709 : : * @start_time_secs: number of seconds since midnight when the user’s session
710 : : * can first start
711 : : * @end_time_secs: number of seconds since midnight when the user’s session can
712 : : * last end
713 : : *
714 : : * Set the session limits in @builder to be a daily schedule, where sessions are
715 : : * allowed between @start_time_secs and @end_time_secs every day.
716 : : * @start_time_secs and @end_time_secs are given as offsets from the start of
717 : : * the day, in seconds. @end_time_secs must be greater than @start_time_secs.
718 : : * @end_time_secs must be at most `24 * 60 * 60`.
719 : : *
720 : : * This will act in addition to any other session limits.
721 : : *
722 : : * Since: 0.5.0
723 : : */
724 : : void
725 : 27 : mct_session_limits_builder_set_daily_schedule (MctSessionLimitsBuilder *builder,
726 : : guint start_time_secs,
727 : : guint end_time_secs)
728 : : {
729 : 27 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
730 : :
731 : 27 : g_return_if_fail (_builder != NULL);
732 : 27 : g_return_if_fail (start_time_secs < end_time_secs);
733 : 27 : g_return_if_fail (end_time_secs <= 24 * 60 * 60);
734 : :
735 : 27 : _builder->limit_type |= MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE;
736 : 27 : _builder->daily_schedule.start_time = start_time_secs;
737 : 27 : _builder->daily_schedule.end_time = end_time_secs;
738 : : }
739 : :
740 : : /**
741 : : * mct_session_limits_builder_set_daily_limit:
742 : : * @builder: an initialised #MctSessionLimitsBuilder
743 : : * @daily_limit_secs: maximum length for the user’s active session time each
744 : : * day, in seconds
745 : : *
746 : : * Set the session limits in @builder to be a daily limit, where the total
747 : : * active session time for the user has a given limit each day.
748 : : *
749 : : * @daily_limit_secs must be at most `24 * 60 * 60`.
750 : : *
751 : : * This will act in addition to any other session limits.
752 : : *
753 : : * Since: 0.14.0
754 : : */
755 : : void
756 : 6 : mct_session_limits_builder_set_daily_limit (MctSessionLimitsBuilder *builder,
757 : : unsigned int daily_limit_secs)
758 : : {
759 : 6 : MctSessionLimitsBuilderReal *_builder = (MctSessionLimitsBuilderReal *) builder;
760 : :
761 : 6 : g_return_if_fail (_builder != NULL);
762 : 6 : g_return_if_fail (daily_limit_secs <= 24 * 60 * 60);
763 : :
764 : 6 : _builder->limit_type |= MCT_SESSION_LIMITS_TYPE_DAILY_LIMIT;
765 : 6 : _builder->daily_limit_secs = daily_limit_secs;
766 : : }
|