/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* vim: set sw=2 ts=2 sts=2 et: */
/*
* Copyright © 2013 Igalia S.L.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#include "ephy-form-auth-data.h"
#include "ephy-string.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 void
normalize_and_prepare_uri (SoupURI *uri)
{
g_assert (uri != NULL);
/* We normalize https? schemes here so that we use passwords
* we stored in https sites in their http counterparts, and
* vice-versa. */
if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
soup_uri_set_query (uri, NULL);
soup_uri_set_path (uri, "/");
}
static GHashTable *
ephy_form_auth_data_get_secret_attributes_table (const char *uri,
const char *field_username,
const char *field_password,
const char *username)
{
return secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA,
URI_KEY, uri,
FORM_USERNAME_KEY, field_username,
FORM_PASSWORD_KEY, field_password,
username ? USERNAME_KEY : NULL, username,
NULL);
}
static void
store_form_password_cb (SecretService *service,
GAsyncResult *res,
GSimpleAsyncResult *async)
{
GError *error = NULL;
secret_service_store_finish (service, res, &error);
if (error != NULL)
g_simple_async_result_take_error (async, error);
g_simple_async_result_complete (async);
g_object_unref (async);
}
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)
{
SoupURI *fake_uri;
char *fake_uri_str;
SecretValue *value;
GHashTable *attributes;
char *label;
GSimpleAsyncResult *res;
g_return_if_fail (uri);
g_return_if_fail (form_username);
g_return_if_fail (form_password);
g_return_if_fail (username);
g_return_if_fail (password);
fake_uri = soup_uri_new (uri);
g_return_if_fail (fake_uri);
res = g_simple_async_result_new (NULL, callback, userdata, ephy_form_auth_data_store);
normalize_and_prepare_uri (fake_uri);
fake_uri_str = soup_uri_to_string (fake_uri, FALSE);
value = secret_value_new (password, -1, "text/plain");
attributes = ephy_form_auth_data_get_secret_attributes_table (fake_uri_str, form_username,
form_password, username);
/* Translators: The first %s is the username and the second one is the
* hostname where this is happening. Example: gnome@gmail.com and
* mail.google.com.
*/
label = g_strdup_printf (_("Password for %s in a form in %s"),
username, fake_uri_str);
secret_service_store (NULL, EPHY_FORM_PASSWORD_SCHEMA,
attributes, NULL, label, value,
NULL,
(GAsyncReadyCallback)store_form_password_cb,
g_object_ref (res));
g_free (label);
secret_value_unref (value);
g_hash_table_unref (attributes);
soup_uri_free (fake_uri);
g_free (fake_uri_str);
g_object_unref (res);
}
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_simple_async_result_is_valid (result, NULL, ephy_form_auth_data_store), FALSE);
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (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)
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);
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,
EphyFormAuthDataQueryCallback callback,
gpointer user_data,
GDestroyNotify destroy_data)
{
SoupURI *key;
char *key_str;
EphyFormAuthDataQueryClosure *closure;
GHashTable *attributes;
g_return_if_fail (uri);
g_return_if_fail (form_username);
g_return_if_fail (form_password);
key = soup_uri_new (uri);
g_return_if_fail (key);
normalize_and_prepare_uri (key);
key_str = soup_uri_to_string (key, FALSE);
attributes = ephy_form_auth_data_get_secret_attributes_table (key_str, form_username,
form_password, NULL);
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);
soup_uri_free (key);
g_free (key_str);
}
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
screcet_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;
char *host;
attributes = secret_item_get_attributes (item);
host = ephy_string_get_host_name (g_hash_table_lookup (attributes, URI_KEY));
ephy_form_auth_data_cache_add (cache, host,
g_hash_table_lookup (attributes, FORM_USERNAME_KEY),
g_hash_table_lookup (attributes, FORM_PASSWORD_KEY),
g_hash_table_lookup (attributes, USERNAME_KEY));
g_free (host);
g_hash_table_unref (attributes);
}
g_list_free_full (results, g_object_unref);
}
static void
ephy_form_auth_data_cache_init (EphyFormAuthDataCache *cache)
{
GHashTable *attributes;
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)screcet_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;
g_return_if_fail (cache);
g_return_if_fail (uri);
g_return_if_fail (form_username);
g_return_if_fail (form_password);
g_return_if_fail (username);
data = ephy_form_auth_data_new (form_username, form_password, username);
l = g_hash_table_lookup (cache->form_auth_data_map, uri);
l = g_slist_append (l, data);
g_hash_table_replace (cache->form_auth_data_map,
g_strdup (uri), l);
}
GSList *
ephy_form_auth_data_cache_get_list (EphyFormAuthDataCache *cache,
const char *uri)
{
g_return_val_if_fail (cache, NULL);
g_return_val_if_fail (uri, NULL);
return g_hash_table_lookup (cache->form_auth_data_map, uri);
}