[epiphany/wip/sync: 4/4] [wip] sync: Implement saved passwords sync
- From: Gabriel Ivașcu <gabrielivascu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/wip/sync: 4/4] [wip] sync: Implement saved passwords sync
- Date: Sun, 30 Apr 2017 19:13:12 +0000 (UTC)
commit 9503634b9a95191b6653976fd57f221c9931e7ae
Author: Gabriel Ivascu <ivascu gabriel59 gmail com>
Date: Sun Apr 30 22:11:15 2017 +0300
[wip] sync: Implement saved passwords sync
data/org.gnome.epiphany.gschema.xml | 15 +
embed/meson.build | 1 +
embed/web-extension/ephy-embed-form-auth.c | 14 +
embed/web-extension/ephy-embed-form-auth.h | 23 +-
embed/web-extension/ephy-web-extension.c | 38 +-
embed/web-extension/meson.build | 1 +
lib/ephy-password-manager.c | 480 ---------------------
lib/ephy-password-manager.h | 77 ----
lib/ephy-password-record.c | 236 -----------
lib/ephy-prefs.h | 3 +
lib/meson.build | 2 -
lib/sync/ephy-password-manager.c | 580 ++++++++++++++++++++++++++
lib/sync/ephy-password-manager.h | 81 ++++
lib/sync/ephy-password-record.c | 379 +++++++++++++++++
lib/{ => sync}/ephy-password-record.h | 18 +-
lib/sync/meson.build | 2 +
meson.build | 2 +-
src/ephy-shell.c | 9 +-
src/passwords-dialog.c | 8 +-
src/prefs-dialog.c | 29 +-
src/profile-migrator/ephy-profile-migrator.c | 13 +-
src/resources/gtk/prefs-dialog.ui | 7 +
22 files changed, 1165 insertions(+), 853 deletions(-)
---
diff --git a/data/org.gnome.epiphany.gschema.xml b/data/org.gnome.epiphany.gschema.xml
index 2264bbd..ddc61dc 100644
--- a/data/org.gnome.epiphany.gschema.xml
+++ b/data/org.gnome.epiphany.gschema.xml
@@ -302,6 +302,21 @@
<summary>Initial sync or normal sync</summary>
<description>TRUE if bookmarks collection needs to be synced for the first time,
FALSE otherwise.</description>
</key>
+ <key type="b" name="sync-passwords-enabled">
+ <default>false</default>
+ <summary>Enable passwords sync</summary>
+ <description>TRUE if passwords collection should be synced, FALSE
otherwise.</description>
+ </key>
+ <key type="d" name="sync-passwords-time">
+ <default>0</default>
+ <summary>Passwords sync timestamp</summary>
+ <description>The timestamp at which last passwords sync was made.</description>
+ </key>
+ <key type="b" name="sync-passwords-initial">
+ <default>true</default>
+ <summary>Initial sync or normal sync</summary>
+ <description>TRUE if passwords collection needs to be synced for the first time,
FALSE otherwise.</description>
+ </key>
</schema>
<enum id="org.gnome.Epiphany.Permission">
<value nick="undecided" value="-1"/>
diff --git a/embed/meson.build b/embed/meson.build
index 99812c5..f6d2297 100644
--- a/embed/meson.build
+++ b/embed/meson.build
@@ -54,6 +54,7 @@ libephyembed_includes = include_directories(
'../lib/contrib',
'../lib/contrib/gvdb',
'../lib/history',
+ '../lib/sync',
'../lib/widgets',
'../lib/widgets/contrib'
)
diff --git a/embed/web-extension/ephy-embed-form-auth.c b/embed/web-extension/ephy-embed-form-auth.c
index 930cb3a..ae36027 100644
--- a/embed/web-extension/ephy-embed-form-auth.c
+++ b/embed/web-extension/ephy-embed-form-auth.c
@@ -29,6 +29,7 @@ struct _EphyEmbedFormAuth {
WebKitDOMNode *username_node;
WebKitDOMNode *password_node;
char *username;
+ gboolean password_updated;
};
G_DEFINE_TYPE (EphyEmbedFormAuth, ephy_embed_form_auth, G_TYPE_OBJECT)
@@ -110,6 +111,19 @@ ephy_embed_form_auth_get_username (EphyEmbedFormAuth *form_auth)
return form_auth->username;
}
+gboolean
+ephy_embed_form_auth_get_password_updated (EphyEmbedFormAuth *form_auth)
+{
+ return form_auth->password_updated;
+}
+
+void
+ephy_embed_form_auth_set_password_updated (EphyEmbedFormAuth *form_auth,
+ gboolean password_updated)
+{
+ form_auth->password_updated = password_updated;
+}
+
WebKitDOMDocument *
ephy_embed_form_auth_get_owner_document (EphyEmbedFormAuth *form_auth)
{
diff --git a/embed/web-extension/ephy-embed-form-auth.h b/embed/web-extension/ephy-embed-form-auth.h
index cb78e06..3266771 100644
--- a/embed/web-extension/ephy-embed-form-auth.h
+++ b/embed/web-extension/ephy-embed-form-auth.h
@@ -30,15 +30,18 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (EphyEmbedFormAuth, ephy_embed_form_auth, EPHY, EMBED_FORM_AUTH, GObject)
-EphyEmbedFormAuth *ephy_embed_form_auth_new (WebKitWebPage *web_page,
- WebKitDOMNode *username_node,
- WebKitDOMNode *password_node,
- const char *username);
-WebKitDOMNode *ephy_embed_form_auth_get_username_node (EphyEmbedFormAuth *form_auth);
-WebKitDOMNode *ephy_embed_form_auth_get_password_node (EphyEmbedFormAuth *form_auth);
-SoupURI *ephy_embed_form_auth_get_uri (EphyEmbedFormAuth *form_auth);
-guint64 ephy_embed_form_auth_get_page_id (EphyEmbedFormAuth *form_auth);
-const char *ephy_embed_form_auth_get_username (EphyEmbedFormAuth *form_auth);
-WebKitDOMDocument *ephy_embed_form_auth_get_owner_document(EphyEmbedFormAuth *form_auth);
+EphyEmbedFormAuth *ephy_embed_form_auth_new (WebKitWebPage *web_page,
+ WebKitDOMNode *username_node,
+ WebKitDOMNode *password_node,
+ const char *username);
+WebKitDOMNode *ephy_embed_form_auth_get_username_node (EphyEmbedFormAuth *form_auth);
+WebKitDOMNode *ephy_embed_form_auth_get_password_node (EphyEmbedFormAuth *form_auth);
+SoupURI *ephy_embed_form_auth_get_uri (EphyEmbedFormAuth *form_auth);
+guint64 ephy_embed_form_auth_get_page_id (EphyEmbedFormAuth *form_auth);
+const char *ephy_embed_form_auth_get_username (EphyEmbedFormAuth *form_auth);
+gboolean ephy_embed_form_auth_get_password_updated (EphyEmbedFormAuth *form_auth);
+void ephy_embed_form_auth_set_password_updated (EphyEmbedFormAuth *form_auth,
+ gboolean password_updated);
+WebKitDOMDocument *ephy_embed_form_auth_get_owner_document (EphyEmbedFormAuth *form_auth);
G_END_DECLS
diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c
index b628358..588cd94 100644
--- a/embed/web-extension/ephy-web-extension.c
+++ b/embed/web-extension/ephy-web-extension.c
@@ -267,6 +267,7 @@ store_password (EphyEmbedFormAuth *form_auth)
char *username_field_value = NULL;
char *password_field_name = NULL;
char *password_field_value = NULL;
+ gboolean password_updated;
WebKitDOMNode *username_node;
EphyWebExtension *extension = ephy_web_extension_get ();
@@ -283,12 +284,14 @@ store_password (EphyEmbedFormAuth *form_auth)
uri = ephy_embed_form_auth_get_uri (form_auth);
uri_str = soup_uri_to_string (uri, FALSE);
+ password_updated = ephy_embed_form_auth_get_password_updated (form_auth);
ephy_password_manager_save (extension->password_manager,
uri_str,
username_field_name,
password_field_name,
username_field_value,
- password_field_value);
+ password_field_value,
+ !password_updated);
g_free (uri_str);
g_free (username_field_name);
@@ -417,15 +420,18 @@ should_store_cb (GSList *records,
LOG ("User/password already stored. Not asking about storing.");
} else if (permission == EPHY_PERMISSION_PERMIT) {
LOG ("User/password not yet stored. Storing.");
+ ephy_embed_form_auth_set_password_updated (form_auth, TRUE);
store_password (form_auth);
} else {
LOG ("User/password not yet stored. Asking about storing.");
+ ephy_embed_form_auth_set_password_updated (form_auth, TRUE);
request_decision_on_storing (g_object_ref (form_auth));
}
g_free (username_field_value);
} else {
LOG ("No result on query; asking whether we should store.");
+ ephy_embed_form_auth_set_password_updated (form_auth, FALSE);
request_decision_on_storing (g_object_ref (form_auth));
}
@@ -730,8 +736,7 @@ show_user_choices (WebKitDOMDocument *document,
WebKitDOMNode *body;
WebKitDOMElement *main_div;
WebKitDOMElement *ul;
- GSList *iter;
- GSList *password_records_list;
+ GSList *cached_users;
gboolean username_node_ever_edited;
double x, y;
double input_width;
@@ -777,32 +782,27 @@ show_user_choices (WebKitDOMDocument *document,
"padding: 0;",
NULL);
- password_records_list = (GSList *)g_object_get_data (G_OBJECT (username_node),
- "ephy-password-records-list");
+ cached_users = (GSList *)g_object_get_data (G_OBJECT (username_node), "ephy-cached-users");
username_node_ever_edited =
GPOINTER_TO_INT (g_object_get_data (G_OBJECT (username_node),
"ephy-user-ever-edited"));
- for (iter = password_records_list; iter; iter = iter->next) {
- EphyPasswordRecord *record;
+ for (GSList *l = cached_users; l && l->data; l = l->next) {
+ const char *user = l->data;
WebKitDOMElement *li;
WebKitDOMElement *anchor;
char *child_style;
- const char *record_username;
gboolean is_selected;
- record = EPHY_PASSWORD_RECORD (iter->data);
- record_username = ephy_password_record_get_username (record);
-
/* Filter out the available names that do not match, but show all options in
* case we have been triggered by something other than the user editing the
* input.
*/
- if (username_node_ever_edited && !g_str_has_prefix (record_username, username))
+ if (username_node_ever_edited && !g_str_has_prefix (user, username))
continue;
- is_selected = !g_strcmp0 (username, record_username);
+ is_selected = !g_strcmp0 (username, user);
li = webkit_dom_document_create_element (document, "li", NULL);
webkit_dom_element_set_attribute (li, "tabindex", "-1", NULL);
@@ -834,7 +834,7 @@ show_user_choices (WebKitDOMDocument *document,
username_node);
webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (anchor),
- record_username,
+ user,
NULL);
}
@@ -1108,7 +1108,7 @@ web_page_form_controls_associated (WebKitWebPage *web_page,
/* We have a field that may be the user, and one for a password. */
if (ephy_web_dom_utils_find_form_auth_elements (form, &username_node, &password_node)) {
EphyEmbedFormAuth *form_auth;
- GSList *password_records_list;
+ GSList *cached_users;
const char *uri;
LOG ("Hooking and pre-filling a form");
@@ -1126,11 +1126,11 @@ web_page_form_controls_associated (WebKitWebPage *web_page,
/* Plug in the user autocomplete */
uri = webkit_web_page_get_uri (web_page);
- password_records_list = ephy_password_manager_get_cached_by_uri (extension->password_manager, uri);
+ cached_users = ephy_password_manager_get_cached_users_for_uri (extension->password_manager, uri);
- if (password_records_list && password_records_list->next && username_node) {
+ if (cached_users && cached_users->next && username_node) {
LOG ("More than 1 password saved, hooking menu for choosing which on focus");
- g_object_set_data (G_OBJECT (username_node), "ephy-password-records-list", password_records_list);
+ g_object_set_data (G_OBJECT (username_node), "ephy-cached-users", cached_users);
g_object_set_data (G_OBJECT (username_node), "ephy-form-auth", form_auth);
g_object_set_data (G_OBJECT (username_node), "ephy-document", document);
webkit_dom_event_target_add_event_listener (WEBKIT_DOM_EVENT_TARGET (username_node), "input",
@@ -1149,7 +1149,7 @@ web_page_form_controls_associated (WebKitWebPage *web_page,
G_CALLBACK (username_node_changed_cb), FALSE,
web_page);
} else
- LOG ("No items or a single item in password_records_list, not hooking menu for choosing.");
+ LOG ("No items or a single item in cached_users, not hooking menu for choosing.");
pre_fill_form (form_auth);
diff --git a/embed/web-extension/meson.build b/embed/web-extension/meson.build
index de6be46..af53a64 100644
--- a/embed/web-extension/meson.build
+++ b/embed/web-extension/meson.build
@@ -10,6 +10,7 @@ web_extension_sources = [
web_extension_deps = [
ephymisc_dep,
+ ephysync_dep,
webkit2gtk_web_extension_dep
]
diff --git a/lib/ephy-prefs.h b/lib/ephy-prefs.h
index 3167d12..d91fdf2 100644
--- a/lib/ephy-prefs.h
+++ b/lib/ephy-prefs.h
@@ -157,6 +157,9 @@ static const char * const ephy_prefs_web_schema[] = {
#define EPHY_PREFS_SYNC_BOOKMARKS_ENABLED "sync-bookmarks-enabled"
#define EPHY_PREFS_SYNC_BOOKMARKS_TIME "sync-bookmarks-time"
#define EPHY_PREFS_SYNC_BOOKMARKS_INITIAL "sync-bookmarks-initial"
+#define EPHY_PREFS_SYNC_PASSWORDS_ENABLED "sync-passwords-enabled"
+#define EPHY_PREFS_SYNC_PASSWORDS_TIME "sync-passwords-time"
+#define EPHY_PREFS_SYNC_PASSWORDS_INITIAL "sync-passwords-initial"
static struct {
const char *schema;
diff --git a/lib/meson.build b/lib/meson.build
index 6e88051..b81c721 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -22,8 +22,6 @@ libephymisc_sources = [
'ephy-filters-manager.c',
'ephy-gui.c',
'ephy-langs.c',
- 'ephy-password-manager.c',
- 'ephy-password-record.c',
'ephy-permissions-manager.c',
'ephy-profile-utils.c',
'ephy-search-engine-manager.c',
diff --git a/lib/sync/ephy-password-manager.c b/lib/sync/ephy-password-manager.c
new file mode 100644
index 0000000..ed44e4a
--- /dev/null
+++ b/lib/sync/ephy-password-manager.c
@@ -0,0 +1,580 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Gabriel Ivascu <ivascu gabriel59 gmail com>
+ *
+ * This file is part of Epiphany.
+ *
+ * Epiphany 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany 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 Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-password-manager.h"
+
+#include "ephy-debug.h"
+#include "ephy-settings.h"
+#include "ephy-synchronizable-manager.h"
+#include "ephy-uri-helpers.h"
+
+#include <glib/gi18n.h>
+#include <stdio.h>
+
+const SecretSchema *
+ephy_password_manager_get_password_schema (void)
+{
+ static const SecretSchema schema = {
+ "org.epiphany.FormPassword", SECRET_SCHEMA_NONE,
+ {
+ { ID_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { URI_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { USERNAME_FIELD_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { PASSWORD_FIELD_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { USERNAME_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { SERVER_TIME_MODIFIED_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING},
+ { "NULL", 0 },
+ }
+ };
+ return &schema;
+}
+
+struct _EphyPasswordManager {
+ GObject parent_instance;
+
+ GHashTable *cache;
+};
+
+static void ephy_synchronizable_manager_iface_init (EphySynchronizableManagerInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EphyPasswordManager, ephy_password_manager, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (EPHY_TYPE_SYNCHRONIZABLE_MANAGER,
+ ephy_synchronizable_manager_iface_init))
+
+typedef struct {
+ EphyPasswordManagerQueryCallback callback;
+ gpointer user_data;
+} QueryAsyncData;
+
+static QueryAsyncData *
+query_async_data_new (EphyPasswordManagerQueryCallback callback,
+ gpointer user_data)
+{
+ QueryAsyncData *data;
+
+ data = g_slice_new (QueryAsyncData);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ return data;
+}
+
+static void
+query_async_data_free (QueryAsyncData *data)
+{
+ g_slice_free (QueryAsyncData, data);
+}
+
+static void
+ephy_password_manager_cache_clear (EphyPasswordManager *self)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_assert (EPHY_IS_PASSWORD_MANAGER (self));
+ g_assert (self->cache);
+
+ g_hash_table_iter_init (&iter, self->cache);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ g_slist_free_full (value, g_free);
+ g_hash_table_remove_all (self->cache);
+}
+
+static void
+ephy_password_manager_cache_remove (EphyPasswordManager *self,
+ const char *hostname,
+ const char *username)
+{
+ GSList *usernames;
+ GSList *new_usernames = NULL;
+
+ g_assert (EPHY_IS_PASSWORD_MANAGER (self));
+ g_assert (self->cache);
+ g_assert (hostname);
+ g_assert (username);
+
+ usernames = g_hash_table_lookup (self->cache, hostname);
+ if (usernames) {
+ for (GSList *l = usernames; l && l->data; l = l->next) {
+ if (g_strcmp0 (username, l->data))
+ new_usernames = g_slist_prepend (new_usernames, g_strdup (l->data));
+ }
+ g_hash_table_replace (self->cache, g_strdup (hostname), new_usernames);
+ g_slist_free_full (usernames, g_free);
+ }
+}
+
+static void
+ephy_password_manager_cache_add (EphyPasswordManager *self,
+ const char *hostname,
+ const char *username)
+{
+ GSList *usernames;
+
+ g_assert (EPHY_IS_PASSWORD_MANAGER (self));
+ g_assert (self->cache);
+ g_assert (hostname);
+ g_assert (username);
+
+ usernames = g_hash_table_lookup (self->cache, hostname);
+ for (GSList *l = usernames; l && l->data; l = l->next) {
+ if (!g_strcmp0 (username, l->data))
+ return;
+ }
+ usernames = g_slist_prepend (usernames, g_strdup (username));
+ g_hash_table_replace (self->cache, g_strdup (hostname), usernames);
+}
+
+static void
+populate_cache_cb (GSList *records,
+ gpointer user_data)
+{
+ EphyPasswordManager *self;
+
+ g_assert (EPHY_IS_PASSWORD_MANAGER (user_data));
+ self = EPHY_PASSWORD_MANAGER (user_data);
+ g_assert (self->cache);
+
+ for (GSList *l = records; l && l->data; l = l->next) {
+ EphyPasswordRecord *record = EPHY_PASSWORD_RECORD (l->data);
+ const char *hostname = ephy_password_record_get_hostname (record);
+ const char *username = ephy_password_record_get_username (record);
+
+ ephy_password_manager_cache_add (self, hostname, username);
+ }
+
+ g_slist_free_full (records, g_object_unref);
+}
+
+static void
+ephy_password_manager_dispose (GObject *object)
+{
+ EphyPasswordManager *self = EPHY_PASSWORD_MANAGER (object);
+
+ if (self->cache) {
+ ephy_password_manager_cache_clear (self);
+ g_clear_pointer (&self->cache, g_hash_table_unref);
+ }
+
+ G_OBJECT_CLASS (ephy_password_manager_parent_class)->dispose (object);
+}
+
+static void
+ephy_password_manager_class_init (EphyPasswordManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ephy_password_manager_dispose;
+}
+
+static void
+ephy_password_manager_init (EphyPasswordManager *self)
+{
+ LOG ("Loading usernames into internal cache...");
+ self->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ ephy_password_manager_query (self, NULL, NULL, NULL, NULL,
+ populate_cache_cb, self);
+}
+
+EphyPasswordManager *
+ephy_password_manager_new (void)
+{
+ return EPHY_PASSWORD_MANAGER (g_object_new (EPHY_TYPE_PASSWORD_MANAGER, NULL));
+}
+
+GSList *
+ephy_password_manager_get_cached_users_for_uri (EphyPasswordManager *self,
+ const char *uri)
+{
+ GSList *list;
+ char *hostname;
+
+ g_return_val_if_fail (EPHY_IS_PASSWORD_MANAGER (self), NULL);
+ g_return_val_if_fail (uri, NULL);
+
+ hostname = ephy_uri_to_security_origin (uri);
+ list = g_hash_table_lookup (self->cache, hostname);
+ g_free (hostname);
+
+ return list;
+}
+
+void
+ephy_password_manager_save (EphyPasswordManager *self,
+ const char *uri,
+ const char *username_field,
+ const char *password_field,
+ const char *username,
+ const char *password,
+ gboolean is_new)
+{
+ char *hostname;
+ char *id = NULL;
+
+ g_return_if_fail (EPHY_IS_PASSWORD_MANAGER (self));
+ g_return_if_fail (uri);
+ g_return_if_fail (password);
+ g_return_if_fail (!username_field || username);
+ g_return_if_fail (!password_field || password);
+
+ if (is_new) {
+ char *uuid = g_uuid_string_random ();
+ id = g_strdup_printf ("{%s}", uuid);
+ g_free (uuid);
+ }
+
+ hostname = ephy_uri_to_security_origin (uri);
+ ephy_password_manager_cache_add (self, hostname, username);
+
+ /* Add/overwrite the password in the secret schema. */
+ ephy_password_manager_store (id, hostname,
+ username_field, password_field,
+ username, password,
+ NULL, NULL);
+
+ g_free (hostname);
+ g_free (id);
+}
+
+static GHashTable *
+get_attributes_table (const char *id,
+ const char *uri,
+ const char *username_field,
+ const char *password_field,
+ const char *username)
+{
+ GHashTable *attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL);
+
+ if (id)
+ g_hash_table_insert (attributes, g_strdup (ID_KEY), g_strdup (id));
+ if (uri)
+ g_hash_table_insert (attributes, g_strdup (URI_KEY), ephy_uri_to_security_origin (uri));
+ if (username)
+ g_hash_table_insert (attributes, g_strdup (USERNAME_KEY), g_strdup (username));
+ if (username_field)
+ g_hash_table_insert (attributes, g_strdup (USERNAME_FIELD_KEY), g_strdup (username_field));
+ if (password_field)
+ g_hash_table_insert (attributes, g_strdup (PASSWORD_FIELD_KEY), g_strdup (password_field));
+
+ return attributes;
+}
+
+static void
+secret_service_search_cb (SecretService *service,
+ GAsyncResult *result,
+ QueryAsyncData *data)
+{
+ GList *matches;
+ GSList *records = NULL;
+ GError *error = NULL;
+
+ matches = secret_service_search_finish (service, result, &error);
+ if (error) {
+ g_warning ("Failed to search secrets in password schema: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ for (GList *l = matches; l && l->data; l = l->next) {
+ SecretItem *item = (SecretItem *)l->data;
+ GHashTable *attributes = secret_item_get_attributes (item);
+ SecretValue *value = secret_item_get_secret (item);
+ const char *id = g_hash_table_lookup (attributes, ID_KEY);
+ const char *hostname = g_hash_table_lookup (attributes, URI_KEY);
+ const char *username_field = g_hash_table_lookup (attributes, USERNAME_FIELD_KEY);
+ const char *password_field = g_hash_table_lookup (attributes, PASSWORD_FIELD_KEY);
+ const char *username = g_hash_table_lookup (attributes, USERNAME_KEY);
+ const char *timestamp = g_hash_table_lookup (attributes, SERVER_TIME_MODIFIED_KEY);
+ const char *password = secret_value_get (value, NULL);
+ EphyPasswordRecord *record;
+
+ LOG ("Found password record for (%s, %s, %s, %s)",
+ hostname, username_field, password_field, username);
+LOG ("id: %s, time created: %lu, time modified: %lu, server_time_modified: %s", id, secret_item_get_created
(item), secret_item_get_modified (item), timestamp);
+
+ record = ephy_password_record_new (id, hostname,
+ username_field, password_field,
+ username, password,
+ secret_item_get_created (item) * 1000,
+ secret_item_get_modified (item) * 1000);
+ if (timestamp) {
+ double server_time_modified;
+ sscanf (timestamp, "%lf", &server_time_modified);
+ ephy_synchronizable_set_server_time_modified (EPHY_SYNCHRONIZABLE (record),
+ server_time_modified);
+ }
+ records = g_slist_prepend (records, record);
+
+ secret_value_unref (value);
+ g_hash_table_unref (attributes);
+ }
+
+out:
+ if (data->callback)
+ data->callback (records, data->user_data);
+ query_async_data_free (data);
+}
+
+void
+ephy_password_manager_query (EphyPasswordManager *self,
+ const char *uri,
+ const char *username_field,
+ const char *password_field,
+ const char *username,
+ EphyPasswordManagerQueryCallback callback,
+ gpointer user_data)
+{
+ QueryAsyncData *data;
+ GHashTable *attributes;
+
+ g_return_if_fail (EPHY_IS_PASSWORD_MANAGER (self));
+
+ LOG ("Querying password records for (%s, %s, %s, %s)",
+ uri, username_field, password_field, username);
+
+ attributes = get_attributes_table (NULL, uri, username_field, password_field, username);
+ data = query_async_data_new (callback, user_data);
+
+ secret_service_search (NULL,
+ EPHY_FORM_PASSWORD_SCHEMA,
+ attributes,
+ SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
+ NULL,
+ (GAsyncReadyCallback)secret_service_search_cb,
+ data);
+
+ g_hash_table_unref (attributes);
+}
+
+static void
+secret_service_store_cb (SecretService *service,
+ GAsyncResult *result,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ secret_service_store_finish (service, result, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+void
+ephy_password_manager_store (const char *id,
+ const char *uri,
+ const char *username_field,
+ const char *password_field,
+ const char *username,
+ const char *password,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SecretValue *value;
+ GHashTable *attributes;
+ GTask *task;
+ char *hostname;
+ char *label;
+
+ g_return_if_fail (uri);
+ g_return_if_fail (password);
+ g_return_if_fail (!username_field || username);
+ g_return_if_fail (!password_field || password);
+
+ task = g_task_new (NULL, NULL, callback, user_data);
+ value = secret_value_new (password, -1, "text/plain");
+ attributes = get_attributes_table (id, uri, username_field, password_field, username);
+ hostname = ephy_uri_to_security_origin (uri);
+
+ if (username) {
+ /* Translators: The first %s is the username and the second one is the
+ * security origin where this is happening. Example: gnome gmail com and
+ * https://mail.google.com. */
+ label = g_strdup_printf (_("Password for %s in a form in %s"), username, hostname);
+ } else {
+ /* Translators: The %s is the security origin where this is happening.
+ * Example: https://mail.google.com. */
+ label = g_strdup_printf (_("Password in a form in %s"), hostname);
+ }
+
+ LOG ("Storing password record for (%s, %s, %s, %s)",
+ uri, username_field, password_field, username);
+
+ secret_service_store (NULL, EPHY_FORM_PASSWORD_SCHEMA,
+ attributes, NULL, label, value, NULL,
+ (GAsyncReadyCallback)secret_service_store_cb,
+ g_object_ref (task));
+
+ g_free (label);
+ g_free (hostname);
+ secret_value_unref (value);
+ g_hash_table_unref (attributes);
+ g_object_unref (task);
+}
+
+gboolean
+ephy_password_manager_store_finish (GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (!error || !(*error), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+secret_service_clear_cb (SecretService *service,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ secret_service_clear_finish (service, result, &error);
+ if (error) {
+ g_warning ("Failed to clear secrets from password schema: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+void
+ephy_password_manager_forget (EphyPasswordManager *self,
+ const char *hostname,
+ const char *username_field,
+ const char *password_field,
+ const char *username)
+{
+ GHashTable *attributes;
+
+ g_return_if_fail (EPHY_IS_PASSWORD_MANAGER (self));
+ g_return_if_fail (hostname);
+ g_return_if_fail (password_field);
+ g_return_if_fail (!username_field || username);
+
+ attributes = get_attributes_table (NULL, hostname, username_field, password_field, username);
+ secret_service_clear (NULL, EPHY_FORM_PASSWORD_SCHEMA, attributes, NULL,
+ (GAsyncReadyCallback)secret_service_clear_cb, NULL);
+
+ ephy_password_manager_cache_remove (self, hostname, username);
+}
+
+void
+ephy_password_manager_forget_all (EphyPasswordManager *self)
+{
+ GHashTable *attributes;
+
+ g_return_if_fail (EPHY_IS_PASSWORD_MANAGER (self));
+
+ attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL);
+ secret_service_clear (NULL, EPHY_FORM_PASSWORD_SCHEMA, attributes, NULL,
+ (GAsyncReadyCallback)secret_service_clear_cb, NULL);
+
+ ephy_password_manager_cache_clear (self);
+
+ g_hash_table_unref (attributes);
+}
+
+static const char *
+synchronizable_manager_get_collection_name (EphySynchronizableManager *manager)
+{
+ gboolean sync_with_firefox = g_settings_get_boolean (EPHY_SETTINGS_SYNC,
+ EPHY_PREFS_SYNC_WITH_FIREFOX);
+
+ return sync_with_firefox ? "passwords" : "ephy-passwords";
+}
+
+static GType
+synchronizable_manager_get_synchronizable_type (EphySynchronizableManager *manager)
+{
+ return EPHY_TYPE_PASSWORD_RECORD;
+}
+
+static gboolean
+synchronizable_manager_is_initial_sync (EphySynchronizableManager *manager)
+{
+ return g_settings_get_boolean (EPHY_SETTINGS_SYNC,
+ EPHY_PREFS_SYNC_PASSWORDS_INITIAL);
+}
+
+static void
+synchronizable_manager_set_is_initial_sync (EphySynchronizableManager *manager,
+ gboolean is_initial)
+{
+ g_settings_set_boolean (EPHY_SETTINGS_SYNC,
+ EPHY_PREFS_SYNC_PASSWORDS_INITIAL,
+ is_initial);
+}
+
+static double
+synchronizable_manager_get_sync_time (EphySynchronizableManager *manager)
+{
+ return g_settings_get_double (EPHY_SETTINGS_SYNC,
+ EPHY_PREFS_SYNC_PASSWORDS_TIME);
+}
+
+static void
+synchronizable_manager_set_sync_time (EphySynchronizableManager *manager,
+ double sync_time)
+{
+ g_settings_set_double (EPHY_SETTINGS_SYNC,
+ EPHY_PREFS_SYNC_PASSWORDS_TIME,
+ sync_time);
+}
+
+static void
+synchronizable_manager_add (EphySynchronizableManager *manager,
+ EphySynchronizable *synchronizable)
+{
+ /* TODO: Implement this. */
+}
+
+static void
+synchronizable_manager_remove (EphySynchronizableManager *manager,
+ EphySynchronizable *synchronizable)
+{
+ /* TODO: Implement this. */
+}
+
+static GSList *
+synchronizable_manager_merge_remotes (EphySynchronizableManager *manager,
+ gboolean is_initial,
+ GSList *remotes_deleted,
+ GSList *remotes_updated)
+{
+ /* TODO: Implement this. */
+
+ return NULL;
+}
+
+static void
+ephy_synchronizable_manager_iface_init (EphySynchronizableManagerInterface *iface)
+{
+ iface->get_collection_name = synchronizable_manager_get_collection_name;
+ iface->get_synchronizable_type = synchronizable_manager_get_synchronizable_type;
+ iface->is_initial_sync = synchronizable_manager_is_initial_sync;
+ iface->set_is_initial_sync = synchronizable_manager_set_is_initial_sync;
+ iface->get_sync_time = synchronizable_manager_get_sync_time;
+ iface->set_sync_time = synchronizable_manager_set_sync_time;
+ iface->add = synchronizable_manager_add;
+ iface->remove = synchronizable_manager_remove;
+ iface->merge_remotes = synchronizable_manager_merge_remotes;
+}
diff --git a/lib/sync/ephy-password-manager.h b/lib/sync/ephy-password-manager.h
new file mode 100644
index 0000000..9ea6bbf
--- /dev/null
+++ b/lib/sync/ephy-password-manager.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Gabriel Ivascu <ivascu gabriel59 gmail com>
+ *
+ * This file is part of Epiphany.
+ *
+ * Epiphany 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany 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 Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ephy-password-record.h"
+
+#include <glib-object.h>
+#include <libsecret/secret.h>
+
+G_BEGIN_DECLS
+
+const SecretSchema *ephy_password_manager_get_password_schema (void) G_GNUC_CONST;
+
+#define ID_KEY "id"
+#define URI_KEY "uri"
+#define USERNAME_FIELD_KEY "form_username"
+#define PASSWORD_FIELD_KEY "form_password"
+#define USERNAME_KEY "username"
+#define SERVER_TIME_MODIFIED_KEY "server_time_modified"
+
+#define EPHY_FORM_PASSWORD_SCHEMA ephy_password_manager_get_password_schema ()
+
+#define EPHY_TYPE_PASSWORD_MANAGER (ephy_password_manager_get_type ())
+
+G_DECLARE_FINAL_TYPE (EphyPasswordManager, ephy_password_manager, EPHY, PASSWORD_MANAGER, GObject)
+
+typedef void (*EphyPasswordManagerQueryCallback) (GSList *records, gpointer user_data);
+
+EphyPasswordManager *ephy_password_manager_new (void);
+GSList *ephy_password_manager_get_cached_users_for_uri (EphyPasswordManager *self,
+ const char *uri);
+void ephy_password_manager_save (EphyPasswordManager *self,
+ const char *uri,
+ const char *username_field,
+ const char *password_field,
+ const char *username,
+ const char *password,
+ gboolean is_new);
+void ephy_password_manager_query (EphyPasswordManager *self,
+ const char *uri,
+ const char
*username_field,
+ const char
*password_field,
+ const char
*username,
+ EphyPasswordManagerQueryCallback
callback,
+ gpointer
user_data);
+void ephy_password_manager_store (const char *id,
+ const char *uri,
+ const char *username_field,
+ const char *password_field,
+ const char *username,
+ const char *password,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ephy_password_manager_store_finish (GAsyncResult *result,
+ GError **error);
+void ephy_password_manager_forget (EphyPasswordManager *self,
+ const char *hostname,
+ const char *username_field,
+ const char *password_field,
+ const char *username);
+void ephy_password_manager_forget_all (EphyPasswordManager *self);
+
+G_END_DECLS
diff --git a/lib/sync/ephy-password-record.c b/lib/sync/ephy-password-record.c
new file mode 100644
index 0000000..bd6fa5b
--- /dev/null
+++ b/lib/sync/ephy-password-record.c
@@ -0,0 +1,379 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Gabriel Ivascu <ivascu gabriel59 gmail com>
+ *
+ * This file is part of Epiphany.
+ *
+ * Epiphany 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Epiphany 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 Epiphany. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-password-record.h"
+
+#include "ephy-synchronizable.h"
+
+struct _EphyPasswordRecord {
+ GObject parent_instance;
+
+ char *id;
+ char *hostname;
+ char *form_submit_url;
+ char *http_realm;
+ char *username_field;
+ char *password_field;
+ char *username;
+ char *password;
+ guint64 time_created;
+ guint64 time_password_changed;
+
+ double server_time_modified;
+};
+
+static void json_serializable_iface_init (JsonSerializableIface *iface);
+static void ephy_synchronizable_iface_init (EphySynchronizableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EphyPasswordRecord, ephy_password_record, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE,
+ json_serializable_iface_init)
+ G_IMPLEMENT_INTERFACE (EPHY_TYPE_SYNCHRONIZABLE,
+ ephy_synchronizable_iface_init))
+
+enum {
+ PROP_0,
+ PROP_ID, /* Firefox Sync */
+ PROP_HOSTNAME, /* Epiphany && Firefox Sync */
+ PROP_FORM_SUBMIT_URL, /* Firefox Sync */
+ PROP_HTTP_REALM, /* Firefox Sync */
+ PROP_USERNAME_FIELD, /* Epiphany && Firefox Sync */
+ PROP_PASSWORD_FIELD, /* Epiphany && Firefox Sync */
+ PROP_USERNAME, /* Epiphany && Firefox Sync */
+ PROP_PASSWORD, /* Epiphany && Firefox Sync */
+ PROP_TIME_CREATED, /* Firefox Sync */
+ PROP_TIME_PASSWORD_CHANGED, /* Firefox Sync */
+ LAST_PROP,
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+static void
+ephy_password_record_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EphyPasswordRecord *self = EPHY_PASSWORD_RECORD (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_free (self->id);
+ self->id = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_HOSTNAME:
+ g_free (self->hostname);
+ self->hostname = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_FORM_SUBMIT_URL:
+ g_free (self->form_submit_url);
+ self->form_submit_url = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_HTTP_REALM:
+ g_free (self->http_realm);
+ self->http_realm = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_USERNAME_FIELD:
+ g_free (self->username_field);
+ self->username_field = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_PASSWORD_FIELD:
+ g_free (self->password_field);
+ self->password_field = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_USERNAME:
+ g_free (self->username);
+ self->username = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_PASSWORD:
+ g_free (self->password);
+ self->password = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_TIME_CREATED:
+ self->time_created = g_value_get_uint64 (value);
+ break;
+ case PROP_TIME_PASSWORD_CHANGED:
+ self->time_password_changed = g_value_get_uint64 (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_password_record_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EphyPasswordRecord *self = EPHY_PASSWORD_RECORD (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_value_set_string (value, self->id);
+ break;
+ case PROP_HOSTNAME:
+ g_value_set_string (value, self->hostname);
+ break;
+ case PROP_FORM_SUBMIT_URL:
+ g_value_set_string (value, self->form_submit_url);
+ break;
+ case PROP_HTTP_REALM:
+ g_value_set_string (value, self->http_realm);
+ break;
+ case PROP_USERNAME_FIELD:
+ g_value_set_string (value, self->username_field);
+ break;
+ case PROP_PASSWORD_FIELD:
+ g_value_set_string (value, self->password_field);
+ break;
+ case PROP_USERNAME:
+ g_value_set_string (value, self->username);
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, self->password);
+ break;
+ case PROP_TIME_CREATED:
+ g_value_set_uint64 (value, self->time_created);
+ break;
+ case PROP_TIME_PASSWORD_CHANGED:
+ g_value_set_uint64 (value, self->time_password_changed);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ephy_password_record_dispose (GObject *object)
+{
+ EphyPasswordRecord *self = EPHY_PASSWORD_RECORD (object);
+
+ g_clear_pointer (&self->id, g_free);
+ g_clear_pointer (&self->hostname, g_free);
+ g_clear_pointer (&self->form_submit_url, g_free);
+ g_clear_pointer (&self->http_realm, g_free);
+ g_clear_pointer (&self->username_field, g_free);
+ g_clear_pointer (&self->password_field, g_free);
+ g_clear_pointer (&self->username, g_free);
+ g_clear_pointer (&self->password, g_free);
+
+ G_OBJECT_CLASS (ephy_password_record_parent_class)->dispose (object);
+}
+
+static void
+ephy_password_record_class_init (EphyPasswordRecordClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = ephy_password_record_set_property;
+ object_class->get_property = ephy_password_record_get_property;
+ object_class->dispose = ephy_password_record_dispose;
+
+ obj_properties[PROP_ID] =
+ g_param_spec_string ("id",
+ "Id",
+ "Id of the password record",
+ "Default id",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_HOSTNAME] =
+ g_param_spec_string ("hostname",
+ "Hostname",
+ "Hostname url that password is applicable at",
+ "Default hostname",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_FORM_SUBMIT_URL] =
+ g_param_spec_string ("formSubmitURL",
+ "Form submit URL",
+ "Submission URL set by form",
+ "Default form submit URL",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_HTTP_REALM] =
+ g_param_spec_string ("httpRealm",
+ "HTTP realm",
+ "HTTP realm for which the login is valid",
+ "Default HTTP realm",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_USERNAME_FIELD] =
+ g_param_spec_string ("usernameField",
+ "Username field",
+ "HTML field name of the username",
+ "Default username field",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_PASSWORD_FIELD] =
+ g_param_spec_string ("passwordField",
+ "Password field",
+ "HTML field name of the password",
+ "Default password field",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_USERNAME] =
+ g_param_spec_string ("username",
+ "Username",
+ "Username to log in as",
+ "Default username",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_PASSWORD] =
+ g_param_spec_string ("password",
+ "Password",
+ "Password for the username",
+ "Default password",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_TIME_CREATED] =
+ g_param_spec_uint64 ("timeCreated",
+ "Time created",
+ "Unix timestamp in milliseconds at which the password was created",
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+ obj_properties[PROP_TIME_PASSWORD_CHANGED] =
+ g_param_spec_uint64 ("timePasswordChanged",
+ "Time password changed",
+ "Unix timestamp in milliseconds at which the password was changed",
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+}
+
+static void
+ephy_password_record_init (EphyPasswordRecord *self)
+{
+}
+
+EphyPasswordRecord *
+ephy_password_record_new (const char *id,
+ const char *hostname,
+ const char *username_field,
+ const char *password_field,
+ const char *username,
+ const char *password,
+ guint64 time_created,
+ guint64 time_password_changed)
+{
+ return EPHY_PASSWORD_RECORD (g_object_new (EPHY_TYPE_PASSWORD_RECORD,
+ "id", id,
+ "hostname", hostname,
+ "formSubmitURL", hostname,
+ "httpRealm", hostname,
+ "usernameField", username_field,
+ "passwordField", password_field,
+ "username", username,
+ "password", password,
+ "timeCreated", time_created,
+ "timePasswordChanged", time_password_changed,
+ NULL));
+}
+
+const char *
+ephy_password_record_get_id (EphyPasswordRecord *self)
+{
+ g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL);
+
+ return self->id;
+}
+
+const char *
+ephy_password_record_get_hostname (EphyPasswordRecord *self)
+{
+ g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL);
+
+ return self->hostname;
+}
+
+const char *
+ephy_password_record_get_username_field (EphyPasswordRecord *self)
+{
+ g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL);
+
+ return self->username_field;
+}
+
+const char *
+ephy_password_record_get_password_field (EphyPasswordRecord *self)
+{
+ g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL);
+
+ return self->password_field;
+}
+
+const char *
+ephy_password_record_get_username (EphyPasswordRecord *self)
+{
+ g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL);
+
+ return self->username;
+}
+
+const char *
+ephy_password_record_get_password (EphyPasswordRecord *self)
+{
+ g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL);
+
+ return self->password;
+}
+
+void
+ephy_password_record_set_password (EphyPasswordRecord *self,
+ const char *password)
+{
+ g_return_if_fail (EPHY_IS_PASSWORD_RECORD (self));
+
+ g_free (self->password);
+ self->password = g_strdup (password);
+}
+
+static void
+json_serializable_iface_init (JsonSerializableIface *iface)
+{
+ iface->serialize_property = json_serializable_default_serialize_property;
+ iface->deserialize_property = json_serializable_default_deserialize_property;
+}
+
+static const char *
+synchronizable_get_id (EphySynchronizable *synchronizable)
+{
+ return ephy_password_record_get_id (EPHY_PASSWORD_RECORD (synchronizable));
+}
+
+static double
+synchronizable_get_server_time_modified (EphySynchronizable *synchronizable)
+{
+ return EPHY_PASSWORD_RECORD (synchronizable)->server_time_modified;
+}
+
+static void
+synchronizable_set_server_time_modified (EphySynchronizable *synchronizable,
+ double server_time_modified)
+{
+ EPHY_PASSWORD_RECORD (synchronizable)->server_time_modified = server_time_modified;
+}
+
+static void
+ephy_synchronizable_iface_init (EphySynchronizableInterface *iface)
+{
+ iface->get_id = synchronizable_get_id;
+ iface->get_server_time_modified = synchronizable_get_server_time_modified;
+ iface->set_server_time_modified = synchronizable_set_server_time_modified;
+ iface->to_bso = ephy_synchronizable_default_to_bso;
+}
diff --git a/lib/ephy-password-record.h b/lib/sync/ephy-password-record.h
similarity index 74%
rename from lib/ephy-password-record.h
rename to lib/sync/ephy-password-record.h
index 2dcd128..a0f316f 100644
--- a/lib/ephy-password-record.h
+++ b/lib/sync/ephy-password-record.h
@@ -29,14 +29,18 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (EphyPasswordRecord, ephy_password_record, EPHY, PASSWORD_RECORD, GObject)
-EphyPasswordRecord *ephy_password_record_new (const char *origin,
- const char *form_username,
- const char *form_passwords,
+EphyPasswordRecord *ephy_password_record_new (const char *id,
+ const char *hostname,
+ const char *username_field,
+ const char *password_field,
const char *username,
- const char *password);
-const char *ephy_password_record_get_origin (EphyPasswordRecord *self);
-const char *ephy_password_record_get_form_username (EphyPasswordRecord *self);
-const char *ephy_password_record_get_form_password (EphyPasswordRecord *self);
+ const char *password,
+ guint64 time_created,
+ guint64 time_password_changed);
+const char *ephy_password_record_get_id (EphyPasswordRecord *self);
+const char *ephy_password_record_get_hostname (EphyPasswordRecord *self);
+const char *ephy_password_record_get_username_field (EphyPasswordRecord *self);
+const char *ephy_password_record_get_password_field (EphyPasswordRecord *self);
const char *ephy_password_record_get_username (EphyPasswordRecord *self);
const char *ephy_password_record_get_password (EphyPasswordRecord *self);
void ephy_password_record_set_password (EphyPasswordRecord *self,
diff --git a/lib/sync/meson.build b/lib/sync/meson.build
index b4e6a22..658c17c 100644
--- a/lib/sync/meson.build
+++ b/lib/sync/meson.build
@@ -1,4 +1,6 @@
libephysync_sources = [
+ 'ephy-password-manager.c',
+ 'ephy-password-record.c',
'ephy-sync-crypto.c',
'ephy-sync-service.c',
'ephy-synchronizable-manager.c',
diff --git a/meson.build b/meson.build
index 1119843..61e755f 100644
--- a/meson.build
+++ b/meson.build
@@ -41,7 +41,7 @@ configure_file(
configuration: conf
)
-glib_requirement = '>= 2.46.0'
+glib_requirement = '>= 2.52.0'
gtk_requirement = '>= 3.22.0'
nettle_requirement = '>= 3.2'
webkitgtk_requirement = '>= 2.16.0'
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index 6f917a5..4c36fd0 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -346,13 +346,15 @@ ephy_shell_startup (GApplication *application)
G_BINDING_SYNC_CREATE);
}
- ephy_shell->password_manager = ephy_password_manager_new ();
-
/* Create the sync service and register synchronizable managers. */
ephy_shell->sync_service = ephy_sync_service_new ();
if (g_settings_get_boolean (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_BOOKMARKS_ENABLED))
ephy_sync_service_register_manager (ephy_shell->sync_service,
EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_bookmarks_manager
(ephy_shell)));
+ if (g_settings_get_boolean (EPHY_SETTINGS_SYNC, EPHY_PREFS_SYNC_PASSWORDS_ENABLED))
+ ephy_sync_service_register_manager (ephy_shell->sync_service,
+ EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_password_manager
(ephy_shell)));
+
gtk_application_set_app_menu (GTK_APPLICATION (application),
G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
} else {
@@ -821,6 +823,9 @@ ephy_shell_get_password_manager (EphyShell *shell)
{
g_return_val_if_fail (EPHY_IS_SHELL (shell), NULL);
+ if (shell->password_manager == NULL)
+ shell->password_manager = ephy_password_manager_new ();
+
return shell->password_manager;
}
diff --git a/src/passwords-dialog.c b/src/passwords-dialog.c
index 2f0637f..ddb7377 100644
--- a/src/passwords-dialog.c
+++ b/src/passwords-dialog.c
@@ -140,9 +140,9 @@ forget (GSimpleAction *action,
gtk_tree_model_get_value (model, &iter, COL_PASSWORDS_DATA, &val);
record = g_value_get_object (&val);
ephy_password_manager_forget (dialog->manager,
- ephy_password_record_get_origin (record),
- ephy_password_record_get_form_username (record),
- ephy_password_record_get_form_password (record),
+ ephy_password_record_get_hostname (record),
+ ephy_password_record_get_username_field (record),
+ ephy_password_record_get_password_field (record),
ephy_password_record_get_username (record));
dialog->records = g_slist_remove (dialog->records, record);
g_object_unref (record);
@@ -370,7 +370,7 @@ populate_model_cb (GSList *records,
gtk_list_store_insert_with_values (GTK_LIST_STORE (dialog->liststore),
&iter,
-1,
- COL_PASSWORDS_ORIGIN, ephy_password_record_get_origin (record),
+ COL_PASSWORDS_ORIGIN, ephy_password_record_get_hostname (record),
COL_PASSWORDS_USER, ephy_password_record_get_username (record),
COL_PASSWORDS_PASSWORD, ephy_password_record_get_password (record),
COL_PASSWORDS_INVISIBLE, "●●●●●●●●",
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index 46c4e73..e05d8c3 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -121,6 +121,7 @@ struct _PrefsDialog {
GtkWidget *sync_options_box;
GtkWidget *sync_with_firefox_checkbutton;
GtkWidget *sync_bookmarks_checkbutton;
+ GtkWidget *sync_passwords_checkbutton;
GtkWidget *sync_frequency_5_min_radiobutton;
GtkWidget *sync_frequency_15_min_radiobutton;
GtkWidget *sync_frequency_30_min_radiobutton;
@@ -175,19 +176,20 @@ prefs_dialog_finalize (GObject *object)
}
static void
-sync_bookmarks_toggled_cb (GtkToggleButton *button,
- PrefsDialog *dialog)
+sync_collection_toggled_cb (GtkToggleButton *button,
+ PrefsDialog *dialog)
{
- EphyBookmarksManager *manager;
+ EphySynchronizableManager *manager;
- manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
+ if (GTK_WIDGET (button) == dialog->sync_bookmarks_checkbutton)
+ manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_bookmarks_manager (ephy_shell_get_default ()));
+ else if (GTK_WIDGET (button) == dialog->sync_passwords_checkbutton)
+ manager = EPHY_SYNCHRONIZABLE_MANAGER (ephy_shell_get_password_manager (ephy_shell_get_default ()));
if (gtk_toggle_button_get_active (button))
- ephy_sync_service_register_manager (dialog->sync_service,
- EPHY_SYNCHRONIZABLE_MANAGER (manager));
+ ephy_sync_service_register_manager (dialog->sync_service, manager);
else
- ephy_sync_service_unregister_manager (dialog->sync_service,
- EPHY_SYNCHRONIZABLE_MANAGER (manager));
+ ephy_sync_service_unregister_manager (dialog->sync_service, manager);
}
static void
@@ -586,6 +588,7 @@ prefs_dialog_class_init (PrefsDialogClass *klass)
gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_options_box);
gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_with_firefox_checkbutton);
gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_bookmarks_checkbutton);
+ gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_passwords_checkbutton);
gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_frequency_5_min_radiobutton);
gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_frequency_15_min_radiobutton);
gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_frequency_30_min_radiobutton);
@@ -1640,7 +1643,10 @@ setup_sync_page (PrefsDialog *dialog)
G_CALLBACK (sync_finished_cb),
dialog, 0);
g_signal_connect_object (dialog->sync_bookmarks_checkbutton, "toggled",
- G_CALLBACK (sync_bookmarks_toggled_cb),
+ G_CALLBACK (sync_collection_toggled_cb),
+ dialog, 0);
+ g_signal_connect_object (dialog->sync_passwords_checkbutton, "toggled",
+ G_CALLBACK (sync_collection_toggled_cb),
dialog, 0);
g_settings_bind (sync_settings,
@@ -1653,6 +1659,11 @@ setup_sync_page (PrefsDialog *dialog)
dialog->sync_bookmarks_checkbutton,
"active",
G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (sync_settings,
+ EPHY_PREFS_SYNC_PASSWORDS_ENABLED,
+ dialog->sync_passwords_checkbutton,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
g_settings_bind_with_mapping (sync_settings,
EPHY_PREFS_SYNC_FREQUENCY,
dialog->sync_frequency_5_min_radiobutton,
diff --git a/src/profile-migrator/ephy-profile-migrator.c b/src/profile-migrator/ephy-profile-migrator.c
index 1ce5335..e0bb528 100644
--- a/src/profile-migrator/ephy-profile-migrator.c
+++ b/src/profile-migrator/ephy-profile-migrator.c
@@ -309,7 +309,7 @@ load_collection_items_cb (SecretCollection *collection,
SecretValue *secret;
GList *l;
GHashTable *attributes, *t;
- const char *server, *username, *form_username, *form_password, *password;
+ const char *server, *username, *username_field, *password_field, *password;
char *actual_server;
SoupURI *uri;
GError *error = NULL;
@@ -338,16 +338,17 @@ load_collection_items_cb (SecretCollection *collection,
username = g_hash_table_lookup (attributes, "user");
uri = soup_uri_new (server);
t = soup_form_decode (uri->query);
- form_username = g_hash_table_lookup (t, FORM_USERNAME_KEY);
- form_password = g_hash_table_lookup (t, FORM_PASSWORD_KEY);
+ username_field = g_hash_table_lookup (t, USERNAME_FIELD_KEY);
+ password_field = g_hash_table_lookup (t, PASSWORD_FIELD_KEY);
soup_uri_set_query (uri, NULL);
actual_server = soup_uri_to_string (uri, FALSE);
secret_item_load_secret_sync (item, NULL, NULL);
secret = secret_item_get_secret (item);
password = secret_value_get (secret, NULL);
- ephy_password_manager_store (actual_server,
- form_username,
- form_password,
+ ephy_password_manager_store (NULL,
+ actual_server,
+ username_field,
+ password_field,
username,
password,
(GAsyncReadyCallback)store_form_auth_data_cb,
diff --git a/src/resources/gtk/prefs-dialog.ui b/src/resources/gtk/prefs-dialog.ui
index 09c5ac9..ac5bc0f 100644
--- a/src/resources/gtk/prefs-dialog.ui
+++ b/src/resources/gtk/prefs-dialog.ui
@@ -915,6 +915,13 @@
<property name="use-underline">True</property>
</object>
</child>
+ <child>
+ <object class="GtkCheckButton" id="sync_passwords_checkbutton">
+ <property name="label" translatable="yes">_Passwords</property>
+ <property name="visible">True</property>
+ <property name="use-underline">True</property>
+ </object>
+ </child>
</object>
</child>
<child>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]