Default to TLS for https connections, and fall back to SSLv3 on failure
[libsoup-meego2:libsoup-meego2.git] / libsoup / soup-connection.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-connection.c: A single HTTP/HTTPS connection
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 #include <glib.h>
16
17 #include <fcntl.h>
18 #include <sys/types.h>
19
20 #include "soup-address.h"
21 #include "soup-connection.h"
22 #include "soup-marshal.h"
23 #include "soup-message.h"
24 #include "soup-message-private.h"
25 #include "soup-message-queue.h"
26 #include "soup-misc.h"
27 #include "soup-misc-private.h"
28 #include "soup-socket.h"
29 #include "soup-ssl.h"
30 #include "soup-uri.h"
31 #include "soup-enum-types.h"
32
33 typedef struct {
34         SoupSocket  *socket;
35
36         SoupAddress *remote_addr, *tunnel_addr;
37         SoupURI     *proxy_uri;
38         gpointer     ssl_creds;
39         gboolean     ssl_strict;
40         gboolean     ssl_fallback;
41
42         GMainContext      *async_context;
43
44         SoupMessageQueueItem *cur_item;
45         SoupConnectionState state;
46         time_t       unused_timeout;
47         guint        io_timeout, idle_timeout;
48         GSource     *idle_timeout_src;
49 } SoupConnectionPrivate;
50 #define SOUP_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CONNECTION, SoupConnectionPrivate))
51
52 G_DEFINE_TYPE (SoupConnection, soup_connection, G_TYPE_OBJECT)
53
54 enum {
55         DISCONNECTED,
56         LAST_SIGNAL
57 };
58
59 static guint signals[LAST_SIGNAL] = { 0 };
60
61 enum {
62         PROP_0,
63
64         PROP_REMOTE_ADDRESS,
65         PROP_TUNNEL_ADDRESS,
66         PROP_PROXY_URI,
67         PROP_SSL_CREDS,
68         PROP_SSL_STRICT,
69         PROP_SSL_FALLBACK,
70         PROP_ASYNC_CONTEXT,
71         PROP_TIMEOUT,
72         PROP_IDLE_TIMEOUT,
73         PROP_STATE,
74         PROP_MESSAGE,
75
76         LAST_PROP
77 };
78
79 static void set_property (GObject *object, guint prop_id,
80                           const GValue *value, GParamSpec *pspec);
81 static void get_property (GObject *object, guint prop_id,
82                           GValue *value, GParamSpec *pspec);
83
84 static void stop_idle_timer (SoupConnectionPrivate *priv);
85 static void clear_current_item (SoupConnection *conn);
86
87 /* Number of seconds after which we close a connection that hasn't yet
88  * been used.
89  */
90 #define SOUP_CONNECTION_UNUSED_TIMEOUT 3
91
92 static void
93 soup_connection_init (SoupConnection *conn)
94 {
95         ;
96 }
97
98 static void
99 finalize (GObject *object)
100 {
101         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
102
103         if (priv->remote_addr)
104                 g_object_unref (priv->remote_addr);
105         if (priv->tunnel_addr)
106                 g_object_unref (priv->tunnel_addr);
107         if (priv->proxy_uri)
108                 soup_uri_free (priv->proxy_uri);
109
110         if (priv->async_context)
111                 g_main_context_unref (priv->async_context);
112
113         G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object);
114 }
115
116 static void
117 dispose (GObject *object)
118 {
119         SoupConnection *conn = SOUP_CONNECTION (object);
120         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
121
122         stop_idle_timer (priv);
123         /* Make sure clear_current_item doesn't re-establish the timeout */
124         priv->idle_timeout = 0;
125
126         if (priv->cur_item) {
127                 g_warning ("Disposing connection with cur_item set");
128                 clear_current_item (conn);
129         }
130         if (priv->socket) {
131                 g_warning ("Disposing connection while connected");
132                 soup_connection_disconnect (conn);
133         }
134
135         G_OBJECT_CLASS (soup_connection_parent_class)->dispose (object);
136 }
137
138 static void
139 soup_connection_class_init (SoupConnectionClass *connection_class)
140 {
141         GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
142
143         g_type_class_add_private (connection_class, sizeof (SoupConnectionPrivate));
144
145         /* virtual method override */
146         object_class->dispose = dispose;
147         object_class->finalize = finalize;
148         object_class->set_property = set_property;
149         object_class->get_property = get_property;
150
151         /* signals */
152         signals[DISCONNECTED] =
153                 g_signal_new ("disconnected",
154                               G_OBJECT_CLASS_TYPE (object_class),
155                               G_SIGNAL_RUN_FIRST,
156                               G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
157                               NULL, NULL,
158                               soup_marshal_NONE__NONE,
159                               G_TYPE_NONE, 0);
160
161         /* properties */
162         g_object_class_install_property (
163                 object_class, PROP_REMOTE_ADDRESS,
164                 g_param_spec_object (SOUP_CONNECTION_REMOTE_ADDRESS,
165                                      "Remote address",
166                                      "The address of the HTTP or proxy server",
167                                      SOUP_TYPE_ADDRESS,
168                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
169         g_object_class_install_property (
170                 object_class, PROP_TUNNEL_ADDRESS,
171                 g_param_spec_object (SOUP_CONNECTION_TUNNEL_ADDRESS,
172                                      "Tunnel address",
173                                      "The address of the HTTPS server this tunnel connects to",
174                                      SOUP_TYPE_ADDRESS,
175                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
176         g_object_class_install_property (
177                 object_class, PROP_PROXY_URI,
178                 g_param_spec_boxed (SOUP_CONNECTION_PROXY_URI,
179                                     "Proxy URI",
180                                     "URI of the HTTP proxy this connection connects to",
181                                     SOUP_TYPE_URI,
182                                     G_PARAM_READWRITE));
183         g_object_class_install_property (
184                 object_class, PROP_SSL_CREDS,
185                 g_param_spec_pointer (SOUP_CONNECTION_SSL_CREDENTIALS,
186                                       "SSL credentials",
187                                       "Opaque SSL credentials for this connection",
188                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
189         g_object_class_install_property (
190                 object_class, PROP_SSL_STRICT,
191                 g_param_spec_boolean (SOUP_CONNECTION_SSL_STRICT,
192                                       "Strictly validate SSL certificates",
193                                       "Whether certificate errors should be considered a connection error",
194                                       TRUE,
195                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
196         g_object_class_install_property (
197                 object_class, PROP_SSL_FALLBACK,
198                 g_param_spec_boolean (SOUP_CONNECTION_SSL_FALLBACK,
199                                       "SSLv3 fallback",
200                                       "Use SSLv3 instead of TLS",
201                                       FALSE,
202                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
203         g_object_class_install_property (
204                 object_class, PROP_ASYNC_CONTEXT,
205                 g_param_spec_pointer (SOUP_CONNECTION_ASYNC_CONTEXT,
206                                       "Async GMainContext",
207                                       "GMainContext to dispatch this connection's async I/O in",
208                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
209         g_object_class_install_property (
210                 object_class, PROP_TIMEOUT,
211                 g_param_spec_uint (SOUP_CONNECTION_TIMEOUT,
212                                    "Timeout value",
213                                    "Value in seconds to timeout a blocking I/O",
214                                    0, G_MAXUINT, 0,
215                                    G_PARAM_READWRITE));
216         g_object_class_install_property (
217                 object_class, PROP_IDLE_TIMEOUT,
218                 g_param_spec_uint (SOUP_CONNECTION_IDLE_TIMEOUT,
219                                    "Idle Timeout",
220                                    "Connection lifetime when idle",
221                                    0, G_MAXUINT, 0,
222                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
223         g_object_class_install_property (
224                 object_class, PROP_STATE,
225                 g_param_spec_enum (SOUP_CONNECTION_STATE,
226                                    "Connection state",
227                                    "Current state of connection",
228                                    SOUP_TYPE_CONNECTION_STATE, SOUP_CONNECTION_NEW,
229                                    G_PARAM_READWRITE));
230         g_object_class_install_property (
231                 object_class, PROP_MESSAGE,
232                 g_param_spec_object (SOUP_CONNECTION_MESSAGE,
233                                      "Message",
234                                      "Message being processed",
235                                      SOUP_TYPE_MESSAGE,
236                                      G_PARAM_READABLE));
237 }
238
239
240 SoupConnection *
241 soup_connection_new (const char *propname1, ...)
242 {
243         SoupConnection *conn;
244         va_list ap;
245
246         va_start (ap, propname1);
247         conn = (SoupConnection *)g_object_new_valist (SOUP_TYPE_CONNECTION,
248                                                       propname1, ap);
249         va_end (ap);
250
251         return conn;
252 }
253
254 static void
255 set_property (GObject *object, guint prop_id,
256               const GValue *value, GParamSpec *pspec)
257 {
258         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
259
260         switch (prop_id) {
261         case PROP_REMOTE_ADDRESS:
262                 priv->remote_addr = g_value_dup_object (value);
263                 break;
264         case PROP_TUNNEL_ADDRESS:
265                 priv->tunnel_addr = g_value_dup_object (value);
266                 break;
267         case PROP_PROXY_URI:
268                 if (priv->proxy_uri)
269                         soup_uri_free (priv->proxy_uri);
270                 priv->proxy_uri = g_value_dup_boxed (value);
271                 break;
272         case PROP_SSL_CREDS:
273                 priv->ssl_creds = g_value_get_pointer (value);
274                 break;
275         case PROP_SSL_STRICT:
276                 priv->ssl_strict = g_value_get_boolean (value);
277                 break;
278         case PROP_SSL_FALLBACK:
279                 priv->ssl_fallback = g_value_get_boolean (value);
280                 break;
281         case PROP_ASYNC_CONTEXT:
282                 priv->async_context = g_value_get_pointer (value);
283                 if (priv->async_context)
284                         g_main_context_ref (priv->async_context);
285                 break;
286         case PROP_TIMEOUT:
287                 priv->io_timeout = g_value_get_uint (value);
288                 break;
289         case PROP_IDLE_TIMEOUT:
290                 priv->idle_timeout = g_value_get_uint (value);
291                 break;
292         case PROP_STATE:
293                 soup_connection_set_state (SOUP_CONNECTION (object), g_value_get_uint (value));
294                 break;
295         default:
296                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
297                 break;
298         }
299 }
300
301 static void
302 get_property (GObject *object, guint prop_id,
303               GValue *value, GParamSpec *pspec)
304 {
305         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
306
307         switch (prop_id) {
308         case PROP_REMOTE_ADDRESS:
309                 g_value_set_object (value, priv->remote_addr);
310                 break;
311         case PROP_TUNNEL_ADDRESS:
312                 g_value_set_object (value, priv->tunnel_addr);
313                 break;
314         case PROP_PROXY_URI:
315                 g_value_set_boxed (value, priv->proxy_uri);
316                 break;
317         case PROP_SSL_CREDS:
318                 g_value_set_pointer (value, priv->ssl_creds);
319                 break;
320         case PROP_SSL_STRICT:
321                 g_value_set_boolean (value, priv->ssl_strict);
322                 break;
323         case PROP_SSL_FALLBACK:
324                 g_value_set_boolean (value, priv->ssl_fallback);
325                 break;
326         case PROP_ASYNC_CONTEXT:
327                 g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
328                 break;
329         case PROP_TIMEOUT:
330                 g_value_set_uint (value, priv->io_timeout);
331                 break;
332         case PROP_IDLE_TIMEOUT:
333                 g_value_set_uint (value, priv->idle_timeout);
334                 break;
335         case PROP_STATE:
336                 g_value_set_enum (value, priv->state);
337                 break;
338         case PROP_MESSAGE:
339                 if (priv->cur_item)
340                         g_value_set_object (value, priv->cur_item->msg);
341                 else
342                         g_value_set_object (value, NULL);
343                 break;
344         default:
345                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346                 break;
347         }
348 }
349
350 static gboolean
351 idle_timeout (gpointer conn)
352 {
353         soup_connection_disconnect (conn);
354         return FALSE;
355 }
356
357 static void
358 start_idle_timer (SoupConnection *conn)
359 {
360         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
361
362         if (priv->idle_timeout > 0 && !priv->idle_timeout_src) {
363                 priv->idle_timeout_src =
364                         soup_add_timeout (priv->async_context,
365                                           priv->idle_timeout * 1000,
366                                           idle_timeout, conn);
367         }
368 }
369
370 static void
371 stop_idle_timer (SoupConnectionPrivate *priv)
372 {
373         if (priv->idle_timeout_src) {
374                 g_source_destroy (priv->idle_timeout_src);
375                 priv->idle_timeout_src = NULL;
376         }
377 }
378
379 static void
380 set_current_item (SoupConnection *conn, SoupMessageQueueItem *item)
381 {
382         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
383
384         g_return_if_fail (priv->cur_item == NULL);
385
386         g_object_freeze_notify (G_OBJECT (conn));
387
388         stop_idle_timer (priv);
389
390         item->state = SOUP_MESSAGE_RUNNING;
391         priv->cur_item = item;
392         g_object_notify (G_OBJECT (conn), "message");
393
394         if (priv->state == SOUP_CONNECTION_IDLE ||
395             item->msg->method != SOUP_METHOD_CONNECT)
396                 soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
397
398         g_object_thaw_notify (G_OBJECT (conn));
399 }
400
401 static void
402 clear_current_item (SoupConnection *conn)
403 {
404         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
405
406         g_object_freeze_notify (G_OBJECT (conn));
407
408         priv->unused_timeout = 0;
409         start_idle_timer (conn);
410
411         if (priv->cur_item) {
412                 SoupMessageQueueItem *item;
413
414                 item = priv->cur_item;
415                 priv->cur_item = NULL;
416                 g_object_notify (G_OBJECT (conn), "message");
417
418                 if (item->msg->method == SOUP_METHOD_CONNECT &&
419                     SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code)) {
420                         /* We're now effectively no longer proxying */
421                         soup_uri_free (priv->proxy_uri);
422                         priv->proxy_uri = NULL;
423                 }
424
425                 if (!soup_message_is_keepalive (item->msg))
426                         soup_connection_disconnect (conn);
427         }
428
429         g_object_thaw_notify (G_OBJECT (conn));
430 }
431
432 static void
433 socket_disconnected (SoupSocket *sock, gpointer conn)
434 {
435         soup_connection_disconnect (conn);
436 }
437
438 typedef struct {
439         SoupConnection *conn;
440         SoupConnectionCallback callback;
441         gpointer callback_data;
442         GCancellable *cancellable;
443 } SoupConnectionAsyncConnectData;
444
445 static void
446 socket_connect_finished (SoupSocket *socket, guint status, gpointer user_data)
447 {
448         SoupConnectionAsyncConnectData *data = user_data;
449         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
450
451         if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
452                 g_signal_connect (priv->socket, "disconnected",
453                                   G_CALLBACK (socket_disconnected), data->conn);
454
455                 soup_connection_set_state (data->conn, SOUP_CONNECTION_IN_USE);
456                 priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
457                 start_idle_timer (data->conn);
458         } else if (status == SOUP_STATUS_TLS_FAILED) {
459                 priv->ssl_fallback = TRUE;
460                 status = SOUP_STATUS_TRY_AGAIN;
461         }
462
463         if (data->callback) {
464                 if (priv->proxy_uri != NULL)
465                         status = soup_status_proxify (status);
466                 data->callback (data->conn, status, data->callback_data);
467         }
468         g_object_unref (data->conn);
469         if (data->cancellable)
470                 g_object_unref (data->cancellable);
471         g_slice_free (SoupConnectionAsyncConnectData, data);
472 }
473
474 static void
475 socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
476 {
477         SoupConnectionAsyncConnectData *data = user_data;
478         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
479
480         if (SOUP_STATUS_IS_SUCCESSFUL (status) &&
481             priv->ssl_creds && !priv->tunnel_addr) {
482                 if (soup_socket_start_ssl (sock, data->cancellable)) {
483                         soup_socket_handshake_async (sock, data->cancellable,
484                                                      socket_connect_finished, data);
485                         return;
486                 }
487
488                 status = SOUP_STATUS_SSL_FAILED;
489         }
490
491         socket_connect_finished (sock, status, data);
492 }
493
494 void
495 soup_connection_connect_async (SoupConnection *conn,
496                                GCancellable *cancellable,
497                                SoupConnectionCallback callback,
498                                gpointer user_data)
499 {
500         SoupConnectionAsyncConnectData *data;
501         SoupConnectionPrivate *priv;
502
503         g_return_if_fail (SOUP_IS_CONNECTION (conn));
504         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
505         g_return_if_fail (priv->socket == NULL);
506
507         soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
508
509         data = g_slice_new (SoupConnectionAsyncConnectData);
510         data->conn = g_object_ref (conn);
511         data->callback = callback;
512         data->callback_data = user_data;
513         data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
514
515         priv->socket =
516                 soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
517                                  SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
518                                  SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
519                                  SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
520                                  SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
521                                  SOUP_SOCKET_TIMEOUT, priv->io_timeout,
522                                  "clean-dispose", TRUE,
523                                  NULL);
524         soup_socket_connect_async (priv->socket, cancellable,
525                                    socket_connect_result, data);
526 }
527
528 guint
529 soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable)
530 {
531         SoupConnectionPrivate *priv;
532         guint status;
533
534         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
535         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
536         g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
537
538         soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
539
540         priv->socket =
541                 soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
542                                  SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
543                                  SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
544                                  SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
545                                  SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
546                                  SOUP_SOCKET_TIMEOUT, priv->io_timeout,
547                                  "clean-dispose", TRUE,
548                                  NULL);
549
550         status = soup_socket_connect_sync (priv->socket, cancellable);
551
552         if (!SOUP_STATUS_IS_SUCCESSFUL (status))
553                 goto fail;
554                 
555         if (priv->ssl_creds && !priv->tunnel_addr) {
556                 if (!soup_socket_start_ssl (priv->socket, cancellable))
557                         status = SOUP_STATUS_SSL_FAILED;
558                 else {
559                         status = soup_socket_handshake_sync (priv->socket, cancellable);
560                         if (status == SOUP_STATUS_TLS_FAILED) {
561                                 priv->ssl_fallback = TRUE;
562                                 status = SOUP_STATUS_TRY_AGAIN;
563                         }
564                 }
565         }
566
567         if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
568                 g_signal_connect (priv->socket, "disconnected",
569                                   G_CALLBACK (socket_disconnected), conn);
570
571                 soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
572                 priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
573                 start_idle_timer (conn);
574         } else {
575         fail:
576                 if (priv->socket) {
577                         soup_socket_disconnect (priv->socket);
578                         g_object_unref (priv->socket);
579                         priv->socket = NULL;
580                 }
581         }
582
583         if (priv->proxy_uri != NULL)
584                 status = soup_status_proxify (status);
585         return status;
586 }
587
588 SoupAddress *
589 soup_connection_get_tunnel_addr (SoupConnection *conn)
590 {
591         SoupConnectionPrivate *priv;
592
593         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
594         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
595
596         return priv->tunnel_addr;
597 }
598
599 guint
600 soup_connection_start_ssl_sync (SoupConnection *conn,
601                                 GCancellable   *cancellable)
602 {
603         SoupConnectionPrivate *priv;
604         const char *server_name;
605         guint status;
606
607         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
608         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
609
610         server_name = soup_address_get_name (priv->tunnel_addr ?
611                                              priv->tunnel_addr :
612                                              priv->remote_addr);
613         if (!soup_socket_start_proxy_ssl (priv->socket, server_name,
614                                           cancellable))
615                 return SOUP_STATUS_SSL_FAILED;
616
617         status = soup_socket_handshake_sync (priv->socket, cancellable);
618         if (status == SOUP_STATUS_TLS_FAILED) {
619                 priv->ssl_fallback = TRUE;
620                 status = SOUP_STATUS_TRY_AGAIN;
621         }
622
623         return status;
624 }
625
626 static void
627 start_ssl_completed (SoupSocket *socket, guint status, gpointer user_data)
628 {
629         SoupConnectionAsyncConnectData *data = user_data;
630         SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
631
632         if (status == SOUP_STATUS_TLS_FAILED) {
633                 priv->ssl_fallback = TRUE;
634                 status = SOUP_STATUS_TRY_AGAIN;
635         }
636
637         data->callback (data->conn, status, data->callback_data);
638         g_object_unref (data->conn);
639         g_slice_free (SoupConnectionAsyncConnectData, data);
640 }
641
642 static gboolean
643 idle_start_ssl_completed (gpointer user_data)
644 {
645         SoupConnectionAsyncConnectData *data = user_data;
646
647         start_ssl_completed (NULL, SOUP_STATUS_SSL_FAILED, data);
648         return FALSE;
649 }
650
651 void
652 soup_connection_start_ssl_async (SoupConnection   *conn,
653                                  GCancellable     *cancellable,
654                                  SoupConnectionCallback callback,
655                                  gpointer          user_data)
656 {
657         SoupConnectionPrivate *priv;
658         const char *server_name;
659         SoupConnectionAsyncConnectData *data;
660
661         g_return_if_fail (SOUP_IS_CONNECTION (conn));
662         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
663
664         data = g_slice_new (SoupConnectionAsyncConnectData);
665         data->conn = g_object_ref (conn);
666         data->callback = callback;
667         data->callback_data = user_data;
668
669         server_name = soup_address_get_name (priv->tunnel_addr ?
670                                              priv->tunnel_addr :
671                                              priv->remote_addr);
672         if (!soup_socket_start_proxy_ssl (priv->socket, server_name,
673                                           cancellable)) {
674                 soup_add_completion (priv->async_context,
675                                      idle_start_ssl_completed, data);
676                 return;
677         }
678
679         soup_socket_handshake_async (priv->socket, cancellable,
680                                      start_ssl_completed, data);
681 }
682
683 /**
684  * soup_connection_disconnect:
685  * @conn: a connection
686  *
687  * Disconnects @conn's socket and emits a %disconnected signal.
688  * After calling this, @conn will be essentially useless.
689  **/
690 void
691 soup_connection_disconnect (SoupConnection *conn)
692 {
693         SoupConnectionPrivate *priv;
694         SoupConnectionState old_state;
695
696         g_return_if_fail (SOUP_IS_CONNECTION (conn));
697         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
698
699         old_state = priv->state;
700         if (old_state != SOUP_CONNECTION_DISCONNECTED)
701                 soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
702
703         if (priv->socket) {
704                 g_signal_handlers_disconnect_by_func (priv->socket,
705                                                       socket_disconnected, conn);
706                 soup_socket_disconnect (priv->socket);
707                 g_object_unref (priv->socket);
708                 priv->socket = NULL;
709         }
710
711         if (old_state != SOUP_CONNECTION_DISCONNECTED)
712                 g_signal_emit (conn, signals[DISCONNECTED], 0);
713 }
714
715 SoupSocket *
716 soup_connection_get_socket (SoupConnection *conn)
717 {
718         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
719
720         return SOUP_CONNECTION_GET_PRIVATE (conn)->socket;
721 }
722
723 SoupURI *
724 soup_connection_get_proxy_uri (SoupConnection *conn)
725 {
726         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
727
728         return SOUP_CONNECTION_GET_PRIVATE (conn)->proxy_uri;
729 }
730
731 gboolean
732 soup_connection_is_via_proxy (SoupConnection *conn)
733 {
734         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
735
736         return SOUP_CONNECTION_GET_PRIVATE (conn)->proxy_uri != NULL;
737 }
738
739 SoupConnectionState
740 soup_connection_get_state (SoupConnection *conn)
741 {
742         SoupConnectionPrivate *priv;
743
744         g_return_val_if_fail (SOUP_IS_CONNECTION (conn),
745                               SOUP_CONNECTION_DISCONNECTED);
746         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
747
748 #ifdef G_OS_UNIX
749         if (priv->state == SOUP_CONNECTION_IDLE) {
750                 GPollFD pfd;
751
752                 pfd.fd = soup_socket_get_fd (priv->socket);
753                 pfd.events = G_IO_IN;
754                 pfd.revents = 0;
755                 if (g_poll (&pfd, 1, 0) == 1)
756                         soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED);
757         }
758 #endif
759         if (priv->state == SOUP_CONNECTION_IDLE &&
760             priv->unused_timeout && priv->unused_timeout < time (NULL))
761                 soup_connection_set_state (conn, SOUP_CONNECTION_REMOTE_DISCONNECTED);
762
763         return priv->state;
764 }
765
766 void
767 soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
768 {
769         SoupConnectionPrivate *priv;
770         SoupConnectionState old_state;
771
772         g_return_if_fail (SOUP_IS_CONNECTION (conn));
773         g_return_if_fail (state >= SOUP_CONNECTION_NEW &&
774                           state <= SOUP_CONNECTION_DISCONNECTED);
775
776         g_object_freeze_notify (G_OBJECT (conn));
777
778         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
779         old_state = priv->state;
780         priv->state = state;
781         if ((state == SOUP_CONNECTION_IDLE ||
782              state == SOUP_CONNECTION_DISCONNECTED) &&
783             old_state == SOUP_CONNECTION_IN_USE)
784                 clear_current_item (conn);
785
786         g_object_notify (G_OBJECT (conn), "state");
787         g_object_thaw_notify (G_OBJECT (conn));
788 }
789
790 gboolean
791 soup_connection_get_ever_used (SoupConnection *conn)
792 {
793         g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
794
795         return SOUP_CONNECTION_GET_PRIVATE (conn)->unused_timeout == 0;
796 }
797
798 gboolean
799 soup_connection_get_ssl_fallback (SoupConnection *conn)
800 {
801         return SOUP_CONNECTION_GET_PRIVATE (conn)->ssl_fallback;
802 }
803
804 void
805 soup_connection_send_request (SoupConnection          *conn,
806                               SoupMessageQueueItem    *item,
807                               SoupMessageCompletionFn  completion_cb,
808                               gpointer                 user_data)
809 {
810         SoupConnectionPrivate *priv;
811
812         g_return_if_fail (SOUP_IS_CONNECTION (conn));
813         g_return_if_fail (item != NULL);
814         priv = SOUP_CONNECTION_GET_PRIVATE (conn);
815         g_return_if_fail (priv->state != SOUP_CONNECTION_NEW && priv->state != SOUP_CONNECTION_DISCONNECTED);
816
817         if (item != priv->cur_item)
818                 set_current_item (conn, item);
819         soup_message_send_request (item, completion_cb, user_data);
820 }