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 --- gnome-initial-setup/gnome-initial-setup.c | 387 ++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 gnome-initial-setup/gnome-initial-setup.c (limited to 'gnome-initial-setup/gnome-initial-setup.c') diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c new file mode 100644 index 0000000..113ded3 --- /dev/null +++ b/gnome-initial-setup/gnome-initial-setup.c @@ -0,0 +1,387 @@ +/* -*- 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 + */ + +#include "config.h" + +#include "gnome-initial-setup.h" + +#include +#include +#include +#include +#include + +#include "pages/welcome/gis-welcome-page.h" +#include "pages/language/gis-language-page.h" +#include "pages/keyboard/gis-keyboard-page.h" +#include "pages/network/gis-network-page.h" +#include "pages/timezone/gis-timezone-page.h" +#include "pages/privacy/gis-privacy-page.h" +#include "pages/software/gis-software-page.h" +#include "pages/goa/gis-goa-page.h" +#include "pages/account/gis-account-pages.h" +#include "pages/parental-controls/gis-parental-controls-page.h" +#include "pages/password/gis-password-page.h" +#include "pages/summary/gis-summary-page.h" + +#define VENDOR_PAGES_GROUP "pages" +#define VENDOR_SKIP_KEY "skip" +#define VENDOR_NEW_USER_ONLY_KEY "new_user_only" +#define VENDOR_EXISTING_USER_ONLY_KEY "existing_user_only" + +static gboolean force_existing_user_mode; + +static GPtrArray *skipped_pages; + +typedef GisPage *(*PreparePage) (GisDriver *driver); + +typedef struct { + const gchar *page_id; + PreparePage prepare_page_func; + gboolean new_user_only; +} PageData; + +#define PAGE(name, new_user_only) { #name, gis_prepare_ ## name ## _page, new_user_only } + +static PageData page_table[] = { + PAGE (welcome, FALSE), + PAGE (language, FALSE), + PAGE (keyboard, FALSE), + PAGE (network, FALSE), + PAGE (privacy, FALSE), + PAGE (timezone, TRUE), + PAGE (software, TRUE), + PAGE (goa, FALSE), + PAGE (account, TRUE), + PAGE (password, TRUE), +#ifdef HAVE_PARENTAL_CONTROLS + PAGE (parental_controls, TRUE), + PAGE (parent_password, TRUE), +#endif + PAGE (summary, FALSE), + { NULL }, +}; + +#undef PAGE + +static gboolean +should_skip_page (const gchar *page_id, + gchar **skip_pages) +{ + guint i = 0; + + /* special case welcome. We only want to show it if language + * is skipped + */ + if (strcmp (page_id, "welcome") == 0) + return !should_skip_page ("language", skip_pages); + + /* check through our skip pages list for pages we don't want */ + if (skip_pages) { + while (skip_pages[i]) { + if (g_strcmp0 (skip_pages[i], page_id) == 0) + return TRUE; + i++; + } + } + + return FALSE; +} + +static gchar ** +strv_append (gchar **a, + gchar **b) +{ + guint n = g_strv_length (a); + guint m = g_strv_length (b); + + a = g_renew (gchar *, a, n + m + 1); + for (guint i = 0; i < m; i++) + a[n + i] = g_strdup (b[i]); + a[n + m] = NULL; + + return a; +} + +static gchar ** +pages_to_skip_from_file (GisDriver *driver, + gboolean is_new_user) +{ + GStrv skip_pages = NULL; + GStrv additional_skip_pages = NULL; + + /* This code will read the keyfile containing vendor customization options and + * look for options under the "pages" group, and supports the following keys: + * - skip (optional): list of pages to be skipped always + * - new_user_only (optional): list of pages to be skipped in existing user mode + * - existing_user_only (optional): list of pages to be skipped in new user mode + * + * This is how this file might look on a vendor image: + * + * [pages] + * skip=timezone + * existing_user_only=language;keyboard + */ + + skip_pages = gis_driver_conf_get_string_list (driver, VENDOR_PAGES_GROUP, + VENDOR_SKIP_KEY, NULL); + additional_skip_pages = + gis_driver_conf_get_string_list (driver, VENDOR_PAGES_GROUP, + is_new_user ? VENDOR_EXISTING_USER_ONLY_KEY : VENDOR_NEW_USER_ONLY_KEY, + NULL); + + if (!skip_pages && additional_skip_pages) { + skip_pages = additional_skip_pages; + } else if (skip_pages && additional_skip_pages) { + skip_pages = strv_append (skip_pages, additional_skip_pages); + g_strfreev (additional_skip_pages); + } + + return skip_pages; +} + +static void +destroy_pages_after (GisAssistant *assistant, + GisPage *page) +{ + GList *pages, *l, *next; + + pages = gis_assistant_get_all_pages (assistant); + + for (l = pages; l != NULL; l = l->next) + if (l->data == page) + break; + + l = l->next; + for (; l != NULL; l = next) { + next = l->next; + gis_assistant_remove_page (assistant, l->data); + } +} + +static void +destroy_page (gpointer data) +{ + GtkWidget *assistant; + GisPage *page; + + page = data; + assistant = gtk_widget_get_ancestor (GTK_WIDGET (page), GIS_TYPE_ASSISTANT); + + if (assistant) + gis_assistant_remove_page (GIS_ASSISTANT (assistant), page); +} + +static void +rebuild_pages_cb (GisDriver *driver) +{ + PageData *page_data; + GisPage *page; + GisAssistant *assistant; + GisPage *current_page; + gchar **skip_pages; + gboolean is_new_user, skipped; + + assistant = gis_driver_get_assistant (driver); + current_page = gis_assistant_get_current_page (assistant); + page_data = page_table; + + g_ptr_array_free (skipped_pages, TRUE); + skipped_pages = g_ptr_array_new_with_free_func (destroy_page); + + if (current_page != NULL) { + destroy_pages_after (assistant, current_page); + + for (page_data = page_table; page_data->page_id != NULL; ++page_data) + if (g_str_equal (page_data->page_id, GIS_PAGE_GET_CLASS (current_page)->page_id)) + break; + + ++page_data; + } + + is_new_user = (gis_driver_get_mode (driver) == GIS_DRIVER_MODE_NEW_USER); + skip_pages = pages_to_skip_from_file (driver, is_new_user); + + for (; page_data->page_id != NULL; ++page_data) { + skipped = FALSE; + + if ((page_data->new_user_only && !is_new_user) || + (should_skip_page (page_data->page_id, skip_pages))) + skipped = TRUE; + + page = page_data->prepare_page_func (driver); + if (!page) + continue; + + if (skipped) { + gis_page_skip (page); + g_ptr_array_add (skipped_pages, page); + } else { + gis_driver_add_page (driver, page); + } + } + + g_strfreev (skip_pages); +} + +static GisDriverMode +get_mode (void) +{ + if (force_existing_user_mode) + return GIS_DRIVER_MODE_EXISTING_USER; + else + return GIS_DRIVER_MODE_NEW_USER; +} + +static gboolean +initial_setup_disabled_by_anaconda (void) +{ + const gchar *file_name = SYSCONFDIR "/sysconfig/anaconda"; + g_autoptr(GError) error = NULL; + g_autoptr(GKeyFile) key_file = g_key_file_new (); + + if (!g_key_file_load_from_file (key_file, file_name, G_KEY_FILE_NONE, &error)) { + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && + !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { + g_warning ("Could not read %s: %s", file_name, error->message); + } + return FALSE; + } + + return g_key_file_get_boolean (key_file, "General", "post_install_tools_disabled", NULL); +} + +int +main (int argc, char *argv[]) +{ + GisDriver *driver; + int status; + GOptionContext *context; + GisDriverMode mode; + + GOptionEntry entries[] = { + { "existing-user", 0, 0, G_OPTION_ARG_NONE, &force_existing_user_mode, + _("Force existing user mode"), NULL }, + { NULL } + }; + + g_unsetenv ("GIO_USE_VFS"); + + /* By default, libadwaita reads settings from the Settings portal, which causes + * the portal to be started, which causes gnome-keyring to be started. This + * interferes with our attempt below to manually start gnome-keyring and set + * the login keyring password to a well-known value, which we overwrite with + * the user's password once they choose one. + */ + g_setenv ("ADW_DISABLE_PORTAL", "1", TRUE); + + context = g_option_context_new (_("— GNOME initial setup")); + g_option_context_add_main_entries (context, entries, NULL); + + g_option_context_parse (context, &argc, &argv, NULL); + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + g_message ("Starting gnome-initial-setup"); + if (gis_get_mock_mode ()) + g_message ("Mock mode: changes will not be saved to disk"); + else + g_message ("Production mode: changes will be saved to disk"); + + skipped_pages = g_ptr_array_new_with_free_func (destroy_page); + mode = get_mode (); + + /* When we are running as the gnome-initial-setup user we + * dont have a normal user session and need to initialize + * the keyring manually so that we can pass the credentials + * along to the new user in the handoff. + */ + if (mode == GIS_DRIVER_MODE_NEW_USER && !gis_get_mock_mode ()) + gis_ensure_login_keyring (); + + driver = gis_driver_new (mode); + adw_style_manager_set_color_scheme (adw_style_manager_get_default (), + ADW_COLOR_SCHEME_PREFER_LIGHT); + + /* On first login, GNOME Shell offers to run a tour. If we also run Initial + * Setup, the two immovable, centred windows will sit atop one another. + * Until we have the ability to run Initial Setup in the "kiosk" mode, like + * it does in new-user mode, disable Initial Setup for existing users. + * + * https://gitlab.gnome.org/GNOME/gnome-initial-setup/-/issues/120#note_1019004 + * https://gitlab.gnome.org/GNOME/gnome-initial-setup/-/issues/12 + */ + if (mode == GIS_DRIVER_MODE_EXISTING_USER) { + g_message ("Skipping gnome-initial-setup for existing user"); + gis_ensure_stamp_files (driver); + exit (EXIT_SUCCESS); + } + + /* We only do this in existing-user mode, because if gdm launches us + * in new-user mode and we just exit, gdm's special g-i-s session + * never terminates. */ + if (initial_setup_disabled_by_anaconda () && + mode == GIS_DRIVER_MODE_EXISTING_USER) { + gis_ensure_stamp_files (driver); + exit (EXIT_SUCCESS); + } + + g_signal_connect (driver, "rebuild-pages", G_CALLBACK (rebuild_pages_cb), NULL); + status = g_application_run (G_APPLICATION (driver), argc, argv); + + g_ptr_array_free (skipped_pages, TRUE); + + g_object_unref (driver); + g_option_context_free (context); + return status; +} + +void +gis_ensure_stamp_files (GisDriver *driver) +{ + g_autofree gchar *done_file = NULL; + g_autoptr(GError) error = NULL; + + done_file = g_build_filename (g_get_user_config_dir (), "gnome-initial-setup-done", NULL); + if (!g_file_set_contents (done_file, "yes", -1, &error)) { + g_warning ("Unable to create %s: %s", done_file, error->message); + g_clear_error (&error); + } +} + +/** + * gis_get_mock_mode: + * + * Gets whether gnome-initial-setup has been built for development, and hence + * shouldn’t permanently change any system configuration. + * + * By default, mock mode is enabled when running in a build environment. This + * heuristic may be changed in future. + * + * Returns: %TRUE if in mock mode, %FALSE otherwise + */ +gboolean +gis_get_mock_mode (void) +{ + return (g_getenv ("UNDER_JHBUILD") != NULL); +} -- cgit v1.2.3