From e6bfad96d51f1a23d3034da336da4b5200788b56 Mon Sep 17 00:00:00 2001 From: JosephMcc Date: Mon, 14 Oct 2024 18:39:19 -0700 Subject: [PATCH] Convert the keyring unlock dialog to a clutter dialog --- data/theme/cinnamon-sass/widgets/_base.scss | 8 + .../cinnamon-sass/widgets/_switch-check.scss | 15 + debian/control | 2 + js/ui/checkBox.js | 38 + js/ui/cinnamonEntry.js | 56 ++ js/ui/keyringPrompt.js | 199 +++++ js/ui/main.js | 3 + meson.build | 1 + src/cinnamon-keyring-prompt.c | 833 ++++++++++++++++++ src/cinnamon-keyring-prompt.h | 58 ++ src/cinnamon-secure-text-buffer.c | 192 ++++ src/cinnamon-secure-text-buffer.h | 40 + src/meson.build | 6 + 13 files changed, 1451 insertions(+) create mode 100644 js/ui/keyringPrompt.js create mode 100644 src/cinnamon-keyring-prompt.c create mode 100644 src/cinnamon-keyring-prompt.h create mode 100644 src/cinnamon-secure-text-buffer.c create mode 100644 src/cinnamon-secure-text-buffer.h diff --git a/data/theme/cinnamon-sass/widgets/_base.scss b/data/theme/cinnamon-sass/widgets/_base.scss index cd80f4f4ac..a4d6d0992f 100644 --- a/data/theme/cinnamon-sass/widgets/_base.scss +++ b/data/theme/cinnamon-sass/widgets/_base.scss @@ -1,5 +1,13 @@ .flashspot { background-color: white; } +// Caps-lock warning +.caps-lock-warning-label { + text-align: center; + padding-bottom: 8px; + @extend %caption; + color: $warning_color; +} + // links .cinnamon-link { color: $link_color; diff --git a/data/theme/cinnamon-sass/widgets/_switch-check.scss b/data/theme/cinnamon-sass/widgets/_switch-check.scss index a7d7b334fa..57e6184787 100644 --- a/data/theme/cinnamon-sass/widgets/_switch-check.scss +++ b/data/theme/cinnamon-sass/widgets/_switch-check.scss @@ -31,6 +31,21 @@ $check_width: 20px; &:focus:checked StBin { background-image: url("checkbox.svg"); } } +// New CheckBoxes + +.check-box-2 { + StBoxLayout { spacing: 0.8em; } + + StBin { + width: $check_width; + height: $check_height; + background-image: url("checkbox-off.svg"); + } + &:focus StBin { background-image: url("checkbox-off.svg"); } + &:checked StBin { background-image: url("checkbox.svg"); } + &:focus:checked StBin { background-image: url("checkbox.svg"); } +} + // radio .radiobutton { diff --git a/debian/control b/debian/control index 7023f3e0c2..cb96845286 100644 --- a/debian/control +++ b/debian/control @@ -15,6 +15,7 @@ Build-Depends: libcinnamon-menu-3-dev (>= 4.8), libcjs-dev (>= 4.8), libdbus-1-dev, + libgcr-3-dev, libgirepository1.0-dev (>= 1.29.15), libgl1-mesa-dev, libglib2.0-dev (>= 2.52), @@ -57,6 +58,7 @@ Depends: gir1.2-cvc-1.0, gir1.2-ecal-2.0, gir1.2-edataserver-1.2, + gir1.2-gcr-3, gir1.2-gdkpixbuf-2.0, gir1.2-gkbd-3.0, gir1.2-gsound-1.0, diff --git a/js/ui/checkBox.js b/js/ui/checkBox.js index d71cd6a7e1..8f556cc995 100644 --- a/js/ui/checkBox.js +++ b/js/ui/checkBox.js @@ -1,4 +1,5 @@ const Clutter = imports.gi.Clutter; +const GObject = imports.gi.GObject; const Pango = imports.gi.Pango; const Cinnamon = imports.gi.Cinnamon; const St = imports.gi.St; @@ -151,3 +152,40 @@ var CheckBox = class extends CheckBoxBase { return this._container.label; } } + +var CheckBox2 = GObject.registerClass( +class CheckBox2 extends St.Button { + _init(label) { + let container = new St.BoxLayout(); + super._init({ + style_class: 'check-box-2', + important: true, + child: container, + button_mask: St.ButtonMask.ONE, + toggle_mode: true, + can_focus: true, + x_fill: true, + y_fill: true, + }); + + this._box = new St.Bin(); + this._box.set_y_align(Clutter.ActorAlign.START); + container.add_actor(this._box); + + this._label = new St.Label({ y_align: Clutter.ActorAlign.CENTER }); + this._label.clutter_text.set_line_wrap(true); + this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE); + container.add_actor(this._label); + + if (label) + this.setLabel(label); + } + + setLabel(label) { + this._label.set_text(label); + } + + getLabelActor() { + return this._label; + } +}); diff --git a/js/ui/cinnamonEntry.js b/js/ui/cinnamonEntry.js index 472e5ddf62..7c69f5cc5c 100644 --- a/js/ui/cinnamonEntry.js +++ b/js/ui/cinnamonEntry.js @@ -1,7 +1,9 @@ const Clutter = imports.gi.Clutter; const Cinnamon = imports.gi.Cinnamon; +const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang; +const Pango = imports.gi.Pango; const St = imports.gi.St; const Main = imports.ui.main; @@ -164,3 +166,57 @@ function addContextMenu(entry, params) { entry.clutter_text.connect('button-press-event', _onClicked); entry.connect('popup-menu', _onPopup); } + +var CapsLockWarning = GObject.registerClass( +class CapsLockWarning extends St.Label { + _init(params) { + let defaultParams = { style_class: 'prompt-dialog-error-label' }; + super._init(Object.assign(defaultParams, params)); + + this.text = _('Caps lock is on'); + + this.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; + this.clutter_text.line_wrap = true; + + let seat = Clutter.get_default_backend().get_default_seat(); + this._keymap = seat.get_keymap(); + + this.connect('notify::mapped', () => { + if (this.is_mapped()) { + this._stateChangedId = this._keymap.connect('state-changed', + () => this._sync(true)); + } else { + this._keymap.disconnect(this._stateChangedId); + this.stateChangedId = 0; + } + + this._sync(false); + }); + + this.connect('destroy', () => { + if (this._stateChangedId) + this._keymap.disconnect(this._stateChangedId); + }); + } + + _sync(animate) { + let capsLockOn = this._keymap.get_caps_lock_state(); + + this.remove_all_transitions(); + + const { naturalHeightSet } = this; + this.natural_height_set = false; + let [, height] = this.get_preferred_height(-1); + this.natural_height_set = naturalHeightSet; + + this.ease({ + height: capsLockOn ? height : 0, + opacity: capsLockOn ? 255 : 0, + duration: animate ? 200 : 0, + onComplete: () => { + if (capsLockOn) + this.height = -1; + }, + }); + } +}); diff --git a/js/ui/keyringPrompt.js b/js/ui/keyringPrompt.js new file mode 100644 index 0000000000..bb1e565438 --- /dev/null +++ b/js/ui/keyringPrompt.js @@ -0,0 +1,199 @@ +const Cinnamon = imports.gi.Cinnamon; +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 Dialog = imports.ui.dialog; +const ModalDialog = imports.ui.modalDialog; +const CinnamonEntry = imports.ui.cinnamonEntry; +const CheckBox = imports.ui.checkBox; +const Util = imports.misc.util; + +var KeyringDialog = GObject.registerClass( +class KeyringDialog extends ModalDialog.ModalDialog { + _init() { + super._init({ styleClass: 'prompt-dialog' }); + + this.prompt = new Cinnamon.KeyringPrompt(); + this.prompt.connect('show-password', this._onShowPassword.bind(this)); + this.prompt.connect('show-confirm', this._onShowConfirm.bind(this)); + this.prompt.connect('prompt-close', this._onHidePrompt.bind(this)); + + let content = new Dialog.MessageDialogContent(); + + this.prompt.bind_property('message', + content, 'title', GObject.BindingFlags.SYNC_CREATE); + this.prompt.bind_property('description', + content, 'description', GObject.BindingFlags.SYNC_CREATE); + + let passwordBox = new St.BoxLayout({ + style_class: 'prompt-dialog-password-layout', + vertical: true, + }); + + this._passwordEntry = new St.Entry({ + style_class: 'prompt-dialog-password-entry', + can_focus: true, + x_align: Clutter.ActorAlign.CENTER, + }); + this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE + CinnamonEntry.addContextMenu(this._passwordEntry, { isPassword: true }); + this._passwordEntry.clutter_text.connect('activate', this._onPasswordActivate.bind(this)); + this.prompt.bind_property('password-visible', + this._passwordEntry, 'visible', GObject.BindingFlags.SYNC_CREATE); + passwordBox.add_child(this._passwordEntry); + + this._confirmEntry = new St.Entry({ + style_class: 'prompt-dialog-password-entry', + can_focus: true, + x_align: Clutter.ActorAlign.CENTER, + }); + this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE + CinnamonEntry.addContextMenu(this._confirmEntry, { isPassword: true }); + this._confirmEntry.clutter_text.connect('activate', this._onConfirmActivate.bind(this)); + this.prompt.bind_property('confirm-visible', + this._confirmEntry, 'visible', GObject.BindingFlags.SYNC_CREATE); + passwordBox.add_child(this._confirmEntry); + + this.prompt.set_password_actor(this._passwordEntry.clutter_text); + this.prompt.set_confirm_actor(this._confirmEntry.clutter_text); + + let warningBox = new St.BoxLayout({ vertical: true }); + + let capsLockWarning = new CinnamonEntry.CapsLockWarning(); + let syncCapsLockWarningVisibility = () => { + capsLockWarning.visible = + this.prompt.password_visible || this.prompt.confirm_visible; + }; + this.prompt.connect('notify::password-visible', syncCapsLockWarningVisibility); + this.prompt.connect('notify::confirm-visible', syncCapsLockWarningVisibility); + warningBox.add_child(capsLockWarning); + + let warning = new St.Label({ style_class: 'prompt-dialog-error-label' }); + warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; + warning.clutter_text.line_wrap = true; + this.prompt.bind_property('warning', + warning, 'text', GObject.BindingFlags.SYNC_CREATE); + this.prompt.connect('notify::warning-visible', () => { + warning.opacity = this.prompt.warning_visible ? 255 : 0; + }); + this.prompt.connect('notify::warning', () => { + if (this._passwordEntry && this.prompt.warning !== '') + Util.wiggle(this._passwordEntry); + }); + warningBox.add_child(warning); + + passwordBox.add_child(warningBox); + content.add_child(passwordBox); + + this._choice = new CheckBox.CheckBox2(); + this.prompt.bind_property('choice-label', this._choice.getLabelActor(), + 'text', GObject.BindingFlags.SYNC_CREATE); + this.prompt.bind_property('choice-chosen', this._choice, + 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL); + this.prompt.bind_property('choice-visible', this._choice, + 'visible', GObject.BindingFlags.SYNC_CREATE); + content.add_child(this._choice); + + this.contentLayout.add_child(content); + + this._cancelButton = this.addButton({ + label: '', + action: this._onCancelButton.bind(this), + key: Clutter.KEY_Escape, + }); + this._continueButton = this.addButton({ + label: '', + action: this._onContinueButton.bind(this), + default: true, + }); + + 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); + } + + _updateSensitivity(sensitive) { + if (this._passwordEntry) + this._passwordEntry.reactive = sensitive; + + if (this._confirmEntry) + this._confirmEntry.reactive = sensitive; + + this._continueButton.can_focus = sensitive; + this._continueButton.reactive = sensitive; + } + + _ensureOpen() { + // 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 + // Cinnamon 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() { + this._ensureOpen(); + this._updateSensitivity(true); + this._passwordEntry.text = ''; + this._passwordEntry.grab_key_focus(); + } + + _onShowConfirm() { + this._ensureOpen(); + this._updateSensitivity(true); + this._confirmEntry.text = ''; + this._continueButton.grab_key_focus(); + } + + _onHidePrompt() { + this.close(); + } + + _onPasswordActivate() { + if (this.prompt.confirm_visible) + this._confirmEntry.grab_key_focus(); + else + this._onContinueButton(); + } + + _onConfirmActivate() { + this._onContinueButton(); + } + + _onContinueButton() { + this._updateSensitivity(false); + this.prompt.complete(); + } + + _onCancelButton() { + this.prompt.cancel(); + } +}); + +function init() { + prompter = new Gcr.SystemPrompter(); + prompter.connect('new-prompt', () => { + 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.ALLOW_REPLACEMENT, null, null); +} diff --git a/js/ui/main.js b/js/ui/main.js index 12bcfc0cfc..40996653ba 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -103,6 +103,7 @@ const Expo = imports.ui.expo; const Panel = imports.ui.panel; const PlacesManager = imports.ui.placesManager; const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent; +const KeyringPrompt = imports.ui.keyringPrompt; const RunDialog = imports.ui.runDialog; const Layout = imports.ui.layout; const LookingGlass = imports.ui.lookingGlass; @@ -434,6 +435,8 @@ function start() { PolkitAuthenticationAgent.init(); } + KeyringPrompt.init(); + _startDate = new Date(); global.display.connect('restart', () => { diff --git a/meson.build b/meson.build index bbcce8d084..818450ecbd 100644 --- a/meson.build +++ b/meson.build @@ -28,6 +28,7 @@ cmenu = dependency('libcinnamon-menu-3.0', version: '>= 4.8.0') cogl = dependency('muffin-cogl-0') cogl_path = dependency('muffin-cogl-path-0') dbus = dependency('dbus-1') +gcr = dependency('gcr-base-3', version:'>=3.7.5') gdkx11 = dependency('gdk-x11-3.0') gi = dependency('gobject-introspection-1.0', version: '>= 0.9.2') polkit = dependency('polkit-agent-1', version: '>= 0.100') diff --git a/src/cinnamon-keyring-prompt.c b/src/cinnamon-keyring-prompt.c new file mode 100644 index 0000000000..f70b71b658 --- /dev/null +++ b/src/cinnamon-keyring-prompt.c @@ -0,0 +1,833 @@ + +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright 2012 Red Hat, Inc. + * 2012 Stef 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Author: Stef Walter + */ + +#include "config.h" + +#include "cinnamon-keyring-prompt.h" +#include "cinnamon-secure-text-buffer.h" + +#define GCR_API_SUBJECT_TO_CHANGE +#include + +#include + +#include + +typedef struct _CinnamonPasswordPromptPrivate CinnamonPasswordPromptPrivate; + +typedef enum +{ + PROMPTING_NONE, + PROMPTING_FOR_CONFIRM, + PROMPTING_FOR_PASSWORD +} PromptingMode; + +struct _CinnamonKeyringPrompt +{ + 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; + + GTask *task; + ClutterText *password_actor; + ClutterText *confirm_actor; + PromptingMode mode; + gboolean shown; +}; + +enum { + PROP_0, + + PROP_PASSWORD_VISIBLE, + PROP_CONFIRM_VISIBLE, + PROP_WARNING_VISIBLE, + PROP_CHOICE_VISIBLE, + PROP_PASSWORD_ACTOR, + PROP_CONFIRM_ACTOR, + + N_PROPS, + + /* GcrPrompt */ + 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 +}; + +static GParamSpec *props[N_PROPS] = { NULL, }; + +static void cinnamon_keyring_prompt_iface (GcrPromptIface *iface); + +G_DEFINE_TYPE_WITH_CODE (CinnamonKeyringPrompt, cinnamon_keyring_prompt, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GCR_TYPE_PROMPT, cinnamon_keyring_prompt_iface); +); + +enum { + SIGNAL_SHOW_PASSWORD, + SIGNAL_SHOW_CONFIRM, + SIGNAL_LAST +}; + +static gint signals[SIGNAL_LAST]; + +static void +cinnamon_keyring_prompt_init (CinnamonKeyringPrompt *self) +{ + +} + +static gchar * +remove_mnemonics (const GValue *value) +{ + const gchar mnemonic = '_'; + gchar *stripped_label, *temp; + const gchar *label; + + g_return_val_if_fail (value != NULL, NULL); + g_return_val_if_fail (G_VALUE_HOLDS_STRING (value), NULL); + + label = g_value_get_string (value); + if (!label) + return NULL; + + /* Stripped label will have the original label length at most */ + stripped_label = temp = g_new (gchar, strlen(label) + 1); + g_assert (stripped_label != NULL); + + while (*label != '\0') + { + if (*label == mnemonic) + label++; + *(temp++) = *(label++); + } + *temp = '\0'; + + return stripped_label; +} + +static void +cinnamon_keyring_prompt_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CinnamonKeyringPrompt *self = CINNAMON_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_by_pspec (obj, props[PROP_WARNING_VISIBLE]); + break; + case PROP_CHOICE_LABEL: + g_free (self->choice_label); + self->choice_label = remove_mnemonics (value); + if (!self->choice_label) + self->choice_label = g_strdup (""); + g_object_notify (obj, "choice-label"); + g_object_notify_by_pspec (obj, props[PROP_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_by_pspec (obj, props[PROP_CONFIRM_VISIBLE]); + break; + case PROP_CALLER_WINDOW: + /* ignored */ + break; + case PROP_CONTINUE_LABEL: + g_free (self->continue_label); + self->continue_label = remove_mnemonics (value); + g_object_notify (obj, "continue-label"); + break; + case PROP_CANCEL_LABEL: + g_free (self->cancel_label); + self->cancel_label = remove_mnemonics (value); + g_object_notify (obj, "cancel-label"); + break; + case PROP_PASSWORD_ACTOR: + cinnamon_keyring_prompt_set_password_actor (self, g_value_get_object (value)); + break; + case PROP_CONFIRM_ACTOR: + cinnamon_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 +cinnamon_keyring_prompt_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CinnamonKeyringPrompt *self = CINNAMON_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, cinnamon_keyring_prompt_get_password_actor (self)); + break; + case PROP_CONFIRM_ACTOR: + g_value_set_object (value, cinnamon_keyring_prompt_get_confirm_actor (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +cinnamon_keyring_prompt_dispose (GObject *obj) +{ + CinnamonKeyringPrompt *self = CINNAMON_KEYRING_PROMPT (obj); + + if (self->shown) + gcr_prompt_close (GCR_PROMPT (self)); + + if (self->task) + cinnamon_keyring_prompt_cancel (self); + g_assert (self->task == NULL); + + cinnamon_keyring_prompt_set_password_actor (self, NULL); + cinnamon_keyring_prompt_set_confirm_actor (self, NULL); + + G_OBJECT_CLASS (cinnamon_keyring_prompt_parent_class)->dispose (obj); +} + +static void +cinnamon_keyring_prompt_finalize (GObject *obj) +{ + CinnamonKeyringPrompt *self = CINNAMON_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 (cinnamon_keyring_prompt_parent_class)->finalize (obj); +} + +static void +cinnamon_keyring_prompt_class_init (CinnamonKeyringPromptClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->get_property = cinnamon_keyring_prompt_get_property; + gobject_class->set_property = cinnamon_keyring_prompt_set_property; + gobject_class->dispose = cinnamon_keyring_prompt_dispose; + gobject_class->finalize = cinnamon_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"); + + /** + * CinnamonKeyringPrompt:password-visible: + * + * Whether the password entry is visible or not. + */ + props[PROP_PASSWORD_VISIBLE] = + g_param_spec_boolean ("password-visible", + "Password visible", + "Password field is visible", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * CinnamonKeyringPrompt:confirm-visible: + * + * Whether the password confirm entry is visible or not. + */ + props[PROP_CONFIRM_VISIBLE] = + g_param_spec_boolean ("confirm-visible", + "Confirm visible", + "Confirm field is visible", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * CinnamonKeyringPrompt:warning-visible: + * + * Whether the warning label is visible or not. + */ + props[PROP_WARNING_VISIBLE] = + g_param_spec_boolean ("warning-visible", + "Warning visible", + "Warning is visible", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * CinnamonKeyringPrompt:choice-visible: + * + * Whether the choice check box is visible or not. + */ + props[PROP_CHOICE_VISIBLE] = + g_param_spec_boolean ("choice-visible", + "Choice visible", + "Choice is visible", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + /** + * CinnamonKeyringPrompt:password-actor: + * + * Text field for password + */ + props[PROP_PASSWORD_ACTOR] = + g_param_spec_object ("password-actor", + "Password actor", + "Text field for password", + CLUTTER_TYPE_TEXT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * CinnamonKeyringPrompt:confirm-actor: + * + * Text field for confirmation password + */ + props[PROP_CONFIRM_ACTOR] = + g_param_spec_object ("confirm-actor", + "Confirm actor", + "Text field for confirming password", + CLUTTER_TYPE_TEXT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (gobject_class, N_PROPS, props); + + 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); +} + +static void +cinnamon_keyring_prompt_password_async (GcrPrompt *prompt, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + CinnamonKeyringPrompt *self = CINNAMON_KEYRING_PROMPT (prompt); + GObject *obj; + + if (self->task != NULL) { + g_warning ("this prompt can only show one prompt at a time"); + return; + } + + self->mode = PROMPTING_FOR_PASSWORD; + self->task = g_task_new (self, NULL, callback, user_data); + g_task_set_source_tag (self->task, cinnamon_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 * +cinnamon_keyring_prompt_password_finish (GcrPrompt *prompt, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_get_source_object (G_TASK (result)) == prompt, NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, + cinnamon_keyring_prompt_password_async), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + +static void +cinnamon_keyring_prompt_confirm_async (GcrPrompt *prompt, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + CinnamonKeyringPrompt *self = CINNAMON_KEYRING_PROMPT (prompt); + GObject *obj; + + if (self->task != NULL) { + g_warning ("this prompt is already prompting"); + return; + } + + self->mode = PROMPTING_FOR_CONFIRM; + self->task = g_task_new (self, NULL, callback, user_data); + g_task_set_source_tag (self->task, cinnamon_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 +cinnamon_keyring_prompt_confirm_finish (GcrPrompt *prompt, + GAsyncResult *result, + GError **error) +{ + GTask *task = G_TASK (result); + gssize res; + + g_return_val_if_fail (g_task_get_source_object (task) == prompt, + GCR_PROMPT_REPLY_CANCEL); + g_return_val_if_fail (g_async_result_is_tagged (result, + cinnamon_keyring_prompt_confirm_async), GCR_PROMPT_REPLY_CANCEL); + + res = g_task_propagate_int (task, error); + return res == -1 ? GCR_PROMPT_REPLY_CANCEL : (GcrPromptReply)res; +} + +static void +cinnamon_keyring_prompt_close (GcrPrompt *prompt) +{ + CinnamonKeyringPrompt *self = CINNAMON_KEYRING_PROMPT (prompt); + + /* + * We expect keyring.js to connect to this signal and do the + * actual work of closing the prompt. + */ + + self->shown = FALSE; +} + +static void +cinnamon_keyring_prompt_iface (GcrPromptIface *iface) +{ + iface->prompt_password_async = cinnamon_keyring_prompt_password_async; + iface->prompt_password_finish = cinnamon_keyring_prompt_password_finish; + iface->prompt_confirm_async = cinnamon_keyring_prompt_confirm_async; + iface->prompt_confirm_finish = cinnamon_keyring_prompt_confirm_finish; + iface->prompt_close = cinnamon_keyring_prompt_close; +} + +/** + * cinnamon_keyring_prompt_new: + * + * Create new internal prompt base + * + * Returns: (transfer full): new internal prompt + */ +CinnamonKeyringPrompt * +cinnamon_keyring_prompt_new (void) +{ + return g_object_new (CINNAMON_TYPE_KEYRING_PROMPT, NULL); +} + +/** + * cinnamon_keyring_prompt_get_password_actor: + * @self: the internal prompt + * + * Get the prompt password text actor + * + * Returns: (transfer none) (nullable): the password actor + */ +ClutterText * +cinnamon_keyring_prompt_get_password_actor (CinnamonKeyringPrompt *self) +{ + g_return_val_if_fail (CINNAMON_IS_KEYRING_PROMPT (self), NULL); + return self->password_actor; +} + +/** + * cinnamon_keyring_prompt_get_confirm_actor: + * @self: the internal prompt + * + * Get the prompt password text actor + * + * Returns: (transfer none) (nullable): the password actor + */ +ClutterText * +cinnamon_keyring_prompt_get_confirm_actor (CinnamonKeyringPrompt *self) +{ + g_return_val_if_fail (CINNAMON_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) +{ + CinnamonKeyringPrompt *self = CINNAMON_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"); +} + +/** + * cinnamon_keyring_prompt_set_password_actor: + * @self: the internal prompt + * @password_actor: (nullable): the password actor + * + * Set the prompt password text actor + */ +void +cinnamon_keyring_prompt_set_password_actor (CinnamonKeyringPrompt *self, + ClutterText *password_actor) +{ + ClutterTextBuffer *buffer; + + g_return_if_fail (CINNAMON_IS_KEYRING_PROMPT (self)); + g_return_if_fail (password_actor == NULL || CLUTTER_IS_TEXT (password_actor)); + + if (self->password_actor == password_actor) + return; + + if (password_actor) + { + buffer = cinnamon_secure_text_buffer_new (); + clutter_text_set_buffer (password_actor, buffer); + g_object_unref (buffer); + + 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_by_pspec (G_OBJECT (self), props[PROP_PASSWORD_ACTOR]); +} + +/** + * cinnamon_keyring_prompt_set_confirm_actor: + * @self: the internal prompt + * @confirm_actor: (nullable): the confirm password actor + * + * Set the prompt password confirmation text actor + */ +void +cinnamon_keyring_prompt_set_confirm_actor (CinnamonKeyringPrompt *self, + ClutterText *confirm_actor) +{ + ClutterTextBuffer *buffer; + + g_return_if_fail (CINNAMON_IS_KEYRING_PROMPT (self)); + g_return_if_fail (confirm_actor == NULL || CLUTTER_IS_TEXT (confirm_actor)); + + if (self->confirm_actor == confirm_actor) + return; + + if (confirm_actor) + { + buffer = cinnamon_secure_text_buffer_new (); + clutter_text_set_buffer (confirm_actor, buffer); + g_object_unref (buffer); + + g_object_ref (confirm_actor); + } + if (self->confirm_actor) + g_object_unref (self->confirm_actor); + self->confirm_actor = confirm_actor; + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CONFIRM_ACTOR]); +} + +/** + * cinnamon_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 +cinnamon_keyring_prompt_complete (CinnamonKeyringPrompt *self) +{ + GTask *res; + PromptingMode mode; + const gchar *password; + const gchar *confirm; + const gchar *env; + + g_return_val_if_fail (CINNAMON_IS_KEYRING_PROMPT (self), FALSE); + g_return_val_if_fail (self->mode != PROMPTING_NONE, FALSE); + g_return_val_if_fail (self->task != NULL, FALSE); + + password = clutter_text_get_text (self->password_actor); + + if (self->mode == PROMPTING_FOR_PASSWORD) + { + /* 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"); + } + + res = self->task; + mode = self->mode; + self->task = NULL; + self->mode = PROMPTING_NONE; + + if (mode == PROMPTING_FOR_CONFIRM) + g_task_return_int (res, (gssize)GCR_PROMPT_REPLY_CONTINUE); + else + g_task_return_pointer (res, (gpointer)password, NULL); + g_object_unref (res); + + return TRUE; +} + +/** + * cinnamon_keyring_prompt_cancel: + * @self: the internal prompt + * + * Called by implementation when the prompt is cancelled. + */ +void +cinnamon_keyring_prompt_cancel (CinnamonKeyringPrompt *self) +{ + GTask *res; + PromptingMode mode; + + g_return_if_fail (CINNAMON_IS_KEYRING_PROMPT (self)); + + /* + * If cancelled while not prompting, we should just close the prompt, + * the user wants it to go away. + */ + if (self->mode == PROMPTING_NONE) + { + if (self->shown) + gcr_prompt_close (GCR_PROMPT (self)); + return; + } + + g_return_if_fail (self->task != NULL); + + res = self->task; + mode = self->mode; + self->task = NULL; + self->mode = PROMPTING_NONE; + + if (mode == PROMPTING_FOR_CONFIRM) + g_task_return_int (res, (gssize) GCR_PROMPT_REPLY_CANCEL); + else + g_task_return_pointer (res, NULL, NULL); + g_object_unref (res); +} diff --git a/src/cinnamon-keyring-prompt.h b/src/cinnamon-keyring-prompt.h new file mode 100644 index 0000000000..a01dd1a1ef --- /dev/null +++ b/src/cinnamon-keyring-prompt.h @@ -0,0 +1,58 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* cinnamon-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 +*/ + +#ifndef __CINNAMON_KEYRING_PROMPT_H__ +#define __CINNAMON_KEYRING_PROMPT_H__ + +#include +#include + +#include + +G_BEGIN_DECLS + +typedef struct _CinnamonKeyringPrompt CinnamonKeyringPrompt; + +#define CINNAMON_TYPE_KEYRING_PROMPT (cinnamon_keyring_prompt_get_type ()) +G_DECLARE_FINAL_TYPE (CinnamonKeyringPrompt, cinnamon_keyring_prompt, + CINNAMON, KEYRING_PROMPT, GObject) + +CinnamonKeyringPrompt * cinnamon_keyring_prompt_new (void); + +ClutterText * cinnamon_keyring_prompt_get_password_actor (CinnamonKeyringPrompt *self); + +void cinnamon_keyring_prompt_set_password_actor (CinnamonKeyringPrompt *self, + ClutterText *password_actor); + +ClutterText * cinnamon_keyring_prompt_get_confirm_actor (CinnamonKeyringPrompt *self); + +void cinnamon_keyring_prompt_set_confirm_actor (CinnamonKeyringPrompt *self, + ClutterText *confirm_actor); + +gboolean cinnamon_keyring_prompt_complete (CinnamonKeyringPrompt *self); + +void cinnamon_keyring_prompt_cancel (CinnamonKeyringPrompt *self); + +G_END_DECLS + +#endif /* __CINNAMON_KEYRING_PROMPT_H__ */ + diff --git a/src/cinnamon-secure-text-buffer.c b/src/cinnamon-secure-text-buffer.c new file mode 100644 index 0000000000..03a6497bd4 --- /dev/null +++ b/src/cinnamon-secure-text-buffer.c @@ -0,0 +1,192 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* cinnamon-secure-text-buffer.c - secure memory clutter text buffer + + Copyright (C) 2009 Stefan Walter + Copyright (C) 2012 Red Hat Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + see . + + Author: Stef Walter +*/ + +#include "config.h" + +#include "cinnamon-secure-text-buffer.h" + +#define GCR_API_SUBJECT_TO_CHANGE +#include + +#include + +struct _CinnamonSecureTextBuffer { + ClutterTextBuffer parent; + gchar *text; + gsize text_size; + gsize text_bytes; + guint text_chars; +}; + +/* Initial size of buffer, in bytes */ +#define MIN_SIZE 16 + +G_DEFINE_TYPE (CinnamonSecureTextBuffer, cinnamon_secure_text_buffer, CLUTTER_TYPE_TEXT_BUFFER); + +static const gchar * +cinnamon_secure_text_buffer_real_get_text (ClutterTextBuffer *buffer, + gsize *n_bytes) +{ + CinnamonSecureTextBuffer *self = CINNAMON_SECURE_TEXT_BUFFER (buffer); + if (n_bytes) + *n_bytes = self->text_bytes; + if (!self->text) + return ""; + return self->text; +} + +static guint +cinnamon_secure_text_buffer_real_get_length (ClutterTextBuffer *buffer) +{ + CinnamonSecureTextBuffer *self = CINNAMON_SECURE_TEXT_BUFFER (buffer); + return self->text_chars; +} + +static guint +cinnamon_secure_text_buffer_real_insert_text (ClutterTextBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars) +{ + CinnamonSecureTextBuffer *self = CINNAMON_SECURE_TEXT_BUFFER (buffer); + gsize n_bytes; + gsize at; + + n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars; + + /* Need more memory */ + if (n_bytes + self->text_bytes + 1 > self->text_size) + { + /* Calculate our new buffer size */ + while (n_bytes + self->text_bytes + 1 > self->text_size) + { + if (self->text_size == 0) + { + self->text_size = MIN_SIZE; + } + else + { + if (2 * self->text_size < CLUTTER_TEXT_BUFFER_MAX_SIZE) + { + self->text_size *= 2; + } + else + { + self->text_size = CLUTTER_TEXT_BUFFER_MAX_SIZE; + if (n_bytes > self->text_size - self->text_bytes - 1) + { + n_bytes = self->text_size - self->text_bytes - 1; + n_bytes = g_utf8_find_prev_char (chars, chars + n_bytes + 1) - chars; + n_chars = g_utf8_strlen (chars, n_bytes); + } + break; + } + } + } + self->text = gcr_secure_memory_realloc (self->text, self->text_size); + } + + /* Actual text insertion */ + at = g_utf8_offset_to_pointer (self->text, position) - self->text; + memmove (self->text + at + n_bytes, self->text + at, self->text_bytes - at); + memcpy (self->text + at, chars, n_bytes); + + /* Book keeping */ + self->text_bytes += n_bytes; + self->text_chars += n_chars; + self->text[self->text_bytes] = '\0'; + + clutter_text_buffer_emit_inserted_text (buffer, position, chars, n_chars); + return n_chars; +} + +static guint +cinnamon_secure_text_buffer_real_delete_text (ClutterTextBuffer *buffer, + guint position, + guint n_chars) +{ + CinnamonSecureTextBuffer *self = CINNAMON_SECURE_TEXT_BUFFER (buffer); + gsize start, end; + + if (position > self->text_chars) + position = self->text_chars; + if (position + n_chars > self->text_chars) + n_chars = self->text_chars - position; + + if (n_chars > 0) + { + start = g_utf8_offset_to_pointer (self->text, position) - self->text; + end = g_utf8_offset_to_pointer (self->text, position + n_chars) - self->text; + + memmove (self->text + start, self->text + end, self->text_bytes + 1 - end); + self->text_chars -= n_chars; + self->text_bytes -= (end - start); + + clutter_text_buffer_emit_deleted_text (buffer, position, n_chars); + } + + return n_chars; +} + +static void +cinnamon_secure_text_buffer_init (CinnamonSecureTextBuffer *self) +{ + +} + +static void +cinnamon_secure_text_buffer_finalize (GObject *obj) +{ + CinnamonSecureTextBuffer *self = CINNAMON_SECURE_TEXT_BUFFER (obj); + + if (self->text) + { + gcr_secure_memory_strfree (self->text); + self->text = NULL; + self->text_bytes = self->text_size = 0; + self->text_chars = 0; + } + + G_OBJECT_CLASS (cinnamon_secure_text_buffer_parent_class)->finalize (obj); +} + +static void +cinnamon_secure_text_buffer_class_init (CinnamonSecureTextBufferClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterTextBufferClass *buffer_class = CLUTTER_TEXT_BUFFER_CLASS (klass); + + gobject_class->finalize = cinnamon_secure_text_buffer_finalize; + + buffer_class->get_text = cinnamon_secure_text_buffer_real_get_text; + buffer_class->get_length = cinnamon_secure_text_buffer_real_get_length; + buffer_class->insert_text = cinnamon_secure_text_buffer_real_insert_text; + buffer_class->delete_text = cinnamon_secure_text_buffer_real_delete_text; +} + +ClutterTextBuffer * +cinnamon_secure_text_buffer_new (void) +{ + return g_object_new (CINNAMON_TYPE_SECURE_TEXT_BUFFER, NULL); +} + diff --git a/src/cinnamon-secure-text-buffer.h b/src/cinnamon-secure-text-buffer.h new file mode 100644 index 0000000000..902b882503 --- /dev/null +++ b/src/cinnamon-secure-text-buffer.h @@ -0,0 +1,40 @@ + +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* cinnamon-secure-text-buffer.h - secure memory clutter text buffer + + Copyright (C) 2009 Stefan Walter + Copyright (C) 2012 Red Hat Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + see . + + Author: Stef Walter +*/ + +#ifndef __CINNAMON_SECURE_TEXT_BUFFER_H__ +#define __CINNAMON_SECURE_TEXT_BUFFER_H__ + +#include + +G_BEGIN_DECLS + +#define CINNAMON_TYPE_SECURE_TEXT_BUFFER (cinnamon_secure_text_buffer_get_type ()) +G_DECLARE_FINAL_TYPE (CinnamonSecureTextBuffer, cinnamon_secure_text_buffer, + CINNAMON, SECURE_TEXT_BUFFER, ClutterTextBuffer) + +ClutterTextBuffer * cinnamon_secure_text_buffer_new (void); + +G_END_DECLS + +#endif /* __CINNAMON_SECURE_TEXT_BUFFER_H__ */ diff --git a/src/meson.build b/src/meson.build index e952e5206f..5c05d3a684 100644 --- a/src/meson.build +++ b/src/meson.build @@ -51,11 +51,15 @@ cinnamon_sources = [ 'cinnamon-glsl-effect.c', 'cinnamon-gtk-embed.c', 'cinnamon-global.c', + 'cinnamon-keyring-prompt.c', + 'cinnamon-keyring-prompt.h', 'cinnamon-perf-log.c', 'cinnamon-polkit-authentication-agent.c', 'cinnamon-polkit-authentication-agent.h', 'cinnamon-screen.c', 'cinnamon-screenshot.c', + 'cinnamon-secure-text-buffer.c', + 'cinnamon-secure-text-buffer.h', 'cinnamon-slicer.c', 'cinnamon-stack.c', 'cinnamon-touchegg-client.c', @@ -94,6 +98,7 @@ libcinnamon_deps = [ cogl, cogl_path, dbus, + gcr, gdkx11, gi, gio, @@ -191,6 +196,7 @@ cinnamon_gir_includes = [ 'Cogl-0', 'Meta-0', 'CMenu-3.0', + 'Gcr-3', 'PolkitAgent-1.0', st_gir[0], ]