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 struct SoupConnectionAuthPrivate {
22 GHashTable *conns;
23 };
24
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(SoupConnectionAuth,soup_connection_auth,SOUP_TYPE_AUTH)25 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SoupConnectionAuth, soup_connection_auth, SOUP_TYPE_AUTH)
26
27 static void
28 soup_connection_auth_init (SoupConnectionAuth *auth)
29 {
30 auth->priv = soup_connection_auth_get_instance_private (auth);
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
soup_connection_auth_free_connection_state(SoupConnectionAuth * auth,SoupConnection * conn,gpointer state)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
connection_disconnected(SoupConnection * conn,gpointer user_data)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
soup_connection_auth_finalize(GObject * object)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
75 /**
76 * soup_connection_auth_get_connection_state_for_message:
77 * @auth: a #SoupConnectionAuth
78 * @msg: a #SoupMessage
79 *
80 * Returns an associated connection state object for the given @auth and @msg.
81 *
82 * This function is only useful from within implementations of SoupConnectionAuth
83 * subclasses.
84 *
85 * Return value: (transfer none): the connection state
86 *
87 * Since: 2.58
88 **/
89 gpointer
soup_connection_auth_get_connection_state_for_message(SoupConnectionAuth * auth,SoupMessage * msg)90 soup_connection_auth_get_connection_state_for_message (SoupConnectionAuth *auth,
91 SoupMessage *msg)
92 {
93 SoupConnection *conn;
94 gpointer state;
95
96 g_return_val_if_fail (SOUP_IS_CONNECTION_AUTH (auth), NULL);
97 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
98
99 conn = soup_message_get_connection (msg);
100 state = g_hash_table_lookup (auth->priv->conns, conn);
101 if (state)
102 return state;
103
104 state = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->create_connection_state (auth);
105 if (conn) {
106 g_signal_connect (conn, "disconnected",
107 G_CALLBACK (connection_disconnected), auth);
108 }
109
110 g_hash_table_insert (auth->priv->conns, conn, state);
111 return state;
112 }
113
114 static gboolean
soup_connection_auth_update(SoupAuth * auth,SoupMessage * msg,GHashTable * auth_params)115 soup_connection_auth_update (SoupAuth *auth,
116 SoupMessage *msg,
117 GHashTable *auth_params)
118 {
119 SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
120 gpointer conn = soup_connection_auth_get_connection_state_for_message (cauth, msg);
121 GHashTableIter iter;
122 GString *auth_header;
123 gpointer key, value;
124 gboolean result;
125
126 /* Recreate @auth_header out of @auth_params. If the
127 * base64 data ended with 1 or more "="s, then it
128 * will have been parsed as key=value. Otherwise
129 * it will all have been parsed as key, and value
130 * will be %NULL.
131 */
132 auth_header = g_string_new (soup_auth_get_scheme_name (auth));
133 g_hash_table_iter_init (&iter, auth_params);
134 if (g_hash_table_iter_next (&iter, &key, &value)) {
135 if (value) {
136 g_string_append_printf (auth_header, " %s=%s",
137 (char *)key,
138 (char *)value);
139 } else {
140 g_string_append_printf (auth_header, " %s",
141 (char *)key);
142 }
143
144 if (g_hash_table_iter_next (&iter, &key, &value)) {
145 g_string_free (auth_header, TRUE);
146 return FALSE;
147 }
148 }
149
150 result = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
151 update_connection (cauth, msg, auth_header->str, conn);
152
153 g_string_free (auth_header, TRUE);
154 return result;
155 }
156
157 static char *
soup_connection_auth_get_authorization(SoupAuth * auth,SoupMessage * msg)158 soup_connection_auth_get_authorization (SoupAuth *auth,
159 SoupMessage *msg)
160 {
161 SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
162 gpointer conn = soup_connection_auth_get_connection_state_for_message (cauth, msg);
163
164 return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
165 get_connection_authorization (cauth, msg, conn);
166 }
167
168 static gboolean
soup_connection_auth_is_ready(SoupAuth * auth,SoupMessage * msg)169 soup_connection_auth_is_ready (SoupAuth *auth,
170 SoupMessage *msg)
171 {
172 SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
173 gpointer conn = soup_connection_auth_get_connection_state_for_message (cauth, msg);
174
175 return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
176 is_connection_ready (SOUP_CONNECTION_AUTH (auth), msg, conn);
177 }
178
179 static void
soup_connection_auth_class_init(SoupConnectionAuthClass * connauth_class)180 soup_connection_auth_class_init (SoupConnectionAuthClass *connauth_class)
181 {
182 SoupAuthClass *auth_class = SOUP_AUTH_CLASS (connauth_class);
183 GObjectClass *object_class = G_OBJECT_CLASS (connauth_class);
184
185 auth_class->update = soup_connection_auth_update;
186 auth_class->get_authorization = soup_connection_auth_get_authorization;
187 auth_class->is_ready = soup_connection_auth_is_ready;
188
189 object_class->finalize = soup_connection_auth_finalize;
190 }
191