LCOV - code coverage report
Current view: top level - libmalcontent-ui - user-controls.c (source / functions) Coverage Total Hit
Test: 2 coverage DB files Lines: 7.6 % 511 39
Test Date: 2025-09-15 13:55:46 Functions: 8.7 % 46 4
Branches: 1.9 % 212 4

             Branch data     Line data    Source code
       1                 :             : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
       2                 :             :  *
       3                 :             :  * Copyright © 2018, 2019, 2020 Endless Mobile, Inc.
       4                 :             :  *
       5                 :             :  * SPDX-License-Identifier: GPL-2.0-or-later
       6                 :             :  *
       7                 :             :  * This program is free software; you can redistribute it and/or modify
       8                 :             :  * it under the terms of the GNU General Public License as published by
       9                 :             :  * the Free Software Foundation; either version 2 of the License, or
      10                 :             :  * (at your option) any later version.
      11                 :             :  *
      12                 :             :  * This program is distributed in the hope that it will be useful,
      13                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15                 :             :  * GNU General Public License for more details.
      16                 :             :  *
      17                 :             :  * You should have received a copy of the GNU General Public License
      18                 :             :  * along with this program; if not, see <http://www.gnu.org/licenses/>.
      19                 :             :  *
      20                 :             :  * Authors:
      21                 :             :  *  - Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
      22                 :             :  *  - Philip Withnall <withnall@endlessm.com>
      23                 :             :  */
      24                 :             : 
      25                 :             : #include "config.h"
      26                 :             : 
      27                 :             : #include <appstream.h>
      28                 :             : #include <libmalcontent/malcontent.h>
      29                 :             : #include <locale.h>
      30                 :             : #include <gio/gio.h>
      31                 :             : #include <gio/gdesktopappinfo.h>
      32                 :             : #include <glib/gi18n-lib.h>
      33                 :             : #include <strings.h>
      34                 :             : 
      35                 :             : #include "restrict-applications-dialog.h"
      36                 :             : #include "user-controls.h"
      37                 :             : 
      38                 :             : 
      39                 :             : #define WEB_BROWSERS_CONTENT_TYPE "x-scheme-handler/http"
      40                 :             : 
      41                 :             : /* The value which we store as an age to indicate that OARS filtering is disabled. */
      42                 :             : static const guint32 oars_disabled_age = (guint32) -1;
      43                 :             : 
      44                 :             : /**
      45                 :             :  * MctUserControls:
      46                 :             :  *
      47                 :             :  * A group of widgets which allow setting the parental controls for a given
      48                 :             :  * user.
      49                 :             :  *
      50                 :             :  * If [property@MalcontentUi.UserControls:user] is set, the current parental
      51                 :             :  * controls settings for that user will be loaded and displayed, and any changes
      52                 :             :  * made via the controls will be automatically saved for that user (potentially
      53                 :             :  * after a short timeout).
      54                 :             :  *
      55                 :             :  * If [property@MalcontentUi.UserControls:user] is unset (for example, if
      56                 :             :  * setting the parental controls for a user account which hasn’t yet been
      57                 :             :  * created), the controls can be initialised by setting:
      58                 :             :  *
      59                 :             :  *  - [property@MalcontentUi.UserControls:app-filter]
      60                 :             :  *  - [property@MalcontentUi.UserControls:user-account-type]
      61                 :             :  *  - [property@MalcontentUi.UserControls:user-locale]
      62                 :             :  *  - [property@MalcontentUi.UserControls:user-display-name]
      63                 :             :  *
      64                 :             :  * When [property@MalcontentUi.UserControls:user] is unset, changes made to the
      65                 :             :  * parental controls cannot be saved automatically, and must be queried using
      66                 :             :  * [method@MalcontentUi.UserControls.build_app_filter], then saved by the
      67                 :             :  * calling code.
      68                 :             :  *
      69                 :             :  * As parental controls are system settings, privileges are needed to view and
      70                 :             :  * edit them (for the current user or for other users). These can be acquired
      71                 :             :  * using polkit. [property@MalcontentUi.UserControls:permission] is used to
      72                 :             :  * query the current permissions for getting/setting parental controls. If it’s
      73                 :             :  * `NULL`, or if permissions are not currently granted, the user controls will
      74                 :             :  * be insensitive.
      75                 :             :  *
      76                 :             :  * Since: 0.5.0
      77                 :             :  */
      78                 :             : struct _MctUserControls
      79                 :             : {
      80                 :             :   AdwBin     parent_instance;
      81                 :             : 
      82                 :             :   GtkLabel *description_label;
      83                 :             :   GMenu      *age_menu;
      84                 :             :   GtkSwitch  *restrict_software_installation_switch;
      85                 :             :   AdwActionRow *restrict_software_installation_row;
      86                 :             :   GtkSwitch  *restrict_web_browsers_switch;
      87                 :             :   AdwActionRow *restrict_web_browsers_row;
      88                 :             :   GtkMenuButton *oars_button;
      89                 :             :   GtkPopoverMenu *oars_popover;
      90                 :             :   MctRestrictApplicationsDialog *restrict_applications_dialog;
      91                 :             :   GtkLabel   *restrict_applications_description;
      92                 :             :   AdwActionRow *restrict_applications_row;
      93                 :             : 
      94                 :             :   GSimpleActionGroup *action_group; /* (owned) */
      95                 :             : 
      96                 :             :   MctUser *user; /* (owned) (nullable) */
      97                 :             :   unsigned long user_notify_id;
      98                 :             : 
      99                 :             :   GPermission *permission;  /* (owned) (nullable) */
     100                 :             :   gulong permission_allowed_id;
     101                 :             : 
     102                 :             :   GDBusConnection *dbus_connection;  /* (owned) */
     103                 :             :   GCancellable *cancellable; /* (owned) */
     104                 :             :   MctManager   *manager; /* (owned) */
     105                 :             :   MctAppFilter *filter; /* (owned) (nullable); updated by the user of #MctUserControls */
     106                 :             :   MctAppFilter *last_saved_filter; /* (owned) (nullable); updated each time we internally time out and save the app filter */
     107                 :             :   guint         selected_age; /* @oars_disabled_age to disable OARS */
     108                 :             : 
     109                 :             :   guint         blocklist_apps_source_id;
     110                 :             :   gboolean      flushed_on_dispose;
     111                 :             :   unsigned long app_filter_changed_id;
     112                 :             : 
     113                 :             :   MctUserType         user_account_type;
     114                 :             :   gchar              *user_locale;  /* (nullable) (owned) */
     115                 :             :   gchar              *user_display_name;  /* (nullable) (owned) */
     116                 :             :   gchar              *description;  /* (nullable) (owned) */
     117                 :             : };
     118                 :             : 
     119                 :             : static gboolean blocklist_apps_cb (gpointer data);
     120                 :             : 
     121                 :             : static void on_restrict_installation_switch_active_changed_cb (GtkSwitch        *s,
     122                 :             :                                                                GParamSpec       *pspec,
     123                 :             :                                                                MctUserControls *self);
     124                 :             : 
     125                 :             : static void on_restrict_web_browsers_switch_active_changed_cb (GtkSwitch        *s,
     126                 :             :                                                                GParamSpec       *pspec,
     127                 :             :                                                                MctUserControls *self);
     128                 :             : 
     129                 :             : static void on_restrict_applications_action_activated (GSimpleAction *action,
     130                 :             :                                                        GVariant      *param,
     131                 :             :                                                        gpointer       user_data);
     132                 :             : 
     133                 :             : static void on_restrict_applications_dialog_closed_cb (AdwDialog *dialog,
     134                 :             :                                                        gpointer   user_data);
     135                 :             : 
     136                 :             : static void on_set_age_action_activated (GSimpleAction *action,
     137                 :             :                                          GVariant      *param,
     138                 :             :                                          gpointer       user_data);
     139                 :             : 
     140                 :             : static void on_permission_allowed_cb (GObject    *obj,
     141                 :             :                                       GParamSpec *pspec,
     142                 :             :                                       gpointer    user_data);
     143                 :             : 
     144   [ +  +  +  -  :           3 : G_DEFINE_TYPE (MctUserControls, mct_user_controls, ADW_TYPE_BIN)
                   +  - ]
     145                 :             : 
     146                 :             : typedef enum
     147                 :             : {
     148                 :             :   PROP_USER = 1,
     149                 :             :   PROP_PERMISSION,
     150                 :             :   PROP_APP_FILTER,
     151                 :             :   PROP_USER_ACCOUNT_TYPE,
     152                 :             :   PROP_USER_LOCALE,
     153                 :             :   PROP_USER_DISPLAY_NAME,
     154                 :             :   PROP_DBUS_CONNECTION,
     155                 :             :   PROP_DESCRIPTION,
     156                 :             : } MctUserControlsProperty;
     157                 :             : 
     158                 :             : static GParamSpec *properties[PROP_DESCRIPTION + 1];
     159                 :             : 
     160                 :             : static const GActionEntry actions[] = {
     161                 :             :   { "set-age", on_set_age_action_activated, "u", NULL, NULL, { 0, }},
     162                 :             :   { "restrict-applications", on_restrict_applications_action_activated, NULL, NULL, NULL, { 0, }}
     163                 :             : };
     164                 :             : 
     165                 :             : /* Auxiliary methods */
     166                 :             : 
     167                 :             : static AsContentRatingSystem
     168                 :           0 : get_content_rating_system (MctUserControls *self)
     169                 :             : {
     170         [ #  # ]:           0 :   if (self->user_locale == NULL)
     171                 :           0 :     return AS_CONTENT_RATING_SYSTEM_UNKNOWN;
     172                 :             : 
     173                 :           0 :   return as_content_rating_system_from_locale (self->user_locale);
     174                 :             : }
     175                 :             : 
     176                 :             : static const gchar *
     177                 :           0 : get_user_locale (MctUser *user)
     178                 :             : {
     179                 :             :   const gchar *locale;
     180                 :             : 
     181                 :           0 :   g_return_val_if_fail (MCT_IS_USER (user), "C");
     182                 :             : 
     183                 :             :   /* This can return %NULL if loading over D-Bus failed. */
     184                 :           0 :   locale = mct_user_get_locale (user);
     185         [ #  # ]:           0 :   if (locale == NULL)
     186                 :           0 :     return NULL;
     187                 :             : 
     188                 :             :   /* It can return the empty string if the user uses the system default locale. */
     189         [ #  # ]:           0 :   if (*locale == '\0')
     190                 :           0 :     locale = setlocale (LC_MESSAGES, NULL);
     191                 :             : 
     192   [ #  #  #  # ]:           0 :   if (locale == NULL || *locale == '\0')
     193                 :           0 :     locale = "C";
     194                 :             : 
     195                 :           0 :   return locale;
     196                 :             : }
     197                 :             : 
     198                 :             : static void
     199                 :           0 : schedule_update_blocklisted_apps (MctUserControls *self)
     200                 :             : {
     201         [ #  # ]:           0 :   if (self->blocklist_apps_source_id > 0)
     202                 :           0 :     return;
     203                 :             : 
     204                 :             :   /* Use a timeout to batch multiple quick changes into a single
     205                 :             :    * update. 1 second is an arbitrary sufficiently small number */
     206                 :           0 :   self->blocklist_apps_source_id = g_timeout_add_seconds (1, blocklist_apps_cb, self);
     207                 :             : }
     208                 :             : 
     209                 :             : static void
     210                 :           0 : flush_update_blocklisted_apps (MctUserControls *self)
     211                 :             : {
     212         [ #  # ]:           0 :   if (self->blocklist_apps_source_id > 0)
     213                 :             :     {
     214                 :             :       /* Remove the timer and forcefully call the timer callback. */
     215                 :           0 :       g_source_remove (self->blocklist_apps_source_id);
     216                 :           0 :       self->blocklist_apps_source_id = 0;
     217                 :             : 
     218                 :           0 :       blocklist_apps_cb (self);
     219                 :             :     }
     220                 :           0 : }
     221                 :             : 
     222                 :             : static void
     223                 :           0 : update_app_filter_from_user (MctUserControls *self)
     224                 :             : {
     225         [ #  # ]:           0 :   g_autoptr(GError) error = NULL;
     226                 :             : 
     227         [ #  # ]:           0 :   if (self->user == NULL)
     228                 :           0 :     return;
     229                 :             : 
     230                 :             :   /* FIXME: It’s expected that, unless authorised already, a user cannot read
     231                 :             :    * another user’s app filter. accounts-service currently (incorrectly) ignores
     232                 :             :    * the missing ‘interactive’ flag and prompts the user for permission if so,
     233                 :             :    * so don’t query at all in that case. */
     234         [ #  # ]:           0 :   if (mct_user_get_uid (self->user) != getuid () &&
     235   [ #  #  #  # ]:           0 :       (self->permission == NULL ||
     236                 :           0 :        !g_permission_get_allowed (self->permission)))
     237                 :           0 :     return;
     238                 :             : 
     239                 :             :   /* FIXME: make it asynchronous */
     240         [ #  # ]:           0 :   g_clear_pointer (&self->filter, mct_app_filter_unref);
     241         [ #  # ]:           0 :   g_clear_pointer (&self->last_saved_filter, mct_app_filter_unref);
     242                 :           0 :   self->filter = mct_manager_get_app_filter (self->manager,
     243                 :             :                                              mct_user_get_uid (self->user),
     244                 :             :                                              MCT_MANAGER_GET_VALUE_FLAGS_NONE,
     245                 :             :                                              self->cancellable,
     246                 :             :                                              &error);
     247                 :             : 
     248         [ #  # ]:           0 :   if (error)
     249                 :             :     {
     250                 :           0 :       g_warning ("Error retrieving app filter for user '%s': %s",
     251                 :             :                  mct_user_get_username (self->user),
     252                 :             :                  error->message);
     253                 :           0 :       return;
     254                 :             :     }
     255                 :             : 
     256                 :           0 :   self->last_saved_filter = mct_app_filter_ref (self->filter);
     257                 :             : 
     258                 :           0 :   g_debug ("Retrieved new app filter for user '%s'", mct_user_get_username (self->user));
     259                 :             : }
     260                 :             : 
     261                 :             : static void
     262                 :           0 : update_restricted_apps (MctUserControls *self)
     263                 :             : {
     264                 :           0 :   mct_restrict_applications_dialog_set_app_filter (self->restrict_applications_dialog, self->filter);
     265                 :           0 : }
     266                 :             : 
     267                 :             : static void
     268                 :           0 : update_categories_from_language (MctUserControls *self)
     269                 :             : {
     270                 :             :   AsContentRatingSystem rating_system;
     271                 :           0 :   g_auto(GStrv) entries = NULL;
     272                 :             :   const gchar *rating_system_str;
     273                 :             :   const guint *ages;
     274                 :             :   gsize i, n_ages;
     275                 :           0 :   g_autofree gchar *disabled_action = NULL;
     276                 :           0 :   g_autoptr(GMenu) top_subsection = NULL, bottom_subsection = NULL;
     277                 :             : 
     278                 :           0 :   rating_system = get_content_rating_system (self);
     279                 :           0 :   rating_system_str = as_content_rating_system_to_string (rating_system);
     280                 :             : 
     281                 :           0 :   g_debug ("Using rating system %s", rating_system_str);
     282                 :             : 
     283                 :           0 :   entries = as_content_rating_system_get_formatted_ages (rating_system);
     284                 :           0 :   ages = as_content_rating_system_get_csm_ages (rating_system, &n_ages);
     285                 :             : 
     286                 :             :   /* Fill in the age menu */
     287                 :           0 :   g_menu_remove_all (self->age_menu);
     288                 :             : 
     289                 :           0 :   disabled_action = g_strdup_printf ("permissions.set-age(uint32 %u)", oars_disabled_age);
     290                 :             : 
     291                 :           0 :   top_subsection = g_menu_new ();
     292                 :           0 :   g_menu_append (top_subsection, _("Unrestricted"), disabled_action);
     293                 :           0 :   g_menu_append_section (self->age_menu, NULL, G_MENU_MODEL (top_subsection));
     294                 :             : 
     295                 :           0 :   bottom_subsection = g_menu_new ();
     296         [ #  # ]:           0 :   for (i = 0; entries[i] != NULL; i++)
     297                 :             :     {
     298                 :           0 :       g_autofree gchar *action = g_strdup_printf ("permissions.set-age(uint32 %u)", ages[i]);
     299                 :             : 
     300                 :             :       /* Prevent the unlikely case that one of the real ages is the same as our
     301                 :             :        * special ‘disabled’ value. */
     302                 :           0 :       g_assert (ages[i] != oars_disabled_age);
     303                 :             : 
     304                 :           0 :       g_menu_append (bottom_subsection, entries[i], action);
     305                 :             :     }
     306                 :             : 
     307                 :           0 :   g_assert (i == n_ages);
     308                 :           0 :   g_menu_append_section (self->age_menu, NULL, G_MENU_MODEL (bottom_subsection));
     309                 :           0 : }
     310                 :             : 
     311                 :             : /* Returns a human-readable but untranslated string, not suitable
     312                 :             :  * to be shown in any UI */
     313                 :             : static const gchar *
     314                 :           0 : oars_value_to_string (MctAppFilterOarsValue oars_value)
     315                 :             : {
     316   [ #  #  #  #  :           0 :   switch (oars_value)
                   #  # ]
     317                 :             :     {
     318                 :           0 :     case MCT_APP_FILTER_OARS_VALUE_UNKNOWN:
     319                 :           0 :       return "unknown";
     320                 :           0 :     case MCT_APP_FILTER_OARS_VALUE_NONE:
     321                 :           0 :       return "none";
     322                 :           0 :     case MCT_APP_FILTER_OARS_VALUE_MILD:
     323                 :           0 :       return "mild";
     324                 :           0 :     case MCT_APP_FILTER_OARS_VALUE_MODERATE:
     325                 :           0 :       return "moderate";
     326                 :           0 :     case MCT_APP_FILTER_OARS_VALUE_INTENSE:
     327                 :           0 :       return "intense";
     328                 :           0 :     default:
     329                 :           0 :       return "";
     330                 :             :     }
     331                 :             : }
     332                 :             : 
     333                 :             : /* Ensure the enum casts below are safe. */
     334                 :             : G_STATIC_ASSERT ((int) MCT_APP_FILTER_OARS_VALUE_UNKNOWN == (int) AS_CONTENT_RATING_VALUE_UNKNOWN);
     335                 :             : G_STATIC_ASSERT ((int) MCT_APP_FILTER_OARS_VALUE_NONE == (int) AS_CONTENT_RATING_VALUE_NONE);
     336                 :             : G_STATIC_ASSERT ((int) MCT_APP_FILTER_OARS_VALUE_MILD == (int) AS_CONTENT_RATING_VALUE_MILD);
     337                 :             : G_STATIC_ASSERT ((int) MCT_APP_FILTER_OARS_VALUE_MODERATE == (int) AS_CONTENT_RATING_VALUE_MODERATE);
     338                 :             : G_STATIC_ASSERT ((int) MCT_APP_FILTER_OARS_VALUE_INTENSE == (int) AS_CONTENT_RATING_VALUE_INTENSE);
     339                 :             : 
     340                 :             : static void
     341                 :           0 : update_oars_level (MctUserControls *self)
     342                 :             : {
     343                 :             :   AsContentRatingSystem rating_system;
     344                 :           0 :   g_autofree gchar *rating_age_category = NULL;
     345                 :             :   guint maximum_age, selected_age;
     346                 :             :   gsize i;
     347                 :             :   gboolean all_categories_unset;
     348                 :           0 :   g_autofree const gchar **oars_categories = as_content_rating_get_all_rating_ids ();
     349                 :             : 
     350                 :           0 :   g_assert (self->filter != NULL);
     351                 :             : 
     352                 :           0 :   maximum_age = 0;
     353                 :           0 :   all_categories_unset = TRUE;
     354                 :             : 
     355         [ #  # ]:           0 :   for (i = 0; oars_categories[i] != NULL; i++)
     356                 :             :     {
     357                 :             :       MctAppFilterOarsValue oars_value;
     358                 :             :       guint age;
     359                 :             : 
     360                 :           0 :       oars_value = mct_app_filter_get_oars_value (self->filter, oars_categories[i]);
     361                 :           0 :       all_categories_unset &= (oars_value == MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
     362                 :           0 :       age = as_content_rating_attribute_to_csm_age (oars_categories[i], (AsContentRatingValue) oars_value);
     363                 :             : 
     364                 :           0 :       g_debug ("OARS value for '%s': %s", oars_categories[i], oars_value_to_string (oars_value));
     365                 :             : 
     366         [ #  # ]:           0 :       if (age > maximum_age)
     367                 :           0 :         maximum_age = age;
     368                 :             :     }
     369                 :             : 
     370         [ #  # ]:           0 :   g_debug ("Effective age for this user: %u; %s", maximum_age,
     371                 :             :            all_categories_unset ? "all categories unset" : "some categories set");
     372                 :             : 
     373                 :           0 :   rating_system = get_content_rating_system (self);
     374                 :           0 :   rating_age_category = as_content_rating_system_format_age (rating_system, maximum_age);
     375                 :             : 
     376                 :             :   /* Unrestricted? */
     377   [ #  #  #  # ]:           0 :   if (rating_age_category == NULL || all_categories_unset)
     378                 :             :     {
     379         [ #  # ]:           0 :       g_clear_pointer (&rating_age_category, g_free);
     380                 :           0 :       rating_age_category = g_strdup (_("Unrestricted"));
     381                 :           0 :       selected_age = oars_disabled_age;
     382                 :             :     }
     383                 :             :   else
     384                 :             :     {
     385                 :           0 :       selected_age = maximum_age;
     386                 :             :     }
     387                 :             : 
     388                 :           0 :   gtk_menu_button_set_label (self->oars_button, rating_age_category);
     389                 :           0 :   self->selected_age = selected_age;
     390                 :           0 : }
     391                 :             : 
     392                 :             : static void
     393                 :           0 : update_allow_app_installation (MctUserControls *self)
     394                 :             : {
     395                 :             :   gboolean restrict_software_installation;
     396                 :           0 :   gboolean non_admin_user = TRUE;
     397                 :             : 
     398         [ #  # ]:           0 :   if (self->user_account_type == MCT_USER_TYPE_PARENT)
     399                 :           0 :     non_admin_user = FALSE;
     400                 :             : 
     401                 :             :   /* Admins are always allowed to install apps for all users. This behaviour is governed
     402                 :             :    * by flatpak polkit rules. Hence, these hide these defunct switches for admins. */
     403                 :           0 :   gtk_widget_set_visible (GTK_WIDGET (self->restrict_software_installation_switch), non_admin_user);
     404                 :             : 
     405                 :             :   /* If user is admin, we are done here, bail out. */
     406         [ #  # ]:           0 :   if (!non_admin_user)
     407                 :             :     {
     408                 :           0 :       g_debug ("User ‘%s’ is an administrator, hiding app installation controls",
     409                 :             :                self->user_display_name);
     410                 :           0 :       return;
     411                 :             :     }
     412                 :             : 
     413                 :             :   /* While the underlying permissions storage allows the system and user settings
     414                 :             :    * to be stored completely independently, force the system setting to OFF if
     415                 :             :    * the user setting is OFF in the UI. This keeps the policy in use for most
     416                 :             :    * people simpler. */
     417                 :           0 :   restrict_software_installation = !mct_app_filter_is_user_installation_allowed (self->filter);
     418                 :             : 
     419                 :           0 :   g_signal_handlers_block_by_func (self->restrict_software_installation_switch,
     420                 :             :                                    on_restrict_installation_switch_active_changed_cb,
     421                 :             :                                    self);
     422                 :             : 
     423                 :           0 :   gtk_switch_set_active (self->restrict_software_installation_switch, restrict_software_installation);
     424                 :             : 
     425         [ #  # ]:           0 :   g_debug ("Restrict system installation: %s", restrict_software_installation ? "yes" : "no");
     426         [ #  # ]:           0 :   g_debug ("Restrict user installation: %s", restrict_software_installation ? "yes" : "no");
     427                 :             : 
     428                 :           0 :   g_signal_handlers_unblock_by_func (self->restrict_software_installation_switch,
     429                 :             :                                      on_restrict_installation_switch_active_changed_cb,
     430                 :             :                                      self);
     431                 :             : }
     432                 :             : 
     433                 :             : static void
     434                 :           0 : update_restrict_web_browsers (MctUserControls *self)
     435                 :             : {
     436                 :             :   gboolean restrict_web_browsers;
     437                 :             : 
     438                 :           0 :   restrict_web_browsers = !mct_app_filter_is_content_type_allowed (self->filter,
     439                 :             :                                                                    WEB_BROWSERS_CONTENT_TYPE);
     440                 :             : 
     441                 :           0 :   g_signal_handlers_block_by_func (self->restrict_web_browsers_switch,
     442                 :             :                                    on_restrict_web_browsers_switch_active_changed_cb,
     443                 :             :                                    self);
     444                 :             : 
     445                 :           0 :   gtk_switch_set_active (self->restrict_web_browsers_switch, restrict_web_browsers);
     446                 :             : 
     447         [ #  # ]:           0 :   g_debug ("Restrict web browsers: %s", restrict_web_browsers ? "yes" : "no");
     448                 :             : 
     449                 :           0 :   g_signal_handlers_unblock_by_func (self->restrict_web_browsers_switch,
     450                 :             :                                      on_restrict_web_browsers_switch_active_changed_cb,
     451                 :             :                                      self);
     452                 :           0 : }
     453                 :             : 
     454                 :             : static void
     455                 :           0 : update_labels_from_name (MctUserControls *self)
     456                 :             : {
     457                 :           0 :   g_autofree gchar *l = NULL;
     458                 :             : 
     459                 :           0 :   gtk_label_set_markup (self->description_label, self->description);
     460                 :             : 
     461                 :             :   /* Translators: The placeholder is a user’s display name. */
     462                 :           0 :   l = g_strdup_printf (_("Prevents %s from running web browsers. Limited web content may still be available in other applications."), self->user_display_name);
     463                 :           0 :   adw_action_row_set_subtitle (self->restrict_web_browsers_row, l);
     464         [ #  # ]:           0 :   g_clear_pointer (&l, g_free);
     465                 :             : 
     466                 :             :   /* Translators: The placeholder is a user’s display name. */
     467                 :           0 :   l = g_strdup_printf (_("Prevents specified applications from being used by %s."), self->user_display_name);
     468                 :           0 :   adw_action_row_set_subtitle (self->restrict_applications_row, l);
     469         [ #  # ]:           0 :   g_clear_pointer (&l, g_free);
     470                 :             : 
     471                 :             :   /* Translators: The placeholder is a user’s display name. */
     472                 :           0 :   l = g_strdup_printf (_("Prevents %s from installing applications."), self->user_display_name);
     473                 :           0 :   adw_action_row_set_subtitle (self->restrict_software_installation_row, l);
     474         [ #  # ]:           0 :   g_clear_pointer (&l, g_free);
     475                 :           0 : }
     476                 :             : 
     477                 :             : static void
     478                 :           0 : setup_parental_control_settings (MctUserControls *self)
     479                 :             : {
     480                 :             :   gboolean is_authorized;
     481                 :             : 
     482                 :           0 :   gtk_widget_set_visible (GTK_WIDGET (self), self->filter != NULL);
     483                 :             : 
     484         [ #  # ]:           0 :   if (!self->filter)
     485                 :           0 :     return;
     486                 :             : 
     487                 :             :   /* We only want to make the controls sensitive if we have permission to save
     488                 :             :    * changes (@is_authorized). */
     489         [ #  # ]:           0 :   if (self->permission != NULL)
     490                 :           0 :     is_authorized = g_permission_get_allowed (G_PERMISSION (self->permission));
     491                 :             :   else
     492                 :           0 :     is_authorized = FALSE;
     493                 :             : 
     494                 :           0 :   gtk_widget_set_sensitive (GTK_WIDGET (self), is_authorized);
     495                 :             : 
     496                 :           0 :   update_restricted_apps (self);
     497                 :           0 :   update_categories_from_language (self);
     498                 :           0 :   update_oars_level (self);
     499                 :           0 :   update_allow_app_installation (self);
     500                 :           0 :   update_restrict_web_browsers (self);
     501                 :           0 :   update_labels_from_name (self);
     502                 :             : }
     503                 :             : 
     504                 :             : /* Callbacks */
     505                 :             : 
     506                 :             : static gboolean
     507                 :           0 : blocklist_apps_cb (gpointer data)
     508                 :             : {
     509                 :           0 :   g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
     510                 :           0 :   g_autoptr(MctAppFilter) new_filter = NULL;
     511                 :           0 :   g_autoptr(GError) error = NULL;
     512                 :           0 :   MctUserControls *self = data;
     513                 :             : 
     514                 :           0 :   self->blocklist_apps_source_id = 0;
     515                 :             : 
     516         [ #  # ]:           0 :   if (self->user == NULL)
     517                 :             :     {
     518                 :           0 :       g_debug ("Not saving app filter as user is unset");
     519                 :           0 :       return G_SOURCE_REMOVE;
     520                 :             :     }
     521                 :             : 
     522                 :           0 :   mct_user_controls_build_app_filter (self, &builder);
     523                 :           0 :   new_filter = mct_app_filter_builder_end (&builder);
     524                 :             : 
     525                 :             :   /* Don’t bother saving the app filter (which could result in asking the user
     526                 :             :    * for admin permission) if it hasn’t changed. */
     527   [ #  #  #  # ]:           0 :   if (self->last_saved_filter != NULL &&
     528                 :           0 :       mct_app_filter_equal (new_filter, self->last_saved_filter))
     529                 :             :     {
     530                 :           0 :       g_debug ("Not saving app filter as it hasn’t changed");
     531                 :           0 :       return G_SOURCE_REMOVE;
     532                 :             :     }
     533                 :             : 
     534                 :             :   /* FIXME: should become asynchronous */
     535                 :           0 :   mct_manager_set_app_filter (self->manager,
     536                 :             :                               mct_user_get_uid (self->user),
     537                 :             :                               new_filter,
     538                 :             :                               MCT_MANAGER_SET_VALUE_FLAGS_INTERACTIVE,
     539                 :             :                               self->cancellable,
     540                 :             :                               &error);
     541                 :             : 
     542         [ #  # ]:           0 :   if (error)
     543                 :             :     {
     544                 :           0 :       g_warning ("Error updating app filter: %s", error->message);
     545                 :           0 :       setup_parental_control_settings (self);
     546                 :             :     }
     547                 :             : 
     548                 :             :   /* Update the cached copy */
     549                 :           0 :   mct_app_filter_unref (self->last_saved_filter);
     550                 :           0 :   self->last_saved_filter = g_steal_pointer (&new_filter);
     551                 :             : 
     552                 :           0 :   return G_SOURCE_REMOVE;
     553                 :             : }
     554                 :             : 
     555                 :             : static void
     556                 :           0 : on_restrict_installation_switch_active_changed_cb (GtkSwitch        *s,
     557                 :             :                                                    GParamSpec       *pspec,
     558                 :             :                                                    MctUserControls *self)
     559                 :             : {
     560                 :             :   /* Save the changes. */
     561                 :           0 :   schedule_update_blocklisted_apps (self);
     562                 :           0 : }
     563                 :             : 
     564                 :             : static void
     565                 :           0 : on_restrict_web_browsers_switch_active_changed_cb (GtkSwitch        *s,
     566                 :             :                                                    GParamSpec       *pspec,
     567                 :             :                                                    MctUserControls *self)
     568                 :             : {
     569                 :             :   /* Save the changes. */
     570                 :           0 :   schedule_update_blocklisted_apps (self);
     571                 :           0 : }
     572                 :             : 
     573                 :             : static void
     574                 :           0 : on_restrict_applications_action_activated (GSimpleAction *action,
     575                 :             :                                            GVariant      *param,
     576                 :             :                                            gpointer       user_data)
     577                 :             : {
     578                 :           0 :   MctUserControls *self = MCT_USER_CONTROLS (user_data);
     579                 :             : 
     580                 :             :   /* Show the restrict applications dialogue modally, making sure to update its
     581                 :             :    * state first. */
     582                 :           0 :   mct_restrict_applications_dialog_set_user_display_name (self->restrict_applications_dialog, self->user_display_name);
     583                 :           0 :   mct_restrict_applications_dialog_set_app_filter (self->restrict_applications_dialog, self->filter);
     584                 :             : 
     585                 :           0 :   adw_dialog_present (ADW_DIALOG (self->restrict_applications_dialog), GTK_WIDGET (self));
     586                 :           0 : }
     587                 :             : 
     588                 :             : static void
     589                 :           0 : on_restrict_applications_dialog_closed_cb (AdwDialog *dialog,
     590                 :             :                                            gpointer   user_data)
     591                 :             : {
     592                 :           0 :   MctUserControls *self = MCT_USER_CONTROLS (user_data);
     593                 :             : 
     594                 :             :   /* Schedule an update to the saved state. */
     595                 :           0 :   schedule_update_blocklisted_apps (self);
     596                 :           0 : }
     597                 :             : 
     598                 :             : static void
     599                 :           0 : on_set_age_action_activated (GSimpleAction *action,
     600                 :             :                              GVariant      *param,
     601                 :             :                              gpointer       user_data)
     602                 :             : {
     603                 :             :   AsContentRatingSystem rating_system;
     604                 :             :   MctUserControls *self;
     605                 :           0 :   g_auto(GStrv) entries = NULL;
     606                 :             :   const guint *ages;
     607                 :             :   guint age;
     608                 :             :   guint i;
     609                 :             :   gsize n_ages;
     610                 :             : 
     611                 :           0 :   self = MCT_USER_CONTROLS (user_data);
     612                 :           0 :   age = g_variant_get_uint32 (param);
     613                 :             : 
     614                 :           0 :   rating_system = get_content_rating_system (self);
     615                 :           0 :   entries = as_content_rating_system_get_formatted_ages (rating_system);
     616                 :           0 :   ages = as_content_rating_system_get_csm_ages (rating_system, &n_ages);
     617                 :             : 
     618                 :             :   /* Update the button */
     619         [ #  # ]:           0 :   if (age == oars_disabled_age)
     620                 :           0 :     gtk_menu_button_set_label (self->oars_button, _("Unrestricted"));
     621                 :             : 
     622   [ #  #  #  # ]:           0 :   for (i = 0; age != oars_disabled_age && entries[i] != NULL; i++)
     623                 :             :     {
     624         [ #  # ]:           0 :       if (ages[i] == age)
     625                 :             :         {
     626                 :           0 :           gtk_menu_button_set_label (self->oars_button, entries[i]);
     627                 :           0 :           break;
     628                 :             :         }
     629                 :             :     }
     630                 :             : 
     631                 :           0 :   g_assert (age == oars_disabled_age || entries[i] != NULL);
     632                 :             : 
     633         [ #  # ]:           0 :   if (age == oars_disabled_age)
     634                 :           0 :     g_debug ("Selected to disable OARS");
     635                 :             :   else
     636                 :           0 :     g_debug ("Selected OARS age: %u", age);
     637                 :             : 
     638                 :           0 :   self->selected_age = age;
     639                 :             : 
     640                 :           0 :   schedule_update_blocklisted_apps (self);
     641                 :           0 : }
     642                 :             : 
     643                 :             : static void
     644                 :           0 : app_filter_changed_cb (MctManager *policy_manager,
     645                 :             :                        uid_t       uid,
     646                 :             :                        void       *user_data)
     647                 :             : {
     648                 :           0 :   MctUserControls *self = MCT_USER_CONTROLS (user_data);
     649                 :             : 
     650         [ #  # ]:           0 :   if (self->user == NULL)
     651                 :           0 :     return;
     652                 :             : 
     653         [ #  # ]:           0 :   if (uid != mct_user_get_uid (self->user))
     654                 :           0 :     return;
     655                 :             : 
     656                 :           0 :   update_app_filter_from_user (self);
     657                 :           0 :   setup_parental_control_settings (self);
     658                 :             : }
     659                 :             : 
     660                 :             : /* GObject overrides */
     661                 :             : 
     662                 :             : static void
     663                 :           0 : mct_user_controls_constructed (GObject *object)
     664                 :             : {
     665                 :           0 :   MctUserControls *self = MCT_USER_CONTROLS (object);
     666                 :             : 
     667                 :             :   /* Chain up. */
     668                 :           0 :   G_OBJECT_CLASS (mct_user_controls_parent_class)->constructed (object);
     669                 :             : 
     670                 :             :   /* FIXME: Ideally there wouldn’t be this sync call in a constructor, but there
     671                 :             :    * seems to be no way around it if #MctUserControls is to be used from a
     672                 :             :    * GtkBuilder template: templates are initialised from within the parent
     673                 :             :    * widget’s init() function (not its constructed() function), so none of its
     674                 :             :    * properties will have been set and it won’t reasonably have been able to
     675                 :             :    * make an async call to initialise the bus connection itself. Binding
     676                 :             :    * construct-only properties in GtkBuilder doesn’t work (and wouldn’t help if
     677                 :             :    * it did). */
     678         [ #  # ]:           0 :   if (self->dbus_connection == NULL)
     679                 :           0 :     self->dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
     680                 :             : 
     681                 :           0 :   g_assert (self->dbus_connection != NULL);
     682                 :           0 :   self->manager = mct_manager_new (self->dbus_connection);
     683                 :             : 
     684                 :           0 :   self->app_filter_changed_id =
     685                 :           0 :       g_signal_connect (self->manager,
     686                 :             :                         "app-filter-changed",
     687                 :             :                         G_CALLBACK (app_filter_changed_cb),
     688                 :             :                         self);
     689                 :           0 : }
     690                 :             : 
     691                 :             : static void
     692                 :           0 : mct_user_controls_finalize (GObject *object)
     693                 :             : {
     694                 :           0 :   MctUserControls *self = (MctUserControls *)object;
     695                 :             : 
     696                 :           0 :   g_assert (self->blocklist_apps_source_id == 0);
     697                 :             : 
     698                 :           0 :   g_cancellable_cancel (self->cancellable);
     699         [ #  # ]:           0 :   g_clear_object (&self->action_group);
     700         [ #  # ]:           0 :   g_clear_object (&self->cancellable);
     701         [ #  # ]:           0 :   g_clear_signal_handler (&self->user_notify_id, self->user);
     702         [ #  # ]:           0 :   g_clear_object (&self->user);
     703         [ #  # ]:           0 :   g_clear_pointer (&self->user_locale, g_free);
     704         [ #  # ]:           0 :   g_clear_pointer (&self->user_display_name, g_free);
     705         [ #  # ]:           0 :   g_clear_pointer (&self->description, g_free);
     706                 :             : 
     707   [ #  #  #  # ]:           0 :   if (self->permission != NULL && self->permission_allowed_id != 0)
     708                 :             :     {
     709                 :           0 :       g_signal_handler_disconnect (self->permission, self->permission_allowed_id);
     710                 :           0 :       self->permission_allowed_id = 0;
     711                 :             :     }
     712         [ #  # ]:           0 :   g_clear_object (&self->permission);
     713                 :             : 
     714         [ #  # ]:           0 :   g_clear_pointer (&self->filter, mct_app_filter_unref);
     715         [ #  # ]:           0 :   g_clear_pointer (&self->last_saved_filter, mct_app_filter_unref);
     716         [ #  # ]:           0 :   g_clear_signal_handler (&self->app_filter_changed_id, self->manager);
     717         [ #  # ]:           0 :   g_clear_object (&self->manager);
     718         [ #  # ]:           0 :   g_clear_object (&self->dbus_connection);
     719                 :             : 
     720                 :             :   /* Hopefully we don’t have data loss. */
     721                 :           0 :   g_assert (self->flushed_on_dispose);
     722                 :             : 
     723                 :           0 :   G_OBJECT_CLASS (mct_user_controls_parent_class)->finalize (object);
     724                 :           0 : }
     725                 :             : 
     726                 :             : 
     727                 :             : static void
     728                 :           0 : mct_user_controls_dispose (GObject *object)
     729                 :             : {
     730                 :           0 :   MctUserControls *self = (MctUserControls *)object;
     731                 :             : 
     732                 :             :   /* Since GTK calls g_object_run_dispose(), dispose() may be called multiple
     733                 :             :    * times. We definitely want to save any unsaved changes, but don’t need to
     734                 :             :    * do it multiple times, and after the first g_object_run_dispose() call,
     735                 :             :    * none of our child widgets are still around to extract data from anyway. */
     736         [ #  # ]:           0 :   if (!self->flushed_on_dispose)
     737                 :           0 :     flush_update_blocklisted_apps (self);
     738                 :           0 :   self->flushed_on_dispose = TRUE;
     739                 :             : 
     740                 :           0 :   G_OBJECT_CLASS (mct_user_controls_parent_class)->dispose (object);
     741                 :           0 : }
     742                 :             : 
     743                 :             : static void
     744                 :           0 : mct_user_controls_get_property (GObject    *object,
     745                 :             :                                 guint       prop_id,
     746                 :             :                                 GValue     *value,
     747                 :             :                                 GParamSpec *pspec)
     748                 :             : {
     749                 :           0 :   MctUserControls *self = MCT_USER_CONTROLS (object);
     750                 :             : 
     751   [ #  #  #  #  :           0 :   switch ((MctUserControlsProperty) prop_id)
             #  #  #  #  
                      # ]
     752                 :             :     {
     753                 :           0 :     case PROP_USER:
     754                 :           0 :       g_value_set_object (value, self->user);
     755                 :           0 :       break;
     756                 :             : 
     757                 :           0 :     case PROP_PERMISSION:
     758                 :           0 :       g_value_set_object (value, self->permission);
     759                 :           0 :       break;
     760                 :             : 
     761                 :           0 :     case PROP_APP_FILTER:
     762                 :           0 :       g_value_set_boxed (value, self->filter);
     763                 :           0 :       break;
     764                 :             : 
     765                 :           0 :     case PROP_USER_ACCOUNT_TYPE:
     766                 :           0 :       g_value_set_enum (value, self->user_account_type);
     767                 :           0 :       break;
     768                 :             : 
     769                 :           0 :     case PROP_USER_LOCALE:
     770                 :           0 :       g_value_set_string (value, self->user_locale);
     771                 :           0 :       break;
     772                 :             : 
     773                 :           0 :     case PROP_USER_DISPLAY_NAME:
     774                 :           0 :       g_value_set_string (value, self->user_display_name);
     775                 :           0 :       break;
     776                 :             : 
     777                 :           0 :     case PROP_DBUS_CONNECTION:
     778                 :           0 :       g_value_set_object (value, self->dbus_connection);
     779                 :           0 :       break;
     780                 :             : 
     781                 :           0 :     case PROP_DESCRIPTION:
     782                 :           0 :       g_value_set_string (value, self->description);
     783                 :           0 :       break;
     784                 :             : 
     785                 :           0 :     default:
     786                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     787                 :             :     }
     788                 :           0 : }
     789                 :             : 
     790                 :             : static void
     791                 :           0 : mct_user_controls_set_property (GObject      *object,
     792                 :             :                                 guint         prop_id,
     793                 :             :                                 const GValue *value,
     794                 :             :                                 GParamSpec   *pspec)
     795                 :             : {
     796                 :           0 :   MctUserControls *self = MCT_USER_CONTROLS (object);
     797                 :             : 
     798   [ #  #  #  #  :           0 :   switch ((MctUserControlsProperty) prop_id)
             #  #  #  #  
                      # ]
     799                 :             :     {
     800                 :           0 :     case PROP_USER:
     801                 :           0 :       mct_user_controls_set_user (self, g_value_get_object (value));
     802                 :           0 :       break;
     803                 :             : 
     804                 :           0 :     case PROP_PERMISSION:
     805                 :           0 :       mct_user_controls_set_permission (self, g_value_get_object (value));
     806                 :           0 :       break;
     807                 :             : 
     808                 :           0 :     case PROP_APP_FILTER:
     809                 :           0 :       mct_user_controls_set_app_filter (self, g_value_get_boxed (value));
     810                 :           0 :       break;
     811                 :             : 
     812                 :           0 :     case PROP_USER_ACCOUNT_TYPE:
     813                 :           0 :       mct_user_controls_set_user_account_type (self, g_value_get_enum (value));
     814                 :           0 :       break;
     815                 :             : 
     816                 :           0 :     case PROP_USER_LOCALE:
     817                 :           0 :       mct_user_controls_set_user_locale (self, g_value_get_string (value));
     818                 :           0 :       break;
     819                 :             : 
     820                 :           0 :     case PROP_USER_DISPLAY_NAME:
     821                 :           0 :       mct_user_controls_set_user_display_name (self, g_value_get_string (value));
     822                 :           0 :       break;
     823                 :             : 
     824                 :           0 :     case PROP_DBUS_CONNECTION:
     825                 :             :       /* Construct only. */
     826                 :           0 :       g_assert (self->dbus_connection == NULL);
     827                 :           0 :       self->dbus_connection = g_value_dup_object (value);
     828                 :           0 :       break;
     829                 :             : 
     830                 :           0 :     case PROP_DESCRIPTION:
     831                 :           0 :       mct_user_controls_set_description (self, g_value_get_string (value));
     832                 :           0 :       break;
     833                 :             : 
     834                 :           0 :     default:
     835                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     836                 :             :     }
     837                 :           0 : }
     838                 :             : 
     839                 :             : static void
     840                 :           1 : mct_user_controls_class_init (MctUserControlsClass *klass)
     841                 :             : {
     842                 :           1 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     843                 :           1 :   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
     844                 :             : 
     845                 :           1 :   object_class->constructed = mct_user_controls_constructed;
     846                 :           1 :   object_class->finalize = mct_user_controls_finalize;
     847                 :           1 :   object_class->dispose = mct_user_controls_dispose;
     848                 :           1 :   object_class->get_property = mct_user_controls_get_property;
     849                 :           1 :   object_class->set_property = mct_user_controls_set_property;
     850                 :             : 
     851                 :             :   /**
     852                 :             :    * MctUserControls:user: (nullable)
     853                 :             :    *
     854                 :             :    * The user the controls are configured for.
     855                 :             :    *
     856                 :             :    * This will be `NULL` if the user is unknown or not currently specified.
     857                 :             :    *
     858                 :             :    * Since: 0.14.0
     859                 :             :    */
     860                 :           1 :   properties[PROP_USER] = g_param_spec_object ("user", NULL, NULL,
     861                 :             :                                                MCT_TYPE_USER,
     862                 :             :                                                G_PARAM_READWRITE |
     863                 :             :                                                G_PARAM_STATIC_STRINGS |
     864                 :             :                                                G_PARAM_EXPLICIT_NOTIFY);
     865                 :             : 
     866                 :             :   /**
     867                 :             :    * MctUserControls:permission: (nullable)
     868                 :             :    *
     869                 :             :    * A [type@Gio.Permission] indicating whether the current user has permission
     870                 :             :    * to view or change parental controls.
     871                 :             :    *
     872                 :             :    * This will be `NULL` if permission is not allowed or is unknown
     873                 :             :    *
     874                 :             :    * Since: 0.5.0
     875                 :             :    */
     876                 :           1 :   properties[PROP_PERMISSION] = g_param_spec_object ("permission", NULL, NULL,
     877                 :             :                                                      G_TYPE_PERMISSION,
     878                 :             :                                                      G_PARAM_READWRITE |
     879                 :             :                                                      G_PARAM_STATIC_STRINGS |
     880                 :             :                                                      G_PARAM_EXPLICIT_NOTIFY);
     881                 :             : 
     882                 :             :   /**
     883                 :             :    * MctUserControls:app-filter: (nullable)
     884                 :             :    *
     885                 :             :    * The user’s current app filter, used to set up the user controls.
     886                 :             :    *
     887                 :             :    * As app filters are immutable, it is not updated as the user controls are
     888                 :             :    * changed. Use [method@MalcontentUi.UserControls.build_app_filter] to build
     889                 :             :    * the new app filter.
     890                 :             :    *
     891                 :             :    * This may be `NULL` if the app filter is unknown, or if querying it from
     892                 :             :    * [property@MalcontentUi.UserControls:user] fails.
     893                 :             :    *
     894                 :             :    * Since: 0.5.0
     895                 :             :    */
     896                 :           1 :   properties[PROP_APP_FILTER] =
     897                 :           1 :       g_param_spec_boxed ("app-filter", NULL, NULL,
     898                 :             :                           MCT_TYPE_APP_FILTER,
     899                 :             :                           G_PARAM_READWRITE |
     900                 :             :                           G_PARAM_STATIC_STRINGS |
     901                 :             :                           G_PARAM_EXPLICIT_NOTIFY);
     902                 :             : 
     903                 :             :   /**
     904                 :             :    * MctUserControls:user-account-type:
     905                 :             :    *
     906                 :             :    * The type of the currently selected user account.
     907                 :             :    *
     908                 :             :    * Since: 0.5.0
     909                 :             :    */
     910                 :           1 :   properties[PROP_USER_ACCOUNT_TYPE] =
     911                 :           1 :       g_param_spec_enum ("user-account-type", NULL, NULL,
     912                 :             :                          MCT_TYPE_USER_TYPE,
     913                 :             :                          MCT_USER_TYPE_UNKNOWN,
     914                 :             :                          G_PARAM_READWRITE |
     915                 :             :                          G_PARAM_STATIC_STRINGS |
     916                 :             :                          G_PARAM_EXPLICIT_NOTIFY);
     917                 :             : 
     918                 :             :   /**
     919                 :             :    * MctUserControls:user-locale: (nullable)
     920                 :             :    *
     921                 :             :    * The locale for the currently selected user account, or `NULL` if no
     922                 :             :    * user is selected.
     923                 :             :    *
     924                 :             :    * If set, it must be in the format documented by [`setlocale()`](man:setlocale(3)):
     925                 :             :    * ```
     926                 :             :    * language[_territory][.codeset][@modifier]
     927                 :             :    * ```
     928                 :             :    * where `language` is an ISO 639 language code, `territory` is an ISO 3166
     929                 :             :    * country code, and `codeset` is a character set or encoding identifier like
     930                 :             :    * `ISO-8859-1` or `UTF-8`.
     931                 :             :    *
     932                 :             :    * Since: 0.5.0
     933                 :             :    */
     934                 :           1 :   properties[PROP_USER_LOCALE] =
     935                 :           1 :       g_param_spec_string ("user-locale", NULL, NULL,
     936                 :             :                            NULL,
     937                 :             :                            G_PARAM_READWRITE |
     938                 :             :                            G_PARAM_STATIC_STRINGS |
     939                 :             :                            G_PARAM_EXPLICIT_NOTIFY);
     940                 :             : 
     941                 :             :   /**
     942                 :             :    * MctUserControls:user-display-name: (nullable)
     943                 :             :    *
     944                 :             :    * The display name for the currently selected user account, or `NULL` if no
     945                 :             :    * user is selected.
     946                 :             :    *
     947                 :             :    * This will typically be the user’s full name (if known) or their username.
     948                 :             :    *
     949                 :             :    * If set, it must be valid UTF-8 and non-empty.
     950                 :             :    *
     951                 :             :    * Since: 0.5.0
     952                 :             :    */
     953                 :           1 :   properties[PROP_USER_DISPLAY_NAME] =
     954                 :           1 :       g_param_spec_string ("user-display-name", NULL, NULL,
     955                 :             :                            NULL,
     956                 :             :                            G_PARAM_READWRITE |
     957                 :             :                            G_PARAM_STATIC_STRINGS |
     958                 :             :                            G_PARAM_EXPLICIT_NOTIFY);
     959                 :             : 
     960                 :             :   /**
     961                 :             :    * MctUserControls:description: (nullable)
     962                 :             :    *
     963                 :             :    * The description for the currently selected user account, or `NULL` if no
     964                 :             :    * user is selected.
     965                 :             :    *
     966                 :             :    * If set, it must be valid UTF-8 and non-empty.
     967                 :             :    *
     968                 :             :    * Since: 0.11.0
     969                 :             :    */
     970                 :           1 :   properties[PROP_DESCRIPTION] =
     971                 :           1 :       g_param_spec_string ("description", NULL, NULL,
     972                 :             :                            NULL,
     973                 :             :                            G_PARAM_READWRITE |
     974                 :             :                            G_PARAM_STATIC_STRINGS |
     975                 :             :                            G_PARAM_EXPLICIT_NOTIFY);
     976                 :             : 
     977                 :             :   /**
     978                 :             :    * MctUserControls:dbus-connection: (not nullable)
     979                 :             :    *
     980                 :             :    * A connection to the system bus.
     981                 :             :    *
     982                 :             :    * This will be used for retrieving details of user accounts, and must be
     983                 :             :    * provided at construction time.
     984                 :             :    *
     985                 :             :    * Since: 0.7.0
     986                 :             :    */
     987                 :           1 :   properties[PROP_DBUS_CONNECTION] =
     988                 :           1 :       g_param_spec_object ("dbus-connection", NULL, NULL,
     989                 :             :                            G_TYPE_DBUS_CONNECTION,
     990                 :             :                            G_PARAM_READWRITE |
     991                 :             :                            G_PARAM_CONSTRUCT_ONLY |
     992                 :             :                            G_PARAM_STATIC_STRINGS |
     993                 :             :                            G_PARAM_EXPLICIT_NOTIFY);
     994                 :             : 
     995                 :           1 :   g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
     996                 :             : 
     997                 :           1 :   gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentUi/ui/user-controls.ui");
     998                 :             : 
     999                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, age_menu);
    1000                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, description_label);
    1001                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_software_installation_switch);
    1002                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_software_installation_row);
    1003                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_web_browsers_switch);
    1004                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_web_browsers_row);
    1005                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, oars_button);
    1006                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, oars_popover);
    1007                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_applications_dialog);
    1008                 :           1 :   gtk_widget_class_bind_template_child (widget_class, MctUserControls, restrict_applications_row);
    1009                 :             : 
    1010                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_restrict_installation_switch_active_changed_cb);
    1011                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_restrict_web_browsers_switch_active_changed_cb);
    1012                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_restrict_applications_dialog_closed_cb);
    1013                 :           1 : }
    1014                 :             : 
    1015                 :             : static void
    1016                 :           0 : mct_user_controls_init (MctUserControls *self)
    1017                 :             : {
    1018                 :           0 :   g_autoptr(GtkCssProvider) provider = NULL;
    1019                 :             : 
    1020                 :             :   /* Ensure the types used in the UI are registered. */
    1021                 :           0 :   g_type_ensure (MCT_TYPE_RESTRICT_APPLICATIONS_DIALOG);
    1022                 :             : 
    1023                 :           0 :   gtk_widget_init_template (GTK_WIDGET (self));
    1024                 :             : 
    1025                 :           0 :   provider = gtk_css_provider_new ();
    1026                 :           0 :   gtk_css_provider_load_from_resource (provider,
    1027                 :             :                                        "/org/freedesktop/MalcontentUi/ui/restricts-switch.css");
    1028                 :           0 :   gtk_style_context_add_provider_for_display (gdk_display_get_default (),
    1029                 :           0 :                                               GTK_STYLE_PROVIDER (provider),
    1030                 :             :                                               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1);
    1031                 :             : 
    1032                 :           0 :   self->selected_age = (guint) -1;
    1033                 :             : 
    1034                 :           0 :   self->cancellable = g_cancellable_new ();
    1035                 :             : 
    1036                 :           0 :   self->action_group = g_simple_action_group_new ();
    1037                 :           0 :   g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
    1038                 :             :                                    actions,
    1039                 :             :                                    G_N_ELEMENTS (actions),
    1040                 :             :                                    self);
    1041                 :             : 
    1042                 :           0 :   gtk_widget_insert_action_group (GTK_WIDGET (self),
    1043                 :             :                                   "permissions",
    1044                 :           0 :                                   G_ACTION_GROUP (self->action_group));
    1045                 :             : 
    1046                 :           0 :   gtk_popover_menu_set_menu_model (self->oars_popover, G_MENU_MODEL (self->age_menu));
    1047                 :           0 : }
    1048                 :             : 
    1049                 :             : /**
    1050                 :             :  * mct_user_controls_get_user:
    1051                 :             :  * @self: a user controls widget
    1052                 :             :  *
    1053                 :             :  * Get the value of [property@MalcontentUi.UserControls:user].
    1054                 :             :  *
    1055                 :             :  * Returns: (transfer none) (nullable): the user the controls are configured for,
    1056                 :             :  *   or `NULL` if unknown
    1057                 :             :  * Since: 0.14.0
    1058                 :             :  */
    1059                 :             : MctUser *
    1060                 :           0 : mct_user_controls_get_user (MctUserControls *self)
    1061                 :             : {
    1062                 :           0 :   g_return_val_if_fail (MCT_IS_USER_CONTROLS (self), NULL);
    1063                 :             : 
    1064                 :           0 :   return self->user;
    1065                 :             : }
    1066                 :             : 
    1067                 :             : static void
    1068                 :           0 : user_notify_cb (GObject    *object,
    1069                 :             :                 GParamSpec *pspec,
    1070                 :             :                 void       *user_data)
    1071                 :             : {
    1072                 :           0 :   MctUser *user = MCT_USER (object);
    1073                 :           0 :   MctUserControls *self = MCT_USER_CONTROLS (user_data);
    1074                 :             : 
    1075                 :           0 :   mct_user_controls_set_user_account_type (self, mct_user_get_user_type (user));
    1076                 :           0 :   mct_user_controls_set_user_locale (self, get_user_locale (user));
    1077                 :           0 :   mct_user_controls_set_user_display_name (self, mct_user_get_display_name (user));
    1078                 :           0 : }
    1079                 :             : 
    1080                 :             : /**
    1081                 :             :  * mct_user_controls_set_user:
    1082                 :             :  * @self: a user controls widget
    1083                 :             :  * @user: (nullable) (transfer none): the user to configure the controls for,
    1084                 :             :  *   or `NULL` if unknown
    1085                 :             :  *
    1086                 :             :  * Set the value of [property@MalcontentUi.UserControls:user].
    1087                 :             :  *
    1088                 :             :  * Since: 0.14.0
    1089                 :             :  */
    1090                 :             : void
    1091                 :           0 : mct_user_controls_set_user (MctUserControls *self,
    1092                 :             :                             MctUser         *user)
    1093                 :             : {
    1094         [ #  # ]:           0 :   g_autoptr(MctUser) old_user = NULL;
    1095                 :             : 
    1096                 :           0 :   g_return_if_fail (MCT_IS_USER_CONTROLS (self));
    1097                 :           0 :   g_return_if_fail (user == NULL || MCT_IS_USER (user));
    1098                 :             : 
    1099                 :             :   /* If we have pending unsaved changes from the previous user, force them to be
    1100                 :             :    * saved first. */
    1101                 :           0 :   flush_update_blocklisted_apps (self);
    1102                 :             : 
    1103         [ #  # ]:           0 :   old_user = (self->user != NULL) ? g_object_ref (self->user) : NULL;
    1104                 :             : 
    1105         [ #  # ]:           0 :   if (g_set_object (&self->user, user))
    1106                 :             :     {
    1107                 :           0 :       g_object_freeze_notify (G_OBJECT (self));
    1108                 :             : 
    1109         [ #  # ]:           0 :       if (old_user != NULL)
    1110         [ #  # ]:           0 :         g_clear_signal_handler (&self->user_notify_id, old_user);
    1111                 :             : 
    1112                 :             :       /* Update the starting widget state from the user. */
    1113         [ #  # ]:           0 :       if (user != NULL)
    1114                 :             :         {
    1115                 :           0 :           self->user_notify_id = g_signal_connect (user, "notify",
    1116                 :             :                                                    G_CALLBACK (user_notify_cb), self);
    1117                 :           0 :           user_notify_cb (G_OBJECT (user), NULL, self);
    1118                 :             :         }
    1119                 :             : 
    1120                 :           0 :       update_app_filter_from_user (self);
    1121                 :           0 :       setup_parental_control_settings (self);
    1122                 :             : 
    1123                 :           0 :       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER]);
    1124                 :           0 :       g_object_thaw_notify (G_OBJECT (self));
    1125                 :             :     }
    1126                 :             : }
    1127                 :             : 
    1128                 :             : static void
    1129                 :           0 : on_permission_allowed_cb (GObject    *obj,
    1130                 :             :                           GParamSpec *pspec,
    1131                 :             :                           gpointer    user_data)
    1132                 :             : {
    1133                 :           0 :   MctUserControls *self = MCT_USER_CONTROLS (user_data);
    1134                 :             : 
    1135                 :           0 :   update_app_filter_from_user (self);
    1136                 :           0 :   setup_parental_control_settings (self);
    1137                 :           0 : }
    1138                 :             : 
    1139                 :             : /**
    1140                 :             :  * mct_user_controls_get_permission:
    1141                 :             :  * @self: a user controls widget
    1142                 :             :  *
    1143                 :             :  * Get the value of [property@MalcontentUi.UserControls:permission].
    1144                 :             :  *
    1145                 :             :  * Returns: (transfer none) (nullable): a [type@Gio.Permission] indicating
    1146                 :             :  *   whether the current user has permission to view or change parental
    1147                 :             :  *   controls, or `NULL` if permission is not allowed or is unknown
    1148                 :             :  * Since: 0.5.0
    1149                 :             :  */
    1150                 :             : GPermission *
    1151                 :           0 : mct_user_controls_get_permission (MctUserControls *self)
    1152                 :             : {
    1153                 :           0 :   g_return_val_if_fail (MCT_IS_USER_CONTROLS (self), NULL);
    1154                 :             : 
    1155                 :           0 :   return self->permission;
    1156                 :             : }
    1157                 :             : 
    1158                 :             : /**
    1159                 :             :  * mct_user_controls_set_permission:
    1160                 :             :  * @self: a user controls widget
    1161                 :             :  * @permission: (nullable) (transfer none): the [type@Gio.Permission] indicating
    1162                 :             :  *   whether the current user has permission to view or change parental
    1163                 :             :  *   controls, or `NULL` if permission is not allowed or is unknown
    1164                 :             :  *
    1165                 :             :  * Set the value of [property@MalcontentUi.UserControls:permission].
    1166                 :             :  *
    1167                 :             :  * Since: 0.5.0
    1168                 :             :  */
    1169                 :             : void
    1170                 :           0 : mct_user_controls_set_permission (MctUserControls *self,
    1171                 :             :                                   GPermission     *permission)
    1172                 :             : {
    1173                 :           0 :   g_return_if_fail (MCT_IS_USER_CONTROLS (self));
    1174                 :           0 :   g_return_if_fail (permission == NULL || G_IS_PERMISSION (permission));
    1175                 :             : 
    1176         [ #  # ]:           0 :   if (self->permission == permission)
    1177                 :           0 :     return;
    1178                 :             : 
    1179   [ #  #  #  # ]:           0 :   if (self->permission != NULL && self->permission_allowed_id != 0)
    1180                 :             :     {
    1181                 :           0 :       g_signal_handler_disconnect (self->permission, self->permission_allowed_id);
    1182                 :           0 :       self->permission_allowed_id = 0;
    1183                 :             :     }
    1184                 :             : 
    1185         [ #  # ]:           0 :   g_clear_object (&self->permission);
    1186                 :             : 
    1187         [ #  # ]:           0 :   if (permission != NULL)
    1188                 :             :     {
    1189                 :           0 :       self->permission = g_object_ref (permission);
    1190                 :           0 :       self->permission_allowed_id = g_signal_connect (self->permission,
    1191                 :             :                                                       "notify::allowed",
    1192                 :             :                                                       (GCallback) on_permission_allowed_cb,
    1193                 :             :                                                       self);
    1194                 :             :     }
    1195                 :             : 
    1196                 :             :   /* Handle changes. */
    1197                 :           0 :   update_app_filter_from_user (self);
    1198                 :           0 :   setup_parental_control_settings (self);
    1199                 :             : 
    1200                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PERMISSION]);
    1201                 :             : }
    1202                 :             : 
    1203                 :             : /**
    1204                 :             :  * mct_user_controls_get_app_filter:
    1205                 :             :  * @self: a user controls widget
    1206                 :             :  *
    1207                 :             :  * Get the value of [property@MalcontentUi.UserControls:app-filter].
    1208                 :             :  *
    1209                 :             :  * If the app filter is unknown or could not be retrieved from
    1210                 :             :  * [property@MalcontentUi.UserControls:user], this will be `NULL`.
    1211                 :             :  *
    1212                 :             :  * Returns: (transfer none) (nullable): the initial app filter used to
    1213                 :             :  *   populate the user controls, or `NULL` if unknown
    1214                 :             :  * Since: 0.5.0
    1215                 :             :  */
    1216                 :             : MctAppFilter *
    1217                 :           0 : mct_user_controls_get_app_filter (MctUserControls *self)
    1218                 :             : {
    1219                 :           0 :   g_return_val_if_fail (MCT_IS_USER_CONTROLS (self), NULL);
    1220                 :             : 
    1221                 :           0 :   return self->filter;
    1222                 :             : }
    1223                 :             : 
    1224                 :             : /**
    1225                 :             :  * mct_user_controls_set_app_filter:
    1226                 :             :  * @self: a user controls widget
    1227                 :             :  * @app_filter: (nullable) (transfer none): the app filter to configure the user
    1228                 :             :  *   controls from, or `NULL` if unknown
    1229                 :             :  *
    1230                 :             :  * Set the value of [property@MalcontentUi.UserControls:app-filter].
    1231                 :             :  *
    1232                 :             :  * This will overwrite any user changes to the controls, so they should be saved
    1233                 :             :  * first using [method@MalcontentUi.UserControls.build_app_filter] if desired.
    1234                 :             :  * They will be saved automatically if [property@MalcontentUi.UserControls:user]
    1235                 :             :  * is set.
    1236                 :             :  *
    1237                 :             :  * Since: 0.5.0
    1238                 :             :  */
    1239                 :             : void
    1240                 :           0 : mct_user_controls_set_app_filter (MctUserControls *self,
    1241                 :             :                                   MctAppFilter    *app_filter)
    1242                 :             : {
    1243                 :           0 :   g_return_if_fail (MCT_IS_USER_CONTROLS (self));
    1244                 :             : 
    1245                 :             :   /* If we have pending unsaved changes from the previous configuration, force
    1246                 :             :    * them to be saved first. */
    1247                 :           0 :   flush_update_blocklisted_apps (self);
    1248                 :             : 
    1249         [ #  # ]:           0 :   if (self->filter == app_filter)
    1250                 :           0 :     return;
    1251                 :             : 
    1252         [ #  # ]:           0 :   g_clear_pointer (&self->filter, mct_app_filter_unref);
    1253         [ #  # ]:           0 :   g_clear_pointer (&self->last_saved_filter, mct_app_filter_unref);
    1254         [ #  # ]:           0 :   if (app_filter != NULL)
    1255                 :             :     {
    1256                 :           0 :       self->filter = mct_app_filter_ref (app_filter);
    1257                 :           0 :       self->last_saved_filter = mct_app_filter_ref (app_filter);
    1258                 :             :     }
    1259                 :             : 
    1260                 :           0 :   g_debug ("Set new app filter from caller");
    1261                 :           0 :   setup_parental_control_settings (self);
    1262                 :             : 
    1263                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_APP_FILTER]);
    1264                 :             : }
    1265                 :             : 
    1266                 :             : /**
    1267                 :             :  * mct_user_controls_get_user_account_type:
    1268                 :             :  * @self: a user controls widget
    1269                 :             :  *
    1270                 :             :  * Get the value of [property@MalcontentUi.UserControls:user-account-type].
    1271                 :             :  *
    1272                 :             :  * Returns: the account type of the user the controls are configured for
    1273                 :             :  * Since: 0.14.0
    1274                 :             :  */
    1275                 :             : MctUserType
    1276                 :           0 : mct_user_controls_get_user_account_type (MctUserControls *self)
    1277                 :             : {
    1278                 :           0 :   g_return_val_if_fail (MCT_IS_USER_CONTROLS (self), MCT_USER_TYPE_UNKNOWN);
    1279                 :             : 
    1280                 :           0 :   return self->user_account_type;
    1281                 :             : }
    1282                 :             : 
    1283                 :             : /**
    1284                 :             :  * mct_user_controls_set_user_account_type:
    1285                 :             :  * @self: a user controls widget
    1286                 :             :  * @user_account_type: the account type of the user to configure the controls for
    1287                 :             :  *
    1288                 :             :  * Set the value of [property@MalcontentUi.UserControls:user-account-type].
    1289                 :             :  *
    1290                 :             :  * Since: 0.14.0
    1291                 :             :  */
    1292                 :             : void
    1293                 :           0 : mct_user_controls_set_user_account_type (MctUserControls *self,
    1294                 :             :                                          MctUserType      user_account_type)
    1295                 :             : {
    1296                 :           0 :   g_return_if_fail (MCT_IS_USER_CONTROLS (self));
    1297                 :             : 
    1298                 :             :   /* If we have pending unsaved changes from the previous user, force them to be
    1299                 :             :    * saved first. */
    1300                 :           0 :   flush_update_blocklisted_apps (self);
    1301                 :             : 
    1302         [ #  # ]:           0 :   if (self->user_account_type == user_account_type)
    1303                 :           0 :     return;
    1304                 :             : 
    1305                 :           0 :   self->user_account_type = user_account_type;
    1306                 :             : 
    1307                 :           0 :   setup_parental_control_settings (self);
    1308                 :             : 
    1309                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER_ACCOUNT_TYPE]);
    1310                 :             : }
    1311                 :             : 
    1312                 :             : /**
    1313                 :             :  * mct_user_controls_get_user_locale:
    1314                 :             :  * @self: a user controls widget
    1315                 :             :  *
    1316                 :             :  * Get the value of [property@MalcontentUi.UserControls:user-locale].
    1317                 :             :  *
    1318                 :             :  * Returns: (transfer none) (nullable): the locale of the user the controls
    1319                 :             :  *   are configured for, or `NULL` if unknown
    1320                 :             :  * Since: 0.5.0
    1321                 :             :  */
    1322                 :             : const gchar *
    1323                 :           0 : mct_user_controls_get_user_locale (MctUserControls *self)
    1324                 :             : {
    1325                 :           0 :   g_return_val_if_fail (MCT_IS_USER_CONTROLS (self), NULL);
    1326                 :             : 
    1327                 :           0 :   return self->user_locale;
    1328                 :             : }
    1329                 :             : 
    1330                 :             : /**
    1331                 :             :  * mct_user_controls_set_user_locale:
    1332                 :             :  * @self: a user controls widget
    1333                 :             :  * @user_locale: (nullable) (transfer none): the locale of the user
    1334                 :             :  *   to configure the controls for, or `NULL` if unknown
    1335                 :             :  *
    1336                 :             :  * Set the value of [property@MalcontentUi.UserControls:user-locale].
    1337                 :             :  *
    1338                 :             :  * Since: 0.5.0
    1339                 :             :  */
    1340                 :             : void
    1341                 :           0 : mct_user_controls_set_user_locale (MctUserControls *self,
    1342                 :             :                                    const gchar     *user_locale)
    1343                 :             : {
    1344                 :           0 :   g_return_if_fail (MCT_IS_USER_CONTROLS (self));
    1345                 :           0 :   g_return_if_fail (user_locale == NULL ||
    1346                 :             :                     (*user_locale != '\0' &&
    1347                 :             :                      g_utf8_validate (user_locale, -1, NULL)));
    1348                 :             : 
    1349                 :             :   /* If we have pending unsaved changes from the previous user, force them to be
    1350                 :             :    * saved first. */
    1351                 :           0 :   flush_update_blocklisted_apps (self);
    1352                 :             : 
    1353         [ #  # ]:           0 :   if (g_strcmp0 (self->user_locale, user_locale) == 0)
    1354                 :           0 :     return;
    1355                 :             : 
    1356         [ #  # ]:           0 :   g_clear_pointer (&self->user_locale, g_free);
    1357                 :           0 :   self->user_locale = g_strdup (user_locale);
    1358                 :             : 
    1359                 :           0 :   setup_parental_control_settings (self);
    1360                 :             : 
    1361                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER_LOCALE]);
    1362                 :             : }
    1363                 :             : 
    1364                 :             : /**
    1365                 :             :  * mct_user_controls_get_user_display_name:
    1366                 :             :  * @self: a user controls widget
    1367                 :             :  *
    1368                 :             :  * Get the value of [property@MalcontentUi.UserControls:user-display-name].
    1369                 :             :  *
    1370                 :             :  * Returns: (transfer none) (nullable): the display name of the user the
    1371                 :             :  *   controls are configured for, or `NULL` if unknown
    1372                 :             :  * Since: 0.5.0
    1373                 :             :  */
    1374                 :             : const gchar *
    1375                 :           0 : mct_user_controls_get_user_display_name (MctUserControls *self)
    1376                 :             : {
    1377                 :           0 :   g_return_val_if_fail (MCT_IS_USER_CONTROLS (self), NULL);
    1378                 :             : 
    1379                 :           0 :   return self->user_display_name;
    1380                 :             : }
    1381                 :             : 
    1382                 :             : /**
    1383                 :             :  * mct_user_controls_set_user_display_name:
    1384                 :             :  * @self: a user controls widget
    1385                 :             :  * @user_display_name: (nullable) (transfer none): the display name of the user
    1386                 :             :  *   to configure the controls for, or `NULL` if unknown
    1387                 :             :  *
    1388                 :             :  * Set the value of [property@MalcontentUi.UserControls:user-display-name].
    1389                 :             :  *
    1390                 :             :  * Since: 0.5.0
    1391                 :             :  */
    1392                 :             : void
    1393                 :           0 : mct_user_controls_set_user_display_name (MctUserControls *self,
    1394                 :             :                                          const gchar     *user_display_name)
    1395                 :             : {
    1396                 :           0 :   g_return_if_fail (MCT_IS_USER_CONTROLS (self));
    1397                 :           0 :   g_return_if_fail (user_display_name == NULL ||
    1398                 :             :                     (*user_display_name != '\0' &&
    1399                 :             :                      g_utf8_validate (user_display_name, -1, NULL)));
    1400                 :             : 
    1401                 :             :   /* If we have pending unsaved changes from the previous user, force them to be
    1402                 :             :    * saved first. */
    1403                 :           0 :   flush_update_blocklisted_apps (self);
    1404                 :             : 
    1405         [ #  # ]:           0 :   if (g_strcmp0 (self->user_display_name, user_display_name) == 0)
    1406                 :           0 :     return;
    1407                 :             : 
    1408         [ #  # ]:           0 :   g_clear_pointer (&self->user_display_name, g_free);
    1409                 :           0 :   self->user_display_name = g_strdup (user_display_name);
    1410                 :             : 
    1411                 :           0 :   setup_parental_control_settings (self);
    1412                 :             : 
    1413                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USER_DISPLAY_NAME]);
    1414                 :             : }
    1415                 :             : 
    1416                 :             : /**
    1417                 :             :  * mct_user_controls_set_description:
    1418                 :             :  * @self: a user controls widget
    1419                 :             :  * @description: (nullable) (transfer none): the description shown
    1420                 :             :  *   above the controls, or `NULL` if none.
    1421                 :             :  *
    1422                 :             :  * Set the value of [property@MalcontentUi.UserControls:description].
    1423                 :             :  *
    1424                 :             :  * Since: 0.11.0
    1425                 :             :  */
    1426                 :             : void
    1427                 :           0 : mct_user_controls_set_description (MctUserControls *self,
    1428                 :             :                                    const gchar     *description)
    1429                 :             : {
    1430                 :           0 :   g_return_if_fail (MCT_IS_USER_CONTROLS (self));
    1431                 :           0 :   g_return_if_fail (description != NULL);
    1432                 :             : 
    1433                 :             :   /* If we have pending unsaved changes from the previous user, force them to be
    1434                 :             :    * saved first. */
    1435                 :           0 :   flush_update_blocklisted_apps (self);
    1436                 :             : 
    1437         [ #  # ]:           0 :   if (g_strcmp0 (self->description, description) == 0)
    1438                 :           0 :     return;
    1439                 :             : 
    1440         [ #  # ]:           0 :   g_clear_pointer (&self->description, g_free);
    1441                 :           0 :   self->description = g_strdup (description);
    1442                 :             : 
    1443                 :           0 :   setup_parental_control_settings (self);
    1444                 :             : 
    1445                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DESCRIPTION]);
    1446                 :             : }
    1447                 :             : 
    1448                 :             : /**
    1449                 :             :  * mct_user_controls_build_app_filter:
    1450                 :             :  * @self: a user controls widget
    1451                 :             :  * @builder: an existing [struct@Malcontent.AppFilterBuilder] to modify
    1452                 :             :  *
    1453                 :             :  * Get the app filter settings currently configured in the user controls, by
    1454                 :             :  * modifying the given @builder.
    1455                 :             :  *
    1456                 :             :  * This can be used to save the settings manually.
    1457                 :             :  *
    1458                 :             :  * Since: 0.5.0
    1459                 :             :  */
    1460                 :             : void
    1461                 :           0 : mct_user_controls_build_app_filter (MctUserControls     *self,
    1462                 :             :                                     MctAppFilterBuilder *builder)
    1463                 :             : {
    1464                 :             :   gboolean restrict_web_browsers;
    1465                 :             :   gsize i;
    1466         [ #  # ]:           0 :   g_autofree const gchar **oars_categories = as_content_rating_get_all_rating_ids ();
    1467                 :             : 
    1468                 :           0 :   g_return_if_fail (MCT_IS_USER_CONTROLS (self));
    1469                 :           0 :   g_return_if_fail (builder != NULL);
    1470                 :             : 
    1471                 :           0 :   g_debug ("Building parental controls settings…");
    1472                 :             : 
    1473                 :             :   /* Blocklist */
    1474                 :             : 
    1475                 :           0 :   g_debug ("\t → Blocklisting apps");
    1476                 :             : 
    1477                 :           0 :   mct_restrict_applications_dialog_build_app_filter (self->restrict_applications_dialog, builder);
    1478                 :             : 
    1479                 :             :   /* Maturity level */
    1480                 :             : 
    1481                 :           0 :   g_debug ("\t → Maturity level");
    1482                 :             : 
    1483         [ #  # ]:           0 :   if (self->selected_age == oars_disabled_age)
    1484                 :           0 :     g_debug ("\t\t → Disabled");
    1485                 :             : 
    1486   [ #  #  #  # ]:           0 :   for (i = 0; self->selected_age != oars_disabled_age && oars_categories[i] != NULL; i++)
    1487                 :             :     {
    1488                 :             :       MctAppFilterOarsValue oars_value;
    1489                 :             :       const gchar *oars_category;
    1490                 :             : 
    1491                 :           0 :       oars_category = oars_categories[i];
    1492                 :           0 :       oars_value = (MctAppFilterOarsValue) as_content_rating_attribute_from_csm_age (oars_category, self->selected_age);
    1493                 :             : 
    1494                 :           0 :       g_debug ("\t\t → %s: %s", oars_category, oars_value_to_string (oars_value));
    1495                 :             : 
    1496                 :           0 :       mct_app_filter_builder_set_oars_value (builder, oars_category, oars_value);
    1497                 :             :     }
    1498                 :             : 
    1499                 :             :   /* Web browsers */
    1500                 :           0 :   restrict_web_browsers = gtk_switch_get_active (self->restrict_web_browsers_switch);
    1501                 :             : 
    1502         [ #  # ]:           0 :   g_debug ("\t → %s web browsers", restrict_web_browsers ? "Restricting" : "Allowing");
    1503                 :             : 
    1504         [ #  # ]:           0 :   if (restrict_web_browsers)
    1505                 :           0 :     mct_app_filter_builder_blocklist_content_type (builder, WEB_BROWSERS_CONTENT_TYPE);
    1506                 :             : 
    1507                 :             :   /* App installation */
    1508         [ #  # ]:           0 :   if (self->user_account_type != MCT_USER_TYPE_PARENT)
    1509                 :             :     {
    1510                 :             :       gboolean restrict_software_installation;
    1511                 :             : 
    1512                 :           0 :       restrict_software_installation = gtk_switch_get_active (self->restrict_software_installation_switch);
    1513                 :             : 
    1514         [ #  # ]:           0 :       g_debug ("\t → %s system installation", restrict_software_installation ? "Restricting" : "Allowing");
    1515         [ #  # ]:           0 :       g_debug ("\t → %s user installation", restrict_software_installation ? "Restricting" : "Allowing");
    1516                 :             : 
    1517                 :           0 :       mct_app_filter_builder_set_allow_user_installation (builder, !restrict_software_installation);
    1518                 :           0 :       mct_app_filter_builder_set_allow_system_installation (builder, !restrict_software_installation);
    1519                 :             :     }
    1520                 :             : }
        

Generated by: LCOV version 2.0-1