Allow relative URIs, since some servers are lame. Based on a patch from
[platform/upstream/libsoup.git] / libsoup / soup-session.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-session.c
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <unistd.h>
13 #include <string.h>
14 #include <stdlib.h>
15
16 #include "soup-auth.h"
17 #include "soup-session.h"
18 #include "soup-connection.h"
19 #include "soup-connection-ntlm.h"
20 #include "soup-marshal.h"
21 #include "soup-message-filter.h"
22 #include "soup-message-queue.h"
23 #include "soup-ssl.h"
24 #include "soup-uri.h"
25
26 typedef struct {
27         SoupUri    *root_uri;
28
29         GSList     *connections;      /* CONTAINS: SoupConnection */
30         guint       num_conns;
31
32         GHashTable *auth_realms;      /* path -> scheme:realm */
33         GHashTable *auths;            /* scheme:realm -> SoupAuth */
34 } SoupSessionHost;
35
36 typedef struct {
37         SoupUri *proxy_uri;
38         guint max_conns, max_conns_per_host;
39         gboolean use_ntlm;
40
41         char *ssl_ca_file;
42         gpointer ssl_creds;
43
44         GSList *filters;
45
46         GHashTable *hosts; /* SoupUri -> SoupSessionHost */
47         GHashTable *conns; /* SoupConnection -> SoupSessionHost */
48         guint num_conns;
49
50         SoupSessionHost *proxy_host;
51
52         /* Must hold the host_lock before potentially creating a
53          * new SoupSessionHost, or adding/removing a connection.
54          * Must not emit signals or destroy objects while holding it.
55          */
56         GMutex *host_lock;
57 } SoupSessionPrivate;
58 #define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
59
60 static guint    host_uri_hash  (gconstpointer key);
61 static gboolean host_uri_equal (gconstpointer v1, gconstpointer v2);
62 static void     free_host      (SoupSessionHost *host);
63
64 static void setup_message   (SoupMessageFilter *filter, SoupMessage *msg);
65
66 static void queue_message   (SoupSession *session, SoupMessage *msg,
67                              SoupMessageCallbackFn callback,
68                              gpointer user_data);
69 static void requeue_message (SoupSession *session, SoupMessage *msg);
70 static void cancel_message  (SoupSession *session, SoupMessage *msg);
71
72 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
73 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 4
74
75 static void filter_iface_init (SoupMessageFilterClass *filter_class);
76
77 G_DEFINE_TYPE_EXTENDED (SoupSession, soup_session, G_TYPE_OBJECT, 0,
78                         G_IMPLEMENT_INTERFACE (SOUP_TYPE_MESSAGE_FILTER,
79                                                filter_iface_init))
80
81 enum {
82         AUTHENTICATE,
83         REAUTHENTICATE,
84         LAST_SIGNAL
85 };
86
87 static guint signals[LAST_SIGNAL] = { 0 };
88
89 enum {
90         PROP_0,
91
92         PROP_PROXY_URI,
93         PROP_MAX_CONNS,
94         PROP_MAX_CONNS_PER_HOST,
95         PROP_USE_NTLM,
96         PROP_SSL_CA_FILE,
97
98         LAST_PROP
99 };
100
101 static void set_property (GObject *object, guint prop_id,
102                           const GValue *value, GParamSpec *pspec);
103 static void get_property (GObject *object, guint prop_id,
104                           GValue *value, GParamSpec *pspec);
105
106 static void
107 soup_session_init (SoupSession *session)
108 {
109         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
110
111         session->queue = soup_message_queue_new ();
112
113         priv->host_lock = g_mutex_new ();
114         priv->hosts = g_hash_table_new (host_uri_hash, host_uri_equal);
115         priv->conns = g_hash_table_new (NULL, NULL);
116
117         priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
118         priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
119 }
120
121 static gboolean
122 foreach_free_host (gpointer key, gpointer host, gpointer data)
123 {
124         free_host (host);
125         return TRUE;
126 }
127
128 static void
129 cleanup_hosts (SoupSessionPrivate *priv)
130 {
131         g_mutex_lock (priv->host_lock);
132         g_hash_table_foreach_remove (priv->hosts, foreach_free_host, NULL);
133         g_mutex_unlock (priv->host_lock);
134 }
135
136 static void
137 dispose (GObject *object)
138 {
139         SoupSession *session = SOUP_SESSION (object);
140         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
141         GSList *f;
142
143         soup_session_abort (session);
144         cleanup_hosts (priv);
145
146         if (priv->filters) {
147                 for (f = priv->filters; f; f = f->next)
148                         g_object_unref (f->data);
149                 g_slist_free (priv->filters);
150                 priv->filters = NULL;
151         }
152
153         G_OBJECT_CLASS (soup_session_parent_class)->dispose (object);
154 }
155
156 static void
157 finalize (GObject *object)
158 {
159         SoupSession *session = SOUP_SESSION (object);
160         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
161
162         soup_message_queue_destroy (session->queue);
163
164         g_mutex_free (priv->host_lock);
165         g_hash_table_destroy (priv->hosts);
166         g_hash_table_destroy (priv->conns);
167
168         G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
169 }
170
171 static void
172 soup_session_class_init (SoupSessionClass *session_class)
173 {
174         GObjectClass *object_class = G_OBJECT_CLASS (session_class);
175
176         g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
177
178         /* virtual method definition */
179         session_class->queue_message = queue_message;
180         session_class->requeue_message = requeue_message;
181         session_class->cancel_message = cancel_message;
182
183         /* virtual method override */
184         object_class->dispose = dispose;
185         object_class->finalize = finalize;
186         object_class->set_property = set_property;
187         object_class->get_property = get_property;
188
189         /* signals */
190
191         /**
192          * SoupSession::authenticate:
193          * @session: the session
194          * @msg: the #SoupMessage being sent
195          * @auth_type: the authentication type
196          * @auth_realm: the realm being authenticated to
197          * @username: the signal handler should set this to point to
198          * the provided username
199          * @password: the signal handler should set this to point to
200          * the provided password
201          *
202          * Emitted when the session requires authentication. The
203          * credentials may come from the user, or from cached
204          * information. If no credentials are available, leave
205          * @username and @password unchanged.
206          *
207          * If the provided credentials fail, the #reauthenticate
208          * signal will be emitted.
209          **/
210         signals[AUTHENTICATE] =
211                 g_signal_new ("authenticate",
212                               G_OBJECT_CLASS_TYPE (object_class),
213                               G_SIGNAL_RUN_FIRST,
214                               G_STRUCT_OFFSET (SoupSessionClass, authenticate),
215                               NULL, NULL,
216                               soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
217                               G_TYPE_NONE, 5,
218                               SOUP_TYPE_MESSAGE,
219                               G_TYPE_STRING,
220                               G_TYPE_STRING,
221                               G_TYPE_POINTER,
222                               G_TYPE_POINTER);
223
224         /**
225          * SoupSession::reauthenticate:
226          * @session: the session
227          * @msg: the #SoupMessage being sent
228          * @auth_type: the authentication type
229          * @auth_realm: the realm being authenticated to
230          * @username: the signal handler should set this to point to
231          * the provided username
232          * @password: the signal handler should set this to point to
233          * the provided password
234          *
235          * Emitted when the credentials provided by the application to
236          * the #authenticate signal have failed. This gives the
237          * application a second chance to provide authentication
238          * credentials. If the new credentials also fail, #SoupSession
239          * will emit #reauthenticate again, and will continue doing so
240          * until the provided credentials work, or a #reauthenticate
241          * signal emission "fails" (because the handler left @username
242          * and @password unchanged). At that point, the 401 or 407
243          * error status will be returned to the caller.
244          *
245          * If your application only uses cached passwords, it should
246          * only connect to #authenticate, and not #reauthenticate.
247          *
248          * If your application always prompts the user for a password,
249          * and never uses cached information, then you can connect the
250          * same handler to #authenticate and #reauthenticate.
251          *
252          * To get standard web-browser behavior, return either cached
253          * information or a user-provided password (whichever is
254          * available) from the #authenticate handler, but return only
255          * user-provided information from the #reauthenticate handler.
256          **/
257         signals[REAUTHENTICATE] =
258                 g_signal_new ("reauthenticate",
259                               G_OBJECT_CLASS_TYPE (object_class),
260                               G_SIGNAL_RUN_FIRST,
261                               G_STRUCT_OFFSET (SoupSessionClass, reauthenticate),
262                               NULL, NULL,
263                               soup_marshal_NONE__OBJECT_STRING_STRING_POINTER_POINTER,
264                               G_TYPE_NONE, 5,
265                               SOUP_TYPE_MESSAGE,
266                               G_TYPE_STRING,
267                               G_TYPE_STRING,
268                               G_TYPE_POINTER,
269                               G_TYPE_POINTER);
270
271         /* properties */
272         g_object_class_install_property (
273                 object_class, PROP_PROXY_URI,
274                 g_param_spec_pointer (SOUP_SESSION_PROXY_URI,
275                                       "Proxy URI",
276                                       "The HTTP Proxy to use for this session",
277                                       G_PARAM_READWRITE));
278         g_object_class_install_property (
279                 object_class, PROP_MAX_CONNS,
280                 g_param_spec_int (SOUP_SESSION_MAX_CONNS,
281                                   "Max Connection Count",
282                                   "The maximum number of connections that the session can open at once",
283                                   1,
284                                   G_MAXINT,
285                                   10,
286                                   G_PARAM_READWRITE));
287         g_object_class_install_property (
288                 object_class, PROP_MAX_CONNS_PER_HOST,
289                 g_param_spec_int (SOUP_SESSION_MAX_CONNS_PER_HOST,
290                                   "Max Per-Host Connection Count",
291                                   "The maximum number of connections that the session can open at once to a given host",
292                                   1,
293                                   G_MAXINT,
294                                   4,
295                                   G_PARAM_READWRITE));
296         g_object_class_install_property (
297                 object_class, PROP_USE_NTLM,
298                 g_param_spec_boolean (SOUP_SESSION_USE_NTLM,
299                                       "Use NTLM",
300                                       "Whether or not to use NTLM authentication",
301                                       FALSE,
302                                       G_PARAM_READWRITE));
303         g_object_class_install_property (
304                 object_class, PROP_SSL_CA_FILE,
305                 g_param_spec_string (SOUP_SESSION_SSL_CA_FILE,
306                                      "SSL CA file",
307                                      "File containing SSL CA certificates",
308                                      NULL,
309                                      G_PARAM_READWRITE));
310 }
311
312 static void
313 filter_iface_init (SoupMessageFilterClass *filter_class)
314 {
315         /* interface implementation */
316         filter_class->setup_message = setup_message;
317 }
318
319
320 static gboolean
321 safe_uri_equal (const SoupUri *a, const SoupUri *b)
322 {
323         if (!a && !b)
324                 return TRUE;
325
326         if ((a && !b) || (b && !a))
327                 return FALSE;
328
329         return soup_uri_equal (a, b);
330 }
331
332 static gboolean
333 safe_str_equal (const char *a, const char *b)
334 {
335         if (!a && !b)
336                 return TRUE;
337
338         if ((a && !b) || (b && !a))
339                 return FALSE;
340
341         return strcmp (a, b) == 0;
342 }
343
344 static void
345 set_property (GObject *object, guint prop_id,
346               const GValue *value, GParamSpec *pspec)
347 {
348         SoupSession *session = SOUP_SESSION (object);
349         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
350         gpointer pval;
351         gboolean need_abort = FALSE;
352         gboolean ca_file_changed = FALSE;
353         const char *new_ca_file;
354
355         switch (prop_id) {
356         case PROP_PROXY_URI:
357                 pval = g_value_get_pointer (value);
358
359                 if (!safe_uri_equal (priv->proxy_uri, pval))
360                         need_abort = TRUE;
361
362                 if (priv->proxy_uri)
363                         soup_uri_free (priv->proxy_uri);
364
365                 priv->proxy_uri = pval ? soup_uri_copy (pval) : NULL;
366
367                 if (need_abort) {
368                         soup_session_abort (session);
369                         cleanup_hosts (priv);
370                 }
371
372                 break;
373         case PROP_MAX_CONNS:
374                 priv->max_conns = g_value_get_int (value);
375                 break;
376         case PROP_MAX_CONNS_PER_HOST:
377                 priv->max_conns_per_host = g_value_get_int (value);
378                 break;
379         case PROP_USE_NTLM:
380                 priv->use_ntlm = g_value_get_boolean (value);
381                 break;
382         case PROP_SSL_CA_FILE:
383                 new_ca_file = g_value_get_string (value);
384
385                 if (!safe_str_equal (priv->ssl_ca_file, new_ca_file))
386                         ca_file_changed = TRUE;
387
388                 g_free (priv->ssl_ca_file);
389                 priv->ssl_ca_file = g_strdup (new_ca_file);
390
391                 if (ca_file_changed) {
392                         if (priv->ssl_creds) {
393                                 soup_ssl_free_client_credentials (priv->ssl_creds);
394                                 priv->ssl_creds = NULL;
395                         }
396
397                         cleanup_hosts (priv);
398                 }
399
400                 break;
401         default:
402                 break;
403         }
404 }
405
406 static void
407 get_property (GObject *object, guint prop_id,
408               GValue *value, GParamSpec *pspec)
409 {
410         SoupSession *session = SOUP_SESSION (object);
411         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
412
413         switch (prop_id) {
414         case PROP_PROXY_URI:
415                 g_value_set_pointer (value, priv->proxy_uri ?
416                                      soup_uri_copy (priv->proxy_uri) :
417                                      NULL);
418                 break;
419         case PROP_MAX_CONNS:
420                 g_value_set_int (value, priv->max_conns);
421                 break;
422         case PROP_MAX_CONNS_PER_HOST:
423                 g_value_set_int (value, priv->max_conns_per_host);
424                 break;
425         case PROP_USE_NTLM:
426                 g_value_set_boolean (value, priv->use_ntlm);
427                 break;
428         case PROP_SSL_CA_FILE:
429                 g_value_set_string (value, priv->ssl_ca_file);
430                 break;
431         default:
432                 break;
433         }
434 }
435
436
437 /**
438  * soup_session_add_filter:
439  * @session: a #SoupSession
440  * @filter: an object implementing the #SoupMessageFilter interface
441  *
442  * Adds @filter to @session's list of message filters to be applied to
443  * all messages.
444  **/
445 void
446 soup_session_add_filter (SoupSession *session, SoupMessageFilter *filter)
447 {
448         SoupSessionPrivate *priv;
449
450         g_return_if_fail (SOUP_IS_SESSION (session));
451         g_return_if_fail (SOUP_IS_MESSAGE_FILTER (filter));
452         priv = SOUP_SESSION_GET_PRIVATE (session);
453
454         g_object_ref (filter);
455         priv->filters = g_slist_prepend (priv->filters, filter);
456 }
457
458 /**
459  * soup_session_remove_filter:
460  * @session: a #SoupSession
461  * @filter: an object implementing the #SoupMessageFilter interface
462  *
463  * Removes @filter from @session's list of message filters
464  **/
465 void
466 soup_session_remove_filter (SoupSession *session, SoupMessageFilter *filter)
467 {
468         SoupSessionPrivate *priv;
469
470         g_return_if_fail (SOUP_IS_SESSION (session));
471         g_return_if_fail (SOUP_IS_MESSAGE_FILTER (filter));
472         priv = SOUP_SESSION_GET_PRIVATE (session);
473
474         priv->filters = g_slist_remove (priv->filters, filter);
475         g_object_unref (filter);
476 }
477
478
479 /* Hosts */
480 static guint
481 host_uri_hash (gconstpointer key)
482 {
483         const SoupUri *uri = key;
484
485         return (uri->protocol << 16) + uri->port + g_str_hash (uri->host);
486 }
487
488 static gboolean
489 host_uri_equal (gconstpointer v1, gconstpointer v2)
490 {
491         const SoupUri *one = v1;
492         const SoupUri *two = v2;
493
494         if (one->protocol != two->protocol)
495                 return FALSE;
496         if (one->port != two->port)
497                 return FALSE;
498
499         return strcmp (one->host, two->host) == 0;
500 }
501
502 static SoupSessionHost *
503 soup_session_host_new (SoupSession *session, const SoupUri *source_uri)
504 {
505         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
506         SoupSessionHost *host;
507
508         host = g_new0 (SoupSessionHost, 1);
509         host->root_uri = soup_uri_copy_root (source_uri);
510
511         if (host->root_uri->protocol == SOUP_PROTOCOL_HTTPS &&
512             !priv->ssl_creds) {
513                 priv->ssl_creds =
514                         soup_ssl_get_client_credentials (priv->ssl_ca_file);
515         }
516
517         return host;
518 }
519
520 /* Note: get_host_for_message doesn't lock the host_lock. The caller
521  * must do it itself if there's a chance the host doesn't already
522  * exist.
523  */
524 static SoupSessionHost *
525 get_host_for_message (SoupSession *session, SoupMessage *msg)
526 {
527         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
528         SoupSessionHost *host;
529         const SoupUri *source = soup_message_get_uri (msg);
530
531         host = g_hash_table_lookup (priv->hosts, source);
532         if (host)
533                 return host;
534
535         host = soup_session_host_new (session, source);
536         g_hash_table_insert (priv->hosts, host->root_uri, host);
537
538         return host;
539 }
540
541 /* Note: get_proxy_host doesn't lock the host_lock. The caller must do
542  * it itself if there's a chance the host doesn't already exist.
543  */
544 static SoupSessionHost *
545 get_proxy_host (SoupSession *session)
546 {
547         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
548
549         if (priv->proxy_host || !priv->proxy_uri)
550                 return priv->proxy_host;
551
552         priv->proxy_host =
553                 soup_session_host_new (session, priv->proxy_uri);
554         return priv->proxy_host;
555 }
556
557 static void
558 free_realm (gpointer path, gpointer scheme_realm, gpointer data)
559 {
560         g_free (path);
561         g_free (scheme_realm);
562 }
563
564 static void
565 free_auth (gpointer scheme_realm, gpointer auth, gpointer data)
566 {
567         g_free (scheme_realm);
568         g_object_unref (auth);
569 }
570
571 static void
572 free_host (SoupSessionHost *host)
573 {
574         while (host->connections) {
575                 SoupConnection *conn = host->connections->data;
576
577                 host->connections = g_slist_remove (host->connections, conn);
578                 soup_connection_disconnect (conn);
579         }
580
581         if (host->auth_realms) {
582                 g_hash_table_foreach (host->auth_realms, free_realm, NULL);
583                 g_hash_table_destroy (host->auth_realms);
584         }
585         if (host->auths) {
586                 g_hash_table_foreach (host->auths, free_auth, NULL);
587                 g_hash_table_destroy (host->auths);
588         }
589
590         soup_uri_free (host->root_uri);
591         g_free (host);
592 }       
593
594 /* Authentication */
595
596 static SoupAuth *
597 lookup_auth (SoupSession *session, SoupMessage *msg, gboolean proxy)
598 {
599         SoupSessionHost *host;
600         char *path, *dir;
601         const char *realm, *const_path;
602
603         if (proxy) {
604                 host = get_proxy_host (session);
605                 const_path = "/";
606         } else {
607                 host = get_host_for_message (session, msg);
608                 const_path = soup_message_get_uri (msg)->path;
609
610                 if (!const_path)
611                         const_path = "/";
612         }
613         g_return_val_if_fail (host != NULL, NULL);
614
615         if (!host->auth_realms)
616                 return NULL;
617
618         path = g_strdup (const_path);
619         dir = path;
620         do {
621                 realm = g_hash_table_lookup (host->auth_realms, path);
622                 if (realm)
623                         break;
624
625                 dir = strrchr (path, '/');
626                 if (dir)
627                         *dir = '\0';
628         } while (dir);
629
630         g_free (path);
631         if (realm)
632                 return g_hash_table_lookup (host->auths, realm);
633         else
634                 return NULL;
635 }
636
637 static void
638 invalidate_auth (SoupSessionHost *host, SoupAuth *auth)
639 {
640         char *realm;
641         gpointer key, value;
642
643         realm = g_strdup_printf ("%s:%s",
644                                  soup_auth_get_scheme_name (auth),
645                                  soup_auth_get_realm (auth));
646
647         if (g_hash_table_lookup_extended (host->auths, realm, &key, &value) &&
648             auth == (SoupAuth *)value) {
649                 g_hash_table_remove (host->auths, realm);
650                 g_free (key);
651                 g_object_unref (auth);
652         }
653         g_free (realm);
654 }
655
656 static gboolean
657 authenticate_auth (SoupSession *session, SoupAuth *auth,
658                    SoupMessage *msg, gboolean prior_auth_failed,
659                    gboolean proxy)
660 {
661         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
662         const SoupUri *uri;
663         char *username = NULL, *password = NULL;
664
665         if (proxy)
666                 uri = priv->proxy_uri;
667         else
668                 uri = soup_message_get_uri (msg);
669
670         if (uri->passwd && !prior_auth_failed) {
671                 soup_auth_authenticate (auth, uri->user, uri->passwd);
672                 return TRUE;
673         }
674
675         g_signal_emit (session, signals[prior_auth_failed ? REAUTHENTICATE : AUTHENTICATE], 0,
676                        msg, soup_auth_get_scheme_name (auth),
677                        soup_auth_get_realm (auth),
678                        &username, &password);
679         if (username || password)
680                 soup_auth_authenticate (auth, username, password);
681         if (username)
682                 g_free (username);
683         if (password) {
684                 memset (password, 0, strlen (password));
685                 g_free (password);
686         }
687
688         return soup_auth_is_authenticated (auth);
689 }
690
691 static gboolean
692 update_auth_internal (SoupSession *session, SoupMessage *msg,
693                       const GSList *headers, gboolean proxy,
694                       gboolean got_unauthorized)
695 {
696         SoupSessionHost *host;
697         SoupAuth *new_auth, *prior_auth, *old_auth;
698         gpointer old_path, old_realm;
699         const SoupUri *msg_uri;
700         const char *path;
701         char *realm;
702         GSList *pspace, *p;
703         gboolean prior_auth_failed = FALSE;
704
705         if (proxy)
706                 host = get_proxy_host (session);
707         else
708                 host = get_host_for_message (session, msg);
709
710         g_return_val_if_fail (host != NULL, FALSE);
711
712         /* Try to construct a new auth from the headers; if we can't,
713          * there's no way we'll be able to authenticate.
714          */
715         msg_uri = soup_message_get_uri (msg);
716         new_auth = soup_auth_new_from_header_list (headers);
717         if (!new_auth)
718                 return FALSE;
719
720         /* See if this auth is the same auth we used last time */
721         prior_auth = lookup_auth (session, msg, proxy);
722         if (prior_auth &&
723             G_OBJECT_TYPE (prior_auth) == G_OBJECT_TYPE (new_auth) &&
724             !strcmp (soup_auth_get_realm (prior_auth),
725                      soup_auth_get_realm (new_auth))) {
726                 if (!got_unauthorized) {
727                         /* The user is just trying to preauthenticate
728                          * using information we already have, so
729                          * there's nothing more that needs to be done.
730                          */
731                         g_object_unref (new_auth);
732                         return TRUE;
733                 }
734
735                 /* The server didn't like the username/password we
736                  * provided before. Invalidate it and note this fact.
737                  */
738                 invalidate_auth (host, prior_auth);
739                 prior_auth_failed = TRUE;
740         }
741
742         if (!host->auth_realms) {
743                 host->auth_realms = g_hash_table_new (g_str_hash, g_str_equal);
744                 host->auths = g_hash_table_new (g_str_hash, g_str_equal);
745         }
746
747         /* Record where this auth realm is used */
748         realm = g_strdup_printf ("%s:%s",
749                                  soup_auth_get_scheme_name (new_auth),
750                                  soup_auth_get_realm (new_auth));
751
752         /* 
753          * RFC 2617 is somewhat unclear about the scope of protection
754          * spaces with regard to proxies.  The only mention of it is
755          * as an aside in section 3.2.1, where it is defining the fields
756          * of a Digest challenge and says that the protection space is
757          * always the entire proxy.  Is this the case for all authentication
758          * schemes or just Digest?  Who knows, but we're assuming all.
759          */
760         if (proxy)
761                 pspace = g_slist_prepend (NULL, g_strdup (""));
762         else
763                 pspace = soup_auth_get_protection_space (new_auth, msg_uri);
764
765         for (p = pspace; p; p = p->next) {
766                 path = p->data;
767                 if (g_hash_table_lookup_extended (host->auth_realms, path,
768                                                   &old_path, &old_realm)) {
769                         g_hash_table_remove (host->auth_realms, old_path);
770                         g_free (old_path);
771                         g_free (old_realm);
772                 }
773
774                 g_hash_table_insert (host->auth_realms,
775                                      g_strdup (path), g_strdup (realm));
776         }
777         soup_auth_free_protection_space (new_auth, pspace);
778
779         /* Now, make sure the auth is recorded. (If there's a
780          * pre-existing auth, we keep that rather than the new one,
781          * since the old one might already be authenticated.)
782          */
783         old_auth = g_hash_table_lookup (host->auths, realm);
784         if (old_auth) {
785                 g_free (realm);
786                 g_object_unref (new_auth);
787                 new_auth = old_auth;
788         } else 
789                 g_hash_table_insert (host->auths, realm, new_auth);
790
791         /* If we need to authenticate, try to do it. */
792         if (!soup_auth_is_authenticated (new_auth)) {
793                 return authenticate_auth (session, new_auth,
794                                           msg, prior_auth_failed, proxy);
795         }
796
797         /* Otherwise we're good. */
798         return TRUE;
799 }
800
801 static void
802 connection_authenticate (SoupConnection *conn, SoupMessage *msg,
803                          const char *auth_type, const char *auth_realm,
804                          char **username, char **password, gpointer session)
805 {
806         g_signal_emit (session, signals[AUTHENTICATE], 0,
807                        msg, auth_type, auth_realm, username, password);
808 }
809
810 static void
811 connection_reauthenticate (SoupConnection *conn, SoupMessage *msg,
812                            const char *auth_type, const char *auth_realm,
813                            char **username, char **password,
814                            gpointer user_data)
815 {
816         g_signal_emit (conn, signals[REAUTHENTICATE], 0,
817                        msg, auth_type, auth_realm, username, password);
818 }
819
820
821 static void
822 authorize_handler (SoupMessage *msg, gpointer user_data)
823 {
824         SoupSession *session = user_data;
825         const GSList *headers;
826         gboolean proxy;
827
828         if (msg->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
829                 headers = soup_message_get_header_list (msg->response_headers,
830                                                         "Proxy-Authenticate");
831                 proxy = TRUE;
832         } else {
833                 headers = soup_message_get_header_list (msg->response_headers,
834                                                         "WWW-Authenticate");
835                 proxy = FALSE;
836         }
837         if (!headers)
838                 return;
839
840         if (update_auth_internal (session, msg, headers, proxy, TRUE))
841                 soup_session_requeue_message (session, msg);
842 }
843
844 static void
845 redirect_handler (SoupMessage *msg, gpointer user_data)
846 {
847         SoupSession *session = user_data;
848         const char *new_loc;
849         SoupUri *new_uri;
850
851         new_loc = soup_message_get_header (msg->response_headers, "Location");
852         if (!new_loc)
853                 return;
854
855         /* Location is supposed to be an absolute URI, but some sites
856          * are lame, so we use soup_uri_new_with_base().
857          */
858         new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
859         if (!new_uri) {
860                 soup_message_set_status_full (msg,
861                                               SOUP_STATUS_MALFORMED,
862                                               "Invalid Redirect URL");
863                 return;
864         }
865
866         soup_message_set_uri (msg, new_uri);
867         soup_uri_free (new_uri);
868
869         soup_session_requeue_message (session, msg);
870 }
871
872 static void
873 add_auth (SoupSession *session, SoupMessage *msg, gboolean proxy)
874 {
875         const char *header = proxy ? "Proxy-Authorization" : "Authorization";
876         SoupAuth *auth;
877         char *token;
878
879         auth = lookup_auth (session, msg, proxy);
880         if (!auth)
881                 return;
882         if (!soup_auth_is_authenticated (auth) &&
883             !authenticate_auth (session, auth, msg, FALSE, proxy))
884                 return;
885
886         token = soup_auth_get_authorization (auth, msg);
887         if (token) {
888                 soup_message_remove_header (msg->request_headers, header);
889                 soup_message_add_header (msg->request_headers, header, token);
890                 g_free (token);
891         }
892 }
893
894 static void
895 setup_message (SoupMessageFilter *filter, SoupMessage *msg)
896 {
897         SoupSession *session = SOUP_SESSION (filter);
898         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
899         GSList *f;
900
901         for (f = priv->filters; f; f = f->next) {
902                 filter = f->data;
903                 soup_message_filter_setup_message (filter, msg);
904         }
905
906         add_auth (session, msg, FALSE);
907         soup_message_add_status_code_handler (
908                 msg, SOUP_STATUS_UNAUTHORIZED,
909                 SOUP_HANDLER_POST_BODY,
910                 authorize_handler, session);
911
912         if (priv->proxy_uri) {
913                 add_auth (session, msg, TRUE);
914                 soup_message_add_status_code_handler  (
915                         msg, SOUP_STATUS_PROXY_UNAUTHORIZED,
916                         SOUP_HANDLER_POST_BODY,
917                         authorize_handler, session);
918         }
919 }
920
921 static void
922 find_oldest_connection (gpointer key, gpointer host, gpointer data)
923 {
924         SoupConnection *conn = key, **oldest = data;
925
926         /* Don't prune a connection that is currently in use, or
927          * hasn't been used yet.
928          */
929         if (soup_connection_is_in_use (conn) ||
930             soup_connection_last_used (conn) == 0)
931                 return;
932
933         if (!*oldest || (soup_connection_last_used (conn) <
934                          soup_connection_last_used (*oldest)))
935                 *oldest = conn;
936 }
937
938 /**
939  * soup_session_try_prune_connection:
940  * @session: a #SoupSession
941  *
942  * Finds the least-recently-used idle connection in @session and closes
943  * it.
944  *
945  * Return value: %TRUE if a connection was closed, %FALSE if there are
946  * no idle connections.
947  **/
948 gboolean
949 soup_session_try_prune_connection (SoupSession *session)
950 {
951         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
952         SoupConnection *oldest = NULL;
953
954         g_mutex_lock (priv->host_lock);
955         g_hash_table_foreach (priv->conns, find_oldest_connection,
956                               &oldest);
957         if (oldest) {
958                 /* Ref the connection before unlocking the mutex in
959                  * case someone else tries to prune it too.
960                  */
961                 g_object_ref (oldest);
962                 g_mutex_unlock (priv->host_lock);
963                 soup_connection_disconnect (oldest);
964                 g_object_unref (oldest);
965                 return TRUE;
966         } else {
967                 g_mutex_unlock (priv->host_lock);
968                 return FALSE;
969         }
970 }
971
972 static void
973 connection_disconnected (SoupConnection *conn, gpointer user_data)
974 {
975         SoupSession *session = user_data;
976         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
977         SoupSessionHost *host;
978
979         g_mutex_lock (priv->host_lock);
980
981         host = g_hash_table_lookup (priv->conns, conn);
982         if (host) {
983                 g_hash_table_remove (priv->conns, conn);
984                 host->connections = g_slist_remove (host->connections, conn);
985                 host->num_conns--;
986         }
987
988         g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
989         priv->num_conns--;
990
991         g_mutex_unlock (priv->host_lock);
992         g_object_unref (conn);
993 }
994
995 static void
996 connect_result (SoupConnection *conn, guint status, gpointer user_data)
997 {
998         SoupSession *session = user_data;
999         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1000         SoupSessionHost *host;
1001         SoupMessageQueueIter iter;
1002         SoupMessage *msg;
1003
1004         g_mutex_lock (priv->host_lock);
1005
1006         host = g_hash_table_lookup (priv->conns, conn);
1007         if (!host) {
1008                 g_mutex_unlock (priv->host_lock);
1009                 return;
1010         }
1011
1012         if (status == SOUP_STATUS_OK) {
1013                 soup_connection_reserve (conn);
1014                 host->connections = g_slist_prepend (host->connections, conn);
1015                 g_mutex_unlock (priv->host_lock);
1016                 return;
1017         }
1018
1019         /* The connection failed. */
1020         g_mutex_unlock (priv->host_lock);
1021         connection_disconnected (conn, session);
1022
1023         if (host->connections) {
1024                 /* Something went wrong this time, but we have at
1025                  * least one open connection to this host. So just
1026                  * leave the message in the queue so it can use that
1027                  * connection once it's free.
1028                  */
1029                 return;
1030         }
1031
1032         /* There are two possibilities: either status is
1033          * SOUP_STATUS_TRY_AGAIN, in which case the session implementation
1034          * will create a new connection (and all we need to do here
1035          * is downgrade the message from CONNECTING to QUEUED); or
1036          * status is something else, probably CANT_CONNECT or
1037          * CANT_RESOLVE or the like, in which case we need to cancel
1038          * any messages waiting for this host, since they're out
1039          * of luck.
1040          */
1041         for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
1042                 if (get_host_for_message (session, msg) == host) {
1043                         if (status == SOUP_STATUS_TRY_AGAIN) {
1044                                 if (msg->status == SOUP_MESSAGE_STATUS_CONNECTING)
1045                                         msg->status = SOUP_MESSAGE_STATUS_QUEUED;
1046                         } else {
1047                                 soup_message_set_status (msg, status);
1048                                 soup_session_cancel_message (session, msg);
1049                         }
1050                 }
1051         }
1052 }
1053
1054 /**
1055  * soup_session_get_connection:
1056  * @session: a #SoupSession
1057  * @msg: a #SoupMessage
1058  * @try_pruning: on return, whether or not to try pruning a connection
1059  * @is_new: on return, %TRUE if the returned connection is new and not
1060  * yet connected
1061  * 
1062  * Tries to find or create a connection for @msg; this is an internal
1063  * method for #SoupSession subclasses.
1064  *
1065  * If there is an idle connection to the relevant host available, then
1066  * that connection will be returned (with *@is_new set to %FALSE). The
1067  * connection will be marked "reserved", so the caller must call
1068  * soup_connection_release() if it ends up not using the connection
1069  * right away.
1070  *
1071  * If there is no idle connection available, but it is possible to
1072  * create a new connection, then one will be created and returned,
1073  * with *@is_new set to %TRUE. The caller MUST then call
1074  * soup_connection_connect_sync() or soup_connection_connect_async()
1075  * to connect it. If the connection attempt succeeds, the connection
1076  * will be marked "reserved" and added to @session's connection pool
1077  * once it connects. If the connection attempt fails, the connection
1078  * will be unreffed.
1079  *
1080  * If no connection is available and a new connection cannot be made,
1081  * soup_session_get_connection() will return %NULL. If @session has
1082  * the maximum number of open connections open, but does not have the
1083  * maximum number of per-host connections open to the relevant host,
1084  * then *@try_pruning will be set to %TRUE. In this case, the caller
1085  * can call soup_session_try_prune_connection() to close an idle
1086  * connection, and then try soup_session_get_connection() again. (If
1087  * calling soup_session_try_prune_connection() wouldn't help, then
1088  * *@try_pruning is left untouched; it is NOT set to %FALSE.)
1089  *
1090  * Return value: a #SoupConnection, or %NULL
1091  **/
1092 SoupConnection *
1093 soup_session_get_connection (SoupSession *session, SoupMessage *msg,
1094                              gboolean *try_pruning, gboolean *is_new)
1095 {
1096         SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
1097         SoupConnection *conn;
1098         SoupSessionHost *host;
1099         GSList *conns;
1100
1101         g_mutex_lock (priv->host_lock);
1102
1103         host = get_host_for_message (session, msg);
1104         for (conns = host->connections; conns; conns = conns->next) {
1105                 if (!soup_connection_is_in_use (conns->data)) {
1106                         soup_connection_reserve (conns->data);
1107                         g_mutex_unlock (priv->host_lock);
1108                         *is_new = FALSE;
1109                         return conns->data;
1110                 }
1111         }
1112
1113         if (msg->status == SOUP_MESSAGE_STATUS_CONNECTING) {
1114                 /* We already started a connection for this
1115                  * message, so don't start another one.
1116                  */
1117                 g_mutex_unlock (priv->host_lock);
1118                 return NULL;
1119         }
1120
1121         if (host->num_conns >= priv->max_conns_per_host) {
1122                 g_mutex_unlock (priv->host_lock);
1123                 return NULL;
1124         }
1125
1126         if (priv->num_conns >= priv->max_conns) {
1127                 *try_pruning = TRUE;
1128                 g_mutex_unlock (priv->host_lock);
1129                 return NULL;
1130         }
1131
1132         /* Make sure priv->proxy_host gets set now while
1133          * we have the host_lock.
1134          */
1135         if (priv->proxy_uri)
1136                 get_proxy_host (session);
1137
1138         conn = g_object_new (
1139                 (priv->use_ntlm ?
1140                  SOUP_TYPE_CONNECTION_NTLM : SOUP_TYPE_CONNECTION),
1141                 SOUP_CONNECTION_ORIGIN_URI, host->root_uri,
1142                 SOUP_CONNECTION_PROXY_URI, priv->proxy_uri,
1143                 SOUP_CONNECTION_SSL_CREDENTIALS, priv->ssl_creds,
1144                 SOUP_CONNECTION_MESSAGE_FILTER, session,
1145                 NULL);
1146         g_signal_connect (conn, "connect_result",
1147                           G_CALLBACK (connect_result),
1148                           session);
1149         g_signal_connect (conn, "disconnected",
1150                           G_CALLBACK (connection_disconnected),
1151                           session);
1152         g_signal_connect (conn, "authenticate",
1153                           G_CALLBACK (connection_authenticate),
1154                           session);
1155         g_signal_connect (conn, "reauthenticate",
1156                           G_CALLBACK (connection_reauthenticate),
1157                           session);
1158
1159         g_hash_table_insert (priv->conns, conn, host);
1160
1161         /* We increment the connection counts so it counts against the
1162          * totals, but we don't add it to the host's connection list
1163          * yet, since it's not ready for use.
1164          */
1165         priv->num_conns++;
1166         host->num_conns++;
1167
1168         /* Mark the request as connecting, so we don't try to open
1169          * another new connection for it while waiting for this one.
1170          */
1171         msg->status = SOUP_MESSAGE_STATUS_CONNECTING;
1172
1173         g_mutex_unlock (priv->host_lock);
1174         *is_new = TRUE;
1175         return conn;
1176 }
1177
1178 static void
1179 message_finished (SoupMessage *msg, gpointer user_data)
1180 {
1181         SoupSession *session = user_data;
1182
1183         if (!SOUP_MESSAGE_IS_STARTING (msg)) {
1184                 soup_message_queue_remove_message (session->queue, msg);
1185                 g_signal_handlers_disconnect_by_func (msg, message_finished, session);
1186         }
1187 }
1188
1189 static void
1190 queue_message (SoupSession *session, SoupMessage *msg,
1191                SoupMessageCallbackFn callback, gpointer user_data)
1192 {
1193         g_signal_connect_after (msg, "finished",
1194                                 G_CALLBACK (message_finished), session);
1195
1196         if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
1197                 soup_message_add_status_class_handler (
1198                         msg, SOUP_STATUS_CLASS_REDIRECT,
1199                         SOUP_HANDLER_POST_BODY,
1200                         redirect_handler, session);
1201         }
1202
1203         msg->status = SOUP_MESSAGE_STATUS_QUEUED;
1204         soup_message_queue_append (session->queue, msg);
1205 }
1206
1207 /**
1208  * soup_session_queue_message:
1209  * @session: a #SoupSession
1210  * @msg: the message to queue
1211  * @callback: a #SoupMessageCallbackFn which will be called after the
1212  * message completes or when an unrecoverable error occurs.
1213  * @user_data: a pointer passed to @callback.
1214  * 
1215  * Queues the message @msg for sending. All messages are processed
1216  * while the glib main loop runs. If @msg has been processed before,
1217  * any resources related to the time it was last sent are freed.
1218  *
1219  * Upon message completion, the callback specified in @callback will
1220  * be invoked. If after returning from this callback the message has
1221  * not been requeued, @msg will be unreffed.
1222  */
1223 void
1224 soup_session_queue_message (SoupSession *session, SoupMessage *msg,
1225                             SoupMessageCallbackFn callback, gpointer user_data)
1226 {
1227         g_return_if_fail (SOUP_IS_SESSION (session));
1228         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1229
1230         SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
1231                                                          callback, user_data);
1232 }
1233
1234 static void
1235 requeue_message (SoupSession *session, SoupMessage *msg)
1236 {
1237         msg->status = SOUP_MESSAGE_STATUS_QUEUED;
1238 }
1239
1240 /**
1241  * soup_session_requeue_message:
1242  * @session: a #SoupSession
1243  * @msg: the message to requeue
1244  *
1245  * This causes @msg to be placed back on the queue to be attempted
1246  * again.
1247  **/
1248 void
1249 soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
1250 {
1251         g_return_if_fail (SOUP_IS_SESSION (session));
1252         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1253
1254         SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
1255 }
1256
1257
1258 /**
1259  * soup_session_send_message:
1260  * @session: a #SoupSession
1261  * @msg: the message to send
1262  * 
1263  * Synchronously send @msg. This call will not return until the
1264  * transfer is finished successfully or there is an unrecoverable
1265  * error.
1266  *
1267  * @msg is not freed upon return.
1268  *
1269  * Return value: the HTTP status code of the response
1270  */
1271 guint
1272 soup_session_send_message (SoupSession *session, SoupMessage *msg)
1273 {
1274         g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
1275         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
1276
1277         return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
1278 }
1279
1280
1281 static void
1282 cancel_message (SoupSession *session, SoupMessage *msg)
1283 {
1284         soup_message_queue_remove_message (session->queue, msg);
1285         soup_message_finished (msg);
1286 }
1287
1288 /**
1289  * soup_session_cancel_message:
1290  * @session: a #SoupSession
1291  * @msg: the message to cancel
1292  *
1293  * Causes @session to immediately finish processing @msg. You should
1294  * set a status code on @msg with soup_message_set_status() before
1295  * calling this function.
1296  **/
1297 void
1298 soup_session_cancel_message (SoupSession *session, SoupMessage *msg)
1299 {
1300         g_return_if_fail (SOUP_IS_SESSION (session));
1301         g_return_if_fail (SOUP_IS_MESSAGE (msg));
1302
1303         SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg);
1304 }
1305
1306 /**
1307  * soup_session_abort:
1308  * @session: the session
1309  *
1310  * Cancels all pending requests in @session.
1311  **/
1312 void
1313 soup_session_abort (SoupSession *session)
1314 {
1315         SoupMessageQueueIter iter;
1316         SoupMessage *msg;
1317
1318         g_return_if_fail (SOUP_IS_SESSION (session));
1319
1320         for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
1321                 soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
1322                 soup_session_cancel_message (session, msg);
1323         }
1324 }