Git init
[profile/ivi/libsoup2.4.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 #define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
13
14 #include <string.h>
15
16 #include "soup-auth.h"
17 #include "soup-auth-basic.h"
18 #include "soup-auth-digest.h"
19 #include "soup-headers.h"
20 #include "soup-marshal.h"
21 #include "soup-uri.h"
22
23 /**
24  * SECTION:soup-auth
25  * @short_description: HTTP client-side authentication support
26  * @see_also: #SoupSession
27  *
28  * #SoupAuth objects store the authentication data associated with a
29  * given bit of web space. They are created automatically by
30  * #SoupSession.
31  **/
32
33 /**
34  * SoupAuth:
35  *
36  * The abstract base class for handling authentication. Specific HTTP
37  * Authentication mechanisms are implemented by its subclasses, but
38  * applications never need to be aware of the specific subclasses
39  * being used.
40  **/
41
42 typedef struct {
43         gboolean proxy;
44         char *host;
45
46         GHashTable *saved_passwords;
47 } SoupAuthPrivate;
48 #define SOUP_AUTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH, SoupAuthPrivate))
49
50 G_DEFINE_ABSTRACT_TYPE (SoupAuth, soup_auth, G_TYPE_OBJECT)
51
52 enum {
53         SAVE_PASSWORD,
54         LAST_SIGNAL
55 };
56
57 static guint signals[LAST_SIGNAL] = { 0 };
58
59 enum {
60         PROP_0,
61
62         PROP_SCHEME_NAME,
63         PROP_REALM,
64         PROP_HOST,
65         PROP_IS_FOR_PROXY,
66         PROP_IS_AUTHENTICATED,
67
68         LAST_PROP
69 };
70
71 static void set_property (GObject *object, guint prop_id,
72                           const GValue *value, GParamSpec *pspec);
73 static void get_property (GObject *object, guint prop_id,
74                           GValue *value, GParamSpec *pspec);
75
76 static void
77 finalize (GObject *object)
78 {
79         SoupAuth *auth = SOUP_AUTH (object);
80         SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (auth);
81
82         g_free (auth->realm);
83         g_free (priv->host);
84         if (priv->saved_passwords)
85                 g_hash_table_destroy (priv->saved_passwords);
86
87         G_OBJECT_CLASS (soup_auth_parent_class)->finalize (object);
88 }
89
90 static void
91 soup_auth_class_init (SoupAuthClass *auth_class)
92 {
93         GObjectClass *object_class = G_OBJECT_CLASS (auth_class);
94
95         g_type_class_add_private (auth_class, sizeof (SoupAuthPrivate));
96
97         object_class->finalize = finalize;
98         object_class->set_property = set_property;
99         object_class->get_property = get_property;
100
101         /**
102          * SoupAuth::save-password:
103          * @auth: the auth
104          * @username: the username to save
105          * @password: the password to save
106          *
107          * Emitted to request that the @username/@password pair be
108          * saved. If the session supports password-saving, it will
109          * connect to this signal before emitting
110          * #SoupSession::authenticate, so that it record the password
111          * if requested by the caller.
112          *
113          * Since: 2.28
114          **/
115         signals[SAVE_PASSWORD] =
116                 g_signal_new ("save-password",
117                               G_OBJECT_CLASS_TYPE (object_class),
118                               G_SIGNAL_RUN_FIRST,
119                               0, NULL, NULL,
120                               soup_marshal_NONE__STRING_STRING,
121                               G_TYPE_NONE, 2,
122                               G_TYPE_STRING,
123                               G_TYPE_STRING);
124
125         /* properties */
126         /**
127          * SOUP_AUTH_SCHEME_NAME:
128          *
129          * An alias for the #SoupAuth:scheme property. (The
130          * authentication scheme name.)
131          **/
132         g_object_class_install_property (
133                 object_class, PROP_SCHEME_NAME,
134                 g_param_spec_string (SOUP_AUTH_SCHEME_NAME,
135                                      "Scheme name",
136                                      "Authentication scheme name",
137                                      NULL,
138                                      G_PARAM_READABLE));
139         /**
140          * SOUP_AUTH_REALM:
141          *
142          * An alias for the #SoupAuth:realm property. (The
143          * authentication realm.)
144          **/
145         g_object_class_install_property (
146                 object_class, PROP_REALM,
147                 g_param_spec_string (SOUP_AUTH_REALM,
148                                      "Realm",
149                                      "Authentication realm",
150                                      NULL,
151                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
152         /**
153          * SOUP_AUTH_HOST:
154          *
155          * An alias for the #SoupAuth:host property. (The
156          * host being authenticated to.)
157          **/
158         g_object_class_install_property (
159                 object_class, PROP_HOST,
160                 g_param_spec_string (SOUP_AUTH_HOST,
161                                      "Host",
162                                      "Authentication host",
163                                      NULL,
164                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
165         /**
166          * SOUP_AUTH_IS_FOR_PROXY:
167          *
168          * An alias for the #SoupAuth:is-for-proxy property. (Whether
169          * or not the auth is for a proxy server.)
170          **/
171         g_object_class_install_property (
172                 object_class, PROP_IS_FOR_PROXY,
173                 g_param_spec_boolean (SOUP_AUTH_IS_FOR_PROXY,
174                                       "For Proxy",
175                                       "Whether or not the auth is for a proxy server",
176                                       FALSE,
177                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
178         /**
179          * SOUP_AUTH_IS_AUTHENTICATED:
180          *
181          * An alias for the #SoupAuth:is-authenticated property.
182          * (Whether or not the auth has been authenticated.)
183          **/
184         g_object_class_install_property (
185                 object_class, PROP_IS_AUTHENTICATED,
186                 g_param_spec_boolean (SOUP_AUTH_IS_AUTHENTICATED,
187                                       "Authenticated",
188                                       "Whether or not the auth is authenticated",
189                                       FALSE,
190                                       G_PARAM_READABLE));
191 }
192
193 static void
194 soup_auth_init (SoupAuth *auth)
195 {
196 }
197
198 static void
199 set_property (GObject *object, guint prop_id,
200               const GValue *value, GParamSpec *pspec)
201 {
202         SoupAuth *auth = SOUP_AUTH (object);
203         SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (object);
204
205         switch (prop_id) {
206         case PROP_REALM:
207                 auth->realm = g_value_dup_string (value);
208                 break;
209         case PROP_HOST:
210                 priv->host = g_value_dup_string (value);
211                 break;
212         case PROP_IS_FOR_PROXY:
213                 priv->proxy = g_value_get_boolean (value);
214                 break;
215         default:
216                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
217                 break;
218         }
219 }
220
221 static void
222 get_property (GObject *object, guint prop_id,
223               GValue *value, GParamSpec *pspec)
224 {
225         SoupAuth *auth = SOUP_AUTH (object);
226         SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (object);
227
228         switch (prop_id) {
229         case PROP_SCHEME_NAME:
230                 g_value_set_string (value, soup_auth_get_scheme_name (auth));
231                 break;
232         case PROP_REALM:
233                 g_value_set_string (value, soup_auth_get_realm (auth));
234                 break;
235         case PROP_HOST:
236                 g_value_set_string (value, soup_auth_get_host (auth));
237                 break;
238         case PROP_IS_FOR_PROXY:
239                 g_value_set_boolean (value, priv->proxy);
240                 break;
241         case PROP_IS_AUTHENTICATED:
242                 g_value_set_boolean (value, soup_auth_is_authenticated (auth));
243                 break;
244         default:
245                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246                 break;
247         }
248 }
249
250 /**
251  * soup_auth_new:
252  * @type: the type of auth to create (a subtype of #SoupAuth)
253  * @msg: the #SoupMessage the auth is being created for
254  * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
255  *
256  * Creates a new #SoupAuth of type @type with the information from
257  * @msg and @auth_header.
258  *
259  * This is called by #SoupSession; you will normally not create auths
260  * yourself.
261  *
262  * Return value: the new #SoupAuth, or %NULL if it could not be
263  * created
264  **/
265 SoupAuth *
266 soup_auth_new (GType type, SoupMessage *msg, const char *auth_header)
267 {
268         SoupAuth *auth;
269         GHashTable *params;
270         const char *scheme, *realm;
271
272         g_return_val_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH), NULL);
273         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
274         g_return_val_if_fail (auth_header != NULL, NULL);
275
276         auth = g_object_new (type,
277                              SOUP_AUTH_IS_FOR_PROXY, (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED),
278                              SOUP_AUTH_HOST, soup_message_get_uri (msg)->host,
279                              NULL);
280
281         scheme = soup_auth_get_scheme_name (auth);
282         if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0) {
283                 g_object_unref (auth);
284                 return NULL;
285         }
286
287         params = soup_header_parse_param_list (auth_header + strlen (scheme));
288         if (!params) {
289                 g_object_unref (auth);
290                 return NULL;
291         }
292
293         realm = g_hash_table_lookup (params, "realm");
294         if (!realm) {
295                 soup_header_free_param_list (params);
296                 g_object_unref (auth);
297                 return NULL;
298         }
299
300         auth->realm = g_strdup (realm);
301
302         if (!SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params)) {
303                 g_object_unref (auth);
304                 auth = NULL;
305         }
306         soup_header_free_param_list (params);
307         return auth;
308 }
309
310 /**
311  * soup_auth_update:
312  * @auth: a #SoupAuth
313  * @msg: the #SoupMessage @auth is being updated for
314  * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
315  *
316  * Updates @auth with the information from @msg and @auth_header,
317  * possibly un-authenticating it. As with soup_auth_new(), this is
318  * normally only used by #SoupSession.
319  *
320  * Return value: %TRUE if @auth is still a valid (but potentially
321  * unauthenticated) #SoupAuth. %FALSE if something about @auth_params
322  * could not be parsed or incorporated into @auth at all.
323  **/
324 gboolean
325 soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
326 {
327         GHashTable *params;
328         const char *scheme, *realm;
329         gboolean was_authenticated, success;
330
331         g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
332         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
333         g_return_val_if_fail (auth_header != NULL, FALSE);
334
335         scheme = soup_auth_get_scheme_name (auth);
336         if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0)
337                 return FALSE;
338
339         params = soup_header_parse_param_list (auth_header + strlen (scheme));
340         if (!params)
341                 return FALSE;
342
343         realm = g_hash_table_lookup (params, "realm");
344         if (!realm || strcmp (realm, auth->realm) != 0) {
345                 soup_header_free_param_list (params);
346                 return FALSE;
347         }
348
349         was_authenticated = soup_auth_is_authenticated (auth);
350         success = SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params);
351         if (was_authenticated != soup_auth_is_authenticated (auth))
352                 g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
353         soup_header_free_param_list (params);
354         return success;
355 }
356
357 /**
358  * soup_auth_authenticate:
359  * @auth: a #SoupAuth
360  * @username: the username provided by the user or client
361  * @password: the password provided by the user or client
362  *
363  * Call this on an auth to authenticate it; normally this will cause
364  * the auth's message to be requeued with the new authentication info.
365  *
366  * This does not cause the password to be saved to persistent storage;
367  * see soup_auth_save_password() for that.
368  **/
369 void
370 soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
371 {
372         gboolean was_authenticated;
373
374         g_return_if_fail (SOUP_IS_AUTH (auth));
375         g_return_if_fail (username != NULL);
376         g_return_if_fail (password != NULL);
377
378         was_authenticated = soup_auth_is_authenticated (auth);
379         SOUP_AUTH_GET_CLASS (auth)->authenticate (auth, username, password);
380         if (was_authenticated != soup_auth_is_authenticated (auth))
381                 g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
382 }
383
384 /**
385  * soup_auth_is_for_proxy:
386  * @auth: a #SoupAuth
387  *
388  * Tests whether or not @auth is associated with a proxy server rather
389  * than an "origin" server.
390  *
391  * Return value: %TRUE or %FALSE
392  **/
393 gboolean
394 soup_auth_is_for_proxy (SoupAuth *auth)
395 {
396         g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
397
398         return SOUP_AUTH_GET_PRIVATE (auth)->proxy;
399 }
400
401 /**
402  * soup_auth_get_scheme_name:
403  * @auth: a #SoupAuth
404  *
405  * Returns @auth's scheme name. (Eg, "Basic", "Digest", or "NTLM")
406  *
407  * Return value: the scheme name
408  **/
409 const char *
410 soup_auth_get_scheme_name (SoupAuth *auth)
411 {
412         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
413
414         return SOUP_AUTH_GET_CLASS (auth)->scheme_name;
415 }
416
417 /**
418  * soup_auth_get_host:
419  * @auth: a #SoupAuth
420  *
421  * Returns the host that @auth is associated with.
422  *
423  * Return value: the hostname
424  **/
425 const char *
426 soup_auth_get_host (SoupAuth *auth)
427 {
428         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
429
430         return SOUP_AUTH_GET_PRIVATE (auth)->host;
431 }
432
433
434 /**
435  * soup_auth_get_realm:
436  * @auth: a #SoupAuth
437  *
438  * Returns @auth's realm. This is an identifier that distinguishes
439  * separate authentication spaces on a given server, and may be some
440  * string that is meaningful to the user. (Although it is probably not
441  * localized.)
442  *
443  * Return value: the realm name
444  **/
445 const char *
446 soup_auth_get_realm (SoupAuth *auth)
447 {
448         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
449
450         return auth->realm;
451 }
452
453 /**
454  * soup_auth_get_info:
455  * @auth: a #SoupAuth
456  *
457  * Gets an opaque identifier for @auth, for use as a hash key or the
458  * like. #SoupAuth objects from the same server with the same
459  * identifier refer to the same authentication domain (eg, the URLs
460  * associated with them take the same usernames and passwords).
461  *
462  * Return value: the identifier
463  **/
464 char *
465 soup_auth_get_info (SoupAuth *auth)
466 {
467         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
468
469         return g_strdup_printf ("%s:%s",
470                                 SOUP_AUTH_GET_CLASS (auth)->scheme_name,
471                                 auth->realm);
472 }
473
474 /**
475  * soup_auth_is_authenticated:
476  * @auth: a #SoupAuth
477  *
478  * Tests if @auth has been given a username and password
479  *
480  * Return value: %TRUE if @auth has been given a username and password
481  **/
482 gboolean
483 soup_auth_is_authenticated (SoupAuth *auth)
484 {
485         g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
486
487         return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
488 }
489
490 /**
491  * soup_auth_get_authorization:
492  * @auth: a #SoupAuth
493  * @msg: the #SoupMessage to be authorized
494  *
495  * Generates an appropriate "Authorization" header for @msg. (The
496  * session will only call this if soup_auth_is_authenticated()
497  * returned %TRUE.)
498  *
499  * Return value: the "Authorization" header, which must be freed.
500  **/
501 char *
502 soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
503 {
504         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
505         g_return_val_if_fail (msg != NULL, NULL);
506
507         return SOUP_AUTH_GET_CLASS (auth)->get_authorization (auth, msg);
508 }
509
510 /**
511  * soup_auth_get_protection_space:
512  * @auth: a #SoupAuth
513  * @source_uri: the URI of the request that @auth was generated in
514  * response to.
515  *
516  * Returns a list of paths on the server which @auth extends over.
517  * (All subdirectories of these paths are also assumed to be part
518  * of @auth's protection space, unless otherwise discovered not to
519  * be.)
520  *
521  * Return value: (element-type utf8) (transfer full): the list of
522  * paths, which can be freed with soup_auth_free_protection_space().
523  **/
524 GSList *
525 soup_auth_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
526 {
527         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
528         g_return_val_if_fail (source_uri != NULL, NULL);
529
530         return SOUP_AUTH_GET_CLASS (auth)->get_protection_space (auth, source_uri);
531 }
532
533 /**
534  * soup_auth_free_protection_space: (skip)
535  * @auth: a #SoupAuth
536  * @space: the return value from soup_auth_get_protection_space()
537  *
538  * Frees @space.
539  **/
540 void
541 soup_auth_free_protection_space (SoupAuth *auth, GSList *space)
542 {
543         GSList *s;
544
545         for (s = space; s; s = s->next)
546                 g_free (s->data);
547         g_slist_free (space);
548 }
549
550 /**
551  * soup_auth_get_saved_users:
552  * @auth: a #SoupAuth
553  *
554  * Gets a list of usernames for which a saved password is available.
555  * (If the session is not configured to save passwords, this will
556  * always be %NULL.)
557  *
558  * Return value: (transfer container): the list of usernames. You must
559  * free the list with g_slist_free(), but do not free or modify the
560  * contents.
561  *
562  * Since: 2.28
563  **/
564 GSList *
565 soup_auth_get_saved_users (SoupAuth *auth)
566 {
567         SoupAuthPrivate *priv;
568         GSList *users;
569
570         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
571
572         priv = SOUP_AUTH_GET_PRIVATE (auth);
573         users = NULL;
574
575         if (priv->saved_passwords) {
576                 GHashTableIter iter;
577                 gpointer key, value;
578
579                 g_hash_table_iter_init (&iter, priv->saved_passwords);
580                 while (g_hash_table_iter_next (&iter, &key, &value))
581                         users = g_slist_prepend (users, key);
582         }
583         return users;
584 }
585
586 /**
587  * soup_auth_get_saved_password:
588  * @auth: a #SoupAuth
589  * @user: a username from the list returned from
590  * soup_auth_get_saved_users().
591  *
592  * Given a username for which @auth has a saved password, this returns
593  * that password. If @auth doesn't have a passwords saved for @user, it
594  * returns %NULL.
595  *
596  * Return value: the saved password, or %NULL.
597  *
598  * Since: 2.28
599  **/
600 const char *
601 soup_auth_get_saved_password (SoupAuth *auth, const char *user)
602 {
603         SoupAuthPrivate *priv;
604
605         g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
606         g_return_val_if_fail (user != NULL, NULL);
607
608         priv = SOUP_AUTH_GET_PRIVATE (auth);
609         if (!priv->saved_passwords)
610                 return NULL;
611         return g_hash_table_lookup (priv->saved_passwords, user);
612 }
613
614 static void
615 free_password (gpointer password)
616 {
617         memset (password, 0, strlen (password));
618         g_free (password);
619 }
620
621 static inline void
622 init_saved_passwords (SoupAuthPrivate *priv)
623 {
624         priv->saved_passwords = g_hash_table_new_full (
625                 g_str_hash, g_str_equal, g_free, free_password);
626 }
627
628 /**
629  * soup_auth_has_saved_password:
630  * @auth: a #SoupAuth
631  * @username: a username
632  * @password: a password
633  *
634  * Updates @auth to be aware of an already-saved username/password
635  * combination. This method <emphasis>does not</emphasis> cause the
636  * given @username and @password to be saved; use
637  * soup_auth_save_password() for that. (soup_auth_has_saved_password()
638  * is an internal method, which is used by the code that actually
639  * saves and restores the passwords.)
640  *
641  * Since: 2.28
642  **/
643 void
644 soup_auth_has_saved_password (SoupAuth *auth, const char *username,
645                               const char *password)
646 {
647         SoupAuthPrivate *priv;
648
649         g_return_if_fail (SOUP_IS_AUTH (auth));
650         g_return_if_fail (username != NULL);
651         g_return_if_fail (password != NULL);
652
653         priv = SOUP_AUTH_GET_PRIVATE (auth);
654
655         if (!priv->saved_passwords)
656                 init_saved_passwords (priv);
657         g_hash_table_insert (priv->saved_passwords,
658                              g_strdup (username), g_strdup (password));
659 }
660
661 /**
662  * soup_auth_save_password:
663  * @auth: a #SoupAuth
664  * @username: the username provided by the user or client
665  * @password: the password provided by the user or client
666  *
667  * Requests that the username/password pair be saved to whatever form
668  * of persistent password storage the session supports.
669  *
670  * Since: 2.28
671  **/
672 void
673 soup_auth_save_password (SoupAuth *auth, const char *username,
674                          const char *password)
675 {
676         g_return_if_fail (SOUP_IS_AUTH (auth));
677         g_return_if_fail (username != NULL);
678         g_return_if_fail (password != NULL);
679
680         g_signal_emit (auth, signals[SAVE_PASSWORD], 0,
681                        username, password);
682 }