diff options
author | Gabriel Ivascu <ivascu.gabriel59@gmail.com> | 2017-04-29 17:18:56 +0300 |
---|---|---|
committer | Michael Catanzaro <mcatanzaro@igalia.com> | 2017-08-06 09:26:29 -0500 |
commit | 7ad9a03ab010fcb2a875c9cfa3f2958c96f2f5cc (patch) | |
tree | 89b324e295a5927248f94be37de47fa0842e126d | |
parent | e4d2671488fecc030caa212ceef79b02950b6049 (diff) | |
download | epiphany-7ad9a03ab010fcb2a875c9cfa3f2958c96f2f5cc.tar.gz |
lib: Replace ephy-form-auth-data with ephy-password-manager
-rw-r--r-- | embed/ephy-web-view.c | 1 | ||||
-rw-r--r-- | embed/web-extension/ephy-web-extension.c | 168 | ||||
-rw-r--r-- | lib/ephy-form-auth-data.c | 403 | ||||
-rw-r--r-- | lib/ephy-form-auth-data.h | 76 | ||||
-rw-r--r-- | lib/ephy-password-manager.c | 483 | ||||
-rw-r--r-- | lib/ephy-password-manager.h | 77 | ||||
-rw-r--r-- | lib/ephy-password-record.c | 236 | ||||
-rw-r--r-- | lib/ephy-password-record.h | 45 | ||||
-rw-r--r-- | lib/meson.build | 3 | ||||
-rw-r--r-- | po/POTFILES.in | 2 | ||||
-rw-r--r-- | src/clear-data-dialog.c | 1 | ||||
-rw-r--r-- | src/ephy-shell.c | 20 | ||||
-rw-r--r-- | src/ephy-shell.h | 3 | ||||
-rw-r--r-- | src/passwords-dialog.c | 172 | ||||
-rw-r--r-- | src/passwords-dialog.h | 4 | ||||
-rw-r--r-- | src/prefs-dialog.c | 4 | ||||
-rw-r--r-- | src/profile-migrator/ephy-profile-migrator.c | 18 | ||||
-rw-r--r-- | src/resources/gtk/passwords-dialog.ui | 2 |
18 files changed, 1010 insertions, 708 deletions
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c index 71f3e649b..2ef3968b6 100644 --- a/embed/ephy-web-view.c +++ b/embed/ephy-web-view.c @@ -33,7 +33,6 @@ #include "ephy-favicon-helpers.h" #include "ephy-file-helpers.h" #include "ephy-file-monitor.h" -#include "ephy-form-auth-data.h" #include "ephy-history-service.h" #include "ephy-lib-type-builtins.h" #include "ephy-option-menu.h" diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c index edde1f32b..b628358ad 100644 --- a/embed/web-extension/ephy-web-extension.c +++ b/embed/web-extension/ephy-web-extension.c @@ -26,7 +26,7 @@ #include "ephy-debug.h" #include "ephy-embed-form-auth.h" #include "ephy-file-helpers.h" -#include "ephy-form-auth-data.h" +#include "ephy-password-manager.h" #include "ephy-permissions-manager.h" #include "ephy-prefs.h" #include "ephy-settings.h" @@ -51,7 +51,7 @@ struct _EphyWebExtension { GDBusConnection *dbus_connection; GArray *page_created_signals_pending; - EphyFormAuthDataCache *form_auth_data_cache; + EphyPasswordManager *password_manager; GHashTable *form_auth_data_save_requests; EphyWebOverviewModel *overview_model; EphyPermissionsManager *permissions_manager; @@ -283,19 +283,12 @@ store_password (EphyEmbedFormAuth *form_auth) uri = ephy_embed_form_auth_get_uri (form_auth); uri_str = soup_uri_to_string (uri, FALSE); - ephy_form_auth_data_store (uri_str, - username_field_name, - password_field_name, - username_field_value, - password_field_value, - NULL, NULL); - - /* Update internal caching */ - ephy_form_auth_data_cache_add (extension->form_auth_data_cache, - uri_str, - username_field_name, - password_field_name, - username_field_value); + ephy_password_manager_save (extension->password_manager, + uri_str, + username_field_name, + password_field_name, + username_field_value, + password_field_value); g_free (uri_str); g_free (username_field_name); @@ -368,9 +361,8 @@ out: } static void -should_store_cb (const char *username, - const char *password, - gpointer user_data) +should_store_cb (GSList *records, + gpointer user_data) { EphyEmbedFormAuth *form_auth = EPHY_EMBED_FORM_AUTH (user_data); EphyWebExtension *web_extension; @@ -407,9 +399,12 @@ should_store_cb (const char *username, if (password_field_value == NULL || strlen (password_field_value) == 0) goto out; - if (password) { + if (records && records->data) { + EphyPasswordRecord *record = EPHY_PASSWORD_RECORD (records->data); WebKitDOMNode *username_node; char *username_field_value = NULL; + const char *username = ephy_password_record_get_username (record); + const char *password = ephy_password_record_get_password (record); username_node = ephy_embed_form_auth_get_username_node (form_auth); if (username_node) @@ -440,6 +435,8 @@ out: if (origin != NULL) g_free (origin); g_free (uri_string); + g_object_unref (form_auth); + g_slist_free_full (records, g_object_unref); } static gboolean @@ -447,6 +444,7 @@ form_submitted_cb (WebKitDOMHTMLFormElement *dom_form, WebKitDOMEvent *dom_event, WebKitWebPage *web_page) { + EphyWebExtension *extension = ephy_web_extension_get (); EphyEmbedFormAuth *form_auth; SoupURI *uri; WebKitDOMNode *username_node = NULL; @@ -475,13 +473,13 @@ form_submitted_cb (WebKitDOMHTMLFormElement *dom_form, g_object_get (password_node, "name", &password_field_name, NULL); uri_str = soup_uri_to_string (uri, FALSE); - ephy_form_auth_data_query (uri_str, - username_field_name, - password_field_name, - username_field_value, - should_store_cb, - form_auth, - (GDestroyNotify)g_object_unref); + ephy_password_manager_query (extension->password_manager, + uri_str, + username_field_name, + password_field_name, + username_field_value, + should_store_cb, + form_auth); g_free (username_field_name); g_free (username_field_value); @@ -492,19 +490,22 @@ form_submitted_cb (WebKitDOMHTMLFormElement *dom_form, } static void -fill_form_cb (const char *username, - const char *password, - gpointer user_data) +fill_form_cb (GSList *records, + gpointer user_data) { EphyEmbedFormAuth *form_auth = EPHY_EMBED_FORM_AUTH (user_data); WebKitDOMHTMLInputElement *username_node; WebKitDOMHTMLInputElement *password_node; + const char *username; + const char *password; - if (username == NULL && password == NULL) { + if (!(records && records->data)) { LOG ("No result"); return; } + username = ephy_password_record_get_username (EPHY_PASSWORD_RECORD (records->data)); + password = ephy_password_record_get_password (EPHY_PASSWORD_RECORD (records->data)); username_node = WEBKIT_DOM_HTML_INPUT_ELEMENT (ephy_embed_form_auth_get_username_node (form_auth)); password_node = WEBKIT_DOM_HTML_INPUT_ELEMENT (ephy_embed_form_auth_get_password_node (form_auth)); @@ -517,44 +518,20 @@ fill_form_cb (const char *username, } webkit_dom_html_input_element_set_auto_filled (password_node, TRUE); webkit_dom_html_input_element_set_editing_value (password_node, password); -} - -static gint -ephy_form_auth_data_compare (EphyFormAuthData *form_data, - EphyEmbedFormAuth *form_auth) -{ - WebKitDOMNode *username_node; - WebKitDOMNode *password_node; - char *username_field_name = NULL; - char *password_field_name = NULL; - gboolean retval; - - username_node = ephy_embed_form_auth_get_username_node (form_auth); - if (username_node) - g_object_get (username_node, "name", &username_field_name, NULL); - password_node = ephy_embed_form_auth_get_password_node (form_auth); - if (password_node) - g_object_get (password_node, "name", &password_field_name, NULL); - - retval = g_strcmp0 (username_field_name, form_data->form_username) == 0 && - g_strcmp0 (password_field_name, form_data->form_password) == 0; - - g_free (username_field_name); - g_free (password_field_name); - return retval ? 0 : 1; + g_slist_free_full (records, g_object_unref); } static void pre_fill_form (EphyEmbedFormAuth *form_auth) { - GSList *form_auth_data_list; - GSList *l; - EphyFormAuthData *form_data; SoupURI *uri; char *uri_str; - char *username; + char *username = NULL; + char *username_field_name = NULL; + char *password_field_name = NULL; WebKitDOMNode *username_node; + WebKitDOMNode *password_node; EphyWebExtension *extension; uri = ephy_embed_form_auth_get_uri (form_auth); @@ -563,34 +540,31 @@ pre_fill_form (EphyEmbedFormAuth *form_auth) extension = ephy_web_extension_get (); uri_str = soup_uri_to_string (uri, FALSE); - form_auth_data_list = ephy_form_auth_data_cache_get_list (extension->form_auth_data_cache, uri_str); - l = g_slist_find_custom (form_auth_data_list, form_auth, (GCompareFunc)ephy_form_auth_data_compare); - if (!l) { - g_free (uri_str); - return; - } - - form_data = (EphyFormAuthData *)l->data; username_node = ephy_embed_form_auth_get_username_node (form_auth); - if (username_node) + if (username_node) { + g_object_get (username_node, "name", &username_field_name, NULL); g_object_get (username_node, "value", &username, NULL); - else - username = NULL; + } + password_node = ephy_embed_form_auth_get_password_node (form_auth); + if (password_node) + g_object_get (password_node, "name", &password_field_name, NULL); /* The username node is empty, so pre-fill with the default. */ if (username != NULL && g_str_equal (username, "")) g_clear_pointer (&username, g_free); - ephy_form_auth_data_query (uri_str, - form_data->form_username, - form_data->form_password, - username, - fill_form_cb, - g_object_ref (form_auth), - (GDestroyNotify)g_object_unref); + ephy_password_manager_query (extension->password_manager, + uri_str, + username_field_name, + password_field_name, + username, + fill_form_cb, + form_auth); - g_free (username); g_free (uri_str); + g_free (username); + g_free (username_field_name); + g_free (password_field_name); } static void @@ -757,7 +731,7 @@ show_user_choices (WebKitDOMDocument *document, WebKitDOMElement *main_div; WebKitDOMElement *ul; GSList *iter; - GSList *auth_data_list; + GSList *password_records_list; gboolean username_node_ever_edited; double x, y; double input_width; @@ -803,30 +777,32 @@ show_user_choices (WebKitDOMDocument *document, "padding: 0;", NULL); - auth_data_list = (GSList *)g_object_get_data (G_OBJECT (username_node), - "ephy-auth-data-list"); + password_records_list = (GSList *)g_object_get_data (G_OBJECT (username_node), + "ephy-password-records-list"); username_node_ever_edited = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (username_node), "ephy-user-ever-edited")); - for (iter = auth_data_list; iter; iter = iter->next) { - EphyFormAuthData *data; + for (iter = password_records_list; iter; iter = iter->next) { + EphyPasswordRecord *record; WebKitDOMElement *li; WebKitDOMElement *anchor; char *child_style; + const char *record_username; gboolean is_selected; - data = (EphyFormAuthData *)iter->data; + 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 (data->username, username)) + if (username_node_ever_edited && !g_str_has_prefix (record_username, username)) continue; - is_selected = !g_strcmp0 (username, data->username); + is_selected = !g_strcmp0 (username, record_username); li = webkit_dom_document_create_element (document, "li", NULL); webkit_dom_element_set_attribute (li, "tabindex", "-1", NULL); @@ -858,7 +834,7 @@ show_user_choices (WebKitDOMDocument *document, username_node); webkit_dom_node_set_text_content (WEBKIT_DOM_NODE (anchor), - data->username, + record_username, NULL); } @@ -1125,14 +1101,14 @@ web_page_form_controls_associated (WebKitWebPage *web_page, web_page); } - if (!extension->form_auth_data_cache || + if (!extension->password_manager || !g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_REMEMBER_PASSWORDS)) continue; /* 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 *auth_data_list; + GSList *password_records_list; const char *uri; LOG ("Hooking and pre-filling a form"); @@ -1150,11 +1126,11 @@ web_page_form_controls_associated (WebKitWebPage *web_page, /* Plug in the user autocomplete */ uri = webkit_web_page_get_uri (web_page); - auth_data_list = ephy_form_auth_data_cache_get_list (extension->form_auth_data_cache, uri); + password_records_list = ephy_password_manager_get_cached_by_uri (extension->password_manager, uri); - if (auth_data_list && auth_data_list->next && username_node) { + if (password_records_list && password_records_list->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-auth-data-list", auth_data_list); + g_object_set_data (G_OBJECT (username_node), "ephy-password-records-list", password_records_list); 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", @@ -1173,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 auth_data_list, not hooking menu for choosing."); + LOG ("No items or a single item in password_records_list, not hooking menu for choosing."); pre_fill_form (form_auth); @@ -1476,9 +1452,7 @@ ephy_web_extension_dispose (GObject *object) g_clear_object (&extension->uri_tester); g_clear_object (&extension->overview_model); g_clear_object (&extension->permissions_manager); - - g_clear_pointer (&extension->form_auth_data_cache, - ephy_form_auth_data_cache_free); + g_clear_object (&extension->password_manager); if (extension->form_auth_data_save_requests) { g_hash_table_destroy (extension->form_auth_data_save_requests); @@ -1589,7 +1563,7 @@ ephy_web_extension_initialize (EphyWebExtension *extension, extension->extension = g_object_ref (wk_extension); if (!is_private_profile) - extension->form_auth_data_cache = ephy_form_auth_data_cache_new (); + extension->password_manager = ephy_password_manager_new (); extension->permissions_manager = ephy_permissions_manager_new (); diff --git a/lib/ephy-form-auth-data.c b/lib/ephy-form-auth-data.c deleted file mode 100644 index ff407acb2..000000000 --- a/lib/ephy-form-auth-data.c +++ /dev/null @@ -1,403 +0,0 @@ -/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ -/* - * Copyright © 2013 Igalia S.L. - * - * 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-form-auth-data.h" - -#include "ephy-debug.h" -#include "ephy-uri-helpers.h" - -#include <glib/gi18n.h> -#include <libsoup/soup.h> - -const SecretSchema * -ephy_form_auth_data_get_password_schema (void) -{ - static const SecretSchema schema = { - "org.epiphany.FormPassword", SECRET_SCHEMA_NONE, - { - { URI_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING }, - { FORM_USERNAME_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING }, - { FORM_PASSWORD_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING }, - { USERNAME_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING }, - { "NULL", 0 }, - } - }; - return &schema; -} - -static GHashTable * -ephy_form_auth_data_get_secret_attributes_table (const char *uri, - const char *field_username, - const char *field_password, - const char *username) -{ - GHashTable *attributes; - char *origin; - - origin = ephy_uri_to_security_origin (uri); - attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, - URI_KEY, origin, - username ? USERNAME_KEY : NULL, username, - NULL); - g_free (origin); - - if (field_username) - g_hash_table_insert (attributes, g_strdup (FORM_USERNAME_KEY), g_strdup (field_username)); - if (field_password) - g_hash_table_insert (attributes, g_strdup (FORM_PASSWORD_KEY), g_strdup (field_password)); - - return attributes; -} - -static void -store_form_password_cb (SecretService *service, - GAsyncResult *res, - GTask *task) -{ - GError *error = NULL; - - secret_service_store_finish (service, res, &error); - if (error != NULL) - g_task_return_error (task, error); - else - g_task_return_boolean (task, TRUE); - - g_object_unref (task); -} - -void -ephy_form_auth_data_store (const char *uri, - const char *form_username, - const char *form_password, - const char *username, - const char *password, - GAsyncReadyCallback callback, - gpointer userdata) -{ - SecretValue *value; - GHashTable *attributes; - char *origin; - char *label; - GTask *task; - - g_return_if_fail (uri); - g_return_if_fail (password); - g_return_if_fail (!form_username || username); - g_return_if_fail (!form_password || password); - - LOG ("Storing password in secret service for uri=%s form_username=%s form_password=%s username=%s", - uri, form_username, form_password, username); - - task = g_task_new (NULL, NULL, callback, userdata); - - value = secret_value_new (password, -1, "text/plain"); - attributes = ephy_form_auth_data_get_secret_attributes_table (uri, form_username, - form_password, username); - origin = ephy_uri_to_security_origin (uri); - if (username != NULL) { - /* 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, origin); - } else { - /* Translators: The first %s is the security origin where this is happening. - * Example: https://mail.google.com. - */ - label = g_strdup_printf (_("Password in a form in %s"), origin); - } - secret_service_store (NULL, EPHY_FORM_PASSWORD_SCHEMA, - attributes, NULL, label, value, - NULL, - (GAsyncReadyCallback)store_form_password_cb, - g_object_ref (task)); - - g_free (label); - secret_value_unref (value); - g_hash_table_unref (attributes); - g_free (origin); - g_object_unref (task); -} - - -gboolean -ephy_form_auth_data_store_finish (GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -typedef struct { - EphyFormAuthDataQueryCallback callback; - gpointer data; - GDestroyNotify destroy_data; -} EphyFormAuthDataQueryClosure; - -static void -ephy_form_auth_data_query_closure_free (EphyFormAuthDataQueryClosure *closure) -{ - if (closure->destroy_data) - closure->destroy_data (closure->data); - - g_slice_free (EphyFormAuthDataQueryClosure, closure); -} - -static void -search_form_data_cb (SecretService *service, - GAsyncResult *res, - EphyFormAuthDataQueryClosure *closure) -{ - GList *results; - SecretItem *item; - const char *username = NULL, *password = NULL; - SecretValue *value = NULL; - GHashTable *attributes = NULL; - GError *error = NULL; - - results = secret_service_search_finish (service, res, &error); - if (error) { - g_warning ("Couldn't retrieve form data: %s", error->message); - g_error_free (error); - goto out; - } - - if (!results) { - LOG ("...secret service query returned no result."); - goto out; - } - - item = (SecretItem *)results->data; - attributes = secret_item_get_attributes (item); - username = g_hash_table_lookup (attributes, USERNAME_KEY); - value = secret_item_get_secret (item); - password = secret_value_get (value, NULL); - g_list_free_full (results, (GDestroyNotify)g_object_unref); - - LOG ("...secret service query returned a password for username=%s.", username); - - out: - if (closure->callback) - closure->callback (username, password, closure->data); - - if (value) - secret_value_unref (value); - if (attributes) - g_hash_table_unref (attributes); - - ephy_form_auth_data_query_closure_free (closure); -} - -void -ephy_form_auth_data_query (const char *uri, - const char *form_username, - const char *form_password, - const char *username, - EphyFormAuthDataQueryCallback callback, - gpointer user_data, - GDestroyNotify destroy_data) -{ - EphyFormAuthDataQueryClosure *closure; - GHashTable *attributes; - - g_return_if_fail (uri); - - LOG ("Querying secret service for uri=%s form_username=%s form_password=%s username=%s...", - uri, form_username, form_password, username); - - attributes = ephy_form_auth_data_get_secret_attributes_table (uri, - form_username, - form_password, - username); - - closure = g_slice_new0 (EphyFormAuthDataQueryClosure); - closure->callback = callback; - closure->data = user_data; - closure->destroy_data = destroy_data; - - secret_service_search (NULL, - EPHY_FORM_PASSWORD_SCHEMA, - attributes, - SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, - NULL, (GAsyncReadyCallback)search_form_data_cb, - closure); - - g_hash_table_unref (attributes); -} - -static EphyFormAuthData * -ephy_form_auth_data_new (const char *form_username, - const char *form_password, - const char *username) -{ - EphyFormAuthData *data; - - data = g_slice_new (EphyFormAuthData); - data->form_username = g_strdup (form_username); - data->form_password = g_strdup (form_password); - data->username = g_strdup (username); - - return data; -} - -static void -ephy_form_auth_data_free (EphyFormAuthData *data) -{ - g_free (data->form_username); - g_free (data->form_password); - g_free (data->username); - - g_slice_free (EphyFormAuthData, data); -} - -static void -secret_service_search_finished (SecretService *service, - GAsyncResult *result, - EphyFormAuthDataCache *cache) -{ - GList *results, *p; - GError *error = NULL; - - results = secret_service_search_finish (service, result, &error); - if (error != NULL) { - g_warning ("Error caching form data: %s", error->message); - g_error_free (error); - return; - } - - for (p = results; p; p = p->next) { - SecretItem *item = (SecretItem *)p->data; - GHashTable *attributes; - - attributes = secret_item_get_attributes (item); - ephy_form_auth_data_cache_add (cache, - g_hash_table_lookup (attributes, URI_KEY), - g_hash_table_lookup (attributes, FORM_USERNAME_KEY), - g_hash_table_lookup (attributes, FORM_PASSWORD_KEY), - g_hash_table_lookup (attributes, USERNAME_KEY)); - g_hash_table_unref (attributes); - } - - LOG ("...successfully initialized form auth data cache."); - - g_list_free_full (results, g_object_unref); -} - -static void -ephy_form_auth_data_cache_init (EphyFormAuthDataCache *cache) -{ - GHashTable *attributes; - - LOG ("Initializing form auth data cache %p...", cache); - - attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL); - secret_service_search (NULL, - EPHY_FORM_PASSWORD_SCHEMA, - attributes, - SECRET_SEARCH_UNLOCK | SECRET_SEARCH_ALL, - NULL, - (GAsyncReadyCallback)secret_service_search_finished, - cache); - g_hash_table_unref (attributes); -} - -struct _EphyFormAuthDataCache { - GHashTable *form_auth_data_map; -}; - -EphyFormAuthDataCache * -ephy_form_auth_data_cache_new (void) -{ - EphyFormAuthDataCache *cache = g_slice_new (EphyFormAuthDataCache); - - cache->form_auth_data_map = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - NULL); - ephy_form_auth_data_cache_init (cache); - - return cache; -} - -static void -form_auth_data_map_free_value (gpointer key, - gpointer value, - gpointer user_data) -{ - g_slist_free_full ((GSList *)value, (GDestroyNotify)ephy_form_auth_data_free); -} - -void -ephy_form_auth_data_cache_free (EphyFormAuthDataCache *cache) -{ - g_return_if_fail (cache); - - g_hash_table_foreach (cache->form_auth_data_map, - (GHFunc)form_auth_data_map_free_value, - NULL); - g_hash_table_destroy (cache->form_auth_data_map); - - g_slice_free (EphyFormAuthDataCache, cache); -} - -void -ephy_form_auth_data_cache_add (EphyFormAuthDataCache *cache, - const char *uri, - const char *form_username, - const char *form_password, - const char *username) -{ - EphyFormAuthData *data; - GSList *l; - char *origin; - - g_return_if_fail (cache); - g_return_if_fail (uri); - - LOG ("Adding uri=%s form_username=%s form_password=%s username=%s to form auth data cache %p", - uri, form_username, form_password, username, cache); - - data = ephy_form_auth_data_new (form_username, form_password, username); - origin = ephy_uri_to_security_origin (uri); - l = g_hash_table_lookup (cache->form_auth_data_map, origin); - l = g_slist_append (l, data); - g_hash_table_replace (cache->form_auth_data_map, origin, l); -} - -GSList * -ephy_form_auth_data_cache_get_list (EphyFormAuthDataCache *cache, - const char *uri) -{ - char *origin; - GSList *list; - - g_return_val_if_fail (cache, NULL); - g_return_val_if_fail (uri, NULL); - - origin = ephy_uri_to_security_origin (uri); - list = g_hash_table_lookup (cache->form_auth_data_map, origin); - g_free (origin); - - return list; -} diff --git a/lib/ephy-form-auth-data.h b/lib/ephy-form-auth-data.h deleted file mode 100644 index c92992640..000000000 --- a/lib/ephy-form-auth-data.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* - * Copyright © 2013 Igalia S.L. - * - * 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 - -#define SECRET_API_SUBJECT_TO_CHANGE - -#include <glib.h> -#include <libsecret/secret.h> - -#define URI_KEY "uri" -#define FORM_USERNAME_KEY "form_username" -#define FORM_PASSWORD_KEY "form_password" -#define USERNAME_KEY "username" - -void ephy_form_auth_data_store (const char *uri, - const char *form_username, - const char *form_password, - const char *username, - const char *password, - GAsyncReadyCallback callback, - gpointer userdata); - -gboolean ephy_form_auth_data_store_finish (GAsyncResult *result, - GError **error); - -typedef void (*EphyFormAuthDataQueryCallback) (const char *username, - const char *password, - gpointer user_data); - -void ephy_form_auth_data_query (const char *uri, - const char *form_username, - const char *form_password, - const char *username, - EphyFormAuthDataQueryCallback callback, - gpointer user_data, - GDestroyNotify destroy_data); - -const SecretSchema *ephy_form_auth_data_get_password_schema (void) G_GNUC_CONST; - -#define EPHY_FORM_PASSWORD_SCHEMA ephy_form_auth_data_get_password_schema () - -typedef struct { - char *form_username; - char *form_password; - char *username; -} EphyFormAuthData; - -typedef struct _EphyFormAuthDataCache EphyFormAuthDataCache; - -EphyFormAuthDataCache *ephy_form_auth_data_cache_new (void); -void ephy_form_auth_data_cache_free (EphyFormAuthDataCache *cache); -void ephy_form_auth_data_cache_add (EphyFormAuthDataCache *cache, - const char *uri, - const char *form_username, - const char *form_password, - const char *username); -GSList *ephy_form_auth_data_cache_get_list (EphyFormAuthDataCache *cache, - const char *uri); diff --git a/lib/ephy-password-manager.c b/lib/ephy-password-manager.c new file mode 100644 index 000000000..2337755c2 --- /dev/null +++ b/lib/ephy-password-manager.c @@ -0,0 +1,483 @@ +/* -*- 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-uri-helpers.h" + +#include <glib/gi18n.h> + +const SecretSchema * +ephy_password_manager_get_password_schema (void) +{ + static const SecretSchema schema = { + "org.epiphany.FormPassword", SECRET_SCHEMA_NONE, + { + { URI_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING }, + { FORM_USERNAME_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING }, + { FORM_PASSWORD_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING }, + { USERNAME_KEY, SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "NULL", 0 }, + } + }; + return &schema; +} + +struct _EphyPasswordManager { + GObject parent_instance; + + GSList *records; +}; + +G_DEFINE_TYPE (EphyPasswordManager, ephy_password_manager, G_TYPE_OBJECT) + +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_dispose (GObject *object) +{ + EphyPasswordManager *self = EPHY_PASSWORD_MANAGER (object); + + g_slist_free_full (self->records, g_object_unref); + self->records = NULL; + + 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 +secret_service_search_all_cb (SecretService *service, + GAsyncResult *result, + EphyPasswordManager *self) +{ + GList *matches = 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); + return; + } + + for (GList *l = matches; l && l->data; l = l->next) { + SecretItem *item = (SecretItem *)l->data; + SecretValue *value = secret_item_get_secret (item); + GHashTable *attributes = secret_item_get_attributes (item); + const char *uri = g_hash_table_lookup (attributes, URI_KEY); + const char *form_username = g_hash_table_lookup (attributes, FORM_USERNAME_KEY); + const char *form_password = g_hash_table_lookup (attributes, FORM_PASSWORD_KEY); + const char *username = g_hash_table_lookup (attributes, USERNAME_KEY); + const char *password = secret_value_get (value, NULL); + EphyPasswordRecord *record; + + LOG ("Loading password record for uri=%s, form_username=%s, form_password=%s, username=%s", + uri, form_username, form_password, username); + + record = ephy_password_record_new (uri, form_username, form_password, username, password); + self->records = g_slist_prepend (self->records, record); + + secret_value_unref (value); + g_hash_table_unref (attributes); + } + + g_list_free_full (matches, g_object_unref); +} + +static void +ephy_password_manager_load_passwords (EphyPasswordManager *self) +{ + GHashTable *attributes; + + g_assert (EPHY_IS_PASSWORD_MANAGER (self)); + + attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL); + secret_service_search (NULL, + EPHY_FORM_PASSWORD_SCHEMA, + attributes, + SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, + NULL, + (GAsyncReadyCallback)secret_service_search_all_cb, + self); + + g_hash_table_unref (attributes); +} + +static void +ephy_password_manager_init (EphyPasswordManager *self) +{ + ephy_password_manager_load_passwords (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_by_uri (EphyPasswordManager *self, + const char *uri) +{ + GSList *list = NULL; + char *origin; + + g_return_val_if_fail (EPHY_IS_PASSWORD_MANAGER (self), NULL); + g_return_val_if_fail (uri, NULL); + + origin = ephy_uri_to_security_origin (uri); + + for (GSList *l = self->records; l && l->data; l = l->next) { + EphyPasswordRecord *record = EPHY_PASSWORD_RECORD (l->data); + + if (!g_strcmp0 (origin, ephy_password_record_get_origin (record))) + list = g_slist_prepend (list, record); + } + + g_free (origin); + + return list; +} + +static EphyPasswordRecord * +ephy_password_manager_get_cached (EphyPasswordManager *self, + const char *origin, + const char *form_username, + const char *form_password, + const char *username) +{ + g_assert (EPHY_IS_PASSWORD_MANAGER (self)); + + for (GSList *l = self->records; l && l->data; l = l->next) { + EphyPasswordRecord *record = EPHY_PASSWORD_RECORD (l->data); + + if (!g_strcmp0 (origin, ephy_password_record_get_origin (record)) && + !g_strcmp0 (form_username, ephy_password_record_get_form_username (record)) && + !g_strcmp0 (form_password, ephy_password_record_get_form_password (record)) && + !g_strcmp0 (username, ephy_password_record_get_username (record))) + return record; + } + + return NULL; +} + +void +ephy_password_manager_save (EphyPasswordManager *self, + const char *uri, + const char *form_username, + const char *form_password, + const char *username, + const char *password) +{ + EphyPasswordRecord *record = NULL; + char *origin; + + g_return_if_fail (EPHY_IS_PASSWORD_MANAGER (self)); + g_return_if_fail (uri); + g_return_if_fail (password); + g_return_if_fail (!form_username || username); + g_return_if_fail (!form_password || password); + + origin = ephy_uri_to_security_origin (uri); + + /* Check if there is an existing record for the given parameters. + * If there is, don't create a new record, only update password. */ + record = ephy_password_manager_get_cached (self, origin, + form_username, form_password, + username); + if (record) { + ephy_password_record_set_password (record, password); + } else { + record = ephy_password_record_new (origin, form_username, form_password, + username, password); + self->records = g_slist_prepend (self->records, record); + } + + /* Add/overwrite the password in the secret schema. */ + ephy_password_manager_store (origin, + form_username, form_password, + username, password, + NULL, NULL); + + g_free (origin); +} + +static GHashTable * +get_attributes_table (const char *uri, + const char *form_username, + const char *form_password, + const char *username) +{ + GHashTable *attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL); + + 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 (form_username) + g_hash_table_insert (attributes, g_strdup (FORM_USERNAME_KEY), g_strdup (form_username)); + if (form_password) + g_hash_table_insert (attributes, g_strdup (FORM_PASSWORD_KEY), g_strdup (form_password)); + + return attributes; +} + +static void +secret_service_search_cb (SecretService *service, + GAsyncResult *result, + QueryAsyncData *data) +{ + GList *matches = NULL; + 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 *origin = g_hash_table_lookup (attributes, URI_KEY); + const char *form_username = g_hash_table_lookup (attributes, FORM_USERNAME_KEY); + const char *form_password = g_hash_table_lookup (attributes, FORM_PASSWORD_KEY); + const char *username = g_hash_table_lookup (attributes, USERNAME_KEY); + const char *password = secret_value_get (value, NULL); + EphyPasswordRecord *record; + + LOG ("Query found password record for (%s, %s, %s, %s)", + origin, form_username, form_password, username); + + record = ephy_password_record_new (origin, form_username, form_password, username, password); + 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); + g_list_free_full (matches, g_object_unref); +} + +void +ephy_password_manager_query (EphyPasswordManager *self, + const char *uri, + const char *form_username, + const char *form_password, + 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 uri=%s, form_username=%s, form_password=%s, username=%s", + uri, form_username, form_password, username); + + attributes = get_attributes_table (uri, form_username, form_password, 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 *uri, + const char *form_username, + const char *form_password, + const char *username, + const char *password, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SecretValue *value; + GHashTable *attributes; + GTask *task; + char *origin; + char *label; + + g_return_if_fail (uri); + g_return_if_fail (password); + g_return_if_fail (!form_username || username); + g_return_if_fail (!form_password || password); + + task = g_task_new (NULL, NULL, callback, user_data); + value = secret_value_new (password, -1, "text/plain"); + attributes = get_attributes_table (uri, form_username, form_password, username); + origin = 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, origin); + } 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"), origin); + } + + LOG ("Storing password record for uri=%s, form_username=%s, form_password=%s, username=%s", + uri, form_username, form_password, 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 (origin); + 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 *origin, + const char *form_username, + const char *form_password, + const char *username) +{ + EphyPasswordRecord *record; + GHashTable *attributes; + + g_return_if_fail (EPHY_IS_PASSWORD_MANAGER (self)); + g_return_if_fail (origin); + g_return_if_fail (form_password); + g_return_if_fail (!form_username || username); + + attributes = get_attributes_table (origin, form_username, form_password, username); + secret_service_clear (NULL, EPHY_FORM_PASSWORD_SCHEMA, attributes, NULL, + (GAsyncReadyCallback)secret_service_clear_cb, NULL); + + /* If record is in cache, delete it. */ + record = ephy_password_manager_get_cached (self, origin, + form_username, form_password, + username); + if (record) { + self->records = g_slist_remove (self->records, record); + g_object_unref (record); + } +} + +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); + + g_slist_free_full (self->records, g_object_unref); + self->records = NULL; + + g_hash_table_unref (attributes); +} diff --git a/lib/ephy-password-manager.h b/lib/ephy-password-manager.h new file mode 100644 index 000000000..aa967aa19 --- /dev/null +++ b/lib/ephy-password-manager.h @@ -0,0 +1,77 @@ +/* -*- 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 URI_KEY "uri" +#define FORM_USERNAME_KEY "form_username" +#define FORM_PASSWORD_KEY "form_password" +#define USERNAME_KEY "username" + +#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_by_uri (EphyPasswordManager *self, + const char *uri); +void ephy_password_manager_save (EphyPasswordManager *self, + const char *uri, + const char *form_username, + const char *form_password, + const char *username, + const char *password); +void ephy_password_manager_query (EphyPasswordManager *self, + const char *uri, + const char *form_username, + const char *form_password, + const char *username, + EphyPasswordManagerQueryCallback callback, + gpointer user_data); +void ephy_password_manager_store (const char *uri, + const char *form_username, + const char *form_password, + 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 *origin, + const char *form_username, + const char *form_password, + const char *username); +void ephy_password_manager_forget_all (EphyPasswordManager *self); + +G_END_DECLS diff --git a/lib/ephy-password-record.c b/lib/ephy-password-record.c new file mode 100644 index 000000000..1d3852f37 --- /dev/null +++ b/lib/ephy-password-record.c @@ -0,0 +1,236 @@ +/* -*- 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" + +struct _EphyPasswordRecord { + GObject parent_instance; + + char *origin; + char *form_username; + char *form_password; + char *username; + char *password; +}; + +G_DEFINE_TYPE (EphyPasswordRecord, ephy_password_record, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_ORIGIN, + PROP_FORM_USERNAME, + PROP_FORM_PASSWORD, + PROP_USERNAME, + PROP_PASSWORD, + 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_ORIGIN: + g_free (self->origin); + self->origin = g_strdup (g_value_get_string (value)); + break; + case PROP_FORM_USERNAME: + g_free (self->form_username); + self->form_username = g_strdup (g_value_get_string (value)); + break; + case PROP_FORM_PASSWORD: + g_free (self->form_password); + self->form_password = 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: + ephy_password_record_set_password (self, g_value_get_string (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_ORIGIN: + g_value_set_string (value, ephy_password_record_get_origin (self)); + break; + case PROP_FORM_USERNAME: + g_value_set_string (value, ephy_password_record_get_form_username (self)); + break; + case PROP_FORM_PASSWORD: + g_value_set_string (value, ephy_password_record_get_form_password (self)); + break; + case PROP_USERNAME: + g_value_set_string (value, ephy_password_record_get_username (self)); + break; + case PROP_PASSWORD: + g_value_set_string (value, ephy_password_record_get_password (self)); + 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->origin, g_free); + g_clear_pointer (&self->form_username, g_free); + g_clear_pointer (&self->form_password, 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_ORIGIN] = + g_param_spec_string ("origin", + "Origin", + "Origin url that password is applicable at", + "Default origin", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_FORM_USERNAME] = + g_param_spec_string ("form-username", + "Form username", + "HTML field name of the username", + "Default form username", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_FORM_PASSWORD] = + g_param_spec_string ("form-password", + "Form password", + "HTML field name of the password", + "Default form password", + 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); + + 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 *origin, + const char *form_username, + const char *form_password, + const char *username, + const char *password) +{ + return EPHY_PASSWORD_RECORD (g_object_new (EPHY_TYPE_PASSWORD_RECORD, + "origin", origin, + "form-username", form_username, + "form-password", form_password, + "username", username, + "password", password, + NULL)); +} + +const char * +ephy_password_record_get_origin (EphyPasswordRecord *self) +{ + g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL); + + return self->origin; +} + +const char * +ephy_password_record_get_form_username (EphyPasswordRecord *self) +{ + g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL); + + return self->form_username; +} + +const char * +ephy_password_record_get_form_password (EphyPasswordRecord *self) +{ + g_return_val_if_fail (EPHY_IS_PASSWORD_RECORD (self), NULL); + + return self->form_password; +} + +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); +} diff --git a/lib/ephy-password-record.h b/lib/ephy-password-record.h new file mode 100644 index 000000000..2dcd1281c --- /dev/null +++ b/lib/ephy-password-record.h @@ -0,0 +1,45 @@ +/* -*- 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 <glib-object.h> +#include <libsecret/secret.h> + +G_BEGIN_DECLS + +#define EPHY_TYPE_PASSWORD_RECORD (ephy_password_record_get_type ()) + +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, + 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 *ephy_password_record_get_username (EphyPasswordRecord *self); +const char *ephy_password_record_get_password (EphyPasswordRecord *self); +void ephy_password_record_set_password (EphyPasswordRecord *self, + const char *password); + +G_END_DECLS diff --git a/lib/meson.build b/lib/meson.build index 78ce8fa9f..6e880513e 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -20,9 +20,10 @@ libephymisc_sources = [ 'ephy-favicon-helpers.c', 'ephy-file-helpers.c', 'ephy-filters-manager.c', - 'ephy-form-auth-data.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/po/POTFILES.in b/po/POTFILES.in index 62bdad521..04ee4ee2d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -12,9 +12,9 @@ embed/ephy-encodings.c embed/ephy-find-toolbar.c embed/ephy-web-view.c lib/ephy-file-helpers.c -lib/ephy-form-auth-data.c lib/ephy-gui.c lib/ephy-langs.c +lib/ephy-password-manager.c lib/ephy-string.c lib/ephy-time-helpers.c lib/ephy-zoom.h diff --git a/src/clear-data-dialog.c b/src/clear-data-dialog.c index ec532aefa..4934e7122 100644 --- a/src/clear-data-dialog.c +++ b/src/clear-data-dialog.c @@ -26,7 +26,6 @@ #include <string.h> #include <webkit2/webkit2.h> -#include "ephy-form-auth-data.h" #include "ephy-history-service.h" #include "ephy-embed-shell.h" diff --git a/src/ephy-shell.c b/src/ephy-shell.c index 4801cfd71..6f917a5c5 100644 --- a/src/ephy-shell.c +++ b/src/ephy-shell.c @@ -56,6 +56,7 @@ struct _EphyShell { GList *windows; GObject *lockdown; EphyBookmarksManager *bookmarks_manager; + EphyPasswordManager *password_manager; GNetworkMonitor *network_monitor; GtkWidget *history_dialog; GObject *prefs_dialog; @@ -345,6 +346,8 @@ 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)) @@ -618,6 +621,7 @@ ephy_shell_dispose (GObject *object) g_clear_object (&shell->network_monitor); g_clear_object (&shell->sync_service); g_clear_object (&shell->bookmarks_manager); + g_clear_object (&shell->password_manager); g_slist_free_full (shell->open_uris_idle_ids, remove_open_uris_idle_cb); shell->open_uris_idle_ids = NULL; @@ -805,6 +809,22 @@ ephy_shell_get_bookmarks_manager (EphyShell *shell) } /** + * ephy_shell_get_password_manager: + * @shell: the #EphyShell + * + * Returns the passwords manager. + * + * Return value: (transfer none): An #EphyPasswordManager. + */ +EphyPasswordManager * +ephy_shell_get_password_manager (EphyShell *shell) +{ + g_return_val_if_fail (EPHY_IS_SHELL (shell), NULL); + + return shell->password_manager; +} + +/** * ephy_shell_get_net_monitor: * * Return value: (transfer none): diff --git a/src/ephy-shell.h b/src/ephy-shell.h index bd794492d..6734d8248 100644 --- a/src/ephy-shell.h +++ b/src/ephy-shell.h @@ -25,6 +25,7 @@ #include "ephy-bookmarks-manager.h" #include "ephy-embed-shell.h" #include "ephy-embed.h" +#include "ephy-password-manager.h" #include "ephy-session.h" #include "ephy-sync-service.h" #include "ephy-window.h" @@ -102,6 +103,8 @@ GNetworkMonitor *ephy_shell_get_net_monitor (EphyShell *shell); EphyBookmarksManager *ephy_shell_get_bookmarks_manager (EphyShell *shell); +EphyPasswordManager *ephy_shell_get_password_manager (EphyShell *shell); + EphySyncService *ephy_shell_get_sync_service (EphyShell *shell); GtkWidget *ephy_shell_get_history_dialog (EphyShell *shell); diff --git a/src/passwords-dialog.c b/src/passwords-dialog.c index fcdaafae0..2f0637f06 100644 --- a/src/passwords-dialog.c +++ b/src/passwords-dialog.c @@ -27,7 +27,6 @@ #define SECRET_API_SUBJECT_TO_CHANGE #include <libsecret/secret.h> -#include "ephy-form-auth-data.h" #include "ephy-uri-helpers.h" #include "passwords-dialog.h" @@ -39,14 +38,11 @@ typedef enum { COL_PASSWORDS_DATA, } PasswordsDialogColumn; -#define URI_KEY "uri" -#define FORM_USERNAME_KEY "form_username" -#define FORM_PASSWORD_KEY "form_password" -#define USERNAME_KEY "username" - struct _EphyPasswordsDialog { GtkDialog parent_instance; + EphyPasswordManager *manager; + GSList *records; GtkWidget *passwords_treeview; GtkTreeSelection *tree_selection; GtkWidget *liststore; @@ -59,8 +55,6 @@ struct _EphyPasswordsDialog { GActionGroup *action_group; - SecretService *ss; - GCancellable *ss_cancellable; gboolean filled; char *search_text; @@ -83,31 +77,13 @@ ephy_passwords_dialog_dispose (GObject *object) { EphyPasswordsDialog *dialog = EPHY_PASSWORDS_DIALOG (object); - if (dialog->ss_cancellable != NULL) { - g_cancellable_cancel (dialog->ss_cancellable); - g_clear_object (&(dialog->ss_cancellable)); - } - - g_clear_object (&(dialog->ss)); g_free (dialog->search_text); dialog->search_text = NULL; - G_OBJECT_CLASS (ephy_passwords_dialog_parent_class)->dispose (object); -} - -static void -secret_remove_ready_cb (GObject *source, - GAsyncResult *res, - EphyPasswordsDialog *dialog) -{ - secret_item_delete_finish (SECRET_ITEM (source), res, NULL); -} + g_slist_free_full (dialog->records, g_object_unref); + dialog->records = NULL; -static void -secret_remove (EphyPasswordsDialog *dialog, - SecretItem *item) -{ - secret_item_delete (item, NULL, (GAsyncReadyCallback)secret_remove_ready_cb, dialog); + G_OBJECT_CLASS (ephy_passwords_dialog_parent_class)->dispose (object); } static void @@ -155,15 +131,21 @@ forget (GSimpleAction *action, /* Removal */ for (r = rlist; r != NULL; r = r->next) { GValue val = { 0, }; - SecretItem *item; + EphyPasswordRecord *record; GtkTreeIter filter_iter; GtkTreeIter child_iter; path = gtk_tree_row_reference_get_path ((GtkTreeRowReference *)r->data); gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get_value (model, &iter, COL_PASSWORDS_DATA, &val); - item = g_value_get_object (&val); - secret_remove (dialog, item); + 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_username (record)); + dialog->records = g_slist_remove (dialog->records, record); + g_object_unref (record); g_value_unset (&val); gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (dialog->treemodelsort), @@ -362,105 +344,53 @@ ephy_passwords_dialog_class_init (EphyPasswordsDialogClass *klass) } static void -delete_all_passwords_ready_cb (GObject *source_object, - GAsyncResult *res, - EphyPasswordsDialog *dialog) -{ - secret_service_clear_finish (dialog->ss, res, NULL); - reload_model (dialog); -} - -static void forget_all (GSimpleAction *action, GVariant *parameter, gpointer user_data) { EphyPasswordsDialog *dialog = EPHY_PASSWORDS_DIALOG (user_data); - GHashTable *attributes; - - attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL); - secret_service_clear (dialog->ss, - EPHY_FORM_PASSWORD_SCHEMA, - attributes, - dialog->ss_cancellable, - (GAsyncReadyCallback)delete_all_passwords_ready_cb, - dialog); - g_hash_table_unref (attributes); + + ephy_password_manager_forget_all (dialog->manager); + g_slist_free_full (dialog->records, g_object_unref); + dialog->records = NULL; + + reload_model (dialog); } static void -secrets_search_ready_cb (GObject *source_object, - GAsyncResult *res, - EphyPasswordsDialog *dialog) +populate_model_cb (GSList *records, + gpointer user_data) { - GList *matches; - GList *l; - - matches = secret_service_search_finish (dialog->ss, res, NULL); - - for (l = matches; l != NULL; l = l->next) { - SecretItem *item = l->data; - SecretValue *value = NULL; - GHashTable *attributes = NULL; - const char *username = NULL; - const char *password = NULL; - char *origin = NULL; - GtkTreeIter iter; + EphyPasswordsDialog *dialog = EPHY_PASSWORDS_DIALOG (user_data); - attributes = secret_item_get_attributes (item); - username = g_hash_table_lookup (attributes, USERNAME_KEY); - value = secret_item_get_secret (item); - password = secret_value_get (value, NULL); - origin = ephy_uri_to_security_origin (g_hash_table_lookup (attributes, URI_KEY)); - if (origin == NULL) { - g_hash_table_unref (attributes); - continue; - } + for (GSList *l = records; l && l->data; l = l->next) { + EphyPasswordRecord *record = EPHY_PASSWORD_RECORD (l->data); + GtkTreeIter iter; gtk_list_store_insert_with_values (GTK_LIST_STORE (dialog->liststore), &iter, -1, - COL_PASSWORDS_ORIGIN, origin, - COL_PASSWORDS_USER, username, - COL_PASSWORDS_PASSWORD, password, + COL_PASSWORDS_ORIGIN, ephy_password_record_get_origin (record), + COL_PASSWORDS_USER, ephy_password_record_get_username (record), + COL_PASSWORDS_PASSWORD, ephy_password_record_get_password (record), COL_PASSWORDS_INVISIBLE, "●●●●●●●●", - COL_PASSWORDS_DATA, item, + COL_PASSWORDS_DATA, record, -1); - - g_free (origin); - g_hash_table_unref (attributes); } - g_list_free_full (matches, g_object_unref); + dialog->records = records; } static void populate_model (EphyPasswordsDialog *dialog) { - GHashTable *attributes; - + g_assert (EPHY_IS_PASSWORDS_DIALOG (dialog)); g_assert (dialog->filled == FALSE); - attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL); - - secret_service_search (dialog->ss, - EPHY_FORM_PASSWORD_SCHEMA, - attributes, - SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, - dialog->ss_cancellable, - (GAsyncReadyCallback)secrets_search_ready_cb, - dialog); - - g_hash_table_unref (attributes); -} - -static void -secrets_ready_cb (GObject *source_object, - GAsyncResult *res, - EphyPasswordsDialog *dialog) -{ - dialog->ss = secret_service_get_finish (res, NULL); - populate_model (dialog); + /* Ask for all password records. */ + ephy_password_manager_query (dialog->manager, + NULL, NULL, NULL, NULL, + populate_model_cb, dialog); } static gboolean @@ -511,6 +441,15 @@ create_action_group (EphyPasswordsDialog *dialog) } static void +show_dialog_cb (GtkWidget *widget, + gpointer user_data) +{ + EphyPasswordsDialog *dialog = EPHY_PASSWORDS_DIALOG (widget); + + populate_model (dialog); +} + +static void ephy_passwords_dialog_init (EphyPasswordsDialog *dialog) { gtk_widget_init_template (GTK_WIDGET (dialog)); @@ -520,22 +459,23 @@ ephy_passwords_dialog_init (EphyPasswordsDialog *dialog) dialog, NULL); - dialog->ss_cancellable = g_cancellable_new (); - secret_service_get (SECRET_SERVICE_OPEN_SESSION | SECRET_SERVICE_LOAD_COLLECTIONS, - dialog->ss_cancellable, - (GAsyncReadyCallback)secrets_ready_cb, - dialog); - dialog->action_group = create_action_group (dialog); gtk_widget_insert_action_group (GTK_WIDGET (dialog), "passwords", dialog->action_group); update_selection_actions (G_ACTION_MAP (dialog->action_group), FALSE); + + g_signal_connect (GTK_WIDGET (dialog), "show", G_CALLBACK (show_dialog_cb), NULL); } EphyPasswordsDialog * -ephy_passwords_dialog_new (void) +ephy_passwords_dialog_new (EphyPasswordManager *manager) { - return g_object_new (EPHY_TYPE_PASSWORDS_DIALOG, - "use-header-bar", TRUE, - NULL); + EphyPasswordsDialog *dialog; + + dialog = EPHY_PASSWORDS_DIALOG (g_object_new (EPHY_TYPE_PASSWORDS_DIALOG, + "use-header-bar", TRUE, + NULL)); + dialog->manager = manager; + + return dialog; } diff --git a/src/passwords-dialog.h b/src/passwords-dialog.h index 02a256d8b..eafbf2a84 100644 --- a/src/passwords-dialog.h +++ b/src/passwords-dialog.h @@ -20,11 +20,13 @@ #pragma once +#include "ephy-password-manager.h" + G_BEGIN_DECLS #define EPHY_TYPE_PASSWORDS_DIALOG (ephy_passwords_dialog_get_type ()) G_DECLARE_FINAL_TYPE (EphyPasswordsDialog, ephy_passwords_dialog, EPHY, PASSWORDS_DIALOG, GtkDialog); -EphyPasswordsDialog *ephy_passwords_dialog_new (void); +EphyPasswordsDialog *ephy_passwords_dialog_new (EphyPasswordManager *manager); G_END_DECLS diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c index 34d165409..1d4944f2e 100644 --- a/src/prefs-dialog.c +++ b/src/prefs-dialog.c @@ -533,8 +533,10 @@ on_manage_passwords_button_clicked (GtkWidget *button, PrefsDialog *dialog) { EphyPasswordsDialog *passwords_dialog; + EphyPasswordManager *password_manager; - passwords_dialog = ephy_passwords_dialog_new (); + password_manager = ephy_shell_get_password_manager (ephy_shell_get_default ()); + passwords_dialog = ephy_passwords_dialog_new (password_manager); gtk_window_set_transient_for (GTK_WINDOW (passwords_dialog), GTK_WINDOW (dialog)); gtk_window_set_modal (GTK_WINDOW (passwords_dialog), TRUE); diff --git a/src/profile-migrator/ephy-profile-migrator.c b/src/profile-migrator/ephy-profile-migrator.c index ad78997b1..fe5528837 100644 --- a/src/profile-migrator/ephy-profile-migrator.c +++ b/src/profile-migrator/ephy-profile-migrator.c @@ -34,8 +34,8 @@ #include "ephy-bookmarks-manager.h" #include "ephy-debug.h" #include "ephy-file-helpers.h" -#include "ephy-form-auth-data.h" #include "ephy-history-service.h" +#include "ephy-password-manager.h" #include "ephy-prefs.h" #include "ephy-profile-utils.h" #include "ephy-search-engine-manager.h" @@ -285,7 +285,7 @@ store_form_auth_data_cb (GObject *object, { GError *error = NULL; - if (ephy_form_auth_data_store_finish (res, &error) == FALSE) { + if (ephy_password_manager_store_finish (res, &error) == FALSE) { g_warning ("Couldn't store a form password: %s", error->message); g_error_free (error); goto out; @@ -348,13 +348,13 @@ load_collection_items_cb (SecretCollection *collection, secret_item_load_secret_sync (item, NULL, NULL); secret = secret_item_get_secret (item); password = secret_value_get (secret, NULL); - ephy_form_auth_data_store (actual_server, - form_username, - form_password, - username, - password, - (GAsyncReadyCallback)store_form_auth_data_cb, - g_hash_table_ref (attributes)); + ephy_password_manager_store (actual_server, + form_username, + form_password, + username, + password, + (GAsyncReadyCallback)store_form_auth_data_cb, + g_hash_table_ref (attributes)); g_free (actual_server); secret_value_unref (secret); g_hash_table_unref (t); diff --git a/src/resources/gtk/passwords-dialog.ui b/src/resources/gtk/passwords-dialog.ui index 7c6998505..dd9b1a238 100644 --- a/src/resources/gtk/passwords-dialog.ui +++ b/src/resources/gtk/passwords-dialog.ui @@ -12,7 +12,7 @@ <!-- column-name INVISIBLE --> <column type="gchararray"/> <!-- column-name DATA --> - <column type="SecretItem"/> + <column type="EphyPasswordRecord"/> </columns> </object> <object class="GtkTreeModelFilter" id="treemodelfilter"> |