[gnome-shell] Add gnome-keyring prompter



commit 3ee07d0e821325f7f41c0d2a523976458bf0241d
Author: Stef Walter <stefw gnome org>
Date:   Tue Jan 10 16:37:26 2012 +0100

    Add gnome-keyring prompter
    
     * Add a keyring prompter based on GcrSystemPrompter
     * Adds dependency on gcr version 3.3.5 or higher
     * Not yet using unmerged support for non-pageable memory
    
    https://bugzilla.gnome.org/show_bug.cgi?id=652459

 configure.ac               |    4 +-
 data/theme/gnome-shell.css |    4 +
 js/Makefile.am             |    1 +
 js/ui/keyringPrompt.js     |  207 ++++++++++++
 js/ui/main.js              |    4 +
 src/Makefile.am            |    2 +
 src/shell-keyring-prompt.c |  745 ++++++++++++++++++++++++++++++++++++++++++++
 src/shell-keyring-prompt.h |   59 ++++
 8 files changed, 1025 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index c135da5..3d79728 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,6 +74,7 @@ TELEPATHY_GLIB_MIN_VERSION=0.17.5
 TELEPATHY_LOGGER_MIN_VERSION=0.2.4
 POLKIT_MIN_VERSION=0.100
 STARTUP_NOTIFICATION_MIN_VERSION=0.11
+GCR_MIN_VERSION=3.3.5
 
 # Collect more than 20 libraries for a prize!
 PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
@@ -93,7 +94,8 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
                                telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
                                telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
                                polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
-                               libnm-glib libnm-util gnome-keyring-1)
+                               libnm-glib libnm-util gnome-keyring-1
+                               gcr-3 >= $GCR_MIN_VERSION)
 
 PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
 
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 3bc5c93..e8d1dd3 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -1979,6 +1979,10 @@ StScrollBar StButton#vhandle:hover
     spacing-rows: 15px;
 }
 
+.keyring-dialog-control-table {
+    spacing-rows: 15px;
+}
+
 /* Magnifier */
 
 .magnifier-zoom-region {
diff --git a/js/Makefile.am b/js/Makefile.am
index edc8049..e7751cc 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -56,6 +56,7 @@ nobase_dist_js_DATA = 	\
 	ui/flashspot.js		\
 	ui/iconGrid.js		\
 	ui/keyboard.js		\
+	ui/keyringPrompt.js	\
 	ui/layout.js		\
 	ui/lightbox.js		\
 	ui/link.js		\
diff --git a/js/ui/keyringPrompt.js b/js/ui/keyringPrompt.js
new file mode 100644
index 0000000..9116326
--- /dev/null
+++ b/js/ui/keyringPrompt.js
@@ -0,0 +1,207 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+const Shell = imports.gi.Shell;
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+const Pango = imports.gi.Pango;
+const Gio = imports.gi.Gio;
+const GObject = imports.gi.GObject;
+const Gcr = imports.gi.Gcr;
+
+const ModalDialog = imports.ui.modalDialog;
+const ShellEntry = imports.ui.shellEntry;
+const CheckBox = imports.ui.checkBox;
+
+let prompter = null;
+
+const KeyringDialog = new Lang.Class({
+    Name: 'KeyringDialog',
+    Extends: ModalDialog.ModalDialog,
+
+    _init: function() {
+        this.parent({ styleClass: 'prompt-dialog' });
+
+        this.prompt = new Shell.KeyringPrompt();
+        this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
+        this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
+        this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
+
+        let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
+                                                vertical: false });
+        this.contentLayout.add(mainContentBox);
+
+        let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
+        mainContentBox.add(icon,
+                           { x_fill:  true,
+                             y_fill:  false,
+                             x_align: St.Align.END,
+                             y_align: St.Align.START });
+
+        this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
+                                              vertical: true });
+        mainContentBox.add(this._messageBox,
+                           { y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
+
+        let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
+        this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
+
+        this._messageBox.add(subject,
+                             { y_fill:  false,
+                               y_align: St.Align.START });
+
+        let description = new St.Label({ style_class: 'prompt-dialog-description' });
+        description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+        description.clutter_text.line_wrap = true;
+        this.prompt.bind_property('description', description, 'text', GObject.BindingFlags.SYNC_CREATE);
+        this._messageBox.add(description,
+                            { y_fill:  true,
+                              y_align: St.Align.START });
+
+        this._controlTable = null;
+
+        let buttons = [{ label: '',
+                         action: Lang.bind(this, this._onCancelButton),
+                         key:    Clutter.Escape
+                       },
+                       { label: '',
+                         action: Lang.bind(this, this._onContinueButton)
+                       }]
+
+        this.setButtons(buttons);
+        this._cancelButton = buttons[0].button;
+        this._continueButton = buttons[1].button;
+
+        this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
+        this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
+    },
+
+    _buildControlTable: function() {
+        let table = new St.Table({ style_class: 'keyring-dialog-control-table' });
+        let row = 0;
+
+        if (this.prompt.password_visible) {
+            let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
+            label.set_text(_("Password:"));
+            table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
+            this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
+                                                 text: '',
+                                                 can_focus: true});
+            this._passwordEntry.clutter_text.set_password_char('\u25cf'); // â U+25CF BLACK CIRCLE
+            ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
+            this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
+            table.add(this._passwordEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
+            row++;
+        } else {
+            this._passwordEntry = null;
+        }
+
+        if (this.prompt.confirm_visible) {
+            var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
+            label.set_text(_("Type again:"));
+            table.add(label, { row: row, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START });
+            this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
+                                                text: '',
+                                                can_focus: true});
+            this._confirmEntry.clutter_text.set_password_char('\u25cf'); // â U+25CF BLACK CIRCLE
+            ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
+            this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
+            table.add(this._confirmEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
+            row++;
+        } else {
+            this._confirmEntry = null;
+        }
+
+        this.prompt.set_password_actor(this._passwordEntry ? this._passwordEntry.clutter_text : null);
+        this.prompt.set_confirm_actor(this._confirmEntry ? this._confirmEntry.clutter_text : null);
+
+        if (this.prompt.choice_visible) {
+            let choice = new CheckBox.CheckBox();
+            this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
+            this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
+            table.add(choice.actor, { row: row, col: 1, x_expand: false, x_fill: true, x_align: St.Align.START });
+            row++;
+        }
+
+        let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
+        warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+        warning.clutter_text.line_wrap = true;
+        table.add(warning, { row: row, col: 1, x_expand: false, x_fill: false, x_align: St.Align.START });
+        this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
+        this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
+
+        if (this._controlTable) {
+            this._controlTable.destroy_all_children();
+            this._controlTable.destroy();
+        }
+
+        this._controlTable = table;
+        this._messageBox.add(table, { x_fill: true, y_fill: true });
+    },
+
+    _ensureOpen: function() {
+        // NOTE: ModalDialog.open() is safe to call if the dialog is
+        // already open - it just returns true without side-effects
+        if (this.open())
+          return true;
+
+        // The above fail if e.g. unable to get input grab
+        //
+        // In an ideal world this wouldn't happen (because the
+        // Shell is in complete control of the session) but that's
+        // just not how things work right now.
+
+        log('keyringPrompt: Failed to show modal dialog.' +
+            ' Dismissing prompt request');
+        this.prompt.cancel()
+        return false;
+    },
+
+    _onShowPassword: function(prompt) {
+        this._buildControlTable();
+        this._ensureOpen();
+        this._passwordEntry.grab_key_focus();
+    },
+
+    _onShowConfirm: function(prompt) {
+        this._buildControlTable();
+        this._ensureOpen();
+        this._continueButton.grab_key_focus();
+    },
+
+    _onHidePrompt: function(prompt) {
+        this.close();
+    },
+
+    _onPasswordActivate: function() {
+        if (this.prompt.confirm_visible)
+            this._confirmEntry.grab_key_focus();
+        else
+            this._onContinueButton();
+    },
+
+    _onConfirmActivate: function() {
+        this._onContinueButton();
+    },
+
+    _onContinueButton: function() {
+        this.prompt.complete()
+    },
+
+    _onCancelButton: function() {
+        this.prompt.cancel()
+    },
+});
+
+function init() {
+    prompter = new Gcr.SystemPrompter();
+    prompter.connect('new-prompt', function(prompter) {
+        let dialog = new KeyringDialog();
+        return dialog.prompt;
+    });
+
+    let connection = Gio.DBus.session;
+    prompter.register(connection);
+    Gio.bus_own_name_on_connection (connection, 'org.gnome.keyring.SystemPrompter',
+                                    Gio.BusNameOwnerFlags.REPLACE, null, null);
+}
diff --git a/js/ui/main.js b/js/ui/main.js
index 64160d6..11dd81f 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -15,6 +15,7 @@ const AutorunManager = imports.ui.autorunManager;
 const CtrlAltTab = imports.ui.ctrlAltTab;
 const EndSessionDialog = imports.ui.endSessionDialog;
 const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
+const KeyringPrompt = imports.ui.keyringPrompt;
 const Environment = imports.ui.environment;
 const ExtensionSystem = imports.ui.extensionSystem;
 const Keyboard = imports.ui.keyboard;
@@ -232,6 +233,9 @@ function start() {
     // Attempt to become a PolicyKit authentication agent
     PolkitAuthenticationAgent.init()
 
+    // Become a prompter for gnome keyring
+    KeyringPrompt.init();
+
     _startDate = new Date();
 
     global.stage.connect('captured-event', _globalKeyPressHandler);
diff --git a/src/Makefile.am b/src/Makefile.am
index d67f4fa..6ff7e61 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -155,6 +155,8 @@ libgnome_shell_la_SOURCES =		\
 	shell-generic-container.c	\
 	shell-gtk-embed.c		\
 	shell-global.c			\
+	shell-keyring-prompt.h		\
+	shell-keyring-prompt.c		\
 	shell-mobile-providers.c	\
 	shell-mount-operation.c		\
 	shell-network-agent.c		\
diff --git a/src/shell-keyring-prompt.c b/src/shell-keyring-prompt.c
new file mode 100644
index 0000000..977a48b
--- /dev/null
+++ b/src/shell-keyring-prompt.c
@@ -0,0 +1,745 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *           2012 Stef Walter <stefw gnome org>
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author: Stef Walter <stefw gnome org>
+ */
+
+#include "config.h"
+
+#include "shell-keyring-prompt.h"
+
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr-base.h>
+
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+typedef struct _ShellPasswordPromptClass    ShellPasswordPromptClass;
+typedef struct _ShellPasswordPromptPrivate  ShellPasswordPromptPrivate;
+
+typedef enum
+{
+  PROMPTING_NONE,
+  PROMPTING_FOR_CONFIRM,
+  PROMPTING_FOR_PASSWORD
+} PromptingMode;
+
+struct _ShellKeyringPrompt
+{
+  GObject parent;
+
+  gchar *title;
+  gchar *message;
+  gchar *description;
+  gchar *warning;
+  gchar *choice_label;
+  gboolean choice_chosen;
+  gboolean password_new;
+  guint password_strength;
+  gchar *continue_label;
+  gchar *cancel_label;
+
+  GcrPromptReply last_reply;
+  GSimpleAsyncResult *async_result;
+  ClutterText *password_actor;
+  ClutterText *confirm_actor;
+  PromptingMode mode;
+  gboolean shown;
+};
+
+typedef struct _ShellKeyringPromptClass
+{
+  GObjectClass parent_class;
+} ShellKeyringPromptClass;
+
+enum {
+  PROP_0,
+  PROP_TITLE,
+  PROP_MESSAGE,
+  PROP_DESCRIPTION,
+  PROP_WARNING,
+  PROP_CHOICE_LABEL,
+  PROP_CHOICE_CHOSEN,
+  PROP_PASSWORD_NEW,
+  PROP_PASSWORD_STRENGTH,
+  PROP_CALLER_WINDOW,
+  PROP_CONTINUE_LABEL,
+  PROP_CANCEL_LABEL,
+  PROP_PASSWORD_VISIBLE,
+  PROP_CONFIRM_VISIBLE,
+  PROP_WARNING_VISIBLE,
+  PROP_CHOICE_VISIBLE,
+  PROP_PASSWORD_ACTOR,
+  PROP_CONFIRM_ACTOR
+};
+
+static void    shell_keyring_prompt_iface     (GcrPromptIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ShellKeyringPrompt, shell_keyring_prompt, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, shell_keyring_prompt_iface);
+);
+
+enum {
+  SIGNAL_SHOW_PASSWORD,
+  SIGNAL_SHOW_CONFIRM,
+  SIGNAL_HIDE_PROMPT,
+  SIGNAL_LAST
+};
+
+static gint signals[SIGNAL_LAST];
+
+static void
+shell_keyring_prompt_init (ShellKeyringPrompt *self)
+{
+
+}
+
+static void
+shell_keyring_prompt_set_property (GObject      *obj,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (obj);
+
+  switch (prop_id) {
+  case PROP_TITLE:
+    g_free (self->title);
+    self->title = g_value_dup_string (value);
+    g_object_notify (obj, "title");
+    break;
+  case PROP_MESSAGE:
+    g_free (self->message);
+    self->message = g_value_dup_string (value);
+    g_object_notify (obj, "message");
+    break;
+  case PROP_DESCRIPTION:
+    g_free (self->description);
+    self->description = g_value_dup_string (value);
+    g_object_notify (obj, "description");
+    break;
+  case PROP_WARNING:
+    g_free (self->warning);
+    self->warning = g_value_dup_string (value);
+    if (!self->warning)
+        self->warning = g_strdup ("");
+    g_object_notify (obj, "warning");
+    g_object_notify (obj, "warning-visible");
+    break;
+  case PROP_CHOICE_LABEL:
+    g_free (self->choice_label);
+    self->choice_label = g_value_dup_string (value);
+    if (!self->choice_label)
+        self->choice_label = g_strdup ("");
+    g_object_notify (obj, "choice-label");
+    g_object_notify (obj, "choice-visible");
+    break;
+  case PROP_CHOICE_CHOSEN:
+    self->choice_chosen = g_value_get_boolean (value);
+    g_object_notify (obj, "choice-chosen");
+    break;
+  case PROP_PASSWORD_NEW:
+    self->password_new = g_value_get_boolean (value);
+    g_object_notify (obj, "password-new");
+    g_object_notify (obj, "confirm-visible");
+    break;
+  case PROP_CALLER_WINDOW:
+    /* ignored */
+    break;
+  case PROP_CONTINUE_LABEL:
+    g_free (self->continue_label);
+    self->continue_label = g_value_dup_string (value);
+    g_object_notify (obj, "continue-label");
+    break;
+  case PROP_CANCEL_LABEL:
+    g_free (self->cancel_label);
+    self->cancel_label = g_value_dup_string (value);
+    g_object_notify (obj, "cancel-label");
+    break;
+  case PROP_PASSWORD_ACTOR:
+    shell_keyring_prompt_set_password_actor (self, g_value_get_object (value));
+    break;
+  case PROP_CONFIRM_ACTOR:
+    shell_keyring_prompt_set_confirm_actor (self, g_value_get_object (value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+shell_keyring_prompt_get_property (GObject    *obj,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (obj);
+
+  switch (prop_id) {
+  case PROP_TITLE:
+    g_value_set_string (value, self->title ? self->title : "");
+    break;
+  case PROP_MESSAGE:
+    g_value_set_string (value, self->message ? self->message : "");
+    break;
+  case PROP_DESCRIPTION:
+    g_value_set_string (value, self->description ? self->description : "");
+    break;
+  case PROP_WARNING:
+    g_value_set_string (value, self->warning ? self->warning : "");
+    break;
+  case PROP_CHOICE_LABEL:
+    g_value_set_string (value, self->choice_label ? self->choice_label : "");
+    break;
+  case PROP_CHOICE_CHOSEN:
+    g_value_set_boolean (value, self->choice_chosen);
+    break;
+  case PROP_PASSWORD_NEW:
+    g_value_set_boolean (value, self->password_new);
+    break;
+  case PROP_PASSWORD_STRENGTH:
+    g_value_set_int (value, self->password_strength);
+    break;
+  case PROP_CALLER_WINDOW:
+    g_value_set_string (value, "");
+    break;
+  case PROP_CONTINUE_LABEL:
+    g_value_set_string (value, self->continue_label);
+    break;
+  case PROP_CANCEL_LABEL:
+    g_value_set_string (value, self->cancel_label);
+    break;
+  case PROP_PASSWORD_VISIBLE:
+    g_value_set_boolean (value, self->mode == PROMPTING_FOR_PASSWORD);
+    break;
+  case PROP_CONFIRM_VISIBLE:
+    g_value_set_boolean (value, self->password_new &&
+                                self->mode == PROMPTING_FOR_PASSWORD);
+    break;
+  case PROP_WARNING_VISIBLE:
+    g_value_set_boolean (value, self->warning && self->warning[0]);
+    break;
+  case PROP_CHOICE_VISIBLE:
+    g_value_set_boolean (value, self->choice_label && self->choice_label[0]);
+    break;
+  case PROP_PASSWORD_ACTOR:
+    g_value_set_object (value, shell_keyring_prompt_get_password_actor (self));
+    break;
+  case PROP_CONFIRM_ACTOR:
+    g_value_set_object (value, shell_keyring_prompt_get_confirm_actor (self));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+shell_keyring_prompt_dispose (GObject *obj)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (obj);
+
+  if (self->shown) {
+    self->shown = FALSE;
+    g_signal_emit (self, signals[SIGNAL_HIDE_PROMPT], 0);
+  }
+
+  if (self->async_result)
+    shell_keyring_prompt_cancel (self);
+  g_assert (self->async_result == NULL);
+
+  shell_keyring_prompt_set_password_actor (self, NULL);
+  shell_keyring_prompt_set_confirm_actor (self, NULL);
+
+  G_OBJECT_CLASS (shell_keyring_prompt_parent_class)->dispose (obj);
+}
+
+static void
+shell_keyring_prompt_finalize (GObject *obj)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (obj);
+
+  g_free (self->title);
+  g_free (self->message);
+  g_free (self->description);
+  g_free (self->warning);
+  g_free (self->choice_label);
+  g_free (self->continue_label);
+  g_free (self->cancel_label);
+
+  G_OBJECT_CLASS (shell_keyring_prompt_parent_class)->finalize (obj);
+}
+
+static void
+shell_keyring_prompt_class_init (ShellKeyringPromptClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->get_property = shell_keyring_prompt_get_property;
+  gobject_class->set_property = shell_keyring_prompt_set_property;
+  gobject_class->dispose = shell_keyring_prompt_dispose;
+  gobject_class->finalize = shell_keyring_prompt_finalize;
+
+  g_object_class_override_property (gobject_class, PROP_TITLE, "title");
+
+  g_object_class_override_property (gobject_class, PROP_MESSAGE, "message");
+
+  g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description");
+
+  g_object_class_override_property (gobject_class, PROP_WARNING, "warning");
+
+  g_object_class_override_property (gobject_class, PROP_PASSWORD_NEW, "password-new");
+
+  g_object_class_override_property (gobject_class, PROP_PASSWORD_STRENGTH, "password-strength");
+
+  g_object_class_override_property (gobject_class, PROP_CHOICE_LABEL, "choice-label");
+
+  g_object_class_override_property (gobject_class, PROP_CHOICE_CHOSEN, "choice-chosen");
+
+  g_object_class_override_property (gobject_class, PROP_CALLER_WINDOW, "caller-window");
+
+  g_object_class_override_property (gobject_class, PROP_CONTINUE_LABEL, "continue-label");
+
+  g_object_class_override_property (gobject_class, PROP_CANCEL_LABEL, "cancel-label");
+
+  /**
+   * ShellKeyringPrompt:password-visible
+   *
+   * Whether the password entry is visible or not.
+   */
+  g_object_class_install_property (gobject_class, PROP_PASSWORD_VISIBLE,
+             g_param_spec_boolean ("password-visible", "Password visible", "Password field is visible",
+                                   FALSE, G_PARAM_READABLE));
+
+  /**
+    * ShellKeyringPrompt:confirm-visible
+    *
+    * Whether the password confirm entry is visible or not.
+    */
+  g_object_class_install_property (gobject_class, PROP_CONFIRM_VISIBLE,
+             g_param_spec_boolean ("confirm-visible", "Confirm visible", "Confirm field is visible",
+                                   FALSE, G_PARAM_READABLE));
+
+  /**
+   * ShellKeyringPrompt:warning-visible
+   *
+   * Whether the warning label is visible or not.
+   */
+  g_object_class_install_property (gobject_class, PROP_WARNING_VISIBLE,
+             g_param_spec_boolean ("warning-visible", "Warning visible", "Warning is visible",
+                                   FALSE, G_PARAM_READABLE));
+
+  /**
+   * ShellKeyringPrompt:choice-visible
+   *
+   * Whether the choice check box is visible or not.
+   */
+  g_object_class_install_property (gobject_class, PROP_CHOICE_VISIBLE,
+             g_param_spec_boolean ("choice-visible", "Choice visible", "Choice is visible",
+                                   FALSE, G_PARAM_READABLE));
+
+  /**
+   * ShellKeyringPrompt:password-actor
+   *
+   * Text field for password
+   */
+  g_object_class_install_property (gobject_class, PROP_PASSWORD_ACTOR,
+              g_param_spec_object ("password-actor", "Password actor", "Text field for password",
+                                   CLUTTER_TYPE_TEXT, G_PARAM_READWRITE));
+
+  /**
+   * ShellKeyringPrompt:confirm-actor
+   *
+   * Text field for confirmation password
+   */
+  g_object_class_install_property (gobject_class, PROP_CONFIRM_ACTOR,
+              g_param_spec_object ("confirm-actor", "Confirm actor", "Text field for confirming password",
+                                   CLUTTER_TYPE_TEXT, G_PARAM_READWRITE));
+
+  signals[SIGNAL_SHOW_PASSWORD] = g_signal_new ("show-password", G_TYPE_FROM_CLASS (klass),
+                                                0, 0, NULL, NULL,
+                                                g_cclosure_marshal_VOID__VOID,
+                                                G_TYPE_NONE, 0);
+
+  signals[SIGNAL_SHOW_CONFIRM] =  g_signal_new ("show-confirm", G_TYPE_FROM_CLASS (klass),
+                                                0, 0, NULL, NULL,
+                                                g_cclosure_marshal_VOID__VOID,
+                                                G_TYPE_NONE, 0);
+
+  signals[SIGNAL_HIDE_PROMPT] =  g_signal_new ("hide-prompt", G_TYPE_FROM_CLASS (klass),
+                                               0, 0, NULL, NULL,
+                                               g_cclosure_marshal_VOID__VOID,
+                                               G_TYPE_NONE, 0);
+}
+
+static void
+shell_keyring_prompt_password_async (GcrPrompt          *prompt,
+                                     GCancellable       *cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer            user_data)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (prompt);
+  GObject *obj;
+
+  if (self->async_result != NULL) {
+      g_warning ("this prompt can only show one prompt at a time");
+      return;
+  }
+
+  self->mode = PROMPTING_FOR_PASSWORD;
+  self->async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+                                                  shell_keyring_prompt_password_async);
+
+  obj = G_OBJECT (self);
+  g_object_notify (obj, "password-visible");
+  g_object_notify (obj, "confirm-visible");
+  g_object_notify (obj, "warning-visible");
+  g_object_notify (obj, "choice-visible");
+
+  self->shown = TRUE;
+  g_signal_emit (self, signals[SIGNAL_SHOW_PASSWORD], 0);
+}
+
+static const gchar *
+shell_keyring_prompt_password_finish (GcrPrompt    *prompt,
+                                      GAsyncResult *result,
+                                      GError      **error)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (prompt);
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
+                        shell_keyring_prompt_password_async), NULL);
+
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+    return NULL;
+
+  if (self->last_reply == GCR_PROMPT_REPLY_CONTINUE)
+    return clutter_text_get_text (self->password_actor);
+
+  return NULL;
+}
+
+static void
+shell_keyring_prompt_confirm_async (GcrPrompt          *prompt,
+                                    GCancellable       *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer            user_data)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (prompt);
+  GObject *obj;
+
+  if (self->async_result != NULL) {
+      g_warning ("this prompt is already prompting");
+      return;
+  }
+
+  self->mode = PROMPTING_FOR_CONFIRM;
+  self->async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+                                                  shell_keyring_prompt_confirm_async);
+
+  obj = G_OBJECT (self);
+  g_object_notify (obj, "password-visible");
+  g_object_notify (obj, "confirm-visible");
+  g_object_notify (obj, "warning-visible");
+  g_object_notify (obj, "choice-visible");
+
+  self->shown = TRUE;
+  g_signal_emit (self, signals[SIGNAL_SHOW_CONFIRM], 0);
+}
+
+static GcrPromptReply
+shell_keyring_prompt_confirm_finish (GcrPrompt    *prompt,
+                                     GAsyncResult *result,
+                                     GError      **error)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (prompt);
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (prompt),
+                        shell_keyring_prompt_confirm_async), GCR_PROMPT_REPLY_CANCEL);
+
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+    return GCR_PROMPT_REPLY_CANCEL;
+
+  return self->last_reply;
+}
+
+static void
+shell_keyring_prompt_iface (GcrPromptIface *iface)
+{
+  iface->prompt_password_async = shell_keyring_prompt_password_async;
+  iface->prompt_password_finish = shell_keyring_prompt_password_finish;
+  iface->prompt_confirm_async = shell_keyring_prompt_confirm_async;
+  iface->prompt_confirm_finish = shell_keyring_prompt_confirm_finish;
+}
+
+/**
+ * shell_keyring_prompt_new:
+ *
+ * Create new internal prompt base
+ *
+ * Returns: (transfer full): new internal prompt
+ */
+ShellKeyringPrompt *
+shell_keyring_prompt_new (void)
+{
+	return g_object_new (SHELL_TYPE_KEYRING_PROMPT, NULL);
+}
+
+/**
+ * shell_keyring_prompt_get_password_actor:
+ * @self: the internal prompt
+ *
+ * Get the prompt password text actor
+ *
+ * Returns: (transfer none) (allow-none): the password actor
+ */
+ClutterText *
+shell_keyring_prompt_get_password_actor (ShellKeyringPrompt *self)
+{
+  g_return_val_if_fail (SHELL_IS_KEYRING_PROMPT (self), NULL);
+  return self->password_actor;
+}
+
+/**
+ * shell_keyring_prompt_get_confirm_actor:
+ * @self: the internal prompt
+ *
+ * Get the prompt password text actor
+ *
+ * Returns: (transfer none) (allow-none): the password actor
+ */
+ClutterText *
+shell_keyring_prompt_get_confirm_actor (ShellKeyringPrompt *self)
+{
+  g_return_val_if_fail (SHELL_IS_KEYRING_PROMPT (self), NULL);
+  return self->confirm_actor;
+}
+
+static guint
+calculate_password_strength (const gchar *password)
+{
+  int upper, lower, digit, misc;
+  gdouble pwstrength;
+  int length, i;
+
+  /*
+   * This code is based on the Master Password dialog in Firefox
+   * (pref-masterpass.js)
+   * Original code triple-licensed under the MPL, GPL, and LGPL
+   * so is license-compatible with this file
+   */
+
+  length = strlen (password);
+
+  /* Always return 0 for empty passwords */
+  if (length == 0)
+    return 0;
+
+  upper = 0;
+  lower = 0;
+  digit = 0;
+  misc = 0;
+
+  for (i = 0; i < length ; i++)
+    {
+      if (g_ascii_isdigit (password[i]))
+        digit++;
+      else if (g_ascii_islower (password[i]))
+        lower++;
+      else if (g_ascii_isupper (password[i]))
+        upper++;
+      else
+        misc++;
+    }
+
+  if (length > 5)
+    length = 5;
+  if (digit > 3)
+    digit = 3;
+  if (upper > 3)
+    upper = 3;
+  if (misc > 3)
+    misc = 3;
+
+  pwstrength = ((length * 1) - 2) +
+      (digit * 1) +
+      (misc * 1.5) +
+      (upper * 1);
+
+  /* Always return 1+ for non-empty passwords */
+  if (pwstrength < 1.0)
+    pwstrength = 1.0;
+  if (pwstrength > 10.0)
+    pwstrength = 10.0;
+
+  return (guint)pwstrength;
+}
+
+static void
+on_password_changed (ClutterText *text,
+                     gpointer user_data)
+{
+  ShellKeyringPrompt *self = SHELL_KEYRING_PROMPT (user_data);
+  const gchar *password;
+
+  password = clutter_text_get_text (self->password_actor);
+
+  self->password_strength = calculate_password_strength (password);
+  g_object_notify (G_OBJECT (self), "password-strength");
+}
+
+/**
+ * shell_keyring_prompt_set_password_actor:
+ * @self: the internal prompt
+ * @password_actor: (allow-none): the password actor
+ *
+ * Set the prompt password text actor
+ */
+void
+shell_keyring_prompt_set_password_actor (ShellKeyringPrompt *self,
+                                         ClutterText *password_actor)
+{
+  g_return_if_fail (SHELL_IS_KEYRING_PROMPT (self));
+  g_return_if_fail (password_actor == NULL || CLUTTER_IS_TEXT (password_actor));
+
+  if (password_actor)
+    {
+      g_signal_connect (password_actor, "text-changed", G_CALLBACK (on_password_changed), self);
+      g_object_ref (password_actor);
+    }
+  if (self->password_actor)
+    {
+      g_signal_handlers_disconnect_by_func (self->password_actor, on_password_changed, self);
+      g_object_unref (self->password_actor);
+    }
+
+  self->password_actor = password_actor;
+  g_object_notify (G_OBJECT (self), "password-actor");
+}
+
+/**
+ * shell_keyring_prompt_set_confirm_actor:
+ * @self: the internal prompt
+ * @confirm_actor: (allow-none): the confirm password actor
+ *
+ * Set the prompt password confirmation text actor
+ */
+void
+shell_keyring_prompt_set_confirm_actor (ShellKeyringPrompt *self,
+                                        ClutterText *confirm_actor)
+{
+  g_return_if_fail (SHELL_IS_KEYRING_PROMPT (self));
+  g_return_if_fail (confirm_actor == NULL || CLUTTER_IS_TEXT (confirm_actor));
+
+  if (confirm_actor)
+    g_object_ref (confirm_actor);
+  if (self->confirm_actor)
+    g_object_unref (self->confirm_actor);
+  self->confirm_actor = confirm_actor;
+  g_object_notify (G_OBJECT (self), "confirm-actor");
+}
+
+/**
+ * shell_keyring_prompt_complete:
+ * @self: the internal prompt
+ *
+ * Called by the implementation when the prompt completes. There are various
+ * checks done. %TRUE is returned if the prompt actually should complete.
+ *
+ * Returns: whether the prompt completed
+ */
+gboolean
+shell_keyring_prompt_complete (ShellKeyringPrompt *self)
+{
+  GSimpleAsyncResult *res;
+  const gchar *password;
+  const gchar *confirm;
+  const gchar *env;
+
+  g_return_val_if_fail (SHELL_IS_KEYRING_PROMPT (self), FALSE);
+  g_return_val_if_fail (self->mode != PROMPTING_NONE, FALSE);
+  g_return_val_if_fail (self->async_result != NULL, FALSE);
+
+  if (self->mode == PROMPTING_FOR_PASSWORD)
+    {
+      password = clutter_text_get_text (self->password_actor);
+
+      /* Is it a new password? */
+      if (self->password_new)
+        {
+          confirm = clutter_text_get_text (self->confirm_actor);
+
+          /* Do the passwords match? */
+          if (!g_str_equal (password, confirm))
+            {
+              gcr_prompt_set_warning (GCR_PROMPT (self), _("Passwords do not match."));
+              return FALSE;
+          }
+
+          /* Don't allow blank passwords if in paranoid mode */
+          env = g_getenv ("GNOME_KEYRING_PARANOID");
+          if (env && *env)
+            {
+              gcr_prompt_set_warning (GCR_PROMPT (self), _("Password cannot be blank"));
+              return FALSE;
+            }
+        }
+
+      self->password_strength = calculate_password_strength (password);
+      g_object_notify (G_OBJECT (self), "password-strength");
+    }
+
+  self->last_reply = GCR_PROMPT_REPLY_CONTINUE;
+
+  res = self->async_result;
+  self->async_result = NULL;
+  self->mode = PROMPTING_NONE;
+
+  g_simple_async_result_complete (res);
+  g_object_unref (res);
+
+  return TRUE;
+}
+
+/**
+ * shell_keyring_prompt_cancel:
+ * @self: the internal prompt
+ *
+ * Called by implementation when the prompt is cancelled.
+ */
+void
+shell_keyring_prompt_cancel (ShellKeyringPrompt *self)
+{
+  GSimpleAsyncResult *res;
+
+  g_return_if_fail (SHELL_IS_KEYRING_PROMPT (self));
+  g_return_if_fail (self->mode != PROMPTING_NONE);
+  g_return_if_fail (self->async_result != NULL);
+
+  self->last_reply = GCR_PROMPT_REPLY_CANCEL;
+
+  res = self->async_result;
+  self->async_result = NULL;
+  self->mode = PROMPTING_NONE;
+
+  g_simple_async_result_complete (res);
+  g_object_unref (res);
+}
diff --git a/src/shell-keyring-prompt.h b/src/shell-keyring-prompt.h
new file mode 100644
index 0000000..f96b5bc
--- /dev/null
+++ b/src/shell-keyring-prompt.h
@@ -0,0 +1,59 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* shell-keyring-prompt.c - prompt handler for gnome-keyring-daemon
+
+   Copyright (C) 2011 Stefan Walter
+
+   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, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Author: Stef Walter <stef thewalter net>
+*/
+
+#ifndef __SHELL_KEYRING_PROMPT_H__
+#define __SHELL_KEYRING_PROMPT_H__
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include <clutter/clutter.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ShellKeyringPrompt         ShellKeyringPrompt;
+
+#define SHELL_TYPE_KEYRING_PROMPT                  (shell_keyring_prompt_get_type ())
+#define SHELL_KEYRING_PROMPT(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_KEYRING_PROMPT, ShellKeyringPrompt))
+#define SHELL_IS_KEYRING_PROMPT(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_KEYRING_PROMPT))
+
+GType                shell_keyring_prompt_get_type             (void) G_GNUC_CONST;
+
+ShellKeyringPrompt * shell_keyring_prompt_new                  (void);
+
+ClutterText *        shell_keyring_prompt_get_password_actor   (ShellKeyringPrompt *self);
+
+void                 shell_keyring_prompt_set_password_actor   (ShellKeyringPrompt *self,
+                                                                ClutterText *password_actor);
+
+ClutterText *        shell_keyring_prompt_get_confirm_actor    (ShellKeyringPrompt *self);
+
+void                 shell_keyring_prompt_set_confirm_actor    (ShellKeyringPrompt *self,
+                                                                ClutterText *confirm_actor);
+
+gboolean             shell_keyring_prompt_complete             (ShellKeyringPrompt *self);
+
+void                 shell_keyring_prompt_cancel               (ShellKeyringPrompt *self);
+
+G_END_DECLS
+
+#endif /* __SHELL_KEYRING_PROMPT_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]