From e05fb7b3e36c052baf0dd607ddeb22c0a2b5cbde Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:47:04 +0200 Subject: Adding upstream version 43.2. Signed-off-by: Daniel Baumann --- .../pages/password/gis-password-page.c | 521 +++++++++++++++++++++ 1 file changed, 521 insertions(+) create mode 100644 gnome-initial-setup/pages/password/gis-password-page.c (limited to 'gnome-initial-setup/pages/password/gis-password-page.c') diff --git a/gnome-initial-setup/pages/password/gis-password-page.c b/gnome-initial-setup/pages/password/gis-password-page.c new file mode 100644 index 0000000..700d80a --- /dev/null +++ b/gnome-initial-setup/pages/password/gis-password-page.c @@ -0,0 +1,521 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2012 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Jasper St. Pierre + */ + +/* Password page {{{1 */ + +#define PAGE_ID "password" + +#include "config.h" +#include "password-resources.h" +#include "gis-password-page.h" + +#include "gis-keyring.h" + +#include "pw-utils.h" + +#include +#include + +#include "gis-page-header.h" + +#define VALIDATION_TIMEOUT 600 + +struct _GisPasswordPagePrivate +{ + GtkWidget *password_entry; + GtkWidget *confirm_entry; + GtkWidget *password_strength; + GtkWidget *password_explanation; + GtkWidget *confirm_explanation; + GtkWidget *header; + + gboolean valid_confirm; + gboolean valid_password; + guint timeout_id; + const gchar *username; + gboolean parent_mode; +}; +typedef struct _GisPasswordPagePrivate GisPasswordPagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (GisPasswordPage, gis_password_page, GIS_TYPE_PAGE); + +typedef enum +{ + PROP_PARENT_MODE = 1, +} GisPasswordPageProperty; + +static GParamSpec *obj_props[PROP_PARENT_MODE + 1]; + +static void +clear_password_validation_error (GtkWidget *entry) +{ + gtk_widget_remove_css_class (entry, "error"); +} + +static void +set_password_validation_error (GtkWidget *entry) +{ + gtk_widget_add_css_class (entry, "error"); +} + +static void +update_header (GisPasswordPage *page) +{ + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + g_autofree gchar *title = NULL; + g_autofree gchar *subtitle = NULL; + const gchar *icon_name; + GdkPaintable *paintable; + +#ifndef HAVE_PARENTAL_CONTROLS + /* Don’t break UI compatibility if parental controls are disabled. */ + title = g_strdup (_("Set a Password")); + subtitle = g_strdup (_("Be careful not to lose your password.")); + paintable = NULL; + icon_name = "dialog-password-symbolic"; +#else + if (!priv->parent_mode) + { + /* Translators: The placeholder is for the user’s full name. */ + title = g_strdup_printf (_("Set a Password for %s"), + gis_driver_get_full_name (GIS_PAGE (page)->driver)); + subtitle = g_strdup (_("Be careful not to lose your password.")); + paintable = gis_driver_get_avatar (GIS_PAGE (page)->driver); + icon_name = (paintable != NULL) ? NULL : "dialog-password-symbolic"; + } + else + { + title = g_strdup (_("Set a Parent Password")); + /* Translators: The placeholder is the full name of the child user on the system. */ + subtitle = g_strdup_printf (_("This password will control access to the parental controls for %s."), + gis_driver_get_full_name (GIS_PAGE (page)->driver)); + icon_name = "org.freedesktop.MalcontentControl-symbolic"; + paintable = NULL; + } +#endif + + /* Doesn’t make sense to set both. */ + g_assert (icon_name == NULL || paintable == NULL); + + g_object_set (G_OBJECT (priv->header), + "title", title, + "subtitle", subtitle, + NULL); + if (paintable != NULL) + g_object_set (G_OBJECT (priv->header), "paintable", paintable, NULL); + else if (icon_name != NULL) + g_object_set (G_OBJECT (priv->header), "icon-name", icon_name, NULL); +} + +static void +set_parent_mode (GisPasswordPage *page, + gboolean parent_mode) +{ + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + + g_return_if_fail (GIS_IS_PASSWORD_PAGE (page)); + + if (priv->parent_mode == parent_mode) + return; + + priv->parent_mode = parent_mode; + g_object_notify_by_pspec (G_OBJECT (page), obj_props[PROP_PARENT_MODE]); + + update_header (page); +} + +static gboolean +page_validate (GisPasswordPage *page) +{ + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + + return priv->valid_confirm; +} + +static void +update_page_validation (GisPasswordPage *page) +{ + gis_page_set_complete (GIS_PAGE (page), page_validate (page)); +} + +static gboolean +gis_password_page_save_data (GisPage *gis_page, + GError **error) +{ + GisPasswordPage *page = GIS_PASSWORD_PAGE (gis_page); + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + ActUser *act_user; + UmAccountMode account_mode; + const gchar *password = NULL; + + g_assert (gis_page->driver != NULL); + + account_mode = gis_driver_get_account_mode (gis_page->driver); + + if (!priv->parent_mode) + gis_driver_get_user_permissions (gis_page->driver, &act_user, &password); + else + gis_driver_get_parent_permissions (gis_page->driver, &act_user, &password); + + if (account_mode == UM_ENTERPRISE) { + g_assert (!priv->parent_mode); + + if (password != NULL) + gis_update_login_keyring_password (password); + return TRUE; + } + + password = gtk_editable_get_text (GTK_EDITABLE (priv->password_entry)); + + if (strlen (password) == 0) + act_user_set_password_mode (act_user, ACT_USER_PASSWORD_MODE_NONE); + else + act_user_set_password (act_user, password, ""); + + if (!priv->parent_mode) + gis_driver_set_user_permissions (gis_page->driver, act_user, password); + else + gis_driver_set_parent_permissions (gis_page->driver, act_user, password); + + if (!priv->parent_mode) + gis_update_login_keyring_password (password); + + return TRUE; +} + +static void +gis_password_page_shown (GisPage *gis_page) +{ + GisPasswordPage *page = GIS_PASSWORD_PAGE (gis_page); + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + + gtk_widget_grab_focus (priv->password_entry); +} + +static gboolean +validate (GisPasswordPage *page) +{ + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + const gchar *password; + const gchar *verify; + gint strength_level; + const gchar *hint; + + g_clear_handle_id (&priv->timeout_id, g_source_remove); + + password = gtk_editable_get_text (GTK_EDITABLE (priv->password_entry)); + verify = gtk_editable_get_text (GTK_EDITABLE (priv->confirm_entry)); + + pw_strength (password, NULL, priv->username, &hint, &strength_level); + gtk_level_bar_set_value (GTK_LEVEL_BAR (priv->password_strength), strength_level); + gtk_label_set_label (GTK_LABEL (priv->password_explanation), hint); + + gtk_label_set_label (GTK_LABEL (priv->confirm_explanation), ""); + priv->valid_confirm = FALSE; + + priv->valid_password = (strlen (password) && strength_level > 1); + if (priv->valid_password) + clear_password_validation_error (priv->password_entry); + else + set_password_validation_error (priv->password_entry); + + if (strlen (password) > 0 && strlen (verify) > 0) { + priv->valid_confirm = (strcmp (password, verify) == 0); + if (!priv->valid_confirm) + gtk_label_set_label (GTK_LABEL (priv->confirm_explanation), _("The passwords do not match.")); + else + clear_password_validation_error (priv->password_entry); + } + + /* + * We deliberately don’t validate that the parent password and main user + * password are different. It’s more feasible that someone would usefully + * want to set their system up that way, than it is that the parent and child + * would accidentally choose the same password. + */ + + update_page_validation (page); + + return G_SOURCE_REMOVE; +} + +static gboolean +on_focusout (GisPasswordPage *page) +{ + validate (page); + + return FALSE; +} + +static void +password_changed (GtkWidget *w, + GParamSpec *pspec, + GisPasswordPage *page) +{ + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + + clear_password_validation_error (w); + clear_password_validation_error (priv->confirm_entry); + + priv->valid_password = FALSE; + update_page_validation (page); + + if (priv->timeout_id != 0) + g_source_remove (priv->timeout_id); + priv->timeout_id = g_timeout_add (VALIDATION_TIMEOUT, (GSourceFunc)validate, page); +} + +static void +confirm_changed (GtkWidget *w, + GParamSpec *pspec, + GisPasswordPage *page) +{ + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + + clear_password_validation_error (w); + + priv->valid_confirm = FALSE; + update_page_validation (page); + + if (priv->timeout_id != 0) + g_source_remove (priv->timeout_id); + priv->timeout_id = g_timeout_add (VALIDATION_TIMEOUT, (GSourceFunc)validate, page); +} + +static void +username_changed (GObject *obj, GParamSpec *pspec, GisPasswordPage *page) +{ + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + priv->username = gis_driver_get_username (GIS_DRIVER (obj)); + + if (priv->username) + gtk_widget_show (GTK_WIDGET (page)); + else + gtk_widget_hide (GTK_WIDGET (page)); + + clear_password_validation_error (priv->password_entry); + clear_password_validation_error (priv->confirm_entry); + + validate (page); +} + +static void +full_name_or_avatar_changed (GObject *obj, + GParamSpec *pspec, + gpointer user_data) +{ + GisPasswordPage *page = GIS_PASSWORD_PAGE (user_data); + + update_header (page); +} + +static void +confirm (GisPasswordPage *page) +{ + if (page_validate (page)) + gis_assistant_next_page (gis_driver_get_assistant (GIS_PAGE (page)->driver)); +} + +static void +track_focus_out (GisPasswordPage *page, + GtkWidget *widget) +{ + GtkEventController *focus_controller; + + focus_controller = gtk_event_controller_focus_new (); + gtk_widget_add_controller (widget, focus_controller); + + g_signal_connect_swapped (focus_controller, "leave", G_CALLBACK (on_focusout), page); +} + +static void +gis_password_page_constructed (GObject *object) +{ + GisPasswordPage *page = GIS_PASSWORD_PAGE (object); + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + + G_OBJECT_CLASS (gis_password_page_parent_class)->constructed (object); + + g_signal_connect (priv->password_entry, "notify::text", + G_CALLBACK (password_changed), page); + g_signal_connect_swapped (priv->password_entry, "activate", + G_CALLBACK (confirm), page); + track_focus_out (page, priv->password_entry); + + g_signal_connect (priv->confirm_entry, "notify::text", + G_CALLBACK (confirm_changed), page); + g_signal_connect_swapped (priv->confirm_entry, "activate", + G_CALLBACK (confirm), page); + track_focus_out (page, priv->confirm_entry); + + g_signal_connect (GIS_PAGE (page)->driver, "notify::username", + G_CALLBACK (username_changed), page); + g_signal_connect (GIS_PAGE (page)->driver, "notify::full-name", + G_CALLBACK (full_name_or_avatar_changed), page); + g_signal_connect (GIS_PAGE (page)->driver, "notify::avatar", + G_CALLBACK (full_name_or_avatar_changed), page); + + validate (page); + update_header (page); + + gtk_widget_show (GTK_WIDGET (page)); +} + +static void +gis_password_page_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GisPasswordPage *page = GIS_PASSWORD_PAGE (object); + GisPasswordPagePrivate *priv = gis_password_page_get_instance_private (page); + + switch ((GisPasswordPageProperty) prop_id) + { + case PROP_PARENT_MODE: + g_value_set_boolean (value, priv->parent_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gis_password_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GisPasswordPage *page = GIS_PASSWORD_PAGE (object); + + switch ((GisPasswordPageProperty) prop_id) + { + case PROP_PARENT_MODE: + set_parent_mode (page, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gis_password_page_dispose (GObject *object) +{ + if (GIS_PAGE (object)->driver) { + g_signal_handlers_disconnect_by_func (GIS_PAGE (object)->driver, + username_changed, object); + g_signal_handlers_disconnect_by_func (GIS_PAGE (object)->driver, + full_name_or_avatar_changed, object); + } + + G_OBJECT_CLASS (gis_password_page_parent_class)->dispose (object); +} + +static void +gis_password_page_locale_changed (GisPage *page) +{ + gis_page_set_title (GIS_PAGE (page), _("Password")); +} + +static void +gis_password_page_class_init (GisPasswordPageClass *klass) +{ + GisPageClass *page_class = GIS_PAGE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-password-page.ui"); + + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, password_entry); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, confirm_entry); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, password_strength); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, password_explanation); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, confirm_explanation); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisPasswordPage, header); + + page_class->page_id = PAGE_ID; + page_class->locale_changed = gis_password_page_locale_changed; + page_class->save_data = gis_password_page_save_data; + page_class->shown = gis_password_page_shown; + + object_class->constructed = gis_password_page_constructed; + object_class->get_property = gis_password_page_get_property; + object_class->set_property = gis_password_page_set_property; + object_class->dispose = gis_password_page_dispose; + + /** + * GisPasswordPage:parent-mode: + * + * If %FALSE (the default), this page will collect a password for the main + * user account. If %TRUE, it will collect a password for controlling access + * to parental controls — this will affect where the password is stored, and + * the appearance of the page. + * + * Since: 3.36 + */ + obj_props[PROP_PARENT_MODE] = + g_param_spec_boolean ("parent-mode", "Parent Mode", + "Whether to collect a password for the main user account or a parent account.", + FALSE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_props), obj_props); +} + +static void +gis_password_page_init (GisPasswordPage *page) +{ + GtkCssProvider *provider; + + g_resources_register (password_get_resource ()); + g_type_ensure (GIS_TYPE_PAGE_HEADER); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, "/org/gnome/initial-setup/gis-password-page.css"); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_object_unref (provider); + + gtk_widget_init_template (GTK_WIDGET (page)); +} + +GisPage * +gis_prepare_password_page (GisDriver *driver) +{ + return g_object_new (GIS_TYPE_PASSWORD_PAGE, + "driver", driver, + NULL); +} + +GisPage * +gis_prepare_parent_password_page (GisDriver *driver) +{ + /* Skip prompting for the parent password if parental controls aren’t enabled. */ + if (!gis_driver_get_parental_controls_enabled (driver)) + return NULL; + + return g_object_new (GIS_TYPE_PASSWORD_PAGE, + "driver", driver, + "parent-mode", TRUE, + NULL); +} -- cgit v1.2.3