soup-auth-manager: add soup_auth_manager_use_auth()
[platform/upstream/libsoup.git] / libsoup / soup-connection-auth.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-connection-auth.c: Abstract base class for hacky Microsoft
4  * connection-based auth mechanisms (NTLM and Negotiate)
5  *
6  * Copyright (C) 2010 Red Hat, Inc.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <ctype.h>
14 #include <string.h>
15
16 #include "soup-connection-auth.h"
17 #include "soup.h"
18 #include "soup-connection.h"
19 #include "soup-message-private.h"
20
21 G_DEFINE_ABSTRACT_TYPE (SoupConnectionAuth, soup_connection_auth, SOUP_TYPE_AUTH)
22
23 struct SoupConnectionAuthPrivate {
24         GHashTable *conns;
25 };
26
27 static void
28 soup_connection_auth_init (SoupConnectionAuth *auth)
29 {
30         auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (auth, SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuthPrivate);
31
32         auth->priv->conns = g_hash_table_new (NULL, NULL);
33 }
34
35 static void connection_disconnected (SoupConnection *conn, gpointer user_data);
36
37 static void
38 soup_connection_auth_free_connection_state (SoupConnectionAuth *auth,
39                                             SoupConnection     *conn,
40                                             gpointer            state)
41 {
42         g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_disconnected), auth);
43         SOUP_CONNECTION_AUTH_GET_CLASS (auth)->free_connection_state (auth, state);
44 }
45
46 static void
47 connection_disconnected (SoupConnection *conn, gpointer user_data)
48 {
49         SoupConnectionAuth *auth = user_data;
50         gpointer state;
51
52         state = g_hash_table_lookup (auth->priv->conns, conn);
53         g_hash_table_remove (auth->priv->conns, conn);
54         soup_connection_auth_free_connection_state (auth, conn, state);
55 }
56
57 static void
58 soup_connection_auth_finalize (GObject *object)
59 {
60         SoupConnectionAuth *auth = SOUP_CONNECTION_AUTH (object);
61         GHashTableIter iter;
62         gpointer conn, state;
63
64         g_hash_table_iter_init (&iter, auth->priv->conns);
65         while (g_hash_table_iter_next (&iter, &conn, &state)) {
66                 soup_connection_auth_free_connection_state (auth, conn, state);
67                 g_hash_table_iter_remove (&iter);
68         }
69         g_hash_table_destroy (auth->priv->conns);
70
71         G_OBJECT_CLASS (soup_connection_auth_parent_class)->finalize (object);
72 }
73
74 static gpointer
75 get_connection_state_for_message (SoupConnectionAuth *auth, SoupMessage *msg)
76 {
77         SoupConnection *conn;
78         gpointer state;
79
80         conn = soup_message_get_connection (msg);
81         state = g_hash_table_lookup (auth->priv->conns, conn);
82         if (state)
83                 return state;
84
85         state = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->create_connection_state (auth);
86         if (conn) {
87                 g_signal_connect (conn, "disconnected",
88                                   G_CALLBACK (connection_disconnected), auth);
89         }
90
91         g_hash_table_insert (auth->priv->conns, conn, state);
92         return state;
93 }
94
95 static gboolean
96 soup_connection_auth_update (SoupAuth    *auth,
97                              SoupMessage *msg,
98                              GHashTable  *auth_params)
99 {
100         SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
101         gpointer conn = get_connection_state_for_message (cauth, msg);
102         GHashTableIter iter;
103         GString *auth_header;
104         gpointer key, value;
105         gboolean result;
106
107         /* Recreate @auth_header out of @auth_params. If the
108          * base64 data ended with 1 or more "="s, then it
109          * will have been parsed as key=value. Otherwise
110          * it will all have been parsed as key, and value
111          * will be %NULL.
112          */
113         auth_header = g_string_new (soup_auth_get_scheme_name (auth));
114         g_hash_table_iter_init (&iter, auth_params);
115         if (g_hash_table_iter_next (&iter, &key, &value)) {
116                 if (value) {
117                         g_string_append_printf (auth_header, " %s=%s",
118                                                 (char *)key,
119                                                 (char *)value);
120                 } else {
121                         g_string_append_printf (auth_header, " %s",
122                                                 (char *)key);
123                 }
124
125                 if (g_hash_table_iter_next (&iter, &key, &value)) {
126                         g_string_free (auth_header, TRUE);
127                         return FALSE;
128                 }
129         }
130
131         result = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
132                 update_connection (cauth, msg, auth_header->str, conn);
133
134         g_string_free (auth_header, TRUE);
135         return result;
136 }
137
138 static char *
139 soup_connection_auth_get_authorization (SoupAuth    *auth,
140                                         SoupMessage *msg)
141 {
142         SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
143         gpointer conn = get_connection_state_for_message (cauth, msg);
144
145         return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
146                 get_connection_authorization (cauth, msg, conn);
147 }
148
149 static gboolean
150 soup_connection_auth_is_ready (SoupAuth    *auth,
151                                SoupMessage *msg)
152 {
153         SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
154         gpointer conn = get_connection_state_for_message (cauth, msg);
155
156         return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
157                 is_connection_ready (SOUP_CONNECTION_AUTH (auth), msg, conn);
158 }
159
160 static void
161 soup_connection_auth_class_init (SoupConnectionAuthClass *connauth_class)
162 {
163         SoupAuthClass *auth_class = SOUP_AUTH_CLASS (connauth_class);
164         GObjectClass *object_class = G_OBJECT_CLASS (connauth_class);
165
166         g_type_class_add_private (connauth_class, sizeof (SoupConnectionAuthPrivate));
167
168         auth_class->update = soup_connection_auth_update;
169         auth_class->get_authorization = soup_connection_auth_get_authorization;
170         auth_class->is_ready = soup_connection_auth_is_ready;
171
172         object_class->finalize = soup_connection_auth_finalize;
173 }