[libsoup] Add SoupPasswordManager and SoupPasswordManagerGNOME
- From: Dan Winship <danw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [libsoup] Add SoupPasswordManager and SoupPasswordManagerGNOME
- Date: Wed, 12 Aug 2009 14:26:14 +0000 (UTC)
commit f81520bfd3a971f82c2b69aa9977edc1a33b20aa
Author: Dan Winship <danw gnome org>
Date: Wed Aug 12 10:23:39 2009 -0400
Add SoupPasswordManager and SoupPasswordManagerGNOME
SoupPasswordManager (and some new SoupAuth APIs) provide an interface
for saving passwords, and SoupPasswordManagerGNOME provides an
implementation of that interface using gnome-keyring.
configure.in | 10 ++
libsoup/Makefile.am | 13 ++-
libsoup/soup-auth-manager-ntlm.h | 2 +
libsoup/soup-auth-manager.h | 2 -
libsoup/soup-auth.c | 171 ++++++++++++++++++++++++
libsoup/soup-auth.h | 11 ++
libsoup/soup-gnome.h | 1 +
libsoup/soup-marshal.list | 1 +
libsoup/soup-password-manager-gnome.c | 230 +++++++++++++++++++++++++++++++++
libsoup/soup-password-manager-gnome.h | 32 +++++
libsoup/soup-password-manager.c | 97 ++++++++++++++
libsoup/soup-password-manager.h | 55 ++++++++
libsoup/soup-session-async.c | 38 ++++++
libsoup/soup-session-sync.c | 23 +++-
libsoup/soup-session.c | 13 ++-
libsoup/soup-session.h | 5 +-
libsoup/soup.h | 1 +
tests/get.c | 75 +++++++++++
18 files changed, 772 insertions(+), 8 deletions(-)
---
diff --git a/configure.in b/configure.in
index 0d9e2f1..3318992 100644
--- a/configure.in
+++ b/configure.in
@@ -183,6 +183,16 @@ AC_ARG_WITH(gnome,
:, [if test $os_win32 = yes; then with_gnome=no; else with_gnome=yes; fi])
AC_MSG_RESULT($with_gnome)
+if test $with_gnome != no; then
+ PKG_CHECK_MODULES(GNOME_KEYRING, gnome-keyring-1, :,
+ AC_MSG_ERROR(
+[Could not find gnome-keyring devel files.
+Configure with --without-gnome if you wish to build only libsoup
+without GNOME-specific features.]))
+fi
+AC_SUBST(GNOME_KEYRING_CFLAGS)
+AC_SUBST(GNOME_KEYRING_LIBS)
+
AM_CONDITIONAL(BUILD_LIBSOUP_GNOME, test $with_gnome != no)
if test $with_gnome != no; then
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index d5b8be2..a436d4e 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -14,6 +14,7 @@ INCLUDES = \
$(GCONF_CFLAGS) \
$(LIBPROXY_CFLAGS) \
$(SQLITE_CFLAGS) \
+ $(GNOME_KEYRING_CFLAGS) \
$(LIBGCRYPT_CFLAGS) \
$(LIBGNUTLS_CFLAGS)
@@ -69,6 +70,7 @@ soup_headers = \
soup-method.h \
soup-misc.h \
soup-multipart.h \
+ soup-password-manager.h \
soup-portability.h \
soup-proxy-resolver.h \
soup-proxy-uri-resolver.h \
@@ -142,6 +144,7 @@ libsoup_2_4_la_SOURCES = \
soup-misc.c \
soup-multipart.c \
soup-nossl.c \
+ soup-password-manager.c \
soup-path-map.h \
soup-path-map.c \
soup-proxy-resolver.c \
@@ -168,7 +171,8 @@ libsoupgnomeincludedir = $(includedir)/libsoup-gnome-2.4/libsoup
libsoupgnomeinclude_HEADERS = \
soup-cookie-jar-sqlite.h\
soup-gnome.h \
- soup-gnome-features.h
+ soup-gnome-features.h \
+ soup-password-manager-gnome.h
lib_LTLIBRARIES += libsoup-gnome-2.4.la
@@ -179,13 +183,16 @@ libsoup_gnome_2_4_la_LIBADD = \
$(GLIB_LIBS) \
$(GCONF_LIBS) \
$(LIBPROXY_LIBS) \
- $(SQLITE_LIBS)
+ $(SQLITE_LIBS) \
+ $(GNOME_KEYRING_LIBS)
libsoup_gnome_2_4_la_SOURCES = \
soup-cookie-jar-sqlite.c \
soup-gnome-features.c \
soup-proxy-resolver-gnome.h \
- soup-proxy-resolver-gnome.c
+ soup-proxy-resolver-gnome.c \
+ soup-password-manager-gnome.h \
+ soup-password-manager-gnome.c
endif
diff --git a/libsoup/soup-auth-manager-ntlm.h b/libsoup/soup-auth-manager-ntlm.h
index a048950..f0b4f57 100644
--- a/libsoup/soup-auth-manager-ntlm.h
+++ b/libsoup/soup-auth-manager-ntlm.h
@@ -27,6 +27,8 @@ typedef struct {
} SoupAuthManagerNTLMClass;
+#define SOUP_AUTH_MANAGER_NTLM_USE_NTLM "use-ntlm"
+
GType soup_auth_manager_ntlm_get_type (void);
G_END_DECLS
diff --git a/libsoup/soup-auth-manager.h b/libsoup/soup-auth-manager.h
index 1fc44c4..b6759ec 100644
--- a/libsoup/soup-auth-manager.h
+++ b/libsoup/soup-auth-manager.h
@@ -30,8 +30,6 @@ typedef struct {
SoupAuth *auth, gboolean retrying);
} SoupAuthManagerClass;
-#define SOUP_AUTH_MANAGER_NTLM_USE_NTLM "use-ntlm"
-
GType soup_auth_manager_get_type (void);
void soup_auth_manager_add_type (SoupAuthManager *manager,
diff --git a/libsoup/soup-auth.c b/libsoup/soup-auth.c
index d77fd82..a3f2ae7 100644
--- a/libsoup/soup-auth.c
+++ b/libsoup/soup-auth.c
@@ -15,6 +15,7 @@
#include "soup-auth-basic.h"
#include "soup-auth-digest.h"
#include "soup-headers.h"
+#include "soup-marshal.h"
#include "soup-uri.h"
/**
@@ -40,12 +41,20 @@ typedef struct {
gboolean proxy;
char *host;
+ GHashTable *saved_passwords;
} SoupAuthPrivate;
#define SOUP_AUTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH, SoupAuthPrivate))
G_DEFINE_TYPE (SoupAuth, soup_auth, G_TYPE_OBJECT)
enum {
+ SAVE_PASSWORD,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
PROP_0,
PROP_SCHEME_NAME,
@@ -70,6 +79,8 @@ finalize (GObject *object)
g_free (auth->realm);
g_free (priv->host);
+ if (priv->saved_passwords)
+ g_hash_table_destroy (priv->saved_passwords);
G_OBJECT_CLASS (soup_auth_parent_class)->finalize (object);
}
@@ -85,6 +96,30 @@ soup_auth_class_init (SoupAuthClass *auth_class)
object_class->set_property = set_property;
object_class->get_property = get_property;
+ /**
+ * SoupAuth::save-password:
+ * @auth: the auth
+ * @username: the username to save
+ * @password: the password to save
+ *
+ * Emitted to request that the @username/@password pair be
+ * saved. If the session supports password-saving, it will
+ * connect to this signal before emitting
+ * #SoupSession::authenticate, so that it record the password
+ * if requested by the caller.
+ *
+ * Since: 2.28
+ **/
+ signals[SAVE_PASSWORD] =
+ g_signal_new ("save-password",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ soup_marshal_NONE__STRING_STRING,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
/* properties */
/**
* SOUP_AUTH_SCHEME_NAME:
@@ -325,6 +360,9 @@ soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
*
* Call this on an auth to authenticate it; normally this will cause
* the auth's message to be requeued with the new authentication info.
+ *
+ * This does not cause the password to be saved to persistent storage;
+ * see soup_auth_save_password() for that.
**/
void
soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
@@ -506,3 +544,136 @@ soup_auth_free_protection_space (SoupAuth *auth, GSList *space)
g_free (s->data);
g_slist_free (space);
}
+
+/**
+ * soup_auth_get_saved_users:
+ * @auth: a #SoupAuth
+ *
+ * Gets a list of usernames for which a saved password is available.
+ * (If the session is not configured to save passwords, this will
+ * always be %NULL.)
+ *
+ * Return value: the list of usernames. You must free the list with
+ * g_slist_free(), but do not free or modify the contents.
+ *
+ * Since: 2.28
+ **/
+GSList *
+soup_auth_get_saved_users (SoupAuth *auth)
+{
+ SoupAuthPrivate *priv;
+ GSList *users;
+
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+
+ priv = SOUP_AUTH_GET_PRIVATE (auth);
+ users = NULL;
+
+ if (priv->saved_passwords) {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, priv->saved_passwords);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ users = g_slist_prepend (users, key);
+ }
+ return users;
+}
+
+/**
+ * soup_auth_get_saved_password:
+ * @auth: a #SoupAuth
+ * @user: a username from the list returned from
+ * soup_auth_get_saved_users().
+ *
+ * Given a username for which @auth has a saved password, this returns
+ * that password. If @auth doesn't have a passwords saved for @user, it
+ * returns %NULL.
+ *
+ * Return value: the saved password, or %NULL.
+ *
+ * Since: 2.28
+ **/
+const char *
+soup_auth_get_saved_password (SoupAuth *auth, const char *user)
+{
+ SoupAuthPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+ g_return_val_if_fail (user != NULL, NULL);
+
+ priv = SOUP_AUTH_GET_PRIVATE (auth);
+ if (!priv->saved_passwords)
+ return NULL;
+ return g_hash_table_lookup (priv->saved_passwords, user);
+}
+
+static void
+free_password (gpointer password)
+{
+ memset (password, 0, strlen (password));
+ g_free (password);
+}
+
+static inline void
+init_saved_passwords (SoupAuthPrivate *priv)
+{
+ priv->saved_passwords = g_hash_table_new_full (
+ g_str_hash, g_str_equal, g_free, free_password);
+}
+
+/**
+ * soup_auth_has_saved_password:
+ * @auth: a #SoupAuth
+ * @username: a username
+ * @password: a password
+ *
+ * Updates @auth to be aware of an already-saved username/password
+ * combination. This method <emphasis>does not</emphasis> cause the
+ * given @username and @password to be saved; use
+ * soup_auth_save_password() for that. (soup_auth_has_saved_password()
+ * is an internal method, which is used by the code that actually
+ * saves and restores the passwords.)
+ *
+ * Since: 2.28
+ **/
+void
+soup_auth_has_saved_password (SoupAuth *auth, const char *username,
+ const char *password)
+{
+ SoupAuthPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_AUTH (auth));
+ g_return_if_fail (username != NULL);
+ g_return_if_fail (password != NULL);
+
+ priv = SOUP_AUTH_GET_PRIVATE (auth);
+
+ if (!priv->saved_passwords)
+ init_saved_passwords (priv);
+ g_hash_table_insert (priv->saved_passwords,
+ g_strdup (username), g_strdup (password));
+}
+
+/**
+ * soup_auth_save_password:
+ * @auth: a #SoupAuth
+ * @username: the username provided by the user or client
+ * @password: the password provided by the user or client
+ *
+ * Requests that the username/password pair be saved to whatever form
+ * of persistent password storage the session supports.
+ *
+ * Since: 2.28
+ **/
+void
+soup_auth_save_password (SoupAuth *auth, const char *username,
+ const char *password)
+{
+ g_return_if_fail (SOUP_IS_AUTH (auth));
+ g_return_if_fail (username != NULL);
+ g_return_if_fail (password != NULL);
+
+ g_signal_emit (auth, signals[SAVE_PASSWORD], 0,
+ username, password);
+}
diff --git a/libsoup/soup-auth.h b/libsoup/soup-auth.h
index 53eb16b..1259df8 100644
--- a/libsoup/soup-auth.h
+++ b/libsoup/soup-auth.h
@@ -72,6 +72,13 @@ const char *soup_auth_get_host (SoupAuth *auth);
const char *soup_auth_get_realm (SoupAuth *auth);
char *soup_auth_get_info (SoupAuth *auth);
+GSList *soup_auth_get_saved_users (SoupAuth *auth);
+const char *soup_auth_get_saved_password (SoupAuth *auth,
+ const char *user);
+void soup_auth_save_password (SoupAuth *auth,
+ const char *username,
+ const char *password);
+
void soup_auth_authenticate (SoupAuth *auth,
const char *username,
const char *password);
@@ -85,6 +92,10 @@ GSList *soup_auth_get_protection_space (SoupAuth *auth,
void soup_auth_free_protection_space (SoupAuth *auth,
GSList *space);
+void soup_auth_has_saved_password (SoupAuth *auth,
+ const char *username,
+ const char *password);
+
G_END_DECLS
#endif /* SOUP_AUTH_H */
diff --git a/libsoup/soup-gnome.h b/libsoup/soup-gnome.h
index a2134a4..6703cf0 100644
--- a/libsoup/soup-gnome.h
+++ b/libsoup/soup-gnome.h
@@ -10,5 +10,6 @@
#include <libsoup/soup-cookie-jar-sqlite.h>
#include <libsoup/soup-gnome-features.h>
+#include <libsoup/soup-password-manager-gnome.h>
#endif /* SOUP_GNOME_H */
diff --git a/libsoup/soup-marshal.list b/libsoup/soup-marshal.list
index d0c53ef..7714813 100644
--- a/libsoup/soup-marshal.list
+++ b/libsoup/soup-marshal.list
@@ -7,3 +7,4 @@ NONE:OBJECT,POINTER
NONE:BOXED,BOXED
NONE:OBJECT,OBJECT,BOOLEAN
NONE:STRING,BOXED
+NONE:STRING,STRING
diff --git a/libsoup/soup-password-manager-gnome.c b/libsoup/soup-password-manager-gnome.c
new file mode 100644
index 0000000..9b56eae
--- /dev/null
+++ b/libsoup/soup-password-manager-gnome.c
@@ -0,0 +1,230 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-password-manager-gnome.c: GNOME-keyring-based password manager
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-password-manager-gnome.h"
+#include "soup-auth.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+#include <gnome-keyring.h>
+
+static void soup_password_manager_gnome_interface_init (SoupPasswordManagerInterface *password_manager_interface);
+
+G_DEFINE_TYPE_EXTENDED (SoupPasswordManagerGNOME, soup_password_manager_gnome, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, NULL)
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_PASSWORD_MANAGER, soup_password_manager_gnome_interface_init))
+
+static void get_passwords_async (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data);
+static void get_passwords_sync (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ GCancellable *cancellable);
+
+static void
+soup_password_manager_gnome_init (SoupPasswordManagerGNOME *manager_gnome)
+{
+}
+
+static void
+soup_password_manager_gnome_class_init (SoupPasswordManagerGNOMEClass *gnome_class)
+{
+}
+
+static void
+soup_password_manager_gnome_interface_init (SoupPasswordManagerInterface *password_manager_interface)
+{
+ password_manager_interface->get_passwords_async = get_passwords_async;
+ password_manager_interface->get_passwords_sync = get_passwords_sync;
+}
+
+SoupPasswordManager *
+soup_password_manager_gnome_new (void)
+{
+ return g_object_new (SOUP_TYPE_PASSWORD_MANAGER_GNOME, NULL);
+}
+
+
+static void
+save_password_callback (GnomeKeyringResult result, guint32 val, gpointer data)
+{
+}
+
+static void
+async_save_password (SoupAuth *auth, const char *username,
+ const char *password, gpointer user_data)
+{
+ SoupURI *uri = user_data;
+
+ gnome_keyring_set_network_password (
+ NULL, /* use default keyring */
+ username,
+ soup_auth_get_realm (auth),
+ uri->host,
+ NULL,
+ uri->scheme,
+ soup_auth_get_scheme_name (auth),
+ uri->port,
+ password,
+ save_password_callback, NULL, NULL);
+}
+
+static void
+sync_save_password (SoupAuth *auth, const char *username,
+ const char *password, gpointer user_data)
+{
+ SoupURI *uri = user_data;
+ guint32 item_id;
+
+ gnome_keyring_set_network_password_sync (
+ NULL, /* use default keyring */
+ username,
+ soup_auth_get_realm (auth),
+ uri->host,
+ NULL,
+ uri->scheme,
+ soup_auth_get_scheme_name (auth),
+ uri->port,
+ password,
+ &item_id);
+}
+
+static void
+update_auth_for_passwords (SoupAuth *auth, SoupMessage *msg,
+ GList *passwords, gboolean async)
+{
+ GnomeKeyringNetworkPasswordData *pdata;
+ SoupURI *uri;
+
+ while (passwords) {
+ pdata = passwords->data;
+ soup_auth_has_saved_password (auth, pdata->user,
+ pdata->password);
+ passwords = passwords->next;
+ }
+
+ uri = soup_uri_copy (soup_message_get_uri (msg));
+ g_signal_connect (auth, "save_password",
+ G_CALLBACK (async ? async_save_password : sync_save_password),
+ uri);
+ g_object_set_data_full (G_OBJECT (auth),
+ "SoupPasswordManagerGNOME-save_password-uri",
+ uri, (GDestroyNotify)soup_uri_free);
+}
+
+typedef struct {
+ SoupPasswordManager *password_manager;
+ SoupMessage *msg;
+ SoupAuth *auth;
+ gboolean retrying;
+
+ SoupPasswordManagerCallback callback;
+ gpointer user_data;
+
+ gpointer request;
+} SoupPasswordManagerGNOMEAuthData;
+
+static void
+find_password_callback (GnomeKeyringResult result, GList *list,
+ gpointer user_data)
+{
+ SoupPasswordManagerGNOMEAuthData *auth_data = user_data;
+
+ /* FIXME: check result? */
+
+ update_auth_for_passwords (auth_data->auth, auth_data->msg, list, TRUE);
+ auth_data->callback (auth_data->password_manager,
+ auth_data->msg, auth_data->auth,
+ auth_data->retrying, auth_data->user_data);
+
+ /* gnome-keyring will call free_auth_data to clean up for us. */
+}
+
+static void
+free_auth_data (gpointer data)
+{
+ SoupPasswordManagerGNOMEAuthData *auth_data = data;
+
+ g_object_unref (auth_data->auth);
+ g_object_unref (auth_data->msg);
+ g_slice_free (SoupPasswordManagerGNOMEAuthData, auth_data);
+}
+
+static void
+get_passwords_async (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data)
+{
+ SoupPasswordManagerGNOMEAuthData *auth_data;
+ SoupURI *uri = soup_message_get_uri (msg);
+
+ auth_data = g_slice_new (SoupPasswordManagerGNOMEAuthData);
+ auth_data->password_manager = password_manager;
+ auth_data->msg = g_object_ref (msg);
+ auth_data->auth = g_object_ref (auth);
+ auth_data->retrying = retrying;
+
+ /* FIXME: async_context, cancellable */
+
+ auth_data->callback = callback;
+ auth_data->user_data = user_data;
+
+ /* FIXME: should we be specifying protocol and port here, or
+ * leaving them NULL/0 and filtering results in the callback?
+ * We don't want to send https passwords to http, but the
+ * reverse might be OK (if that's how other clients tend to
+ * behave).
+ */
+ auth_data->request = gnome_keyring_find_network_password (
+ NULL, /* user -- accept any */
+ soup_auth_get_realm (auth), /* domain */
+ uri->host, /* server */
+ NULL, /* object -- unused */
+ uri->scheme, /* protocol */
+ soup_auth_get_scheme_name (auth), /* authtype */
+ uri->port, /* port */
+ find_password_callback, auth_data, free_auth_data);
+}
+
+static void
+get_passwords_sync (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ GCancellable *cancellable)
+{
+ SoupURI *uri = soup_message_get_uri (msg);
+ GList *results = NULL;
+
+ /* FIXME: cancellable */
+
+ gnome_keyring_find_network_password_sync (
+ NULL, /* user -- accept any */
+ soup_auth_get_realm (auth), /* domain */
+ uri->host, /* server */
+ NULL, /* object -- unused */
+ uri->scheme, /* protocol */
+ soup_auth_get_scheme_name (auth), /* authtype */
+ uri->port, /* port */
+ &results);
+
+ update_auth_for_passwords (auth, msg, results, FALSE);
+}
diff --git a/libsoup/soup-password-manager-gnome.h b/libsoup/soup-password-manager-gnome.h
new file mode 100644
index 0000000..ad6d362
--- /dev/null
+++ b/libsoup/soup-password-manager-gnome.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PASSWORD_MANAGER_GNOME_H
+#define SOUP_PASSWORD_MANAGER_GNOME_H 1
+
+#include <libsoup/soup-password-manager.h>
+
+#define SOUP_TYPE_PASSWORD_MANAGER_GNOME (soup_password_manager_gnome_get_type ())
+#define SOUP_PASSWORD_MANAGER_GNOME(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PASSWORD_MANAGER_GNOME, SoupPasswordManagerGNOME))
+#define SOUP_PASSWORD_MANAGER_GNOME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PASSWORD_MANAGER_GNOME, SoupPasswordManagerGNOMEClass))
+#define SOUP_IS_PASSWORD_MANAGER_GNOME(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PASSWORD_MANAGER_GNOME))
+#define SOUP_IS_PASSWORD_MANAGER_GNOME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PASSWORD_MANAGER_GNOME))
+#define SOUP_PASSWORD_MANAGER_GNOME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_PASSWORD_MANAGER_GNOME, SoupPasswordManagerGNOMEClass))
+
+typedef struct {
+ GObject parent;
+
+} SoupPasswordManagerGNOME;
+
+typedef struct {
+ GObjectClass parent_class;
+
+} SoupPasswordManagerGNOMEClass;
+
+GType soup_password_manager_gnome_get_type (void);
+
+SoupPasswordManager *soup_password_manager_gnome_new (void);
+
+#endif /* SOUP_PASSWORD_MANAGER_GNOME_H */
diff --git a/libsoup/soup-password-manager.c b/libsoup/soup-password-manager.c
new file mode 100644
index 0000000..5654dc3
--- /dev/null
+++ b/libsoup/soup-password-manager.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-password-manager.c: HTTP auth password manager interface
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-password-manager.h"
+#include "soup-session-feature.h"
+
+GType
+soup_password_manager_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ GType g_define_type_id =
+ g_type_register_static_simple (G_TYPE_INTERFACE,
+ g_intern_static_string ("SoupPasswordManager"),
+ sizeof (SoupPasswordManagerInterface),
+ (GClassInitFunc)NULL,
+ 0,
+ (GInstanceInitFunc)NULL,
+ (GTypeFlags) 0);
+ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+ g_type_interface_add_prerequisite (g_define_type_id, SOUP_TYPE_SESSION_FEATURE);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+/**
+ * soup_password_manager_get_passwords_async:
+ * @password_manager: the #SoupPasswordManager
+ * @msg: the #SoupMessage being authenticated
+ * @auth: the #SoupAuth being authenticated
+ * @retrying: whether or not this is a re-attempt to authenticate
+ * @async_context: the #GMainContext to invoke @callback in
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to invoke after fetching passwords
+ * @user_data: data for @callback
+ *
+ * Asynchronously attempts to look up saved passwords for @auth/@msg
+ * and then calls @callback after updating @auth with the information.
+ * Also registers @auth with @password_manager so that if the caller
+ * calls soup_auth_save_password() on it, the password will be saved.
+ *
+ * #SoupPasswordManager does not actually use the @retrying flag itself;
+ * it just passes its value on to @callback.
+ *
+ * If @cancellable is cancelled, @callback will still be invoked.
+ *
+ * Since: 2.28
+ **/
+void
+soup_password_manager_get_passwords_async (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data)
+{
+ SOUP_PASSWORD_MANAGER_GET_CLASS (password_manager)->
+ get_passwords_async (password_manager, msg, auth, retrying,
+ async_context, cancellable,
+ callback, user_data);
+}
+
+/**
+ * soup_password_manager_get_passwords_sync:
+ * @password_manager: the #SoupPasswordManager
+ * @msg: the #SoupMessage being authenticated
+ * @auth: the #SoupAuth being authenticated
+ * @cancellable: a #GCancellable, or %NULL
+ *
+ * Synchronously attempts to look up saved passwords for @auth/@msg
+ * and updates @auth with the information. Also registers @auth with
+ * @password_manager so that if the caller calls
+ * soup_auth_save_password() on it, the password will be saved.
+ *
+ * Since: 2.28
+ **/
+void
+soup_password_manager_get_passwords_sync (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ GCancellable *cancellable)
+{
+ SOUP_PASSWORD_MANAGER_GET_CLASS (password_manager)->
+ get_passwords_sync (password_manager, msg, auth, cancellable);
+}
diff --git a/libsoup/soup-password-manager.h b/libsoup/soup-password-manager.h
new file mode 100644
index 0000000..2cd0270
--- /dev/null
+++ b/libsoup/soup-password-manager.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PASSWORD_MANAGER_H
+#define SOUP_PASSWORD_MANAGER_H 1
+
+#include <libsoup/soup-types.h>
+#include <gio/gio.h>
+
+#define SOUP_TYPE_PASSWORD_MANAGER (soup_password_manager_get_type ())
+#define SOUP_PASSWORD_MANAGER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PASSWORD_MANAGER, SoupPasswordManager))
+#define SOUP_PASSWORD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PASSWORD_MANAGER, SoupPasswordManagerInterface))
+#define SOUP_IS_PASSWORD_MANAGER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PASSWORD_MANAGER))
+#define SOUP_IS_PASSWORD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PASSWORD_MANAGER))
+#define SOUP_PASSWORD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SOUP_TYPE_PASSWORD_MANAGER, SoupPasswordManagerInterface))
+
+typedef struct _SoupPasswordManager SoupPasswordManager;
+
+typedef void (*SoupPasswordManagerCallback) (SoupPasswordManager *,
+ SoupMessage *, SoupAuth *,
+ gboolean retrying,
+ gpointer user_data);
+
+typedef struct {
+ GTypeInterface base;
+
+ /* virtual methods */
+ void (*get_passwords_async) (SoupPasswordManager *, SoupMessage *,
+ SoupAuth *, gboolean,
+ GMainContext *, GCancellable *,
+ SoupPasswordManagerCallback, gpointer);
+ void (*get_passwords_sync) (SoupPasswordManager *, SoupMessage *,
+ SoupAuth *, GCancellable *);
+
+} SoupPasswordManagerInterface;
+
+GType soup_password_manager_get_type (void);
+
+void soup_password_manager_get_passwords_async (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data);
+
+void soup_password_manager_get_passwords_sync (SoupPasswordManager *password_manager,
+ SoupMessage *msg,
+ SoupAuth *auth,
+ GCancellable *cancellable);
+
+#endif /* SOUP_PASSWORD_MANAGER_H */
diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
index 81fda17..6045481 100644
--- a/libsoup/soup-session-async.c
+++ b/libsoup/soup-session-async.c
@@ -15,6 +15,7 @@
#include "soup-address.h"
#include "soup-message-private.h"
#include "soup-misc.h"
+#include "soup-password-manager.h"
#include "soup-proxy-uri-resolver.h"
#include "soup-uri.h"
@@ -34,6 +35,9 @@ static void queue_message (SoupSession *session, SoupMessage *req,
SoupSessionCallback callback, gpointer user_data);
static guint send_message (SoupSession *session, SoupMessage *req);
+static void auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
+
G_DEFINE_TYPE (SoupSessionAsync, soup_session_async, SOUP_TYPE_SESSION)
typedef struct {
@@ -69,6 +73,7 @@ soup_session_async_class_init (SoupSessionAsyncClass *soup_session_async_class)
/* virtual method override */
session_class->queue_message = queue_message;
session_class->send_message = send_message;
+ session_class->auth_required = auth_required;
object_class->finalize = finalize;
}
@@ -444,3 +449,36 @@ send_message (SoupSession *session, SoupMessage *req)
return req->status_code;
}
+
+static void
+got_passwords (SoupPasswordManager *password_manager, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer session)
+{
+ soup_session_unpause_message (session, msg);
+ SOUP_SESSION_CLASS (soup_session_async_parent_class)->
+ auth_required (session, msg, auth, retrying);
+ g_object_unref (auth);
+}
+
+static void
+auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying)
+{
+ SoupSessionFeature *password_manager;
+
+ password_manager = soup_session_get_feature_for_message (
+ session, SOUP_TYPE_PASSWORD_MANAGER, msg);
+ if (password_manager) {
+ soup_session_pause_message (session, msg);
+ g_object_ref (auth);
+ soup_password_manager_get_passwords_async (
+ SOUP_PASSWORD_MANAGER (password_manager),
+ msg, auth, retrying,
+ soup_session_get_async_context (session),
+ NULL, /* FIXME cancellable */
+ got_passwords, session);
+ } else {
+ SOUP_SESSION_CLASS (soup_session_async_parent_class)->
+ auth_required (session, msg, auth, retrying);
+ }
+}
diff --git a/libsoup/soup-session-sync.c b/libsoup/soup-session-sync.c
index fb19f21..662c029 100644
--- a/libsoup/soup-session-sync.c
+++ b/libsoup/soup-session-sync.c
@@ -14,8 +14,9 @@
#include "soup-session-private.h"
#include "soup-address.h"
#include "soup-message-private.h"
-#include "soup-proxy-uri-resolver.h"
#include "soup-misc.h"
+#include "soup-password-manager.h"
+#include "soup-proxy-uri-resolver.h"
#include "soup-uri.h"
/**
@@ -54,6 +55,8 @@ static void queue_message (SoupSession *session, SoupMessage *msg,
static guint send_message (SoupSession *session, SoupMessage *msg);
static void cancel_message (SoupSession *session, SoupMessage *msg,
guint status_code);
+static void auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
@@ -89,6 +92,7 @@ soup_session_sync_class_init (SoupSessionSyncClass *session_sync_class)
session_class->queue_message = queue_message;
session_class->send_message = send_message;
session_class->cancel_message = cancel_message;
+ session_class->auth_required = auth_required;
object_class->finalize = finalize;
}
@@ -323,3 +327,20 @@ cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
g_cond_broadcast (priv->cond);
}
+static void
+auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying)
+{
+ SoupSessionFeature *password_manager;
+
+ password_manager = soup_session_get_feature_for_message (
+ session, SOUP_TYPE_PASSWORD_MANAGER, msg);
+ if (password_manager) {
+ soup_password_manager_get_passwords_sync (
+ SOUP_PASSWORD_MANAGER (password_manager),
+ msg, auth, NULL); /* FIXME cancellable */
+ }
+
+ SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
+ auth_required (session, msg, auth, retrying);
+}
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index cdb2bf8..d4eb1e5 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -102,6 +102,8 @@ static void queue_message (SoupSession *session, SoupMessage *msg,
static void requeue_message (SoupSession *session, SoupMessage *msg);
static void cancel_message (SoupSession *session, SoupMessage *msg,
guint status_code);
+static void auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
static void auth_manager_authenticate (SoupAuthManager *manager,
SoupMessage *msg, SoupAuth *auth,
@@ -235,6 +237,7 @@ soup_session_class_init (SoupSessionClass *session_class)
session_class->queue_message = queue_message;
session_class->requeue_message = requeue_message;
session_class->cancel_message = cancel_message;
+ session_class->auth_required = auth_required;
/* virtual method override */
object_class->dispose = dispose;
@@ -841,11 +844,19 @@ free_host (SoupSessionHost *host)
}
static void
+auth_required (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying)
+{
+ g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
+}
+
+static void
auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
SoupAuth *auth, gboolean retrying,
gpointer session)
{
- g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
+ SOUP_SESSION_GET_CLASS (session)->auth_required (
+ session, msg, auth, retrying);
}
#define SOUP_METHOD_IS_SAFE(method) (method == SOUP_METHOD_GET || \
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 2eb86b5..598ee45 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -46,8 +46,11 @@ typedef struct {
void (*cancel_message) (SoupSession *session, SoupMessage *msg,
guint status_code);
+ void (*auth_required) (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying);
+
+
/* Padding for future expansion */
- void (*_libsoup_reserved1) (void);
void (*_libsoup_reserved2) (void);
void (*_libsoup_reserved3) (void);
void (*_libsoup_reserved4) (void);
diff --git a/libsoup/soup.h b/libsoup/soup.h
index c761e00..f3b0b3d 100644
--- a/libsoup/soup.h
+++ b/libsoup/soup.h
@@ -28,6 +28,7 @@ extern "C" {
#include <libsoup/soup-method.h>
#include <libsoup/soup-misc.h>
#include <libsoup/soup-multipart.h>
+#include <libsoup/soup-password-manager.h>
#include <libsoup/soup-proxy-resolver.h>
#include <libsoup/soup-proxy-uri-resolver.h>
#include <libsoup/soup-server.h>
diff --git a/tests/get.c b/tests/get.c
index b0e5c57..9bb00a5 100644
--- a/tests/get.c
+++ b/tests/get.c
@@ -14,6 +14,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <termios.h>
#include <unistd.h>
#ifdef HAVE_GNOME
@@ -74,6 +75,75 @@ get_url (const char *url)
}
static void
+authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gpointer user_data)
+{
+ char *uri;
+ GSList *saved_users;
+ struct termios t;
+ int old_lflag;
+ char user[80], pwbuf[80];
+ const char *password;
+
+ if (tcgetattr (STDIN_FILENO, &t) != 0)
+ return;
+
+ uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+ fprintf (stderr, "Authentication required for %s:\n", uri);
+ g_free (uri);
+ fprintf (stderr, " Realm: %s, Auth type: %s\n",
+ soup_auth_get_realm (auth), soup_auth_get_scheme_name (auth));
+
+ saved_users = soup_auth_get_saved_users (auth);
+ if (saved_users) {
+ GSList *u;
+
+ fprintf (stderr, " Passwords saved for: ");
+ for (u = saved_users; u; u = u->next) {
+ if (u != saved_users)
+ fprintf (stderr, ", ");
+ fprintf (stderr, "%s", (char *)u->data);
+ }
+ fprintf (stderr, "\n");
+ }
+ g_slist_free (saved_users);
+
+ fprintf (stderr, " username: ");
+ fflush (stderr);
+
+ if (!fgets (user, sizeof (user), stdin) || user[0] == '\n')
+ return;
+ *strchr (user, '\n') = '\0';
+
+ password = soup_auth_get_saved_password (auth, user);
+ if (!password) {
+ fprintf (stderr, " password: ");
+ fflush (stderr);
+
+ old_lflag = t.c_lflag;
+ t.c_lflag = (t.c_lflag | ICANON | ECHONL) & ~ECHO;
+ tcsetattr (STDIN_FILENO, TCSANOW, &t);
+
+ /* For some reason, fgets can return EINTR on
+ * Linux if ECHO is false...
+ */
+ do
+ password = fgets (pwbuf, sizeof (pwbuf), stdin);
+ while (password == NULL && errno == EINTR);
+
+ t.c_lflag = old_lflag;
+ tcsetattr (STDIN_FILENO, TCSANOW, &t);
+
+ if (!password || pwbuf[0] == '\n')
+ return;
+ *strchr (pwbuf, '\n') = '\0';
+ }
+
+ soup_auth_authenticate (auth, user, password);
+ soup_auth_save_password (auth, user, password);
+}
+
+static void
usage (void)
{
fprintf (stderr, "Usage: get [-c CAfile] [-p proxy URL] [-h] [-d] URL\n");
@@ -90,6 +160,7 @@ main (int argc, char **argv)
g_thread_init (NULL);
g_type_init ();
+ g_set_application_name ("get");
method = SOUP_METHOD_GET;
@@ -144,6 +215,7 @@ main (int argc, char **argv)
SOUP_SESSION_SSL_CA_FILE, cafile,
#ifdef HAVE_GNOME
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PASSWORD_MANAGER_GNOME,
#endif
SOUP_SESSION_USER_AGENT, "get ",
NULL);
@@ -152,10 +224,13 @@ main (int argc, char **argv)
SOUP_SESSION_SSL_CA_FILE, cafile,
#ifdef HAVE_GNOME
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PASSWORD_MANAGER_GNOME,
#endif
SOUP_SESSION_USER_AGENT, "get ",
NULL);
}
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), NULL);
/* Need to do this after creating the session, since adding
* SOUP_TYPE_GNOME_FEATURE_2_26 will add a proxy resolver, thereby
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]