• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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