Make this a GObject and specifically a SoupSessionFeature. Add an
authorDan Winship <danw@src.gnome.org>
Tue, 8 Apr 2008 22:13:03 +0000 (22:13 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 8 Apr 2008 22:13:03 +0000 (22:13 +0000)
* libsoup/soup-auth-manager.c: Make this a GObject and
specifically a SoupSessionFeature. Add an "authenticate" signal,
and emit that rather than explicitly calling into the SoupSession
and telling it when to emit its own authenticate signal.

* libsoup/soup-auth-manager-ntlm.c: Make this a subclass of
SoupAuthManager, with NTLM support controllable via a property.

* libsoup/soup-session.c (soup_session_init): create an
auth_manager of type SOUP_TYPE_AUTH_MANAGER_NTLM, but defaulting
to USE_NTLM=FALSE. Connect to its "authenticate" signal, and call
soup_session_add_feature() on it.
(set_property, get_property): proxy the USE_NTLM property to the
auth manager.
(auth_manager_authenticate): signal handler for SoupAuthManager
"authenticate" signal. (Replaces soup_session_emit_authenticate(),
which is no longer needed)

svn path=/trunk/; revision=1134

ChangeLog
libsoup/soup-auth-manager-ntlm.c
libsoup/soup-auth-manager-ntlm.h
libsoup/soup-auth-manager.c
libsoup/soup-auth-manager.h
libsoup/soup-session-private.h
libsoup/soup-session.c

index 80fb0b0..5ab21b4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,25 @@
 2008-04-08  Dan Winship  <danw@gnome.org>
 
+       * libsoup/soup-auth-manager.c: Make this a GObject and
+       specifically a SoupSessionFeature. Add an "authenticate" signal,
+       and emit that rather than explicitly calling into the SoupSession
+       and telling it when to emit its own authenticate signal.
+
+       * libsoup/soup-auth-manager-ntlm.c: Make this a subclass of
+       SoupAuthManager, with NTLM support controllable via a property.
+
+       * libsoup/soup-session.c (soup_session_init): create an
+       auth_manager of type SOUP_TYPE_AUTH_MANAGER_NTLM, but defaulting
+       to USE_NTLM=FALSE. Connect to its "authenticate" signal, and call
+       soup_session_add_feature() on it.
+       (set_property, get_property): proxy the USE_NTLM property to the
+       auth manager.
+       (auth_manager_authenticate): signal handler for SoupAuthManager
+       "authenticate" signal. (Replaces soup_session_emit_authenticate(),
+       which is no longer needed)
+
+2008-04-08  Dan Winship  <danw@gnome.org>
+
        * libsoup/soup-session-feature.c: New interface type representing
        a feature that can be added to a SoupSession.
 
index ffbae60..bc330ae 100644 (file)
 #include "soup-message-private.h"
 #include "soup-misc.h"
 #include "soup-session.h"
-#include "soup-session-private.h"
+#include "soup-session-feature.h"
 #include "soup-uri.h"
 
+static void soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static SoupSessionFeatureInterface *soup_auth_manager_parent_feature_interface;
+
+static void attach (SoupSessionFeature *feature, SoupSession *session);
+static void request_queued (SoupSessionFeature *feature, SoupSession *session,
+                           SoupMessage *msg);
+static void request_started (SoupSessionFeature *feature, SoupSession *session,
+                            SoupMessage *msg, SoupSocket *socket);
+static void request_unqueued (SoupSessionFeature *feature,
+                             SoupSession *session, SoupMessage *msg);
+
+G_DEFINE_TYPE_WITH_CODE (SoupAuthManagerNTLM, soup_auth_manager_ntlm, SOUP_TYPE_AUTH_MANAGER,
+                        G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+                                               soup_auth_manager_ntlm_session_feature_init));
+
+enum {
+       PROP_0,
+
+       PROP_USE_NTLM,
+
+       LAST_PROP
+};
+
 typedef enum {
        SOUP_NTLM_NEW,
        SOUP_NTLM_SENT_REQUEST,
@@ -39,18 +62,14 @@ typedef struct {
        SoupAuth *auth;
 } SoupNTLMConnection;
 
-struct SoupAuthManagerNTLM {
+typedef struct {
+       gboolean use_ntlm;
+
        SoupSession *session;
        GHashTable *connections_by_msg;
        GHashTable *connections_by_id;
-};
-
-static void ntlm_request_queued (SoupSession *session, SoupMessage *msg,
-                                gpointer ntlm);
-static void ntlm_request_started (SoupSession *session, SoupMessage *msg,
-                                 SoupSocket *socket, gpointer ntlm);
-static void ntlm_request_unqueued (SoupSession *session, SoupMessage *msg,
-                                  gpointer ntlm);
+} SoupAuthManagerNTLMPrivate;
+#define SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMPrivate))
 
 static char     *soup_ntlm_request         (void);
 static gboolean  soup_ntlm_parse_challenge (const char  *challenge,
@@ -62,22 +81,19 @@ static char     *soup_ntlm_response        (const char  *nonce,
                                            const char  *host, 
                                            const char  *domain);
 
-SoupAuthManagerNTLM *
-soup_auth_manager_ntlm_new (SoupSession *session)
+static void set_property (GObject *object, guint prop_id,
+                         const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+                         GValue *value, GParamSpec *pspec);
+
+static void
+soup_auth_manager_ntlm_init (SoupAuthManagerNTLM *ntlm)
 {
-       SoupAuthManagerNTLM *ntlm;
-
-       ntlm = g_slice_new (SoupAuthManagerNTLM);
-       ntlm->session = session;
-       ntlm->connections_by_id = g_hash_table_new (NULL, NULL);
-       ntlm->connections_by_msg = g_hash_table_new (NULL, NULL);
-       g_signal_connect (session, "request_queued",
-                         G_CALLBACK (ntlm_request_queued), ntlm);
-       g_signal_connect (session, "request_started",
-                         G_CALLBACK (ntlm_request_started), ntlm);
-       g_signal_connect (session, "request_unqueued",
-                         G_CALLBACK (ntlm_request_unqueued), ntlm);
-       return ntlm;
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
+
+       priv->connections_by_id = g_hash_table_new (NULL, NULL);
+       priv->connections_by_msg = g_hash_table_new (NULL, NULL);
 }
 
 static void
@@ -97,96 +113,180 @@ free_ntlm_connection_foreach (gpointer key, gpointer value, gpointer user_data)
        free_ntlm_connection (value);
 }
 
-void
-soup_auth_manager_ntlm_free (SoupAuthManagerNTLM *ntlm)
+static void
+finalize (GObject *object)
 {
-       g_hash_table_foreach (ntlm->connections_by_id,
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (object);
+
+       g_hash_table_foreach (priv->connections_by_id,
                              free_ntlm_connection_foreach, NULL);
-       g_hash_table_destroy (ntlm->connections_by_id);
-       g_hash_table_destroy (ntlm->connections_by_msg);
-       g_signal_handlers_disconnect_by_func (ntlm->session,
-                                             ntlm_request_queued, ntlm);
-       g_signal_handlers_disconnect_by_func (ntlm->session,
-                                             ntlm_request_started, ntlm);
-       g_signal_handlers_disconnect_by_func (ntlm->session,
-                                             ntlm_request_unqueued, ntlm);
-
-       g_slice_free (SoupAuthManagerNTLM, ntlm);
+       g_hash_table_destroy (priv->connections_by_id);
+       g_hash_table_destroy (priv->connections_by_msg);
+
+       G_OBJECT_CLASS (soup_auth_manager_ntlm_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_manager_ntlm_class_init (SoupAuthManagerNTLMClass *auth_manager_ntlm_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_ntlm_class);
+
+       g_type_class_add_private (auth_manager_ntlm_class, sizeof (SoupAuthManagerNTLMPrivate));
+
+       object_class->finalize = finalize;
+       object_class->set_property = set_property;
+       object_class->get_property = get_property;
+
+       g_object_class_install_property (
+               object_class, PROP_USE_NTLM,
+               g_param_spec_boolean (SOUP_AUTH_MANAGER_NTLM_USE_NTLM,
+                                     "Use NTLM",
+                                     "Whether or not to use NTLM authentication",
+                                     FALSE,
+                                     G_PARAM_READWRITE));
+}
+
+static void
+soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+                                            gpointer interface_data)
+{
+       soup_auth_manager_parent_feature_interface =
+               g_type_interface_peek_parent (feature_interface);
+
+       feature_interface->attach = attach;
+       feature_interface->request_queued = request_queued;
+       feature_interface->request_started = request_started;
+       feature_interface->request_unqueued = request_unqueued;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+             const GValue *value, GParamSpec *pspec)
+{
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_USE_NTLM:
+               priv->use_ntlm = g_value_get_boolean (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+             GValue *value, GParamSpec *pspec)
+{
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (object);
+
+       switch (prop_id) {
+       case PROP_USE_NTLM:
+               g_value_set_boolean (value, priv->use_ntlm);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+attach (SoupSessionFeature *manager, SoupSession *session)
+{
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (manager);
+
+       /* FIXME: should support multiple sessions */
+       priv->session = session;
+
+       soup_auth_manager_parent_feature_interface->attach (manager, session);
 }
 
 static void
 delete_conn (SoupSocket *socket, gpointer user_data)
 {
-       SoupAuthManagerNTLM *ntlm = user_data;
+       SoupAuthManagerNTLMPrivate *priv = user_data;
        SoupNTLMConnection *conn;
 
-       conn = g_hash_table_lookup (ntlm->connections_by_id, socket);
+       conn = g_hash_table_lookup (priv->connections_by_id, socket);
        if (conn)
                free_ntlm_connection (conn);
-       g_hash_table_remove (ntlm->connections_by_id, socket);
-       g_signal_handlers_disconnect_by_func (socket, delete_conn, ntlm);
+       g_hash_table_remove (priv->connections_by_id, socket);
+       g_signal_handlers_disconnect_by_func (socket, delete_conn, priv);
 }
 
 static SoupNTLMConnection *
-get_connection (SoupAuthManagerNTLM *ntlm, SoupSocket *socket)
+get_connection (SoupAuthManagerNTLMPrivate *priv, SoupSocket *socket)
 {
        SoupNTLMConnection *conn;
 
-       conn = g_hash_table_lookup (ntlm->connections_by_id, socket);
+       conn = g_hash_table_lookup (priv->connections_by_id, socket);
        if (conn)
                return conn;
 
        conn = g_slice_new0 (SoupNTLMConnection);
        conn->socket = socket;
        conn->state = SOUP_NTLM_NEW;
-       g_hash_table_insert (ntlm->connections_by_id, socket, conn);
+       g_hash_table_insert (priv->connections_by_id, socket, conn);
 
        g_signal_connect (socket, "disconnected",
-                         G_CALLBACK (delete_conn), ntlm);
+                         G_CALLBACK (delete_conn), priv);
        return conn;
 }
 
 static void
 unset_conn (SoupMessage *msg, gpointer user_data)
 {
-       SoupAuthManagerNTLM *ntlm = user_data;
+       SoupAuthManagerNTLMPrivate *priv = user_data;
 
-       g_hash_table_remove (ntlm->connections_by_msg, msg);
-       g_signal_handlers_disconnect_by_func (msg, unset_conn, ntlm);
+       g_hash_table_remove (priv->connections_by_msg, msg);
+       g_signal_handlers_disconnect_by_func (msg, unset_conn, priv);
 }
 
 static SoupNTLMConnection *
-set_connection_for_msg (SoupAuthManagerNTLM *ntlm, SoupMessage *msg,
+set_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg,
                        SoupNTLMConnection *conn)
 {
-       if (!g_hash_table_lookup (ntlm->connections_by_msg, msg)) {
+       if (!g_hash_table_lookup (priv->connections_by_msg, msg)) {
                g_signal_connect (msg, "finished",
-                                 G_CALLBACK (unset_conn), ntlm);
+                                 G_CALLBACK (unset_conn), priv);
                g_signal_connect (msg, "restarted",
-                                 G_CALLBACK (unset_conn), ntlm);
+                                 G_CALLBACK (unset_conn), priv);
        }
-       g_hash_table_insert (ntlm->connections_by_msg, msg, conn);
+       g_hash_table_insert (priv->connections_by_msg, msg, conn);
 
        return conn;
 }
 
 static SoupNTLMConnection *
-get_connection_for_msg (SoupAuthManagerNTLM *ntlm, SoupMessage *msg)
+get_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg)
 {
-       return g_hash_table_lookup (ntlm->connections_by_msg, msg);
+       return g_hash_table_lookup (priv->connections_by_msg, msg);
 }
 
 static void
-ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
+ntlm_authorize_pre (SoupMessage *msg, gpointer ntlm)
 {
-       SoupAuthManagerNTLM *ntlm = user_data;
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
        SoupNTLMConnection *conn;
        const char *val;
 
-       conn = get_connection_for_msg (ntlm, msg);
+       conn = get_connection_for_msg (priv, msg);
        if (!conn)
                return;
 
+       val = soup_message_headers_get (msg->response_headers,
+                                       "WWW-Authenticate");
+       if (val)
+               val = strstr (val, "NTLM ");
+       if (!val)
+               return;
+
        if (conn->state > SOUP_NTLM_SENT_REQUEST) {
                /* We already authenticated, but then got another 401.
                 * That means "permission denied", so don't try to
@@ -196,15 +296,6 @@ ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
                goto done;
        }
 
-       val = soup_message_headers_get (msg->response_headers,
-                                       "WWW-Authenticate");
-       if (val)
-               val = strstr (val, "NTLM ");
-       if (!val) {
-               conn->state = SOUP_NTLM_FAILED;
-               goto done;
-       }
-
        if (!soup_ntlm_parse_challenge (val, &conn->nonce, &conn->domain)) {
                conn->state = SOUP_NTLM_FAILED;
                goto done;
@@ -213,7 +304,8 @@ ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
        conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
        conn->auth = soup_auth_ntlm_new (conn->domain,
                                         soup_message_get_uri (msg)->host);
-       soup_session_emit_authenticate (ntlm->session, msg, conn->auth, FALSE);
+       soup_auth_manager_emit_authenticate (SOUP_AUTH_MANAGER (ntlm), msg,
+                                            conn->auth, FALSE);
 
  done:
        /* Remove the WWW-Authenticate headers so the session won't try
@@ -223,14 +315,15 @@ ntlm_authorize_pre (SoupMessage *msg, gpointer user_data)
 }
 
 static void
-ntlm_authorize_post (SoupMessage *msg, gpointer user_data)
+ntlm_authorize_post (SoupMessage *msg, gpointer ntlm)
 {
-       SoupAuthManagerNTLM *ntlm = user_data;
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
        SoupNTLMConnection *conn;
        const char *username = NULL, *password = NULL;
        char *slash, *domain = NULL;
 
-       conn = get_connection_for_msg (ntlm, msg);
+       conn = get_connection_for_msg (priv, msg);
        if (!conn || !conn->auth)
                return;
 
@@ -251,7 +344,7 @@ ntlm_authorize_post (SoupMessage *msg, gpointer user_data)
        conn->response_header = soup_ntlm_response (conn->nonce,
                                                    username, password,
                                                    NULL, domain);
-       soup_session_requeue_message (ntlm->session, msg);
+       soup_session_requeue_message (priv->session, msg);
 
 done:
        if (domain != conn->domain)
@@ -265,28 +358,37 @@ done:
 }
 
 static void
-ntlm_request_queued (SoupSession *session, SoupMessage *msg, gpointer ntlm)
+request_queued (SoupSessionFeature *ntlm, SoupSession *session, SoupMessage *msg)
 {
-       soup_message_add_status_code_handler (msg, "got_headers",
-                                             SOUP_STATUS_UNAUTHORIZED,
-                                             G_CALLBACK (ntlm_authorize_pre),
-                                             ntlm);
-       soup_message_add_status_code_handler (msg, "got_body",
-                                             SOUP_STATUS_UNAUTHORIZED,
-                                             G_CALLBACK (ntlm_authorize_post),
-                                             ntlm);
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
+
+       if (priv->use_ntlm) {
+               soup_message_add_status_code_handler (
+                       msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
+                       G_CALLBACK (ntlm_authorize_pre), ntlm);
+               soup_message_add_status_code_handler (
+                       msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
+                       G_CALLBACK (ntlm_authorize_post), ntlm);
+       }
+
+       soup_auth_manager_parent_feature_interface->request_queued (ntlm, session, msg);
 }
 
 static void
-ntlm_request_started (SoupSession *session, SoupMessage *msg,
-                     SoupSocket *socket, gpointer user_data)
+request_started (SoupSessionFeature *ntlm, SoupSession *session,
+                SoupMessage *msg, SoupSocket *socket)
 {
-       SoupAuthManagerNTLM *ntlm = user_data;
+       SoupAuthManagerNTLMPrivate *priv =
+               SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
        SoupNTLMConnection *conn;
        char *header = NULL;
 
-       conn = get_connection (ntlm, socket);
-       set_connection_for_msg (ntlm, msg, conn);
+       if (!priv->use_ntlm)
+               goto super;
+
+       conn = get_connection (priv, socket);
+       set_connection_for_msg (priv, msg, conn);
 
        switch (conn->state) {
        case SOUP_NTLM_NEW:
@@ -307,14 +409,19 @@ ntlm_request_started (SoupSession *session, SoupMessage *msg,
                                              "Authorization", header);
                g_free (header);
        }
+
+super:
+       soup_auth_manager_parent_feature_interface->request_started (ntlm, session, msg, socket);
 }
 
 static void
-ntlm_request_unqueued (SoupSession *session, SoupMessage *msg,
-                      gpointer ntlm)
+request_unqueued (SoupSessionFeature *ntlm, SoupSession *session,
+                 SoupMessage *msg)
 {
        g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, ntlm);
        g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, ntlm);
+
+       soup_auth_manager_parent_feature_interface->request_unqueued (ntlm, session, msg);
 }
 
 
index 0f62417..a048950 100644 (file)
@@ -6,15 +6,29 @@
 #ifndef SOUP_AUTH_MANAGER_NTLM_H
 #define SOUP_AUTH_MANAGER_NTLM_H 1
 
-#include "soup-types.h"
+#include "soup-auth-manager.h"
 
 G_BEGIN_DECLS
 
-typedef struct SoupAuthManagerNTLM SoupAuthManagerNTLM;
+#define SOUP_TYPE_AUTH_MANAGER_NTLM            (soup_auth_manager_ntlm_get_type ())
+#define SOUP_AUTH_MANAGER_NTLM(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLM))
+#define SOUP_AUTH_MANAGER_NTLM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMClass))
+#define SOUP_IS_AUTH_MANAGER_NTLM(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_MANAGER_NTLM))
+#define SOUP_IS_AUTH_MANAGER_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_MANAGER_NTLM))
+#define SOUP_AUTH_MANAGER_NTLM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMClass))
 
-SoupAuthManagerNTLM *soup_auth_manager_ntlm_new  (SoupSession         *session);
-void                 soup_auth_manager_ntlm_free (SoupAuthManagerNTLM *manager);
+typedef struct {
+       SoupAuthManager parent;
+
+} SoupAuthManagerNTLM;
+
+typedef struct {
+       SoupAuthManagerClass parent_class;
+
+} SoupAuthManagerNTLMClass;
+
+GType soup_auth_manager_ntlm_get_type (void);
 
 G_END_DECLS
 
-#endif /* SOUP_AUTH_MANAGER_NTLM_H */
+#endif /* SOUP_AUTH_MANAGER_NTLM_NTLM_H */
index 55402b4..4b2a5de 100644 (file)
 
 #include "soup-auth-manager.h"
 #include "soup-headers.h"
+#include "soup-marshal.h"
 #include "soup-message-private.h"
 #include "soup-path-map.h"
 #include "soup-session.h"
-#include "soup-session-private.h"
+#include "soup-session-feature.h"
 #include "soup-uri.h"
 
-static void session_request_queued (SoupSession *session, SoupMessage *msg,
-                                   gpointer data);
-static void session_request_started (SoupSession *session, SoupMessage *msg,
-                                    SoupSocket *socket, gpointer data);
-static void session_request_unqueued (SoupSession *session, SoupMessage *msg,
-                                     gpointer data);
+static void soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static SoupSessionFeatureInterface *soup_session_feature_default_interface;
 
-struct SoupAuthManager {
+static void attach (SoupSessionFeature *feature, SoupSession *session);
+static void request_queued  (SoupSessionFeature *feature, SoupSession *session,
+                            SoupMessage *msg);
+static void request_started  (SoupSessionFeature *feature, SoupSession *session,
+                             SoupMessage *msg, SoupSocket *socket);
+static void request_unqueued  (SoupSessionFeature *feature,
+                              SoupSession *session, SoupMessage *msg);
+
+enum {
+       AUTHENTICATE,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_CODE (SoupAuthManager, soup_auth_manager, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+                                               soup_auth_manager_session_feature_init));
+
+typedef struct {
        SoupSession *session;
        GPtrArray *auth_types;
 
        SoupAuth *proxy_auth;
        GHashTable *auth_hosts;
-};
+} SoupAuthManagerPrivate;
+#define SOUP_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerPrivate))
 
 typedef struct {
        SoupURI     *root_uri;
@@ -46,24 +63,14 @@ extern gboolean  soup_uri_host_equal (gconstpointer  v1,
                                      gconstpointer  v2);
 extern SoupURI  *soup_uri_copy_root  (SoupURI *uri);
 
-SoupAuthManager *
-soup_auth_manager_new (SoupSession *session)
+static void
+soup_auth_manager_init (SoupAuthManager *manager)
 {
-       SoupAuthManager *manager;
-
-       manager = g_slice_new0 (SoupAuthManager);
-       manager->session = session;
-       manager->auth_types = g_ptr_array_new ();
-       manager->auth_hosts = g_hash_table_new (soup_uri_host_hash,
-                                               soup_uri_host_equal);
-
-       g_signal_connect (session, "request_queued",
-                         G_CALLBACK (session_request_queued), manager);
-       g_signal_connect (session, "request_started",
-                         G_CALLBACK (session_request_started), manager);
-       g_signal_connect (session, "request_unqueued",
-                         G_CALLBACK (session_request_unqueued), manager);
-       return manager;
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+
+       priv->auth_types = g_ptr_array_new ();
+       priv->auth_hosts = g_hash_table_new (soup_uri_host_hash,
+                                            soup_uri_host_equal);
 }
 
 static gboolean
@@ -82,32 +89,59 @@ foreach_free_host (gpointer key, gpointer value, gpointer data)
        return TRUE;
 }
 
-void
-soup_auth_manager_free (SoupAuthManager *manager)
+static void
+finalize (GObject *object)
 {
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (object);
        int i;
 
-       g_signal_handlers_disconnect_by_func (
-               manager->session,
-               G_CALLBACK (session_request_queued), manager);
-       g_signal_handlers_disconnect_by_func (
-               manager->session,
-               G_CALLBACK (session_request_started), manager);
-       g_signal_handlers_disconnect_by_func (
-               manager->session,
-               G_CALLBACK (session_request_unqueued), manager);
+       for (i = 0; i < priv->auth_types->len; i++)
+               g_type_class_unref (priv->auth_types->pdata[i]);
+       g_ptr_array_free (priv->auth_types, TRUE);
+
+       g_hash_table_foreach_remove (priv->auth_hosts, foreach_free_host, NULL);
+       g_hash_table_destroy (priv->auth_hosts);
+
+       if (priv->proxy_auth)
+               g_object_unref (priv->proxy_auth);
+
+       G_OBJECT_CLASS (soup_auth_manager_parent_class)->finalize (object);
+}
+
+static void
+soup_auth_manager_class_init (SoupAuthManagerClass *auth_manager_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_class);
 
-       for (i = 0; i < manager->auth_types->len; i++)
-               g_type_class_unref (manager->auth_types->pdata[i]);
-       g_ptr_array_free (manager->auth_types, TRUE);
+       g_type_class_add_private (auth_manager_class, sizeof (SoupAuthManagerPrivate));
 
-       g_hash_table_foreach_remove (manager->auth_hosts, foreach_free_host, NULL);
-       g_hash_table_destroy (manager->auth_hosts);
+       object_class->finalize = finalize;
 
-       if (manager->proxy_auth)
-               g_object_unref (manager->proxy_auth);
+       signals[AUTHENTICATE] =
+               g_signal_new ("authenticate",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupAuthManagerClass, authenticate),
+                             NULL, NULL,
+                             soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
+                             G_TYPE_NONE, 3,
+                             SOUP_TYPE_MESSAGE,
+                             SOUP_TYPE_AUTH,
+                             G_TYPE_BOOLEAN);
 
-       g_slice_free (SoupAuthManager, manager);
+}
+
+static void
+soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+                                       gpointer interface_data)
+{
+       soup_session_feature_default_interface =
+               g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
+
+       feature_interface->attach = attach;
+       feature_interface->request_queued = request_queued;
+       feature_interface->request_started = request_started;
+       feature_interface->request_unqueued = request_unqueued;
 }
 
 static int
@@ -122,33 +156,53 @@ auth_type_compare_func (gconstpointer a, gconstpointer b)
 void
 soup_auth_manager_add_type (SoupAuthManager *manager, GType type)
 {
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
        SoupAuthClass *auth_class;
 
        g_return_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH));
 
        auth_class = g_type_class_ref (type);
-       g_ptr_array_add (manager->auth_types, auth_class);
-       g_ptr_array_sort (manager->auth_types, auth_type_compare_func);
+       g_ptr_array_add (priv->auth_types, auth_class);
+       g_ptr_array_sort (priv->auth_types, auth_type_compare_func);
 }
 
 void
 soup_auth_manager_remove_type (SoupAuthManager *manager, GType type)
 {
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
        SoupAuthClass *auth_class;
        int i;
 
        g_return_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH));
 
        auth_class = g_type_class_peek (type);
-       for (i = 0; i < manager->auth_types->len; i++) {
-               if (manager->auth_types->pdata[i] == (gpointer)auth_class) {
-                       g_ptr_array_remove_index (manager->auth_types, i);
+       for (i = 0; i < priv->auth_types->len; i++) {
+               if (priv->auth_types->pdata[i] == (gpointer)auth_class) {
+                       g_ptr_array_remove_index (priv->auth_types, i);
                        g_type_class_unref (auth_class);
                        return;
                }
        }
 }
 
+void
+soup_auth_manager_emit_authenticate (SoupAuthManager *manager, SoupMessage *msg,
+                                    SoupAuth *auth, gboolean retrying)
+{
+       g_signal_emit (manager, signals[AUTHENTICATE], 0, msg, auth, retrying);
+}
+
+static void
+attach (SoupSessionFeature *manager, SoupSession *session)
+{
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+
+       /* FIXME: should support multiple sessions */
+       priv->session = session;
+
+       soup_session_feature_default_interface->attach (manager, session);
+}
+
 static inline const char *
 auth_header_for_message (SoupMessage *msg)
 {
@@ -219,7 +273,7 @@ extract_challenge (const char *challenges, const char *scheme)
 }
 
 static SoupAuth *
-create_auth (SoupAuthManager *manager, SoupMessage *msg)
+create_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
 {
        const char *header;
        SoupAuthClass *auth_class;
@@ -231,8 +285,8 @@ create_auth (SoupAuthManager *manager, SoupMessage *msg)
        if (!header)
                return NULL;
 
-       for (i = manager->auth_types->len - 1; i >= 0; i--) {
-               auth_class = manager->auth_types->pdata[i];
+       for (i = priv->auth_types->len - 1; i >= 0; i--) {
+               auth_class = priv->auth_types->pdata[i];
                challenge = extract_challenge (header, auth_class->scheme_name);
                if (challenge)
                        break;
@@ -246,7 +300,7 @@ create_auth (SoupAuthManager *manager, SoupMessage *msg)
 }
 
 static gboolean
-check_auth (SoupAuthManager *manager, SoupMessage *msg, SoupAuth *auth)
+check_auth (SoupMessage *msg, SoupAuth *auth)
 {
        const char *header;
        char *challenge;
@@ -266,29 +320,29 @@ check_auth (SoupAuthManager *manager, SoupMessage *msg, SoupAuth *auth)
 }
 
 static SoupAuthHost *
-get_auth_host_for_message (SoupAuthManager *manager, SoupMessage *msg)
+get_auth_host_for_message (SoupAuthManagerPrivate *priv, SoupMessage *msg)
 {
        SoupAuthHost *host;
        SoupURI *source = soup_message_get_uri (msg);
 
-       host = g_hash_table_lookup (manager->auth_hosts, source);
+       host = g_hash_table_lookup (priv->auth_hosts, source);
        if (host)
                return host;
 
        host = g_slice_new0 (SoupAuthHost);
        host->root_uri = soup_uri_copy_root (source);
-       g_hash_table_insert (manager->auth_hosts, host->root_uri, host);
+       g_hash_table_insert (priv->auth_hosts, host->root_uri, host);
 
        return host;
 }
 
 static SoupAuth *
-lookup_auth (SoupAuthManager *manager, SoupMessage *msg)
+lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
 {
        SoupAuthHost *host;
        const char *path, *realm;
 
-       host = get_auth_host_for_message (manager, msg);
+       host = get_auth_host_for_message (priv, msg);
        if (!host->auth_realms)
                return NULL;
 
@@ -307,13 +361,14 @@ authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
                   SoupMessage *msg, gboolean prior_auth_failed,
                   gboolean proxy)
 {
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
        SoupURI *uri;
 
        if (soup_auth_is_authenticated (auth))
                return TRUE;
 
        if (proxy) {
-               g_object_get (G_OBJECT (manager->session),
+               g_object_get (G_OBJECT (priv->session),
                              SOUP_SESSION_PROXY_URI, &uri,
                              NULL);
        } else
@@ -326,15 +381,15 @@ authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
        }
        soup_uri_free (uri);
 
-       soup_session_emit_authenticate (manager->session,
-                                       msg, auth, prior_auth_failed);
+       soup_auth_manager_emit_authenticate (manager, msg, auth,
+                                            prior_auth_failed);
        return soup_auth_is_authenticated (auth);
 }
 
 static void
-update_auth (SoupMessage *msg, gpointer user_data)
+update_auth (SoupMessage *msg, gpointer manager)
 {
-       SoupAuthManager *manager = user_data;
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
        SoupAuthHost *host;
        SoupAuth *auth, *prior_auth, *old_auth;
        const char *path;
@@ -342,16 +397,16 @@ update_auth (SoupMessage *msg, gpointer user_data)
        GSList *pspace, *p;
        gboolean prior_auth_failed = FALSE;
 
-       host = get_auth_host_for_message (manager, msg);
+       host = get_auth_host_for_message (priv, msg);
 
        /* See if we used auth last time */
        prior_auth = soup_message_get_auth (msg);
-       if (prior_auth && check_auth (manager, msg, prior_auth)) {
+       if (prior_auth && check_auth (msg, prior_auth)) {
                auth = prior_auth;
                if (!soup_auth_is_authenticated (auth))
                        prior_auth_failed = TRUE;
        } else {
-               auth = create_auth (manager, msg);
+               auth = create_auth (priv, msg);
                if (!auth)
                        return;
        }
@@ -400,56 +455,54 @@ update_auth (SoupMessage *msg, gpointer user_data)
 }
 
 static void
-requeue_if_authenticated (SoupMessage *msg, gpointer user_data)
+requeue_if_authenticated (SoupMessage *msg, gpointer manager)
 {
-       SoupAuthManager *manager = user_data;
-       SoupAuth *auth = lookup_auth (manager, msg);
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+       SoupAuth *auth = lookup_auth (priv, msg);
 
        if (auth && soup_auth_is_authenticated (auth))
-               soup_session_requeue_message (manager->session, msg);
+               soup_session_requeue_message (priv->session, msg);
 }
 
 static void
-update_proxy_auth (SoupMessage *msg, gpointer user_data)
+update_proxy_auth (SoupMessage *msg, gpointer manager)
 {
-       SoupAuthManager *manager = user_data;
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
        SoupAuth *prior_auth;
        gboolean prior_auth_failed = FALSE;
 
        /* See if we used auth last time */
        prior_auth = soup_message_get_proxy_auth (msg);
-       if (prior_auth && check_auth (manager, msg, prior_auth)) {
+       if (prior_auth && check_auth (msg, prior_auth)) {
                if (!soup_auth_is_authenticated (prior_auth))
                        prior_auth_failed = TRUE;
        }
 
-       if (!manager->proxy_auth) {
-               manager->proxy_auth = create_auth (manager, msg);
-               if (!manager->proxy_auth)
+       if (!priv->proxy_auth) {
+               priv->proxy_auth = create_auth (priv, msg);
+               if (!priv->proxy_auth)
                        return;
        }
 
        /* If we need to authenticate, try to do it. */
-       authenticate_auth (manager, manager->proxy_auth, msg,
+       authenticate_auth (manager, priv->proxy_auth, msg,
                           prior_auth_failed, TRUE);
 }
 
 static void
-requeue_if_proxy_authenticated (SoupMessage *msg, gpointer user_data)
+requeue_if_proxy_authenticated (SoupMessage *msg, gpointer manager)
 {
-       SoupAuthManager *manager = user_data;
-       SoupAuth *auth = manager->proxy_auth;
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+       SoupAuth *auth = priv->proxy_auth;
 
        if (auth && soup_auth_is_authenticated (auth))
-               soup_session_requeue_message (manager->session, msg);
+               soup_session_requeue_message (priv->session, msg);
 }
 
 static void
-session_request_queued (SoupSession *session, SoupMessage *msg,
-                       gpointer data)
+request_queued (SoupSessionFeature *manager, SoupSession *session,
+               SoupMessage *msg)
 {
-       SoupAuthManager *manager = data;
-
        soup_message_add_status_code_handler (
                msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
                G_CALLBACK (update_auth), manager);
@@ -466,29 +519,28 @@ session_request_queued (SoupSession *session, SoupMessage *msg,
 }
 
 static void
-session_request_started (SoupSession *session, SoupMessage *msg,
-                        SoupSocket *socket, gpointer data)
+request_started (SoupSessionFeature *feature, SoupSession *session,
+                SoupMessage *msg, SoupSocket *socket)
 {
-       SoupAuthManager *manager = data;
+       SoupAuthManager *manager = SOUP_AUTH_MANAGER (feature);
+       SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
        SoupAuth *auth;
 
-       auth = lookup_auth (manager, msg);
+       auth = lookup_auth (priv, msg);
        if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE))
                auth = NULL;
        soup_message_set_auth (msg, auth);
 
-       auth = manager->proxy_auth;
+       auth = priv->proxy_auth;
        if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE))
                auth = NULL;
        soup_message_set_proxy_auth (msg, auth);
 }
 
 static void
-session_request_unqueued (SoupSession *session, SoupMessage *msg,
-                         gpointer data)
+request_unqueued (SoupSessionFeature *manager, SoupSession *session,
+                 SoupMessage *msg)
 {
-       SoupAuthManager *manager = data;
-
        g_signal_handlers_disconnect_matched (msg, G_SIGNAL_MATCH_DATA,
                                              0, 0, NULL, NULL, manager);
 }
index 89b7bed..1fc44c4 100644 (file)
 
 G_BEGIN_DECLS
 
-typedef struct SoupAuthManager SoupAuthManager;
+#define SOUP_TYPE_AUTH_MANAGER            (soup_auth_manager_get_type ())
+#define SOUP_AUTH_MANAGER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_MANAGER, SoupAuthManager))
+#define SOUP_AUTH_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerClass))
+#define SOUP_IS_AUTH_MANAGER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_MANAGER))
+#define SOUP_IS_AUTH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_MANAGER))
+#define SOUP_AUTH_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerClass))
 
-SoupAuthManager *soup_auth_manager_new         (SoupSession     *session);
+typedef struct {
+       GObject parent;
 
-void             soup_auth_manager_add_type    (SoupAuthManager *manager,
-                                               GType            type);
-void             soup_auth_manager_remove_type (SoupAuthManager *manager,
-                                               GType            type);
+} SoupAuthManager;
 
-void             soup_auth_manager_free        (SoupAuthManager *manager);
+typedef struct {
+       GObjectClass parent_class;
+
+       void (*authenticate) (SoupAuthManager *manager, SoupMessage *msg,
+                             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,
+                                         GType            type);
+void soup_auth_manager_remove_type       (SoupAuthManager *manager,
+                                         GType            type);
+
+void soup_auth_manager_emit_authenticate (SoupAuthManager *manager,
+                                         SoupMessage     *msg,
+                                         SoupAuth        *auth,
+                                         gboolean         retrying);
 
 G_END_DECLS
 
index 5d3313a..7cbcfaf 100644 (file)
 
 G_BEGIN_DECLS
 
-/* internal methods */
-void              soup_session_emit_authenticate    (SoupSession *session,
-                                                    SoupMessage *msg,
-                                                    SoupAuth    *auth,
-                                                    gboolean     retrying);
-
 /* "protected" methods for subclasses */
 SoupMessageQueue *soup_session_get_queue            (SoupSession *session);
 
index f360987..dfedb78 100644 (file)
@@ -16,7 +16,6 @@
 #include "soup-auth.h"
 #include "soup-auth-basic.h"
 #include "soup-auth-digest.h"
-#include "soup-auth-manager.h"
 #include "soup-auth-manager-ntlm.h"
 #include "soup-connection.h"
 #include "soup-marshal.h"
@@ -78,7 +77,6 @@ typedef struct {
 
        GSList *features;
        SoupAuthManager *auth_manager;
-       SoupAuthManagerNTLM *ntlm_manager;
 
        GHashTable *hosts; /* SoupURI -> SoupSessionHost */
        GHashTable *conns; /* SoupConnection -> SoupSessionHost */
@@ -104,6 +102,10 @@ static void requeue_message (SoupSession *session, SoupMessage *msg);
 static void cancel_message  (SoupSession *session, SoupMessage *msg,
                             guint status_code);
 
+static void auth_manager_authenticate (SoupAuthManager *manager,
+                                      SoupMessage *msg, SoupAuth *auth,
+                                      gboolean retrying, gpointer user_data);
+
 /* temporary until we fix this to index hosts by SoupAddress */
 extern guint     soup_uri_host_hash  (gconstpointer  key);
 extern gboolean  soup_uri_host_equal (gconstpointer  v1,
@@ -166,9 +168,14 @@ soup_session_init (SoupSession *session)
        priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
        priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
 
-       priv->auth_manager = soup_auth_manager_new (session);
+       priv->auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM,
+                                          SOUP_AUTH_MANAGER_NTLM_USE_NTLM, FALSE,
+                                          NULL);
+       g_signal_connect (priv->auth_manager, "authenticate",
+                         G_CALLBACK (auth_manager_authenticate), session);
        soup_auth_manager_add_type (priv->auth_manager, SOUP_TYPE_AUTH_BASIC);
        soup_auth_manager_add_type (priv->auth_manager, SOUP_TYPE_AUTH_DIGEST);
+       soup_session_add_feature (session, SOUP_SESSION_FEATURE (priv->auth_manager));
 }
 
 static gboolean
@@ -222,9 +229,8 @@ finalize (GObject *object)
 
        g_free (priv->user_agent);
 
-       soup_auth_manager_free (priv->auth_manager);
-       if (priv->ntlm_manager)
-               soup_auth_manager_ntlm_free (priv->ntlm_manager);
+       if (priv->auth_manager)
+               g_object_unref (priv->auth_manager);
 
        if (priv->proxy_uri)
                soup_uri_free (priv->proxy_uri);
@@ -564,15 +570,9 @@ set_property (GObject *object, guint prop_id,
                priv->max_conns_per_host = g_value_get_int (value);
                break;
        case PROP_USE_NTLM:
-               if (g_value_get_boolean (value)) {
-                       if (!priv->ntlm_manager)
-                               priv->ntlm_manager = soup_auth_manager_ntlm_new (session);
-               } else {
-                       if (priv->ntlm_manager) {
-                               soup_auth_manager_ntlm_free (priv->ntlm_manager);
-                               priv->ntlm_manager = NULL;
-                       }
-               }
+               g_object_set_property (G_OBJECT (priv->auth_manager),
+                                      SOUP_AUTH_MANAGER_NTLM_USE_NTLM,
+                                      value);
                break;
        case PROP_SSL_CA_FILE:
                new_ca_file = g_value_get_string (value);
@@ -652,7 +652,9 @@ get_property (GObject *object, guint prop_id,
                g_value_set_int (value, priv->max_conns_per_host);
                break;
        case PROP_USE_NTLM:
-               g_value_set_boolean (value, priv->ntlm_manager != NULL);
+               g_object_get_property (G_OBJECT (priv->auth_manager),
+                                      SOUP_AUTH_MANAGER_NTLM_USE_NTLM,
+                                      value);
                break;
        case PROP_SSL_CA_FILE:
                g_value_set_string (value, priv->ssl_ca_file);
@@ -752,9 +754,10 @@ free_host (SoupSessionHost *host)
        g_slice_free (SoupSessionHost, host);
 }      
 
-void
-soup_session_emit_authenticate (SoupSession *session, SoupMessage *msg,
-                               SoupAuth *auth, gboolean 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);
 }