/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 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 "gis-account-page-enterprise.h" #include "gnome-initial-setup.h" #include #include #include #include "um-realm-manager.h" #include "um-utils.h" #include "gis-page-header.h" static void join_show_prompt (GisAccountPageEnterprise *page, GError *error); static void on_join_login (GObject *source, GAsyncResult *result, gpointer user_data); static void on_realm_joined (GObject *source, GAsyncResult *result, gpointer user_data); struct _GisAccountPageEnterprise { AdwBin parent; GtkWidget *header; GtkWidget *login; GtkWidget *password; GtkWidget *domain; GtkWidget *domain_entry; GtkTreeModel *realms_model; GtkWidget *join_dialog; GtkWidget *join_name; GtkWidget *join_password; GtkWidget *join_domain; GtkWidget *join_computer; ActUserManager *act_client; ActUser *act_user; guint realmd_watch; UmRealmManager *realm_manager; gboolean domain_chosen; /* Valid during apply */ UmRealmObject *realm; GCancellable *cancellable; gboolean join_prompted; GisPageApplyCallback apply_complete_callback; gpointer apply_complete_data; }; G_DEFINE_TYPE (GisAccountPageEnterprise, gis_account_page_enterprise, ADW_TYPE_BIN); enum { VALIDATION_CHANGED, USER_CACHED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; static void clear_password_validation_error (GtkWidget *entry) { gtk_widget_remove_css_class (entry, "error"); gtk_widget_set_tooltip_text (entry, NULL); } static void set_password_validation_error (GtkWidget *entry, const gchar *text) { gtk_widget_add_css_class (entry, "error"); gtk_widget_set_tooltip_text (entry, text); } static void validation_changed (GisAccountPageEnterprise *page) { g_signal_emit (page, signals[VALIDATION_CHANGED], 0); } static void apply_complete (GisAccountPageEnterprise *page, gboolean valid) { page->apply_complete_callback (NULL, valid, page->apply_complete_data); } static void show_error_dialog (GisAccountPageEnterprise *page, const gchar *message, GError *error) { GtkWidget *dialog; if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (page))), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message); if (error != NULL) { g_dbus_error_strip_remote_error (error); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message); } g_signal_connect (dialog, "response", G_CALLBACK (gtk_window_destroy), NULL); gtk_window_present (GTK_WINDOW (dialog)); } gboolean gis_account_page_enterprise_validate (GisAccountPageEnterprise *page) { const gchar *name; gboolean valid_name; gboolean valid_domain; GtkTreeIter iter; name = gtk_editable_get_text (GTK_EDITABLE (page->login)); valid_name = is_valid_name (name); if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (page->domain), &iter)) { gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (page->domain)), &iter, 0, &name, -1); } else { name = gtk_editable_get_text (GTK_EDITABLE (page->domain_entry)); } valid_domain = is_valid_name (name); return valid_name && valid_domain; } static void on_permit_user_login (GObject *source, GAsyncResult *result, gpointer user_data) { GisAccountPageEnterprise *page = user_data; UmRealmCommon *common; GError *error = NULL; gchar *login; common = UM_REALM_COMMON (source); um_realm_common_call_change_login_policy_finish (common, result, &error); if (error == NULL) { /* * Now tell the account service about this user. The account service * should also lookup information about this via the realm and make * sure all that is functional. */ login = um_realm_calculate_login (common, gtk_editable_get_text (GTK_EDITABLE (page->login))); g_return_if_fail (login != NULL); g_debug ("Caching remote user: %s", login); page->act_user = act_user_manager_cache_user (page->act_client, login, NULL); act_user_set_account_type (page->act_user, ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR); g_signal_emit (page, signals[USER_CACHED], 0, page->act_user, gtk_editable_get_text (GTK_EDITABLE (page->password))); apply_complete (page, TRUE); g_free (login); } else { show_error_dialog (page, _("Failed to register account"), error); g_message ("Couldn't permit logins on account: %s", error->message); g_error_free (error); apply_complete (page, FALSE); } } static void enterprise_permit_user_login (GisAccountPageEnterprise *page, UmRealmObject *realm) { UmRealmCommon *common; gchar *login; const gchar *add[2]; const gchar *remove[1]; GVariant *options; common = um_realm_object_get_common (realm); login = um_realm_calculate_login (common, gtk_editable_get_text (GTK_EDITABLE (page->login))); g_return_if_fail (login != NULL); add[0] = login; add[1] = NULL; remove[0] = NULL; g_debug ("Permitting login for: %s", login); options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0); um_realm_common_call_change_login_policy (common, "", add, remove, options, page->cancellable, on_permit_user_login, page); g_object_unref (common); g_free (login); } static void on_set_static_hostname (GObject *source, GAsyncResult *result, gpointer user_data) { GisAccountPageEnterprise *page = user_data; GError *error = NULL; GVariant *retval; retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); if (error != NULL) { join_show_prompt (page, error); g_error_free (error); return; } g_variant_unref (retval); /* Prompted for some admin credentials, try to use them to log in */ um_realm_login (page->realm, gtk_editable_get_text (GTK_EDITABLE (page->join_name)), gtk_editable_get_text (GTK_EDITABLE (page->join_password)), page->cancellable, on_join_login, page); } static void on_join_response (GtkDialog *dialog, gint response, gpointer user_data) { GisAccountPageEnterprise *page = user_data; GDBusConnection *connection; GError *error = NULL; gchar hostname[128]; const gchar *name; gtk_widget_hide (GTK_WIDGET (dialog)); if (response != GTK_RESPONSE_OK) { apply_complete (page, FALSE); return; } name = gtk_editable_get_text (GTK_EDITABLE (page->join_computer)); if (gethostname (hostname, sizeof (hostname)) == 0 && !g_str_equal (name, hostname)) { g_debug ("Setting StaticHostname to '%s'", name); connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, page->cancellable, &error); if (error != NULL) { apply_complete (page, FALSE); g_warning ("Could not get DBus connection: %s", error->message); g_error_free (error); return; } g_dbus_connection_call (connection, "org.freedesktop.hostname1", "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "SetStaticHostname", g_variant_new ("(sb)", name, TRUE), G_VARIANT_TYPE ("()"), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, on_set_static_hostname, page); } else { name = gtk_editable_get_text (GTK_EDITABLE (page->join_name)); g_debug ("Logging in as admin user: %s", name); /* Prompted for some admin credentials, try to use them to log in */ um_realm_login (page->realm, name, gtk_editable_get_text (GTK_EDITABLE (page->join_password)), NULL, on_join_login, page); } } static void join_show_prompt (GisAccountPageEnterprise *page, GError *error) { UmRealmKerberosMembership *membership; UmRealmKerberos *kerberos; gchar hostname[128]; const gchar *name; gtk_editable_set_text (GTK_EDITABLE (page->join_password), ""); gtk_widget_grab_focus (GTK_WIDGET (page->join_password)); kerberos = um_realm_object_get_kerberos (page->realm); membership = um_realm_object_get_kerberos_membership (page->realm); gtk_label_set_text (GTK_LABEL (page->join_domain), um_realm_kerberos_get_domain_name (kerberos)); if (gethostname (hostname, sizeof (hostname)) == 0) gtk_editable_set_text (GTK_EDITABLE (page->join_computer), hostname); clear_entry_validation_error (GTK_ENTRY (page->join_name)); clear_password_validation_error (page->join_password); if (!page->join_prompted) { name = um_realm_kerberos_membership_get_suggested_administrator (membership); if (name && !g_str_equal (name, "")) { g_debug ("Suggesting admin user: %s", name); gtk_editable_set_text (GTK_EDITABLE (page->join_name), name); } else { gtk_widget_grab_focus (GTK_WIDGET (page->join_name)); } } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_HOSTNAME)) { g_debug ("Bad host name: %s", error->message); set_entry_validation_error (GTK_ENTRY (page->join_computer), error->message); } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) { g_debug ("Bad admin password: %s", error->message); set_password_validation_error (page->join_password, error->message); } else { g_debug ("Admin login failure: %s", error->message); g_dbus_error_strip_remote_error (error); set_entry_validation_error (GTK_ENTRY (page->join_name), error->message); } g_debug ("Showing admin password dialog"); gtk_window_set_transient_for (GTK_WINDOW (page->join_dialog), GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (page)))); gtk_window_set_modal (GTK_WINDOW (page->join_dialog), TRUE); gtk_window_present (GTK_WINDOW (page->join_dialog)); page->join_prompted = TRUE; g_object_unref (kerberos); g_object_unref (membership); /* And now we wait for on_join_response() */ } static void on_join_login (GObject *source, GAsyncResult *result, gpointer user_data) { GisAccountPageEnterprise *page = user_data; GError *error = NULL; GBytes *creds; um_realm_login_finish (page->realm, result, &creds, &error); /* Logged in as admin successfully, use creds to join domain */ if (error == NULL) { if (!um_realm_join_as_admin (page->realm, gtk_editable_get_text (GTK_EDITABLE (page->join_name)), gtk_editable_get_text (GTK_EDITABLE (page->join_password)), creds, NULL, on_realm_joined, page)) { show_error_dialog (page, _("No supported way to authenticate with this domain"), NULL); g_message ("Authenticating as admin is not supported by the realm"); } g_bytes_unref (creds); /* Couldn't login as admin, show prompt again */ } else { join_show_prompt (page, error); g_message ("Couldn't log in as admin to join domain: %s", error->message); g_error_free (error); } } static void on_realm_joined (GObject *source, GAsyncResult *result, gpointer user_data) { GisAccountPageEnterprise *page = user_data; GError *error = NULL; um_realm_join_finish (page->realm, result, &error); /* Yay, joined the domain, register the user locally */ if (error == NULL) { g_debug ("Joining realm completed successfully"); enterprise_permit_user_login (page, page->realm); /* Credential failure while joining domain, prompt for admin creds */ } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN) || g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD) || g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_HOSTNAME)) { g_debug ("Joining realm failed due to credentials or host name"); join_show_prompt (page, error); /* Other failure */ } else { show_error_dialog (page, _("Failed to join domain"), error); g_message ("Failed to join the domain: %s", error->message); apply_complete (page, FALSE); } g_clear_error (&error); } static void on_realm_login (GObject *source, GAsyncResult *result, gpointer user_data) { GisAccountPageEnterprise *page = user_data; GError *error = NULL; GBytes *creds = NULL; um_realm_login_finish (page->realm, result, &creds, &error); /* * User login is valid, but cannot authenticate right now (eg: user needs * to change password at next login etc.) */ if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_CANNOT_AUTH)) { g_clear_error (&error); creds = NULL; } if (error == NULL) { /* Already joined to the domain, just register this user */ if (um_realm_is_configured (page->realm)) { g_debug ("Already joined to this realm"); enterprise_permit_user_login (page, page->realm); /* Join the domain, try using the user's creds */ } else if (creds == NULL || !um_realm_join_as_user (page->realm, gtk_editable_get_text (GTK_EDITABLE (page->login)), gtk_editable_get_text (GTK_EDITABLE (page->password)), creds, page->cancellable, on_realm_joined, page)) { /* If we can't do user auth, try to authenticate as admin */ g_debug ("Cannot join with user credentials"); join_show_prompt (page, error); } g_bytes_unref (creds); /* A problem with the user's login name or password */ } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN)) { g_debug ("Problem with the user's login: %s", error->message); set_entry_validation_error (GTK_ENTRY (page->login), error->message); gtk_widget_grab_focus (page->login); apply_complete (page, FALSE); } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) { g_debug ("Problem with the user's password: %s", error->message); set_password_validation_error (page->password, error->message); gtk_widget_grab_focus (page->password); apply_complete (page, FALSE); /* Other login failure */ } else { show_error_dialog (page, _("Failed to log into domain"), error); g_message ("Couldn't log in as user: %s", error->message); apply_complete (page, FALSE); } g_clear_error (&error); } static void enterprise_check_login (GisAccountPageEnterprise *page) { g_assert (page->realm); um_realm_login (page->realm, gtk_editable_get_text (GTK_EDITABLE (page->login)), gtk_editable_get_text (GTK_EDITABLE (page->password)), page->cancellable, on_realm_login, page); } static void on_realm_discover_input (GObject *source, GAsyncResult *result, gpointer user_data) { GisAccountPageEnterprise *page = user_data; GError *error = NULL; GList *realms; realms = um_realm_manager_discover_finish (page->realm_manager, result, &error); /* Found a realm, log user into domain */ if (error == NULL) { g_assert (realms != NULL); page->realm = g_object_ref (realms->data); enterprise_check_login (page); g_list_free_full (realms, g_object_unref); } else { /* The domain is likely invalid */ g_dbus_error_strip_remote_error (error); g_message ("Couldn't discover domain: %s", error->message); gtk_widget_grab_focus (page->domain_entry); set_entry_validation_error (GTK_ENTRY (page->domain_entry), error->message); apply_complete (page, FALSE); g_error_free (error); } } static void enterprise_add_user (GisAccountPageEnterprise *page) { GtkTreeIter iter; page->join_prompted = FALSE; g_clear_object (&page->realm); /* Already know about this realm, try to login as user */ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (page->domain), &iter)) { gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (page->domain)), &iter, 1, &page->realm, -1); enterprise_check_login (page); /* Something the user typed, we need to discover realm */ } else { um_realm_manager_discover (page->realm_manager, gtk_editable_get_text (GTK_EDITABLE (page->domain_entry)), page->cancellable, on_realm_discover_input, page); } } gboolean gis_account_page_enterprise_apply (GisAccountPageEnterprise *page, GCancellable *cancellable, GisPageApplyCallback callback, gpointer data) { GisPage *account_page = GIS_PAGE (data); /* Parental controls are not enterprise ready. It’s possible for them to have * been enabled if the user enabled them, applied the account-local page, and * then went back and decided to go all enterprise instead. */ gis_driver_set_parental_controls_enabled (account_page->driver, FALSE); page->apply_complete_callback = callback; page->apply_complete_data = data; page->cancellable = g_object_ref (cancellable); enterprise_add_user (page); return TRUE; } static gchar * realm_get_name (UmRealmObject *realm) { UmRealmCommon *common; gchar *name; common = um_realm_object_get_common (realm); name = g_strdup (um_realm_common_get_name (common)); g_object_unref (common); return name; } static gboolean model_contains_realm (GtkTreeModel *model, const gchar *realm_name) { gboolean contains = FALSE; GtkTreeIter iter; gboolean match; gchar *name; gboolean ret; ret = gtk_tree_model_get_iter_first (model, &iter); while (ret) { gtk_tree_model_get (model, &iter, 0, &name, -1); match = (g_strcmp0 (name, realm_name) == 0); g_free (name); if (match) { g_debug ("ignoring duplicate realm: %s", realm_name); contains = TRUE; break; } ret = gtk_tree_model_iter_next (model, &iter); } return contains; } static void enterprise_add_realm (GisAccountPageEnterprise *page, UmRealmObject *realm) { GtkTreeIter iter; gchar *name; name = realm_get_name (realm); /* * Don't add a second realm if we already have one with this name. * Sometimes realmd returns two realms for the same name, if it has * different ways to use that realm. The first one that realmd * returns is the one it prefers. */ if (!model_contains_realm (GTK_TREE_MODEL (page->realms_model), name)) { gtk_list_store_append (GTK_LIST_STORE (page->realms_model), &iter); gtk_list_store_set (GTK_LIST_STORE (page->realms_model), &iter, 0, name, 1, realm, -1); if (!page->domain_chosen && um_realm_is_configured (realm)) gtk_combo_box_set_active_iter (GTK_COMBO_BOX (page->domain), &iter); g_debug ("added realm to drop down: %s %s", name, g_dbus_object_get_object_path (G_DBUS_OBJECT (realm))); } g_free (name); } static void on_manager_realm_added (UmRealmManager *manager, UmRealmObject *realm, gpointer user_data) { GisAccountPageEnterprise *page = user_data; enterprise_add_realm (page, realm); } static void on_realm_manager_created (GObject *source, GAsyncResult *result, gpointer user_data) { GisAccountPageEnterprise *page = user_data; GError *error = NULL; GList *realms, *l; g_clear_object (&page->realm_manager); page->realm_manager = um_realm_manager_new_finish (result, &error); if (error != NULL) { g_warning ("Couldn't contact realmd service: %s", error->message); g_error_free (error); return; } /* Lookup all the realm objects */ realms = um_realm_manager_get_realms (page->realm_manager); for (l = realms; l != NULL; l = g_list_next (l)) enterprise_add_realm (page, l->data); g_list_free (realms); g_signal_connect (page->realm_manager, "realm-added", G_CALLBACK (on_manager_realm_added), page); /* When no realms try to discover a sensible default, triggers realm-added signal */ um_realm_manager_discover (page->realm_manager, "", NULL, NULL, NULL); gtk_widget_set_visible (GTK_WIDGET (page), TRUE); } static void on_realmd_appeared (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { GisAccountPageEnterprise *page = user_data; um_realm_manager_new (NULL, on_realm_manager_created, page); } static void on_realmd_disappeared (GDBusConnection *unused1, const gchar *unused2, gpointer user_data) { GisAccountPageEnterprise *page = user_data; if (page->realm_manager != NULL) { g_signal_handlers_disconnect_by_func (page->realm_manager, on_manager_realm_added, page); g_clear_object (&page->realm_manager); } gtk_widget_set_visible (GTK_WIDGET (page), FALSE); } static void on_domain_changed (GtkComboBox *widget, gpointer user_data) { GisAccountPageEnterprise *page = user_data; page->domain_chosen = TRUE; validation_changed (page); clear_entry_validation_error (GTK_ENTRY (gtk_combo_box_get_child (widget))); } static void on_entry_changed (GtkEditable *editable, gpointer user_data) { GisAccountPageEnterprise *page = user_data; validation_changed (page); clear_entry_validation_error (GTK_ENTRY (editable)); } static void on_password_changed (GtkEditable *editable, gpointer user_data) { clear_password_validation_error (GTK_WIDGET (editable)); } static void gis_account_page_enterprise_realize (GtkWidget *widget) { GisAccountPageEnterprise *page = GIS_ACCOUNT_PAGE_ENTERPRISE (widget); GtkWidget *gis_page; gis_page = gtk_widget_get_ancestor (widget, GIS_TYPE_PAGE); g_object_bind_property (gis_page, "small-screen", page->header, "show-icon", G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); GTK_WIDGET_CLASS (gis_account_page_enterprise_parent_class)->realize (widget); } static void gis_account_page_enterprise_constructed (GObject *object) { GisAccountPageEnterprise *page = GIS_ACCOUNT_PAGE_ENTERPRISE (object); G_OBJECT_CLASS (gis_account_page_enterprise_parent_class)->constructed (object); page->act_client = act_user_manager_get_default (); page->realmd_watch = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "org.freedesktop.realmd", G_BUS_NAME_WATCHER_FLAGS_AUTO_START, on_realmd_appeared, on_realmd_disappeared, page, NULL); g_signal_connect (page->join_dialog, "response", G_CALLBACK (on_join_response), page); g_signal_connect (page->domain, "changed", G_CALLBACK (on_domain_changed), page); g_signal_connect (page->login, "changed", G_CALLBACK (on_entry_changed), page); g_signal_connect (page->password, "changed", G_CALLBACK (on_password_changed), page); g_signal_connect (page->join_password, "changed", G_CALLBACK (on_password_changed), page); } static void gis_account_page_enterprise_dispose (GObject *object) { GisAccountPageEnterprise *page = GIS_ACCOUNT_PAGE_ENTERPRISE (object); if (page->realmd_watch) g_bus_unwatch_name (page->realmd_watch); page->realmd_watch = 0; g_cancellable_cancel (page->cancellable); g_clear_object (&page->realm_manager); g_clear_object (&page->realm); g_clear_object (&page->cancellable); G_OBJECT_CLASS (gis_account_page_enterprise_parent_class)->dispose (object); } static void gis_account_page_enterprise_class_init (GisAccountPageEnterpriseClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->constructed = gis_account_page_enterprise_constructed; object_class->dispose = gis_account_page_enterprise_dispose; widget_class->realize = gis_account_page_enterprise_realize; gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-account-page-enterprise.ui"); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, login); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, password); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, domain); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, realms_model); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, header); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_dialog); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_name); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_password); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_domain); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAccountPageEnterprise, join_computer); signals[VALIDATION_CHANGED] = g_signal_new ("validation-changed", GIS_TYPE_ACCOUNT_PAGE_ENTERPRISE, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[USER_CACHED] = g_signal_new ("user-cached", GIS_TYPE_ACCOUNT_PAGE_ENTERPRISE, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, ACT_TYPE_USER, G_TYPE_STRING); } static void gis_account_page_enterprise_init (GisAccountPageEnterprise *page) { g_type_ensure (GIS_TYPE_PAGE_HEADER); gtk_widget_init_template (GTK_WIDGET (page)); page->domain_entry = gtk_combo_box_get_child (GTK_COMBO_BOX (page->domain)); } void gis_account_page_enterprise_shown (GisAccountPageEnterprise *page) { gtk_widget_grab_focus (page->domain_entry); }