[seahorse] gkr: Port the gnome-keyring/libsecret stuff to vala
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [seahorse] gkr: Port the gnome-keyring/libsecret stuff to vala
- Date: Wed, 26 Jun 2013 10:19:34 +0000 (UTC)
commit 7b202f99b4f66f4b13ae610be4159382cb98ec59
Author: Stef Walter <stefw gnome org>
Date: Thu Jun 20 17:09:39 2013 +0200
gkr: Port the gnome-keyring/libsecret stuff to vala
gkr/Makefile.am | 35 +-
gkr/gkr-backend.vala | 274 ++++++++++
gkr/gkr-dialogs.vala | 4 +-
gkr/gkr-item-add.vala | 121 +++++
gkr/gkr-item-properties.vala | 299 +++++++++++
gkr/gkr-item.vala | 540 ++++++++++++++++++++
gkr/gkr-keyring-add.vala | 78 +++
gkr/gkr-keyring-properties.vala | 81 +++
gkr/gkr-keyring.vala | 275 ++++++++++
gkr/gkr-module.vala | 4 +-
gkr/seahorse-add-keyring.xml | 63 +---
gkr/seahorse-gkr-actions.c | 304 -----------
gkr/seahorse-gkr-actions.h | 34 --
gkr/seahorse-gkr-add-item.c | 188 -------
gkr/seahorse-gkr-add-item.xml | 73 +---
gkr/seahorse-gkr-add-keyring.c | 170 -------
gkr/seahorse-gkr-backend.c | 530 -------------------
gkr/seahorse-gkr-backend.h | 69 ---
gkr/seahorse-gkr-dialogs.h | 41 --
gkr/seahorse-gkr-item-deleter.c | 243 ---------
gkr/seahorse-gkr-item-deleter.h | 42 --
gkr/seahorse-gkr-item-properties.c | 567 ---------------------
gkr/seahorse-gkr-item-properties.xml | 71 +---
gkr/seahorse-gkr-item.c | 899 ---------------------------------
gkr/seahorse-gkr-item.h | 94 ----
gkr/seahorse-gkr-keyring-deleter.c | 182 -------
gkr/seahorse-gkr-keyring-deleter.h | 42 --
gkr/seahorse-gkr-keyring-properties.c | 109 ----
gkr/seahorse-gkr-keyring.c | 525 -------------------
gkr/seahorse-gkr-keyring.h | 56 --
gkr/seahorse-gkr-keyring.xml | 16 +-
src/seahorse-sidebar.c | 13 +-
32 files changed, 1697 insertions(+), 4345 deletions(-)
---
diff --git a/gkr/Makefile.am b/gkr/Makefile.am
index 029ecf4..ba3d781 100644
--- a/gkr/Makefile.am
+++ b/gkr/Makefile.am
@@ -10,13 +10,13 @@ INCLUDES = -I$(top_builddir) \
-I$(top_srcdir)/libseahorse \
$(SEAHORSE_CFLAGS) \
-DSEAHORSE_UIDIR=\""$(uidir)/"\" \
+ -DUIDIR=\""$(uidir)/"\" \
-DLOCALEDIR=\"$(localedir)\" \
-DEXECDIR=\""$(seahorselibexecbindir)"\" \
-DGETTEXT_PACKAGE=\""seahorse\"" \
-DSECRET_API_SUBJECT_TO_CHANGE
noinst_LTLIBRARIES = \
- libvala-code.la \
libseahorse-gkr.la
AM_VALAFLAGS = \
@@ -25,37 +25,32 @@ AM_VALAFLAGS = \
--header=seahorse-gkr.h \
--vapidir $(top_builddir)/common \
--pkg common \
+ --pkg config \
--pkg gtk+-3.0 \
--pkg gcr-3 \
+ --pkg gcr-ui-3 \
--pkg libsecret-1 \
+ --pkg libsecret-unstable \
$(NULL)
-libvala_code_la_SOURCES = \
+libseahorse_gkr_la_CFLAGS = \
+ -include config.h -w
+
+libseahorse_gkr_la_SOURCES = \
+ gkr-backend.vala \
gkr-dialogs.vala \
gkr-module.vala \
+ gkr-item.vala \
+ gkr-item-add.vala \
+ gkr-item-properties.vala \
+ gkr-keyring.vala \
+ gkr-keyring-add.vala \
+ gkr-keyring-properties.vala \
$(NULL)
-libvala_code_la_CFLAGS = \
- -include config.h -w \
- $(NULL)
-
-libseahorse_gkr_la_SOURCES = \
- seahorse-gkr-actions.c seahorse-gkr-actions.h \
- seahorse-gkr-add-item.c \
- seahorse-gkr-add-keyring.c \
- seahorse-gkr-backend.c seahorse-gkr-backend.h \
- seahorse-gkr-dialogs.h \
- seahorse-gkr-item.c seahorse-gkr-item.h \
- seahorse-gkr-item-deleter.c seahorse-gkr-item-deleter.h \
- seahorse-gkr-item-properties.c \
- seahorse-gkr-keyring.c seahorse-gkr-keyring.h \
- seahorse-gkr-keyring-deleter.c seahorse-gkr-keyring-deleter.h \
- seahorse-gkr-keyring-properties.c
-
libseahorse_gkr_la_LIBADD = \
$(top_builddir)/common/libcommon.la \
$(top_builddir)/libseahorse/libseahorse.la \
- libvala-code.la \
$(NULL)
ui_DATA = \
diff --git a/gkr/gkr-backend.vala b/gkr/gkr-backend.vala
new file mode 100644
index 0000000..abc062d
--- /dev/null
+++ b/gkr/gkr-backend.vala
@@ -0,0 +1,274 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program 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; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+namespace Seahorse {
+namespace Gkr {
+
+private class MyService : Secret.Service {
+ public override GLib.Type get_collection_gtype() {
+ return typeof(Keyring);
+ }
+
+ public override GLib.Type get_item_gtype() {
+ return typeof(Item);
+ }
+}
+
+public class Backend: GLib.Object , Gcr.Collection, Seahorse.Backend {
+ public string name {
+ get { return NAME; }
+ }
+
+ public string label {
+ get { return _("Passwords"); }
+ }
+
+ public string description {
+ get { return _("Stored personal passwords, credentials and secrets"); }
+ }
+
+ public Gtk.ActionGroup actions {
+ owned get { return this._actions; }
+ }
+
+ public GLib.HashTable<string, string> aliases {
+ get { return this._aliases; }
+ }
+
+ public Secret.Service? service {
+ get { return this._service; }
+ }
+
+ private static Backend? _instance = null;
+ private Secret.Service _service;
+ private GLib.HashTable<string, Keyring> _keyrings;
+ private GLib.HashTable<string, string> _aliases;
+ private Gtk.ActionGroup _actions;
+
+ construct {
+ return_if_fail(_instance == null);
+ Backend._instance = this;
+
+ this._actions = BackendActions.instance(this);
+ this._keyrings = new GLib.HashTable<string, Keyring>(GLib.str_hash, GLib.str_equal);
+ this._aliases = new GLib.HashTable<string, string>(GLib.str_hash, GLib.str_equal);
+
+ Secret.Service.open.begin(typeof(MyService), null,
+ Secret.ServiceFlags.OPEN_SESSION, null, (obj, res) => {
+ try {
+ this._service = Secret.Service.open.end(res);
+ this._service.notify.connect((pspec) => {
+ if (pspec.name == "collections")
+ refresh_collections();
+ });
+ this._service.load_collections.begin(null, (obj, res) => {
+ refresh_collections();
+ });
+ refresh_aliases();
+ } catch (GLib.Error err) {
+ GLib.warning("couldn't connect to secret service: %s", err.message);
+ }
+ notify_property("service");
+ });
+ }
+
+ public override void dispose() {
+ this._aliases.remove_all();
+ this._keyrings.remove_all();
+ this._actions.sensitive = false;
+ base.dispose();
+ }
+
+ public void refresh_collections() {
+ var seen = new GLib.HashTable<string, weak string>(GLib.str_hash, GLib.str_equal);
+ var keyrings = this._service.get_collections();
+
+ string object_path;
+ foreach (var keyring in keyrings) {
+ object_path = keyring.get_object_path();
+
+ /* Don't list the session keyring */
+ if (this._aliases.lookup("session") == object_path)
+ continue;
+
+ seen.add(object_path);
+ if (this._keyrings.lookup(object_path) == null) {
+ this._keyrings.insert(object_path, (Keyring)keyring);
+ emit_added(keyring);
+ }
+ }
+
+ /* Remove any that we didn't find */
+ var iter = GLib.HashTableIter<string, Keyring>(this._keyrings);
+ while (iter.next(out object_path, null)) {
+ if (seen.lookup(object_path) == null) {
+ var keyring = this._keyrings.lookup(object_path);
+ iter.remove();
+ emit_removed(keyring);
+ }
+ }
+ }
+
+ public uint get_length() {
+ return this._keyrings.size();
+ }
+
+ public GLib.List<weak GLib.Object> get_objects() {
+ return get_keyrings();
+ }
+
+ public bool contains(GLib.Object object) {
+ if (object is Keyring) {
+ var keyring = (Keyring)object;
+ return this._keyrings.lookup(keyring.uri) == keyring;
+ }
+ return false;
+ }
+
+ public Place lookup_place(string uri) {
+ return this._keyrings.lookup(uri);
+ }
+
+ public static void initialize() {
+ return_if_fail(Backend._instance == null);
+ (new Backend()).register();
+ return_if_fail(Backend._instance != null);
+ }
+
+ public static Backend instance() {
+ return_if_fail(Backend._instance != null);
+ return Backend._instance;
+ }
+
+ public GLib.List<weak Keyring> get_keyrings() {
+ return this._keyrings.get_values();
+ }
+
+ private void read_alias(string name) {
+ if (this._service == null)
+ return;
+ this._service.read_alias_dbus_path.begin(name, null, (obj, res) => {
+ try {
+ var object_path = this._service.read_alias_dbus_path.end(res);
+ if (object_path != null) {
+ this._aliases.insert(name, object_path);
+ notify_property("aliases");
+ }
+ } catch (GLib.Error err) {
+ GLib.warning("Couldn't read secret service alias %s: %s", name, err.message);
+ }
+ });
+
+ }
+
+ private void refresh_aliases() {
+ read_alias("default");
+ read_alias("session");
+ read_alias("login");
+ }
+
+ public void refresh() {
+ refresh_aliases();
+ refresh_collections();
+ }
+ public bool has_alias(string alias,
+ Keyring keyring) {
+ string object_path = keyring.get_object_path();
+ return this._aliases.lookup(alias) == object_path;
+ }
+}
+
+public class BackendActions : Seahorse.Actions {
+ public Backend backend { construct; get; }
+ private static WeakRef _instance;
+ private bool _initialized;
+
+ construct {
+ this._initialized = false;
+ this.set_translation_domain(Config.GETTEXT_PACKAGE);
+
+ this.backend.notify.connect_after((pspec) => {
+ if (pspec.name == "service")
+ return;
+ if (this._initialized)
+ return;
+ if (this.backend.service == null)
+ return;
+
+ this._initialized = true;
+ this.add_actions(BACKEND_ACTIONS, null);
+ this.register_definition(BACKEND_UI);
+
+ /* Register another set of actions as a generator */
+ var actions = new Gtk.ActionGroup("gkr-generate");
+ actions.set_translation_domain(Config.GETTEXT_PACKAGE);
+ actions.add_actions(ENTRIES_NEW, null);
+ Registry.register_object(actions, "generator");
+ });
+
+ this.backend.notify_property("service");
+ }
+
+ private BackendActions(Backend backend) {
+ GLib.Object(name: "KeyringBackend", backend: backend);
+ }
+
+ private static void on_new_keyring(Gtk.Action action) {
+ new KeyringAdd(Action.get_window(action));
+ }
+
+ private static void on_new_item(Gtk.Action action) {
+ new ItemAdd(Action.get_window(action));
+ }
+
+ private static const Gtk.ActionEntry[] BACKEND_ACTIONS = {
+ { "keyring-new", null, N_("New password keyring"), "",
+ N_("Used to store application and network passwords"), on_new_keyring },
+ { "keyring-item-new", null, N_("New password..."), "",
+ N_("Safely store a password or secret."), on_new_item },
+ };
+
+ private static const Gtk.ActionEntry[] ENTRIES_NEW = {
+ { "keyring-new", "folder", N_("Password Keyring"), "",
+ N_("Used to store application and network passwords"), on_new_keyring },
+ { "keyring-item-new", Gcr.ICON_PASSWORD, N_("Stored Password"), "",
+ N_("Safely store a password or secret."), on_new_item }
+ };
+
+ private static const string BACKEND_UI =
+ """"<ui>
+ <popup name='SeahorseGkrBackend'>
+ <menuitem action='keyring-new'/>
+ </popup>
+ </ui>""";
+
+ public static Gtk.ActionGroup instance(Backend backend) {
+ BackendActions? actions = (BackendActions?)_instance.get();
+ if (actions != null)
+ return actions;
+ actions = new BackendActions(backend);
+ _instance.set(actions);
+ return actions;
+ }
+}
+
+}
+}
diff --git a/gkr/gkr-dialogs.vala b/gkr/gkr-dialogs.vala
index 94f2ea3..651673d 100644
--- a/gkr/gkr-dialogs.vala
+++ b/gkr/gkr-dialogs.vala
@@ -20,8 +20,9 @@
*/
namespace Seahorse {
+namespace Gkr {
-public class GkrDialog {
+public class Dialog {
private static void update_wait_cursor(Gtk.Widget widget) {
GLib.Cancellable? cancellable = widget.get_data("gkr-request");
@@ -81,3 +82,4 @@ public class GkrDialog {
}
}
+}
\ No newline at end of file
diff --git a/gkr/gkr-item-add.vala b/gkr/gkr-item-add.vala
new file mode 100644
index 0000000..173c484
--- /dev/null
+++ b/gkr/gkr-item-add.vala
@@ -0,0 +1,121 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 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.,
+ * 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+namespace Seahorse {
+namespace Gkr {
+
+public class ItemAdd : Gtk.Dialog {
+ construct {
+ this.title = _("Add Password");
+ this.modal = true;
+ this.window_position = Gtk.WindowPosition.CENTER_ON_PARENT;
+ this.border_width = 5;
+
+ var builder = Util.load_built_contents(this, "gkr-add-item");
+ this.add_buttons(Gtk.Stock.OK, Gtk.ResponseType.ACCEPT,
+ Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL);
+
+ /* Load up a list of all the keyrings, and select the default */
+ var combo = (Gtk.ComboBox)builder.get_object("item-keyring");
+ var store = new Gtk.ListStore(2, typeof(string), typeof(Secret.Collection));
+ combo.set_model(store);
+
+ var cell = new Gtk.CellRendererText();
+ combo.pack_start(cell, true);
+ combo.add_attribute(cell, "text", 0);
+
+ foreach (var keyring in Backend.instance().get_keyrings()) {
+ Gtk.TreeIter iter;
+ store.append(out iter);
+ store.set(iter,
+ 0, keyring.label,
+ 1, keyring,
+ -1);
+ if (keyring.is_default)
+ combo.set_active_iter(iter);
+ }
+
+ var label = (Gtk.Entry)builder.get_object("item-label");
+ this.set_response_sensitive(Gtk.ResponseType.ACCEPT, false);
+ label.changed.connect((editable) => {
+ var value = label.get_text();
+ this.set_response_sensitive(Gtk.ResponseType.ACCEPT, value != "");
+ });
+
+ var area = (Gtk.Container)builder.get_object("password-area");
+ var buffer = new Gcr.SecureEntryBuffer();
+ var entry = new Gtk.Entry.with_buffer(buffer);
+ entry.visibility = false;
+ area.add(entry);
+ entry.show();
+
+ var check = (Gtk.ToggleButton)builder.get_object("show-password");
+ check.toggled.connect(() => {
+ entry.visibility = check.active;
+ });
+
+ this.response.connect((resp) => {
+ if (resp != Gtk.ResponseType.ACCEPT) {
+ this.destroy();
+ return;
+ }
+
+ Gtk.TreeIter iter;
+ if (!combo.get_active_iter(out iter))
+ return;
+
+ Secret.Collection collection;
+ combo.model.get(iter, 1, out collection, -1);
+
+ var secret = new Secret.Value(entry.get_text(), -1, "text/plain");
+ var cancellable = Dialog.begin_request(this);
+ var attributes = new GLib.HashTable<string, string>(GLib.str_hash, GLib.str_equal);
+
+ /* TODO: Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=697681 */
+ var schema = new Secret.Schema("org.gnome.keyring.Note", Secret.SchemaFlags.NONE);
+
+ Secret.Item.create.begin(collection, schema, attributes,
+ label.get_text(), secret, Secret.ItemCreateFlags.NONE,
+ cancellable, (obj, res) => {
+ try {
+ /* Clear the operation without cancelling it since it is complete */
+ Dialog.complete_request(this, false);
+
+ Secret.Item.create.end(res);
+ } catch (GLib.Error err) {
+ Util.show_error(this, _("Couldn't add item"), err.message);
+ }
+
+ this.destroy();
+ });
+
+ });
+ }
+
+ public ItemAdd(Gtk.Window? parent) {
+ GLib.Object(transient_for: parent);
+ this.show();
+ this.present();
+ }
+}
+
+}
+}
diff --git a/gkr/gkr-item-properties.vala b/gkr/gkr-item-properties.vala
new file mode 100644
index 0000000..0c3e447
--- /dev/null
+++ b/gkr/gkr-item-properties.vala
@@ -0,0 +1,299 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2006 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+namespace Seahorse {
+namespace Gkr {
+
+public class ItemProperties : Gtk.Dialog {
+ public Item item { construct; get; }
+
+ private Gtk.Builder _builder;
+ private Gtk.Entry _password_entry;
+ private Gtk.Expander _password_expander;
+ private bool _password_changed;
+ private bool _updating_password;
+ private bool _updating_description;
+
+ construct {
+ this._builder = new Gtk.Builder();
+ try {
+ string path = GLib.Path.build_filename(Config.UIDIR,
"seahorse-gkr-item-properties.xml");
+ this._builder.add_from_file(path);
+ } catch (GLib.Error err) {
+ GLib.critical ("%s", err.message);
+ }
+
+ this.add_button(Gtk.Stock.CLOSE, Gtk.ResponseType.CLOSE);
+ var content = (Gtk.Widget)this._builder.get_object("gkr-item-properties");
+ ((Gtk.Container)this.get_content_area()).add(content);
+ content.show();
+
+ this.response.connect((response) => {
+ this.destroy();
+ });
+
+ /* Setup the image properly */
+ this.item.bind_property ("icon", this._builder.get_object("key-image"), "gicon",
+ GLib.BindingFlags.SYNC_CREATE);
+
+ /* Setup the label properly */
+ Gtk.Entry description = (Gtk.Entry)this._builder.get_object("description-field");
+ this.item.bind_property("label", description, "text", GLib.BindingFlags.SYNC_CREATE);
+ description.activate.connect(() => {
+ description_activate(description);
+ });
+ description.focus_out_event.connect_after(() => {
+ description_activate(description);
+ return false;
+ });
+
+ /* The expander for showing password */
+ this._password_expander = (Gtk.Expander)this._builder.get_object("password-expander");
+ this._password_expander.activate.connect_after(expander_activate);
+
+ /* The check button for password visibility */
+ Gtk.CheckButton check = (Gtk.CheckButton)this._builder.get_object("show-password-check");
+ check.toggled.connect(() => {
+ this._password_entry.visibility = check.active;
+ });
+
+ /* Window title */
+ this.item.bind_property ("label", this, "title", GLib.BindingFlags.SYNC_CREATE);
+
+ /* Update as appropriate */
+ this.item.notify.connect((pspec) => {
+ switch(pspec.name) {
+ case "use":
+ update_use();
+ update_type();
+ update_visibility();
+ break;
+ case "attributes":
+ update_details();
+ update_server();
+ update_user();
+ break;
+ case "has-secret":
+ password_display();
+ break;
+ }
+ });
+
+ /* Create the password entry */
+ var buffer = new Gcr.SecureEntryBuffer();
+ this._password_entry = new Gtk.Entry.with_buffer(buffer);
+ Gtk.Box box = (Gtk.Box)this._builder.get_object("password-box-area");
+ box.add(this._password_entry);
+ this._password_entry.visibility = false;
+ this._password_entry.show();
+ this._password_changed = false;
+ this._updating_password = false;
+
+ /* Now watch for changes in the password */
+ this._password_entry.activate.connect(password_activate);
+ this._password_entry.changed.connect(() => {
+ this._password_changed = true;
+ });
+ this._password_entry.focus_out_event.connect_after(() => {
+ password_activate ();
+ return false;
+ });
+
+ /* Sensitivity of the password entry */
+ this.item.bind_property("has-secret", this._password_entry, "sensitive");
+ }
+
+ public ItemProperties(Item item,
+ Gtk.Window? parent) {
+ GLib.Object (
+ item: item,
+ transient_for: parent
+ );
+
+ item.refresh();
+ }
+
+ private void update_use() {
+ Gtk.Label use = (Gtk.Label)this._builder.get_object("use-field");
+ switch (this.item.use) {
+ case Use.NETWORK:
+ use.label = _("Access a network share or resource");
+ break;
+ case Use.WEB:
+ use.label = _("Access a website");
+ break;
+ case Use.PGP:
+ use.label = _("Unlocks a PGP key");
+ break;
+ case Use.SSH:
+ use.label = _("Unlocks a Secure Shell key");
+ break;
+ case Use.OTHER:
+ use.label = _("Saved password or login");
+ break;
+ default:
+ use.label = "";
+ break;
+ };
+ }
+
+ private void update_type() {
+ Gtk.Label type = (Gtk.Label)this._builder.get_object("type-field");
+ switch (item.use) {
+ case Use.NETWORK:
+ case Use.WEB:
+ type.label = _("Network Credentials");
+ break;
+ case Use.PGP:
+ case Use.SSH:
+ case Use.OTHER:
+ type.label = _("Password");
+ break;
+ default:
+ type.label = "";
+ break;
+ };
+ }
+
+ private void update_visibility() {
+ var use = this.item.use;
+ bool visible = use == Use.NETWORK || use == Use.WEB;
+ this._builder.get_object("server-label").set("visible", visible);
+ this._builder.get_object("server-field").set("visible", visible);
+ this._builder.get_object("login-label").set("visible", visible);
+ this._builder.get_object("login-field").set("visible", visible);
+ }
+
+ private void update_server() {
+ Gtk.Label server = (Gtk.Label)this._builder.get_object("server-label");
+ var value = this.item.get_attribute("server");
+ if (value == null)
+ value = "";
+ server.label = value;
+ }
+
+ private void update_user() {
+ Gtk.Label login = (Gtk.Label)this._builder.get_object("login-label");
+ var value = this.item.get_attribute("user");
+ if (value == null)
+ value = "";
+ login.label = value;
+ }
+
+ private void update_details() {
+ var contents = new GLib.StringBuilder();
+ var attrs = this.item.attributes;
+ var iter = GLib.HashTableIter<string, string>(attrs);
+ string key, value;
+ while (iter.next(out key, out value)) {
+ if (key.has_prefix("gkr:") || key.has_prefix("xdg:"))
+ continue;
+ contents.append_printf("<b>%s</b>: %s\n",
+ GLib.Markup.escape_text(key),
+ GLib.Markup.escape_text(value));
+ }
+ Gtk.Label details = (Gtk.Label)this._builder.get_object("details-box");
+ details.use_markup = true;
+ details.label = contents.str;
+ }
+
+ private void password_activate()
+ {
+ if (!this._password_expander.expanded)
+ return;
+ if (!this._password_changed)
+ return;
+ if (this._updating_password)
+ return;
+
+ this._updating_password = true;
+ this._password_expander.sensitive = false;
+
+ var value = new Secret.Value(this._password_entry.text, -1, "text/plain");
+ this.item.set_secret.begin(value, null, (obj, res) => {
+ try {
+ this.item.set_secret.end(res);
+ password_display();
+ } catch (GLib.Error err) {
+ DBusError.strip_remote_error(err);
+ Util.show_error (this, _("Couldn't change password."), err.message);
+ }
+
+ this._password_expander.sensitive = true;
+ this._updating_password = false;
+ });
+ }
+
+ private void password_display() {
+ if (this._password_expander.expanded) {
+ var secret = this.item.get_secret();
+ if (secret != null) {
+ unowned string? password = secret.get_text();
+ if (password != null) {
+ this._password_entry.set_text(password);
+ this._password_changed = false;
+ return;
+ }
+ }
+ }
+ this._password_entry.set_text("");
+ this._password_changed = false;
+ }
+
+ private void description_activate(Gtk.Entry description)
+ {
+ if (this._updating_description)
+ return;
+
+ this._updating_description = true;
+ description.sensitive = false;
+
+ this.item.set_label.begin(description.text, null, (obj, res) => {
+ try {
+ this.item.set_label.end(res);
+ } catch (GLib.Error err) {
+ description.text = this.item.label;
+ DBusError.strip_remote_error(err);
+ Util.show_error (this, _("Couldn't set description."), err.message);
+ }
+
+ description.sensitive = true;
+ this._updating_description = false;
+ });
+ }
+
+ private void expander_activate (Gtk.Expander expander)
+ {
+ if (!expander.expanded)
+ return;
+
+ /* Always have a hidden password when opening box */
+ Gtk.CheckButton check = (Gtk.CheckButton)this._builder.get_object("show-password-check");
+ check.set_active (false);
+
+ /* Make sure to trigger retrieving the secret */
+ password_display ();
+ }
+}
+
+}
+}
diff --git a/gkr/gkr-item.vala b/gkr/gkr-item.vala
new file mode 100644
index 0000000..b179871
--- /dev/null
+++ b/gkr/gkr-item.vala
@@ -0,0 +1,540 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2006 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+namespace Seahorse {
+namespace Gkr {
+
+public enum Use {
+ OTHER,
+ NETWORK,
+ WEB,
+ PGP,
+ SSH,
+}
+
+private struct DisplayInfo {
+ string? item_type;
+ string? label;
+ string? details;
+ string? description;
+}
+
+[CCode (has_target = false)]
+private delegate void DisplayCustom(string label,
+ GLib.HashTable<string, string> item_attrs,
+ ref DisplayInfo info);
+
+private struct DisplayEntry {
+ string item_type;
+ string description;
+ DisplayCustom? custom_func;
+}
+
+public class Item : Secret.Item, Deletable, Viewable {
+ public string description {
+ owned get {
+ ensure_display_info ();
+ return this._info.description;
+ }
+ }
+
+ public Use use {
+ get {
+ var schema_name = get_attribute("xdg:schema");
+ if (schema_name != null && schema_name == NETWORK_PASSWORD)
+ return Use.NETWORK;
+ return Use.OTHER;
+ }
+ }
+
+ public bool has_secret {
+ get { return _item_secret != null; }
+ }
+
+ public Keyring place {
+ owned get { return (Keyring)_place.get(); }
+ set { _place.set(value); }
+ }
+
+ public Flags object_flags {
+ get { return Flags.DELETABLE | Flags.PERSONAL; }
+ }
+
+ public GLib.Icon icon {
+ owned get { return new GLib.ThemedIcon (Gcr.ICON_PASSWORD); }
+ }
+
+ public new string label {
+ owned get {
+ ensure_display_info ();
+ return this._info.label;
+ }
+ }
+
+ public string markup {
+ owned get {
+ ensure_display_info ();
+ var result = new GLib.StringBuilder("");
+ result.append(GLib.Markup.escape_text(this._info.label));
+ result.append("<span size='small' rise='0' foreground='#555555'>\n");
+ result.append(this._info.details);
+ result.append("</span>");
+ return result.str;
+ }
+ }
+
+ public Usage usage {
+ get { return Usage.CREDENTIALS; }
+ }
+
+ public Gtk.ActionGroup? actions {
+ get { return null; }
+ }
+
+ public bool deletable {
+ get { return true; }
+ }
+
+ private Secret.Value? _item_secret = null;
+ private DisplayInfo? _info = null;
+ private GLib.WeakRef _place;
+ private GLib.Cancellable? _req_secret = null;
+
+ construct {
+ g_properties_changed.connect((changed_properties, invalidated_properties) => {
+ this._info = null;
+ freeze_notify();
+ notify_property("has-secret");
+ notify_property("use");
+ notify_property("label");
+ notify_property("icon");
+ notify_property("markup");
+ notify_property("description");
+ notify_property("object-flags");
+ thaw_notify();
+ });
+ }
+
+ public override void dispose() {
+ if (this._req_secret != null)
+ this._req_secret.cancel();
+ this._req_secret = null;
+ base.dispose();
+ }
+
+ public Deleter create_deleter() {
+ return new ItemDeleter(this);
+ }
+
+ public Gtk.Window? create_viewer(Gtk.Window? parent) {
+ return new ItemProperties(this, parent);
+ }
+
+ private void ensure_display_info() {
+ if (this._info != null)
+ return;
+
+ this._info = DisplayInfo();
+
+ var attrs = this.attributes;
+ var item_type = get_attribute_string (attrs, "xdg:schema");
+ item_type = map_item_type_to_specific (item_type, attrs);
+ assert (item_type != null);
+ this._info.item_type = item_type;
+
+ var label = base.get_label();
+ foreach (var entry in DISPLAY_ENTRIES) {
+ if (entry.item_type == item_type) {
+ if (entry.custom_func != null)
+ entry.custom_func(label, attrs, ref this._info);
+ if (this._info.description == null)
+ this._info.description = entry.description;
+ break;
+ }
+ }
+
+ if (this._info.label == null)
+ this._info.label = label;
+ if (this._info.label == null)
+ this._info.label = "";
+ if (this._info.details == null)
+ this._info.details = "";
+ }
+
+ private void load_item_secret() {
+ if (this._req_secret == null) {
+ this._req_secret = new GLib.Cancellable();
+ load_secret.begin(this._req_secret, (obj, res) => {
+ try {
+ this._req_secret = null;
+ load_secret.end(res);
+ this._item_secret = base.get_secret();
+ notify_property("has-secret");
+ } catch (GLib.Error err) {
+ GLib.warning("Couldn't retrieve secret: %s", err.message);
+ }
+ });
+ }
+ }
+
+
+ public new void refresh() {
+ base.refresh();
+ if (this._item_secret != null)
+ load_item_secret ();
+ }
+
+ public new Secret.Value? get_secret() {
+ if (this._item_secret == null)
+ load_item_secret ();
+ return this._item_secret;
+ }
+
+ public string? get_attribute(string name) {
+ if (this.attributes == null)
+ return null;
+ return this.attributes.lookup(name);
+ }
+
+ public new async bool set_secret(Secret.Value value,
+ GLib.Cancellable? cancellable) throws GLib.Error {
+ yield base.set_secret(value, cancellable);
+ _item_secret = value;
+ notify_property("has-secret");
+ return true;
+ }
+}
+
+private const string GENERIC_SECRET = "org.freedesktop.Secret.Generic";
+private const string NETWORK_PASSWORD = "org.gnome.keyring.NetworkPassword";
+private const string KEYRING_NOTE = "org.gnome.keyring.Note";
+private const string CHAINED_KEYRING = "org.gnome.keyring.ChainedKeyring";
+private const string ENCRYPTION_KEY = "org.gnome.keyring.EncryptionKey";
+private const string PK_STORAGE = "org.gnome.keyring.PkStorage";
+private const string CHROME_PASSWORD = "x.internal.Chrome";
+private const string GOA_PASSWORD = "org.gnome.OnlineAccounts";
+private const string TELEPATHY_PASSWORD = "org.freedesktop.Telepathy";
+private const string EMPATHY_PASSWORD = "org.freedesktop.Empathy";
+private const string NETWORK_MANAGER_SECRET = "org.freedesktop.NetworkManager";
+
+private string? get_attribute_string(GLib.HashTable<string, string>? attrs,
+ string name) {
+ if (attrs == null)
+ return null;
+ return attrs.lookup(name);
+}
+
+private int get_attribute_int(GLib.HashTable<string, string>? attrs,
+ string name)
+{
+ if (attrs == null)
+ return 0;
+
+ var value = attrs.lookup(name);
+ if (value != null)
+ return int.parse(value);
+
+ return 0;
+}
+
+private bool is_network_item(GLib.HashTable<string, string>? attrs,
+ string match)
+{
+ var protocol = get_attribute_string (attrs, "protocol");
+ return protocol != null && protocol.ascii_casecmp(match) == 0;
+}
+
+private bool is_custom_network_label(string? server,
+ string? user,
+ string? object,
+ int port,
+ string? display)
+{
+ /*
+ * For network passwords gnome-keyring generates in a funky looking display
+ * name that's generated from login credentials. We simulate that generating
+ * here and return FALSE if it matches. This allows us to display user
+ * customized display names and ignore the generated ones.
+ */
+
+ if (server == null)
+ return true;
+
+ var generated = new GLib.StringBuilder("");
+ if (user != null)
+ generated.append_printf("%s@", user);
+ generated.append(server);
+ if (port != 0)
+ generated.append_printf(":%d", port);
+ if (object != null)
+ generated.append_printf ("/%s", object);
+
+ return (generated.str == display);
+}
+
+private string? calc_network_label(GLib.HashTable<string, string>? attrs,
+ bool always)
+{
+ /* HTTP usually has a the realm as the "object" display that */
+ if (is_network_item (attrs, "http") && attrs != null) {
+ var val = get_attribute_string (attrs, "object");
+ if (val != null && val != "")
+ return val;
+
+ /* Display the server name as a last resort */
+ if (always) {
+ val = get_attribute_string (attrs, "server");
+ if (val != null && val != "")
+ return val;
+ }
+ }
+
+ return null;
+}
+
+private void network_custom(string? display,
+ GLib.HashTable<string, string>? attrs,
+ ref DisplayInfo info)
+{
+ var server = get_attribute_string (attrs, "server");
+ var protocol = get_attribute_string (attrs, "protocol");
+ var object = get_attribute_string (attrs, "object");
+ var user = get_attribute_string (attrs, "user");
+ var port = get_attribute_int (attrs, "port");
+
+ if (protocol == null)
+ return;
+
+ /* If it's customized by the application or user then display that */
+ if (!is_custom_network_label (server, user, object, port, display))
+ info.label = calc_network_label (attrs, true);
+ if (info.label == null)
+ info.label = display;
+
+ string symbol = "@";
+ if (user == null) {
+ user = "";
+ symbol = "";
+ }
+
+ if (object == null)
+ object = "";
+
+ if (server != null && protocol != null) {
+ info.details = GLib.Markup.printf_escaped("%s://%s%s%s/%s",
+ protocol, user, symbol,
+ server, object);
+ }
+}
+
+private void chrome_custom(string? display,
+ GLib.HashTable<string, string>? attrs,
+ ref DisplayInfo info)
+{
+ var origin = get_attribute_string (attrs, "origin_url");
+
+ /* A brain dead url, parse */
+ if (display != null && display == origin) {
+ try {
+ var regex = new GLib.Regex("[a-z]+://([^/]+)/", GLib.RegexCompileFlags.CASELESS, 0);
+ GLib.MatchInfo match;
+ if (regex.match(display, GLib.RegexMatchFlags.ANCHORED, out match)) {
+ if (match.matches())
+ info.label = match.fetch(1);
+ }
+ } catch (GLib.RegexError err) {
+ GLib.critical("%s", err.message);
+ return;
+ }
+
+ }
+
+ if (info.label == null)
+ info.label = display;
+ if (origin != null)
+ info.details = GLib.Markup.escape_text(origin);
+ else
+ info.details = "";
+}
+
+private string? decode_telepathy_id(string account) {
+ account.replace("_", "%");
+ return GLib.Uri.unescape_string(account);
+}
+
+private void empathy_custom(string? display,
+ GLib.HashTable<string, string>? attrs,
+ ref DisplayInfo info)
+{
+ var account = get_attribute_string(attrs, "account-id");
+
+ /* Translators: This should be the same as the string in empathy */
+ var prefix = _("IM account password for ");
+
+ if (display != null && display.has_prefix(prefix)) {
+ var len = prefix.length;
+ var pos = display.index_of_char ('(', (int)len);
+ if (pos != -1)
+ info.label = display.slice(len, pos);
+
+ try {
+ var regex = new GLib.Regex("^.+/.+/(.+)$", GLib.RegexCompileFlags.CASELESS, 0);
+ GLib.MatchInfo match;
+ if (regex.match(account, GLib.RegexMatchFlags.ANCHORED, out match)) {
+ if (match.matches())
+ info.details = decode_telepathy_id (match.fetch(1));
+ }
+ } catch (GLib.RegexError err) {
+ GLib.critical("%s", err.message);
+ return;
+ }
+ }
+
+ if (info.label == null)
+ info.label = display;
+ if (info.details == null)
+ info.details = GLib.Markup.escape_text (account);
+}
+
+private void telepathy_custom(string? display,
+ GLib.HashTable<string, string>? attrs,
+ ref DisplayInfo info)
+{
+ var account = get_attribute_string (attrs, "account");
+ if (account != null && display != null && display.index_of(account) != -1) {
+ try {
+ var regex = new GLib.Regex("^.+/.+/(.+)$", GLib.RegexCompileFlags.CASELESS, 0);
+ GLib.MatchInfo match;
+ if (regex.match(account, GLib.RegexMatchFlags.ANCHORED, out match)) {
+ if (match.matches()) {
+ var identifier = match.fetch(1);
+ info.label = decode_telepathy_id(identifier);
+ }
+ }
+ } catch (GLib.RegexError err) {
+ GLib.critical("%s", err.message);
+ return;
+ }
+ }
+
+ if(info.label == null)
+ info.label = display;
+ if(account != null)
+ info.details = GLib.Markup.escape_text(account);
+}
+
+
+private const DisplayEntry[] DISPLAY_ENTRIES = {
+ { GENERIC_SECRET, N_("Password or secret"), null},
+ { NETWORK_PASSWORD, N_("Network password"), network_custom },
+ { KEYRING_NOTE, N_("Stored note"), null },
+ { CHAINED_KEYRING, N_("Keyring password"), null },
+ { ENCRYPTION_KEY, N_("Encryption key password"), null },
+ { PK_STORAGE, N_("Key storage password"), null },
+ { CHROME_PASSWORD, N_("Google Chrome password"), chrome_custom },
+ { GOA_PASSWORD, N_("Gnome Online Accounts password"), null },
+ { TELEPATHY_PASSWORD, N_("Telepathy password"), telepathy_custom },
+ { EMPATHY_PASSWORD, N_("Instant messaging password"), empathy_custom },
+ { NETWORK_MANAGER_SECRET, N_("Network Manager secret"), null },
+};
+
+private struct MappingEntry {
+ string item_type;
+ string mapped_type;
+ string match_attribute;
+ string? match_pattern;
+}
+
+private const MappingEntry[] MAPPING_ENTRIES = {
+ { GENERIC_SECRET, CHROME_PASSWORD, "application", "chrome*" },
+ { GENERIC_SECRET, GOA_PASSWORD, "goa-identity", null },
+ { GENERIC_SECRET, TELEPATHY_PASSWORD, "account", "*/*/*" },
+ { GENERIC_SECRET, EMPATHY_PASSWORD, "account-id", "*/*/*" },
+ /* Network secret for Auto anna/802-11-wireless-security/psk */
+ { GENERIC_SECRET, NETWORK_MANAGER_SECRET, "connection-uuid", null },
+};
+
+private string map_item_type_to_specific(string? item_type,
+ GLib.HashTable<string, string>? attrs)
+{
+
+ if (item_type == null)
+ return GENERIC_SECRET;
+ if (attrs == null)
+ return item_type;
+
+ foreach (var mapping in MAPPING_ENTRIES) {
+ if (item_type == mapping.item_type) {
+ var value = get_attribute_string (attrs, mapping.match_attribute);
+ if (value != null && mapping.match_pattern != null) {
+ if (GLib.PatternSpec.match_simple(mapping.match_pattern, value))
+ return mapping.mapped_type;
+ } else if (value != null) {
+ return mapping.mapped_type;
+ }
+ }
+ }
+
+ return item_type;
+}
+
+class ItemDeleter : Deleter {
+ private GLib.List<Item> _items;
+
+ public override Gtk.Dialog create_confirm(Gtk.Window? parent) {
+ var num = this._items.length();
+ if (num == 1) {
+ var label = ((Secret.Item)_items.data).label;
+ return new DeleteDialog(parent, _("Are you sure you want to delete the password
'%s'?"), label);
+ } else {
+ return new DeleteDialog(parent, GLib.ngettext ("Are you sure you want to delete %d
password?",
+ "Are you sure you want to delete %d
passwords?", num), num);
+ }
+ }
+
+ public ItemDeleter(Item item) {
+ if (!add_object(item))
+ GLib.assert_not_reached();
+ }
+
+ public override unowned GLib.List<weak GLib.Object> get_objects() {
+ return this._items;
+ }
+
+ public override bool add_object (GLib.Object obj) {
+ if (obj is Item) {
+ this._items.append((Item)obj);
+ return true;
+ }
+ return false;
+ }
+
+ public override async bool delete(GLib.Cancellable? cancellable) throws GLib.Error {
+ var items = _items.copy();
+ foreach (var item in items)
+ yield item.delete(cancellable);
+ return true;
+ }
+}
+
+}
+}
diff --git a/gkr/gkr-keyring-add.vala b/gkr/gkr-keyring-add.vala
new file mode 100644
index 0000000..54c66e4
--- /dev/null
+++ b/gkr/gkr-keyring-add.vala
@@ -0,0 +1,78 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+namespace Seahorse {
+namespace Gkr {
+
+public class KeyringAdd : Gtk.Dialog {
+ construct {
+ this.title = _("Add Password Keyring");
+ this.modal = true;
+ this.window_position = Gtk.WindowPosition.CENTER_ON_PARENT;
+ this.border_width = 5;
+
+ var builder = Util.load_built_contents(this, "add-keyring");
+ this.add_buttons(Gtk.Stock.OK, Gtk.ResponseType.ACCEPT,
+ Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL);
+
+ var entry = (Gtk.Entry)builder.get_object("keyring-name");
+ this.set_response_sensitive(Gtk.ResponseType.ACCEPT, false);
+ entry.changed.connect((editable) => {
+ var name = entry.get_text();
+ this.set_response_sensitive(Gtk.ResponseType.ACCEPT, name != "");
+ });
+
+ this.response.connect((resp) => {
+ if (resp == Gtk.ResponseType.ACCEPT) {
+ var name = entry.get_text();
+ var cancellable = Dialog.begin_request(this);
+ var service = Backend.instance().service;
+ Secret.Collection.create.begin(service, name, null,
+
Secret.CollectionCreateFlags.COLLECTION_CREATE_NONE,
+ cancellable, (obj, res) => {
+ /* Clear the operation without cancelling it since it is complete */
+ Dialog.complete_request(this, false);
+
+ try {
+ Secret.Collection.create.end(res);
+ } catch (GLib.Error err) {
+ Util.show_error(this, _("Couldn't add keyring"), err.message);
+ }
+
+ this.destroy();
+ });
+ } else {
+ this.destroy();
+ }
+ });
+
+ }
+
+ public KeyringAdd(Gtk.Window? parent) {
+ GLib.Object(transient_for: parent);
+ this.show();
+ this.present();
+ }
+}
+
+}
+}
diff --git a/gkr/gkr-keyring-properties.vala b/gkr/gkr-keyring-properties.vala
new file mode 100644
index 0000000..7fc5190
--- /dev/null
+++ b/gkr/gkr-keyring-properties.vala
@@ -0,0 +1,81 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 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.,
+ * 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+namespace Seahorse {
+namespace Gkr {
+
+public class KeyringProperties : Gtk.Dialog {
+ public Keyring keyring { construct; get; }
+
+ private Gtk.Builder _builder;
+
+ construct {
+ this._builder = new Gtk.Builder();
+ try {
+ string path = GLib.Path.build_filename(Config.UIDIR, "seahorse-gkr-keyring.xml");
+ this._builder.add_from_file(path);
+ } catch (GLib.Error err) {
+ GLib.critical ("%s", err.message);
+ }
+
+ this.add_button(Gtk.Stock.CLOSE, Gtk.ResponseType.CLOSE);
+ var content = (Gtk.Widget)this._builder.get_object("gkr-item-properties");
+ ((Gtk.Container)this.get_content_area()).add(content);
+ content.show();
+
+ this.response.connect((response) => {
+ this.destroy();
+ });
+
+ /* Setup the image properly */
+ this.keyring.bind_property ("icon", this._builder.get_object("keyring-image"), "gicon",
+ GLib.BindingFlags.SYNC_CREATE);
+
+ /* The window title */
+ this.keyring.bind_property ("label", this, "title", GLib.BindingFlags.SYNC_CREATE);
+
+ /* Setup the label properly */
+ var name = (Gtk.Label)this._builder.get_object("name-field");
+ this.keyring.bind_property ("label", name, "label", GLib.BindingFlags.SYNC_CREATE);
+
+ /* The date field */
+ this.keyring.notify.connect((pspec) => {
+ switch(pspec.name) {
+ case "created":
+ var created = (Gtk.Label)this._builder.get_object("created-field");
+ created.label = Util.get_display_date_string((long)this.keyring.created);
+ break;
+ }
+ });
+ }
+
+ public KeyringProperties(Keyring keyring,
+ Gtk.Window? parent) {
+ GLib.Object (
+ keyring: keyring,
+ transient_for: parent
+ );
+ }
+
+}
+
+}
+}
diff --git a/gkr/gkr-keyring.vala b/gkr/gkr-keyring.vala
new file mode 100644
index 0000000..2c76aec
--- /dev/null
+++ b/gkr/gkr-keyring.vala
@@ -0,0 +1,275 @@
+/*
+ * Seahorse
+ *
+ * Copyright (C) 2008 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program 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; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+namespace Seahorse {
+namespace Gkr {
+
+public class Keyring : Secret.Collection, Gcr.Collection, Place, Deletable, Lockable, Viewable {
+ public string description {
+ owned get {
+ if (Backend.instance().has_alias ("login", this))
+ return _("A keyring that is automatically unlocked on login");
+ return _("A keyring used to store passwords");
+ }
+ }
+
+ public string uri {
+ owned get {
+ var object_path = base.get_object_path ();
+ return "secret-service://%s".printf(object_path);
+ }
+ }
+
+ public GLib.Icon icon {
+ owned get { return new GLib.ThemedIcon("folder"); }
+ }
+ public Gtk.ActionGroup actions {
+ owned get {
+ if (this._actions == null)
+ this._actions = create_actions();
+ return this._actions;
+ }
+ }
+
+ public bool is_default {
+ get { return Backend.instance().has_alias ("default", this); }
+ }
+
+ public bool lockable {
+ get { return !get_locked(); }
+ }
+
+ public bool unlockable {
+ get { return get_locked(); }
+ }
+
+ public bool deletable {
+ get { return true; }
+ }
+
+ private GLib.HashTable<string, Item> _items;
+ private Gtk.ActionGroup? _actions;
+
+ construct {
+ this._items = new GLib.HashTable<string, Item>(GLib.str_hash, GLib.str_equal);
+ this.notify.connect((pspec) => {
+ if (pspec.name == "items" || pspec.name == "locked")
+ refresh_collection();
+ });
+ Backend.instance().notify.connect((pspec) => {
+ notify_property ("is-default");
+ notify_property ("description");
+ });
+ }
+
+ public uint get_length() {
+ return _items.size();
+ }
+
+ public GLib.List<weak GLib.Object> get_objects() {
+ return _items.get_values();
+ }
+
+ public bool contains(GLib.Object obj) {
+ if (obj is Item)
+ return _items.lookup(((Item)obj).get_object_path()) != null;
+ return false;
+ }
+
+ public Gtk.Window? create_viewer(Gtk.Window? parent) {
+ return new KeyringProperties(this, parent);
+ }
+
+ public Deleter create_deleter() {
+ return new KeyringDeleter(this);
+ }
+
+ public async bool lock(GLib.TlsInteraction? interaction,
+ GLib.Cancellable? cancellable) throws GLib.Error {
+ var objects = new GLib.List<GLib.DBusProxy>();
+ objects.prepend(this);
+
+ var service = get_service();
+ GLib.List<GLib.DBusProxy> locked;
+ yield service.lock(objects, cancellable, out locked);
+ refresh_collection ();
+ return locked.length() > 0;
+ }
+
+ public async bool unlock(GLib.TlsInteraction? interaction,
+ GLib.Cancellable? cancellable) throws GLib.Error {
+ var objects = new GLib.List<GLib.DBusProxy>();
+ objects.prepend(this);
+
+ var service = get_service();
+ GLib.List<GLib.DBusProxy> unlocked;
+ yield service.unlock(objects, cancellable, out unlocked);
+ refresh_collection ();
+ return unlocked.length() > 0;
+ }
+
+ public async bool load(GLib.Cancellable? cancellable) throws GLib.Error {
+ refresh_collection();
+ return true;
+ }
+
+ private void refresh_collection() {
+ var seen = new GLib.HashTable<string, weak string>(GLib.str_hash, GLib.str_equal);
+
+ GLib.List<Secret.Item> items = null;
+ if (!get_locked())
+ items = get_items();
+
+ foreach (var item in items) {
+ var object_path = item.get_object_path();
+ seen.add(object_path);
+
+ if (_items.lookup(object_path) == null) {
+ item.set("place", this);
+ _items.insert(object_path, (Item)item);
+ emit_added(item);
+ }
+ }
+
+ /* Remove any that we didn't find */
+ var iter = GLib.HashTableIter<string, Item>(_items);
+ string object_path;
+ while (iter.next (out object_path, null)) {
+ if (seen.lookup(object_path) == null) {
+ var item = _items.lookup(object_path);
+ item.set("place", null);
+ iter.remove();
+ emit_removed (item);
+ }
+ }
+ }
+
+ [CCode (instance_pos = -1)]
+ private void on_keyring_default(Gtk.Action action) {
+ var parent = Action.get_window(action);
+ var service = this.service;
+
+ service.set_alias.begin("default", this, null, (obj, res) => {
+ try {
+ service.set_alias.end(res);
+ Backend.instance().refresh();
+ } catch (GLib.Error err) {
+ Util.show_error(parent, _("Couldn't set default keyring"), err.message);
+ }
+ });
+ }
+
+ [CCode (instance_pos = -1)]
+ private void on_keyring_password (Gtk.Action action) {
+ var parent = Action.get_window(action);
+ var service = this.service;
+ service.get_connection().call.begin(service.get_name(),
+ service.get_object_path(),
+
"org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface",
+ "ChangeWithPrompt",
+ new GLib.Variant("(o)", this.get_object_path()),
+ new GLib.VariantType("(o)"),
+ GLib.DBusCallFlags.NONE, -1, null, (obj, res) => {
+ try {
+ var retval = service.get_connection().call.end(res);
+ string prompt_path;
+ retval.get("(o)", out prompt_path);
+ service.prompt_at_dbus_path.begin(prompt_path, null, null, (obj, res) => {
+ try {
+ service.prompt_at_dbus_path.end(res);
+ } catch (GLib.Error err) {
+ Util.show_error(parent, _("Couldn't change keyring
password"), err.message);
+ }
+ Backend.instance().refresh();
+ });
+ } catch (GLib.Error err) {
+ Util.show_error(parent, _("Couldn't change keyring password"), err.message);
+ }
+ });
+ }
+
+ private static const Gtk.ActionEntry[] KEYRING_ACTIONS = {
+ { "keyring-default", null, N_("_Set as default"), null,
+ N_("Applications usually store new passwords in the default keyring."), on_keyring_default
},
+ { "keyring-password", null, N_("Change _Password"), null,
+ N_("Change the unlock password of the password storage keyring"), on_keyring_password },
+ };
+
+ private Gtk.ActionGroup create_actions() {
+ Gtk.ActionGroup actions = new Gtk.ActionGroup("KeyringActions");
+
+ /* Add these actions, but none of them are visible until cloned */
+ actions.set_translation_domain(Config.GETTEXT_PACKAGE);
+ actions.add_actions(KEYRING_ACTIONS, this);
+
+ var action = actions.get_action("keyring-default");
+ this.bind_property("is-default", action, "sensitive",
+ GLib.BindingFlags.INVERT_BOOLEAN | GLib.BindingFlags.SYNC_CREATE);
+
+ return actions;
+ }
+
+}
+
+class KeyringDeleter : Deleter {
+ private Keyring? _keyring;
+ private GLib.List<GLib.Object> _objects;
+
+ public override Gtk.Dialog create_confirm(Gtk.Window? parent) {
+ var dialog = new DeleteDialog(parent,
+ _("Are you sure you want to delete the password keyring '%s'?"),
+ this._keyring.label);
+
+ dialog.check_label = _("I understand that all items will be permanently deleted.");
+ dialog.check_require = true;
+
+ return dialog;
+ }
+
+ public KeyringDeleter(Keyring keyring) {
+ if (!add_object(keyring))
+ GLib.assert_not_reached();
+ }
+
+ public override unowned GLib.List<weak GLib.Object> get_objects() {
+ return this._objects;
+ }
+
+ public override bool add_object (GLib.Object obj) {
+ if (this._keyring != null)
+ return false;
+ if (obj is Keyring) {
+ this._keyring = (Keyring)obj;
+ this._objects.append(obj);
+ return true;
+ }
+ return false;
+ }
+
+ public override async bool delete(GLib.Cancellable? cancellable) throws GLib.Error {
+ yield this._keyring.delete(cancellable);
+ return true;
+ }
+}
+
+}
+}
diff --git a/gkr/gkr-module.vala b/gkr/gkr-module.vala
index 6f3ed1e..a01346c 100644
--- a/gkr/gkr-module.vala
+++ b/gkr/gkr-module.vala
@@ -22,7 +22,9 @@
*/
namespace Seahorse {
+namespace Gkr {
-public const string GKR_NAME = "gkr";
+public const string NAME = "gkr";
+}
}
\ No newline at end of file
diff --git a/gkr/seahorse-add-keyring.xml b/gkr/seahorse-add-keyring.xml
index 42a88f6..743e489 100644
--- a/gkr/seahorse-add-keyring.xml
+++ b/gkr/seahorse-add-keyring.xml
@@ -2,17 +2,7 @@
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkDialog" id="add-keyring">
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Add Password Keyring</property>
- <property name="modal">True</property>
- <property name="window_position">center-on-parent</property>
- <property name="type_hint">dialog</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="skip_pager_hint">True</property>
- <signal name="response" handler="on_add_keyring_properties_response"/>
- <child internal-child="vbox">
- <object class="GtkVBox" id="dialog-vbox1">
+ <object class="GtkVBox" id="add-keyring">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
@@ -76,56 +66,5 @@
<property name="position">1</property>
</packing>
</child>
- <child internal-child="action_area">
- <object class="GtkHButtonBox" id="dialog-action_area1">
- <property name="visible">True</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="cancel">
- <property name="label">gtk-cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="on_widget_closed"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="ok">
- <property name="label">gtk-add</property>
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="has_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
</object>
- </child>
- <action-widgets>
- <action-widget response="-6">cancel</action-widget>
- <action-widget response="-3">ok</action-widget>
- </action-widgets>
- </object>
</interface>
diff --git a/gkr/seahorse-gkr-add-item.xml b/gkr/seahorse-gkr-add-item.xml
index 55ea6bd..8677f3b 100644
--- a/gkr/seahorse-gkr-add-item.xml
+++ b/gkr/seahorse-gkr-add-item.xml
@@ -2,21 +2,7 @@
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkDialog" id="gkr-add-item">
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Add Password</property>
- <property name="modal">True</property>
- <property name="window_position">center-on-parent</property>
- <property name="type_hint">dialog</property>
- <property name="skip_taskbar_hint">True</property>
- <property name="skip_pager_hint">True</property>
- <signal name="response" handler="on_add_item_response"/>
- <child internal-child="vbox">
- <object class="GtkVBox" id="dialog-vbox1">
- <property name="visible">True</property>
- <property name="spacing">2</property>
- <child>
- <object class="GtkVBox" id="vbox1">
+ <object class="GtkVBox" id="gkr-add-item">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="spacing">6</property>
@@ -129,61 +115,4 @@
</packing>
</child>
</object>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child internal-child="action_area">
- <object class="GtkHButtonBox" id="dialog-action_area1">
- <property name="visible">True</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="cancel">
- <property name="label">gtk-cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="on_widget_closed"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="ok">
- <property name="label">gtk-add</property>
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="can_focus">True</property>
- <property name="has_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">cancel</action-widget>
- <action-widget response="-3">ok</action-widget>
- </action-widgets>
- </object>
</interface>
diff --git a/gkr/seahorse-gkr-item-properties.xml b/gkr/seahorse-gkr-item-properties.xml
index 3f4733a..e2fadd0 100644
--- a/gkr/seahorse-gkr-item-properties.xml
+++ b/gkr/seahorse-gkr-item-properties.xml
@@ -2,18 +2,7 @@
<interface>
<requires lib="gtk+" version="3.0"/>
<!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkDialog" id="gkr-item-properties">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Key Properties</property>
- <property name="type_hint">dialog</property>
- <signal name="delete_event" handler="on_widget_delete_event"/>
- <child internal-child="vbox">
- <object class="GtkVBox" id="dialog-vbox1">
- <property name="visible">True</property>
- <property name="spacing">2</property>
- <child>
- <object class="GtkNotebook" id="notebook">
+ <object class="GtkNotebook" id="gkr-item-properties">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">5</property>
@@ -65,8 +54,6 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="activates_default">True</property>
- <signal name="focus_out_event" handler="on_item_description_focus_out"/>
- <signal name="activate" handler="on_item_description_activate"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -213,7 +200,6 @@
<object class="GtkExpander" id="password-expander">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <signal name="activate" handler="on_item_password_expander_activate" after="yes"/>
<child>
<object class="GtkAlignment" id="alignment45">
<property name="visible">True</property>
@@ -243,7 +229,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_item_show_password_toggled"/>
</object>
<packing>
<property name="expand">False</property>
@@ -355,58 +340,4 @@
</packing>
</child>
</object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child internal-child="action_area">
- <object class="GtkHButtonBox" id="dialog-action_area1">
- <property name="visible">True</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="helpbutton1">
- <property name="label">gtk-help</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="on_widget_help"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="closebutton1">
- <property name="label">gtk-close</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="on_widget_closed"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-11">helpbutton1</action-widget>
- <action-widget response="-7">closebutton1</action-widget>
- </action-widgets>
- </object>
</interface>
diff --git a/gkr/seahorse-gkr-keyring.xml b/gkr/seahorse-gkr-keyring.xml
index e9a6c03..83def2b 100644
--- a/gkr/seahorse-gkr-keyring.xml
+++ b/gkr/seahorse-gkr-keyring.xml
@@ -2,15 +2,7 @@
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkDialog" id="gkr-keyring">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <property name="title" translatable="yes">Keyring Properties</property>
- <property name="type_hint">dialog</property>
- <signal name="response" handler="on_keyring_properties_response"/>
- <signal name="delete_event" handler="on_widget_delete_event"/>
- <child internal-child="vbox">
- <object class="GtkVBox" id="dialog-vbox1">
+ <object class="GtkVBox" id="gkr-keyring">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
@@ -184,10 +176,4 @@
</packing>
</child>
</object>
- </child>
- <action-widgets>
- <action-widget response="-11">helpbutton1</action-widget>
- <action-widget response="-7">closebutton1</action-widget>
- </action-widgets>
- </object>
</interface>
diff --git a/src/seahorse-sidebar.c b/src/seahorse-sidebar.c
index 19991d0..d206662 100644
--- a/src/seahorse-sidebar.c
+++ b/src/seahorse-sidebar.c
@@ -27,11 +27,6 @@
#include "seahorse-interaction.h"
#include "seahorse-util.h"
-#include "gkr/seahorse-gkr.h"
-#include "pgp/seahorse-pgp.h"
-#include "pkcs11/seahorse-pkcs11.h"
-#include "ssh/seahorse-ssh.h"
-
#include <glib/gi18n.h>
struct _SeahorseSidebar {
@@ -745,13 +740,13 @@ order_from_backend (GObject *backend)
if (name == NULL)
order = 10;
- else if (g_str_equal (name, SEAHORSE_GKR_NAME))
+ else if (g_str_equal (name, "gkr"))
order = 0;
- else if (g_str_equal (name, SEAHORSE_PGP_NAME))
+ else if (g_str_equal (name, "pgp"))
order = 1;
- else if (g_str_equal (name, SEAHORSE_PKCS11_NAME))
+ else if (g_str_equal (name, "pkcs11"))
order = 2;
- else if (g_str_equal (name, SEAHORSE_SSH_NAME))
+ else if (g_str_equal (name, "ssh"))
order = 3;
else
order = 10;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]