Remove extern "C" wrapping other includes
[platform/upstream/libsoup.git] / libsoup / soup-auth.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-auth.c: HTTP Authentication framework
4  *
5  * Copyright (C) 2001-2003, Ximian, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <string.h>
13
14 #include "soup-auth.h"
15 #include "soup.h"
16 #include "soup-connection-auth.h"
17
18 /**
19  * SECTION:soup-auth
20  * @short_description: HTTP client-side authentication support
21  * @see_also: #SoupSession
22  *
23  * #SoupAuth objects store the authentication data associated with a
24  * given bit of web space. They are created automatically by
25  * #SoupSession.
26  **/
27
28 /**
29  * SoupAuth:
30  *
31  * The abstract base class for handling authentication. Specific HTTP
32  * Authentication mechanisms are implemented by its subclasses, but
33  * applications never need to be aware of the specific subclasses
34  * being used.
35  **/
36
37 typedef struct {
38         gboolean proxy;
39         char *host;
40 } SoupAuthPrivate;
41
42 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SoupAuth, soup_auth, G_TYPE_OBJECT)
43
44 enum {
45         PROP_0,
46
47         PROP_SCHEME_NAME,
48         PROP_REALM,
49         PROP_HOST,
50         PROP_IS_FOR_PROXY,
51         PROP_IS_AUTHENTICATED,
52
53         LAST_PROP
54 };
55
56 static void
57 soup_auth_init (SoupAuth *auth)
58 {
59 }
60
61 static void
62 soup_auth_finalize (GObject *object)
63 {
64         SoupAuth *auth = SOUP_AUTH (object);
65         SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
66
67         g_free (auth->realm);
68         g_free (priv->host);
69
70         G_OBJECT_CLASS (soup_auth_parent_class)->finalize (object);
71 }
72
73 static void
74 soup_auth_set_property (GObject *object, guint prop_id,
75                         const GValue *value, GParamSpec *pspec)
76 {
77         SoupAuth *auth = SOUP_AUTH (object);
78         SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
79
80         switch (prop_id) {
81         case PROP_REALM:
82                 g_free (auth->realm);
83                 auth->realm = g_value_dup_string (value);
84                 break;
85         case PROP_HOST:
86                 g_free (priv->host);
87                 priv->host = g_value_dup_string (value);
88                 break;
89         case PROP_IS_FOR_PROXY:
90                 priv->proxy = g_value_get_boolean (value);
91                 break;
92         default:
93                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
94                 break;
95         }
96 }
97
98 static void
99 soup_auth_get_property (GObject *object, guint prop_id,
100                         GValue *value, GParamSpec *pspec)
101 {
102         SoupAuth *auth = SOUP_AUTH (object);
103         SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
104
105         switch (prop_id) {
106         case PROP_SCHEME_NAME:
107                 g_value_set_string (value, soup_auth_get_scheme_name (auth));
108                 break;
109         case PROP_REALM:
110                 g_value_set_string (value, soup_auth_get_realm (auth));
111                 break;
112         case PROP_HOST:
113                 g_value_set_string (value, soup_auth_get_host (auth));
114                 break;
115         case PROP_IS_FOR_PROXY:
116                 g_value_set_boolean (value, priv->proxy);
117                 break;
118         case PROP_IS_AUTHENTICATED:
119                 g_value_set_boolean (value, soup_auth_is_authenticated (auth));
120                 break;
121         default:
122                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
123                 break;
124         }
125 }
126
127 static gboolean
128 auth_can_authenticate (SoupAuth *auth)
129 {
130         return TRUE;
131 }
132
133 static void
134 soup_auth_class_init (SoupAuthClass *auth_class)
135 {
136         GObjectClass *object_class = G_OBJECT_CLASS (auth_class);
137
138         auth_class->can_authenticate = auth_can_authenticate;
139
140         object_class->finalize     = soup_auth_finalize;
141         object_class->set_property = soup_auth_set_property;
142         object_class->get_property = soup_auth_get_property;
143
144         /* properties */
145         /**
146          * SOUP_AUTH_SCHEME_NAME:
147          *
148          * An alias for the #SoupAuth:scheme-name property. (The
149          * authentication scheme name.)
150          **/
151         g_object_class_install_property (
152                 object_class, PROP_SCHEME_NAME,
153                 g_param_spec_string (SOUP_AUTH_SCHEME_NAME,
154                                      "Scheme name",
155                                      "Authentication scheme name",
156                                      NULL,
157                                      G_PARAM_READABLE |
158                                      G_PARAM_STATIC_STRINGS));
159         /**
160          * SOUP_AUTH_REALM:
161          *
162          * An alias for the #SoupAuth:realm property. (The
163          * authentication realm.)
164          **/
165         g_object_class_install_property (
166                 object_class, PROP_REALM,
167                 g_param_spec_string (SOUP_AUTH_REALM,
168                                      "Realm",
169                                      "Authentication realm",
170                                      NULL,
171                                      G_PARAM_READWRITE |
172                                      G_PARAM_STATIC_STRINGS));
173         /**
174          * SOUP_AUTH_HOST:
175          *
176          * An alias for the #SoupAuth:host property. (The
177          * host being authenticated to.)
178          **/
179         g_object_class_install_property (
180                 object_class, PROP_HOST,
181                 g_param_spec_string (SOUP_AUTH_HOST,
182                                      "Host",
183                                      "Authentication host",
184                                      NULL,
185                                      G_PARAM_READWRITE |
186                                      G_PARAM_STATIC_STRINGS));
187         /**
188          * SOUP_AUTH_IS_FOR_PROXY:
189          *
190          * An alias for the #SoupAuth:is-for-proxy property. (Whether
191          * or not the auth is for a proxy server.)
192          **/
193         g_object_class_install_property (
194                 object_class, PROP_IS_FOR_PROXY,
195                 g_param_spec_boolean (SOUP_AUTH_IS_FOR_PROXY,
196                                       "For Proxy",
197                                       "Whether or not the auth is for a proxy server",
198                                       FALSE,
199                                       G_PARAM_READWRITE |
200                                       G_PARAM_STATIC_STRINGS));
201         /**
202          * SOUP_AUTH_IS_AUTHENTICATED:
203          *
204          * An alias for the #SoupAuth:is-authenticated property.
205          * (Whether or not the auth has been authenticated.)
206          **/
207         g_object_class_install_property (
208                 object_class, PROP_IS_AUTHENTICATED,
209                 g_param_spec_boolean (SOUP_AUTH_IS_AUTHENTICATED,
210                                       "Authenticated",
211                                       "Whether or not the auth is authenticated",
212                                       FALSE,
213                                       G_PARAM_READABLE |
214                                       G_PARAM_STATIC_STRINGS));
215 }
216
217 /**
218  * soup_auth_new:
219  * @type: the type of auth to create (a subtype of #SoupAuth)
220  * @msg: the #SoupMessage the auth is being created for
221  * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
222  *
223  * Creates a new #SoupAuth of type @type with the information from
224  * @msg and @auth_header.
225  *
226  * This is called by #SoupSession; you will normally not create auths
227  * yourself.
228  *
229  * Return value: (nullable): the new #SoupAuth, or %NULL if it could
230  * not be created
231  **/
232 SoupAuth *
233 soup_auth_new (GType type, SoupMessage *msg, const char *auth_header)
234 {
235         SoupAuth *auth;
236         GHashTable *params;
237         const char *scheme, *realm;
238
239         g_return_val_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH), NULL);
240         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
241         g_return_val_if_fail (auth_header != NULL, NULL);
242
243         auth = g_object_new (type,
244                              SOUP_AUTH_IS_FOR_PROXY, (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED),
245                              SOUP_AUTH_HOST, soup_message_get_uri (msg)->host,
246                              NULL);
247
248         scheme = soup_auth_get_scheme_name (auth);
249         if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0) {
250                 g_object_unref (auth);
251                 return NULL;
252         }
253
254         params = soup_header_parse_param_list (auth_header + strlen (scheme));
255         if (!params)
256                 params = g_hash_table_new (NULL, NULL);
257
258         realm = g_hash_table_lookup (params, "realm");
259         if (realm)
260                 auth->realm = g_strdup (realm);
261
262         if (!SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params)) {
263                 g_object_unref (auth);
264                 auth = NULL;
265         }
266         soup_header_free_param_list (params);
267         return auth;
268 }
269
270 /**
271  * soup_auth_update:
272  * @auth: a #SoupAuth
273  * @msg: the #SoupMessage @auth is being updated for
274  * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
275  *
276  * Updates @auth with the information from @msg and @auth_header,
277  * possibly un-authenticating it. As with soup_auth_new(), this is
278  * normally only used by #SoupSession.
279  *
280  * Return value: %TRUE if @auth is still a valid (but potentially
281  * unauthenticated) #SoupAuth. %FALSE if something about @auth_params
282  * could not be parsed or incorporated into @auth at all.
283  **/
284 gboolean
285 soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
286 {
287         GHashTable *params;
288         const char *scheme, *realm;
289         gboolean was_authenticated, success;
290
291         g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
292         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
293         g_return_val_if_fail (auth_header != NULL, FALSE);
294
295         scheme = soup_auth_get_scheme_name (auth);
296         if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0)
297                 return FALSE;
298
299         params = soup_header_parse_param_list (auth_header + strlen (scheme));
300         if (!params)
301                 params = g_hash_table_new (NULL, NULL);
302
303         realm = g_hash_table_lookup (params, "realm");
304         if (realm && auth->realm && strcmp (realm, auth->realm) != 0) {
305                 soup_header_free_param_list (params);
306                 return FALSE;
307         }
308
309         was_authenticated = soup_auth_is_authenticated (auth);
310         success = SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params);
311         if (was_authenticated != soup_auth_is_authenticated (auth))
312                 g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
313         soup_header_free_param_list (params);
314         return success;
315 }
316
317 /**
318  * soup_auth_authenticate:
319  * @auth: a #SoupAuth
320  * @username: the username provided by the user or client
321  * @password: the password provided by the user or client
322  *
323  * Call this on an auth to authenticate it; normally this will cause
324  * the auth's message to be requeued with the new authentication info.
325  **/
326 void
327 soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
328 {
329         gboolean was_authenticated;
330
331         g_return_if_fail (SOUP_IS_AUTH (auth));
332         g_return_if_fail (username != NULL);
333         g_return_if_fail (password != NULL);
334
335         was_authenticated = soup_auth_is_authenticated (auth);
336         SOUP_AUTH_GET_CLASS (auth)->authenticate (auth, username, password);
337         if (was_authenticated != soup_auth_is_authenticated (auth))
338                 g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
339 }
340
341 /**
342  * soup_auth_is_for_proxy:
343  * @auth: a #SoupAuth
344  *
345  * Tests whether or not @auth is associated with a proxy server rather
346  * than an "origin" server.
347  *
348  * Return value: %TRUE or %FALSE
349  **/
350 gboolean
351 soup_auth_is_for_proxy (SoupAuth *auth)
352 {
353         SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
354
355         g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
356
357         return priv->proxy;
358 }
359
360 /**
361  * soup_auth_get_scheme_name:
362  * @auth: a #SoupAuth
363  *
364  * Returns @auth's scheme name. (Eg, "Basic", "Digest", or "NTLM")
365  *
366  * Return value: the scheme name
367  **/
368 const char *
369 soup_auth_get_scheme_name (SoupAuth *auth)
370 {
371         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
372
373         return SOUP_AUTH_GET_CLASS (auth)->scheme_name;
374 }
375
376 /**
377  * soup_auth_get_host:
378  * @auth: a #SoupAuth
379  *
380  * Returns the host that @auth is associated with.
381  *
382  * Return value: the hostname
383  **/
384 const char *
385 soup_auth_get_host (SoupAuth *auth)
386 {
387         SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
388
389         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
390
391         return priv->host;
392 }
393
394 /**
395  * soup_auth_get_realm:
396  * @auth: a #SoupAuth
397  *
398  * Returns @auth's realm. This is an identifier that distinguishes
399  * separate authentication spaces on a given server, and may be some
400  * string that is meaningful to the user. (Although it is probably not
401  * localized.)
402  *
403  * Return value: the realm name
404  **/
405 const char *
406 soup_auth_get_realm (SoupAuth *auth)
407 {
408         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
409
410         return auth->realm;
411 }
412
413 /**
414  * soup_auth_get_info:
415  * @auth: a #SoupAuth
416  *
417  * Gets an opaque identifier for @auth, for use as a hash key or the
418  * like. #SoupAuth objects from the same server with the same
419  * identifier refer to the same authentication domain (eg, the URLs
420  * associated with them take the same usernames and passwords).
421  *
422  * Return value: the identifier
423  **/
424 char *
425 soup_auth_get_info (SoupAuth *auth)
426 {
427         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
428
429         if (SOUP_IS_CONNECTION_AUTH (auth))
430                 return g_strdup (SOUP_AUTH_GET_CLASS (auth)->scheme_name);
431         else {
432                 return g_strdup_printf ("%s:%s",
433                                         SOUP_AUTH_GET_CLASS (auth)->scheme_name,
434                                         auth->realm);
435         }
436 }
437
438 /**
439  * soup_auth_is_authenticated:
440  * @auth: a #SoupAuth
441  *
442  * Tests if @auth has been given a username and password
443  *
444  * Return value: %TRUE if @auth has been given a username and password
445  **/
446 gboolean
447 soup_auth_is_authenticated (SoupAuth *auth)
448 {
449         g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
450
451         return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
452 }
453
454 /**
455  * soup_auth_get_authorization:
456  * @auth: a #SoupAuth
457  * @msg: the #SoupMessage to be authorized
458  *
459  * Generates an appropriate "Authorization" header for @msg. (The
460  * session will only call this if soup_auth_is_authenticated()
461  * returned %TRUE.)
462  *
463  * Return value: the "Authorization" header, which must be freed.
464  **/
465 char *
466 soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
467 {
468         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
469         g_return_val_if_fail (msg != NULL, NULL);
470
471         return SOUP_AUTH_GET_CLASS (auth)->get_authorization (auth, msg);
472 }
473
474 /**
475  * soup_auth_is_ready:
476  * @auth: a #SoupAuth
477  * @msg: a #SoupMessage
478  *
479  * Tests if @auth is ready to make a request for @msg with. For most
480  * auths, this is equivalent to soup_auth_is_authenticated(), but for
481  * some auth types (eg, NTLM), the auth may be sendable (eg, as an
482  * authentication request) even before it is authenticated.
483  *
484  * Return value: %TRUE if @auth is ready to make a request with.
485  *
486  * Since: 2.42
487  **/
488 gboolean
489 soup_auth_is_ready (SoupAuth    *auth,
490                     SoupMessage *msg)
491 {
492         g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
493         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), TRUE);
494
495         if (SOUP_AUTH_GET_CLASS (auth)->is_ready)
496                 return SOUP_AUTH_GET_CLASS (auth)->is_ready (auth, msg);
497         else
498                 return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
499 }
500
501 /**
502  * soup_auth_can_authenticate:
503  * @auth: a #SoupAuth
504  *
505  * Tests if @auth is able to authenticate by providing credentials to the
506  * soup_auth_authenticate().
507  *
508  * Return value: %TRUE if @auth is able to accept credentials.
509  *
510  * Since: 2.54
511  **/
512 gboolean
513 soup_auth_can_authenticate (SoupAuth *auth)
514 {
515         g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
516
517         return SOUP_AUTH_GET_CLASS (auth)->can_authenticate (auth);
518 }
519
520 /**
521  * soup_auth_get_protection_space:
522  * @auth: a #SoupAuth
523  * @source_uri: the URI of the request that @auth was generated in
524  * response to.
525  *
526  * Returns a list of paths on the server which @auth extends over.
527  * (All subdirectories of these paths are also assumed to be part
528  * of @auth's protection space, unless otherwise discovered not to
529  * be.)
530  *
531  * Return value: (element-type utf8) (transfer full): the list of
532  * paths, which can be freed with soup_auth_free_protection_space().
533  **/
534 GSList *
535 soup_auth_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
536 {
537         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
538         g_return_val_if_fail (source_uri != NULL, NULL);
539
540         return SOUP_AUTH_GET_CLASS (auth)->get_protection_space (auth, source_uri);
541 }
542
543 /**
544  * soup_auth_free_protection_space: (skip)
545  * @auth: a #SoupAuth
546  * @space: the return value from soup_auth_get_protection_space()
547  *
548  * Frees @space.
549  **/
550 void
551 soup_auth_free_protection_space (SoupAuth *auth, GSList *space)
552 {
553         g_slist_free_full (space, g_free);
554 }
555
556 /**
557  * soup_auth_get_saved_users:
558  *
559  * Return value: (transfer full) (element-type utf8):
560  */
561 GSList *
562 soup_auth_get_saved_users (SoupAuth *auth)
563 {
564         return NULL;
565 }
566
567 const char *
568 soup_auth_get_saved_password (SoupAuth *auth, const char *user)
569 {
570         return NULL;
571 }
572
573 void
574 soup_auth_has_saved_password (SoupAuth *auth, const char *username,
575                               const char *password)
576 {
577 }
578
579 void
580 soup_auth_save_password (SoupAuth *auth, const char *username,
581                          const char *password)
582 {
583 }