LCOV - code coverage report
Current view: top level - libmalcontent - session-limits.c (source / functions) Coverage Total Hit
Test: 2 coverage DB files Lines: 100.0 % 306 306
Test Date: 2026-01-05 10:41:23 Functions: 100.0 % 26 26
Branches: 90.3 % 144 130

             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 (&copy);
     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                 :             : }
        

Generated by: LCOV version 2.0-1