/*
* Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
*/
#include "evolution-data-server-config.h"
#include
#include
#include
#include
#include "libedataserverui-private.h"
#include "e-credentials-prompter.h"
#include "e-credentials-prompter-impl-password.h"
struct _ECredentialsPrompterImplPasswordPrivate {
gpointer prompt_id;
ESource *auth_source;
ESource *cred_source;
gchar *error_text;
ENamedParameters *credentials;
GtkDialog *dialog;
gulong show_dialog_idle_id;
};
G_DEFINE_TYPE_WITH_PRIVATE (ECredentialsPrompterImplPassword, e_credentials_prompter_impl_password, E_TYPE_CREDENTIALS_PROMPTER_IMPL)
#if !GTK_CHECK_VERSION(4, 0, 0)
static gboolean
password_dialog_map_event_cb (GtkWidget *dialog,
GdkEvent *event,
GtkWidget *entry)
{
gtk_widget_grab_focus (entry);
return FALSE;
}
#endif
static void
credentials_prompter_impl_password_get_prompt_strings (ESourceRegistry *registry,
ESource *source,
gchar **prompt_title,
GString **prompt_description)
{
GString *description;
const gchar *message;
gchar *display_name;
gchar *host_name = NULL, *tmp;
/* Known types */
enum {
TYPE_UNKNOWN,
TYPE_AMBIGUOUS,
TYPE_ADDRESS_BOOK,
TYPE_CALENDAR,
TYPE_MAIL_ACCOUNT,
TYPE_MAIL_TRANSPORT,
TYPE_MEMO_LIST,
TYPE_TASK_LIST
} type = TYPE_UNKNOWN;
/* XXX This is kind of a hack but it should work for now. Build a
* suitable password prompt by checking for various extensions
* in the ESource. If no recognizable extensions are found, or
* if the result is ambiguous, just refer to the data source as
* an "account". */
display_name = e_util_get_source_full_name (registry, source);
if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
ESourceAuthentication *extension;
extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
host_name = e_source_authentication_dup_host (extension);
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
type = TYPE_ADDRESS_BOOK;
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
if (type == TYPE_UNKNOWN)
type = TYPE_CALENDAR;
else
type = TYPE_AMBIGUOUS;
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
if (type == TYPE_UNKNOWN)
type = TYPE_MAIL_ACCOUNT;
else
type = TYPE_AMBIGUOUS;
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
if (type == TYPE_UNKNOWN)
type = TYPE_MAIL_TRANSPORT;
else
type = TYPE_AMBIGUOUS;
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
if (type == TYPE_UNKNOWN)
type = TYPE_MEMO_LIST;
else
type = TYPE_AMBIGUOUS;
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
if (type == TYPE_UNKNOWN)
type = TYPE_TASK_LIST;
else
type = TYPE_AMBIGUOUS;
}
switch (type) {
case TYPE_ADDRESS_BOOK:
message = _("Address book authentication request");
break;
case TYPE_CALENDAR:
case TYPE_MEMO_LIST:
case TYPE_TASK_LIST:
message = _("Calendar authentication request");
break;
case TYPE_MAIL_ACCOUNT:
case TYPE_MAIL_TRANSPORT:
message = _("Mail authentication request");
break;
default: /* generic account prompt */
message = _("Authentication request");
break;
}
description = g_string_sized_new (256);
switch (type) {
case TYPE_ADDRESS_BOOK:
g_string_append_printf (description,
_("Please enter the password for address book “%s”."), display_name);
break;
case TYPE_CALENDAR:
g_string_append_printf (description,
_("Please enter the password for calendar “%s”."), display_name);
break;
case TYPE_MAIL_ACCOUNT:
g_string_append_printf (description,
_("Please enter the password for mail account “%s”."), display_name);
break;
case TYPE_MAIL_TRANSPORT:
g_string_append_printf (description,
_("Please enter the password for mail transport “%s”."), display_name);
break;
case TYPE_MEMO_LIST:
g_string_append_printf (description,
_("Please enter the password for memo list “%s”."), display_name);
break;
case TYPE_TASK_LIST:
g_string_append_printf (description,
_("Please enter the password for task list “%s”."), display_name);
break;
default: /* generic account prompt */
g_string_append_printf (description,
_("Please enter the password for account “%s”."), display_name);
break;
}
if (host_name != NULL) {
/* Translators: This is part of a credential prompt, constructing for example: "Please enter the password for account “%s”.\n(host: hostname)" */
g_string_append_printf (description, _("\n(host: %s)"), host_name);
}
tmp = g_markup_escape_text (description->str, -1);
g_string_assign (description, "");
g_string_append_printf (description, "%s\n\n%s", message, tmp);
g_free (tmp);
*prompt_title = g_strdup (message);
*prompt_description = description;
g_free (display_name);
g_free (host_name);
}
static gboolean
e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPassword *prompter_password)
{
GtkWidget *dialog, *content_area, *widget;
GtkGrid *grid;
GtkEntry *username_entry = NULL;
GtkEntry *password_entry;
#if GTK_CHECK_VERSION(4, 0, 0)
GtkCheckButton *remember_check = NULL;
#else
GtkToggleButton *remember_toggle = NULL;
#endif
GtkWindow *dialog_parent;
ECredentialsPrompter *prompter;
gchar *title;
GString *info_markup;
gint row = 0;
ESourceAuthentication *auth_extension = NULL;
gboolean success, is_scratch_source = TRUE;
g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_password), FALSE);
g_return_val_if_fail (prompter_password->priv->prompt_id != NULL, FALSE);
g_return_val_if_fail (prompter_password->priv->dialog == NULL, FALSE);
prompter = e_credentials_prompter_impl_get_credentials_prompter (E_CREDENTIALS_PROMPTER_IMPL (prompter_password));
g_return_val_if_fail (prompter != NULL, FALSE);
dialog_parent = e_credentials_prompter_get_dialog_parent_full (prompter, prompter_password->priv->auth_source);
credentials_prompter_impl_password_get_prompt_strings (
e_credentials_prompter_get_registry (prompter),
prompter_password->priv->auth_source, &title, &info_markup);
if (prompter_password->priv->error_text && *prompter_password->priv->error_text) {
gchar *escaped = g_markup_printf_escaped ("%s", prompter_password->priv->error_text);
g_string_append_printf (info_markup, "\n\n%s", escaped);
g_free (escaped);
}
dialog = gtk_dialog_new_with_buttons (title, dialog_parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK,
NULL);
prompter_password->priv->dialog = GTK_DIALOG (dialog);
gtk_dialog_set_default_response (prompter_password->priv->dialog, GTK_RESPONSE_OK);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
if (dialog_parent)
gtk_window_set_transient_for (GTK_WINDOW (dialog), dialog_parent);
content_area = gtk_dialog_get_content_area (prompter_password->priv->dialog);
g_object_set (G_OBJECT (content_area),
"margin-start", 12,
"margin-end", 12,
"margin-top", 12,
"margin-bottom", 12,
NULL);
#if !GTK_CHECK_VERSION(4, 0, 0)
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
#endif
/* Override GtkDialog defaults */
gtk_box_set_spacing (GTK_BOX (content_area), 12);
grid = GTK_GRID (gtk_grid_new ());
gtk_grid_set_column_spacing (grid, 12);
gtk_grid_set_row_spacing (grid, 6);
_libedataserverui_box_pack_start (GTK_BOX (content_area), GTK_WIDGET (grid), FALSE, TRUE, 0);
/* Password Image */
#if GTK_CHECK_VERSION(4, 0, 0)
widget = gtk_image_new_from_icon_name ("dialog-password");
gtk_image_set_pixel_size (GTK_IMAGE (widget), 48);
#else
widget = gtk_image_new_from_icon_name ("dialog-password", GTK_ICON_SIZE_DIALOG);
#endif
g_object_set (
G_OBJECT (widget),
"halign", GTK_ALIGN_START,
"vexpand", TRUE,
"valign", GTK_ALIGN_START,
NULL);
gtk_grid_attach (grid, widget, 0, row, 1, 1);
/* Password Label */
widget = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (widget), info_markup->str);
g_object_set (
G_OBJECT (widget),
"hexpand", TRUE,
"halign", GTK_ALIGN_FILL,
"valign", GTK_ALIGN_CENTER,
"width-chars", 60,
"max-width-chars", 80,
"xalign", 0.0,
"wrap", TRUE,
NULL);
gtk_grid_attach (grid, widget, 1, row, 1, 1);
row++;
if (e_source_has_extension (prompter_password->priv->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
GDBusObject *dbus_object;
dbus_object = e_source_ref_dbus_object (prompter_password->priv->cred_source);
is_scratch_source = !dbus_object;
g_clear_object (&dbus_object);
auth_extension = e_source_get_extension (prompter_password->priv->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION);
if (is_scratch_source || e_source_get_writable (prompter_password->priv->cred_source)) {
gchar *username;
username = e_source_authentication_dup_user (auth_extension);
if ((!username || !*username) &&
e_source_has_extension (prompter_password->priv->cred_source, E_SOURCE_EXTENSION_COLLECTION)) {
ESourceCollection *collection_extension;
gchar *tmp;
collection_extension = e_source_get_extension (prompter_password->priv->cred_source, E_SOURCE_EXTENSION_COLLECTION);
tmp = e_source_collection_dup_identity (collection_extension);
if (tmp && *tmp) {
g_free (username);
username = tmp;
tmp = NULL;
}
g_free (tmp);
}
username_entry = GTK_ENTRY (gtk_entry_new ());
g_object_set (
G_OBJECT (username_entry),
"hexpand", TRUE,
"halign", GTK_ALIGN_FILL,
NULL);
gtk_grid_attach (grid, GTK_WIDGET (username_entry), 1, row, 1, 1);
row++;
if (username && *username) {
_libedataserverui_entry_set_text (username_entry, username);
}
g_free (username);
}
}
password_entry = GTK_ENTRY (gtk_entry_new ());
gtk_entry_set_visibility (password_entry, FALSE);
gtk_entry_set_activates_default (password_entry, TRUE);
g_object_set (
G_OBJECT (password_entry),
"hexpand", TRUE,
"halign", GTK_ALIGN_FILL,
"truncate-multiline", TRUE,
NULL);
if (e_named_parameters_get (prompter_password->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
_libedataserverui_entry_set_text (password_entry, e_named_parameters_get (prompter_password->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
widget = GTK_WIDGET ((username_entry && g_strcmp0 (_libedataserverui_entry_get_text (GTK_ENTRY (username_entry)), "") == 0) ? username_entry : password_entry);
#if GTK_CHECK_VERSION(4, 0, 0)
g_signal_connect_swapped (dialog, "map", G_CALLBACK (gtk_widget_grab_focus), widget);
#else
g_signal_connect (dialog, "map-event", G_CALLBACK (password_dialog_map_event_cb), widget);
#endif
gtk_grid_attach (grid, GTK_WIDGET (password_entry), 1, row, 1, 1);
row++;
if (username_entry && password_entry) {
widget = gtk_label_new_with_mnemonic (_("_User Name:"));
g_object_set (
G_OBJECT (widget),
"hexpand", FALSE,
"vexpand", FALSE,
"halign", GTK_ALIGN_END,
"valign", GTK_ALIGN_CENTER,
NULL);
gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (username_entry));
gtk_grid_attach (grid, widget, 0, row - 2, 1, 1);
widget = gtk_label_new_with_mnemonic (_("_Password:"));
g_object_set (
G_OBJECT (widget),
"hexpand", FALSE,
"vexpand", FALSE,
"halign", GTK_ALIGN_END,
"valign", GTK_ALIGN_CENTER,
NULL);
gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (password_entry));
gtk_grid_attach (grid, widget, 0, row - 1, 1, 1);
}
if (auth_extension && !is_scratch_source) {
/* Remember password check */
widget = gtk_check_button_new_with_mnemonic (_("_Add this password to your keyring"));
#if GTK_CHECK_VERSION(4, 0, 0)
remember_check = GTK_CHECK_BUTTON (widget);
gtk_check_button_set_active (remember_check, e_source_authentication_get_remember_password (auth_extension));
#else
remember_toggle = GTK_TOGGLE_BUTTON (widget);
gtk_toggle_button_set_active (remember_toggle, e_source_authentication_get_remember_password (auth_extension));
#endif
g_object_set (
G_OBJECT (widget),
"hexpand", TRUE,
"halign", GTK_ALIGN_FILL,
"valign", GTK_ALIGN_FILL,
"margin-top", 12,
NULL);
gtk_grid_attach (grid, widget, 1, row, 1, 1);
}
#if !GTK_CHECK_VERSION(4, 0, 0)
gtk_widget_show_all (GTK_WIDGET (grid));
#endif
success = _libedataserverui_dialog_run (prompter_password->priv->dialog) == GTK_RESPONSE_OK;
if (success) {
if (username_entry)
e_named_parameters_set (prompter_password->priv->credentials,
E_SOURCE_CREDENTIAL_USERNAME, _libedataserverui_entry_get_text (username_entry));
e_named_parameters_set (prompter_password->priv->credentials,
E_SOURCE_CREDENTIAL_PASSWORD, _libedataserverui_entry_get_text (password_entry));
#if GTK_CHECK_VERSION(4, 0, 0)
if (auth_extension && remember_check) {
e_source_authentication_set_remember_password (auth_extension,
gtk_check_button_get_active (remember_check));
}
#else
if (auth_extension && remember_toggle) {
e_source_authentication_set_remember_password (auth_extension,
gtk_toggle_button_get_active (remember_toggle));
}
#endif
}
#if GTK_CHECK_VERSION(4, 0, 0)
gtk_window_destroy (GTK_WINDOW (dialog));
#else
gtk_widget_destroy (dialog);
#endif
prompter_password->priv->dialog = NULL;
g_string_free (info_markup, TRUE);
g_free (title);
return success;
}
static void
e_credentials_prompter_impl_password_free_prompt_data (ECredentialsPrompterImplPassword *prompter_password)
{
g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_password));
prompter_password->priv->prompt_id = NULL;
g_clear_object (&prompter_password->priv->auth_source);
g_clear_object (&prompter_password->priv->cred_source);
g_free (prompter_password->priv->error_text);
prompter_password->priv->error_text = NULL;
e_named_parameters_free (prompter_password->priv->credentials);
prompter_password->priv->credentials = NULL;
}
static gboolean
e_credentials_prompter_impl_password_show_dialog_idle_cb (gpointer user_data)
{
ECredentialsPrompterImplPassword *prompter_password = user_data;
if (g_source_is_destroyed (g_main_current_source ()))
return FALSE;
g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_password), FALSE);
if (g_source_get_id (g_main_current_source ()) == prompter_password->priv->show_dialog_idle_id) {
gboolean success;
prompter_password->priv->show_dialog_idle_id = 0;
g_warn_if_fail (prompter_password->priv->dialog == NULL);
success = e_credentials_prompter_impl_password_show_dialog (prompter_password);
e_credentials_prompter_impl_prompt_finish (
E_CREDENTIALS_PROMPTER_IMPL (prompter_password),
prompter_password->priv->prompt_id,
success ? prompter_password->priv->credentials : NULL);
e_credentials_prompter_impl_password_free_prompt_data (prompter_password);
}
return FALSE;
}
static void
e_credentials_prompter_impl_password_process_prompt (ECredentialsPrompterImpl *prompter_impl,
gpointer prompt_id,
ESource *auth_source,
ESource *cred_source,
const gchar *error_text,
const ENamedParameters *credentials)
{
ECredentialsPrompterImplPassword *prompter_password;
g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_impl));
prompter_password = E_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_impl);
g_return_if_fail (prompter_password->priv->prompt_id == NULL);
g_return_if_fail (prompter_password->priv->show_dialog_idle_id == 0);
prompter_password->priv->prompt_id = prompt_id;
prompter_password->priv->auth_source = g_object_ref (auth_source);
prompter_password->priv->cred_source = g_object_ref (cred_source);
prompter_password->priv->error_text = g_strdup (error_text);
prompter_password->priv->credentials = e_named_parameters_new_clone (credentials);
prompter_password->priv->show_dialog_idle_id = g_idle_add (
e_credentials_prompter_impl_password_show_dialog_idle_cb,
prompter_password);
}
static void
e_credentials_prompter_impl_password_cancel_prompt (ECredentialsPrompterImpl *prompter_impl,
gpointer prompt_id)
{
ECredentialsPrompterImplPassword *prompter_password;
g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_impl));
prompter_password = E_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_impl);
g_return_if_fail (prompter_password->priv->prompt_id == prompt_id);
/* This also closes the dialog. */
gtk_dialog_response (prompter_password->priv->dialog, GTK_RESPONSE_CANCEL);
}
static void
e_credentials_prompter_impl_password_dispose (GObject *object)
{
ECredentialsPrompterImplPassword *prompter_password = E_CREDENTIALS_PROMPTER_IMPL_PASSWORD (object);
if (prompter_password->priv->show_dialog_idle_id) {
g_source_remove (prompter_password->priv->show_dialog_idle_id);
prompter_password->priv->show_dialog_idle_id = 0;
}
g_warn_if_fail (prompter_password->priv->prompt_id == NULL);
g_warn_if_fail (prompter_password->priv->dialog == NULL);
e_credentials_prompter_impl_password_free_prompt_data (prompter_password);
/* Chain up to parent's method. */
G_OBJECT_CLASS (e_credentials_prompter_impl_password_parent_class)->dispose (object);
}
static void
e_credentials_prompter_impl_password_class_init (ECredentialsPrompterImplPasswordClass *class)
{
static const gchar *authentication_methods[] = {
"", /* register as the default credentials prompter */
NULL
};
GObjectClass *object_class;
ECredentialsPrompterImplClass *prompter_impl_class;
object_class = G_OBJECT_CLASS (class);
object_class->dispose = e_credentials_prompter_impl_password_dispose;
prompter_impl_class = E_CREDENTIALS_PROMPTER_IMPL_CLASS (class);
prompter_impl_class->authentication_methods = (const gchar * const *) authentication_methods;
prompter_impl_class->process_prompt = e_credentials_prompter_impl_password_process_prompt;
prompter_impl_class->cancel_prompt = e_credentials_prompter_impl_password_cancel_prompt;
_libedataserverui_init_icon_theme ();
}
static void
e_credentials_prompter_impl_password_init (ECredentialsPrompterImplPassword *prompter_password)
{
prompter_password->priv = e_credentials_prompter_impl_password_get_instance_private (prompter_password);
}
/**
* e_credentials_prompter_impl_password_new:
*
* Creates a new instance of an #ECredentialsPrompterImplPassword.
*
* Returns: (transfer full): a newly created #ECredentialsPrompterImplPassword,
* which should be freed with g_object_unref() when no longer needed.
*
* Since: 3.16
**/
ECredentialsPrompterImpl *
e_credentials_prompter_impl_password_new (void)
{
return g_object_new (E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD, NULL);
}