LCOV - code coverage report
Current view: top level - libmalcontent - session-limits.c (source / functions) Coverage Total Hit
Test: 2 coverage DB files Lines: 99.0 % 201 199
Test Date: 2025-09-15 13:55:46 Functions: 100.0 % 23 23
Branches: 84.8 % 92 78

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

Generated by: LCOV version 2.0-1