Add SoupPasswordManager and SoupPasswordManagerGNOME
authorDan Winship <danw@gnome.org>
Wed, 12 Aug 2009 14:23:39 +0000 (10:23 -0400)
committerDan Winship <danw@gnome.org>
Wed, 12 Aug 2009 14:24:09 +0000 (10:24 -0400)
SoupPasswordManager (and some new SoupAuth APIs) provide an interface
for saving passwords, and SoupPasswordManagerGNOME provides an
implementation of that interface using gnome-keyring.

18 files changed:
configure.in
libsoup/Makefile.am
libsoup/soup-auth-manager-ntlm.h
libsoup/soup-auth-manager.h
libsoup/soup-auth.c
libsoup/soup-auth.h
libsoup/soup-gnome.h
libsoup/soup-marshal.list
libsoup/soup-password-manager-gnome.c [new file with mode: 0644]
libsoup/soup-password-manager-gnome.h [new file with mode: 0644]
libsoup/soup-password-manager.c [new file with mode: 0644]
libsoup/soup-password-manager.h [new file with mode: 0644]
libsoup/soup-session-async.c
libsoup/soup-session-sync.c
libsoup/soup-session.c
libsoup/soup-session.h
libsoup/soup.h
tests/get.c

index 0d9e2f1..3318992 100644 (file)
@@ -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
index d5b8be2..a436d4e 100644 (file)
@@ -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
 
index a048950..f0b4f57 100644 (file)
@@ -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
index 1fc44c4..b6759ec 100644 (file)
@@ -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,
index d77fd82..a3f2ae7 100644 (file)
@@ -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);
+}
index 53eb16b..1259df8 100644 (file)
@@ -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 */
index a2134a4..6703cf0 100644 (file)
@@ -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 */
index d0c53ef..7714813 100644 (file)
@@ -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 (file)
index 0000000..9b56eae
--- /dev/null
@@ -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 (file)
index 0000000..ad6d362
--- /dev/null
@@ -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 (file)
index 0000000..5654dc3
--- /dev/null
@@ -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 (file)
index 0000000..2cd0270
--- /dev/null
@@ -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 */
index 81fda17..6045481 100644 (file)
@@ -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);
+       }
+}
index fb19f21..662c029 100644 (file)
@@ -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);
+}
index cdb2bf8..d4eb1e5 100644 (file)
@@ -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 || \
index 2eb86b5..598ee45 100644 (file)
@@ -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);
index c761e00..f3b0b3d 100644 (file)
@@ -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>
index b0e5c57..9bb00a5 100644 (file)
@@ -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