Imported Upstream version 2.60.1
[platform/upstream/glib-networking.git] / tls / gnutls / gtlsoutputstream-gnutls.c
index 462d74e..062b8ef 100644 (file)
@@ -1,11 +1,13 @@
-/* GIO - GLib Input, Output and Streaming Library
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
  *
- * Copyright © 2010 Red Hat, Inc.
+ * Copyright 2010 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
  */
 
 #include "config.h"
 #include "gtlsoutputstream-gnutls.h"
 
+#include <glib/gi18n.h>
+
+struct _GTlsOutputStreamGnutls
+{
+  GOutputStream parent_instance;
+
+  GWeakRef weak_conn;
+};
+
 static void g_tls_output_stream_gnutls_pollable_iface_init (GPollableOutputStreamInterface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (GTlsOutputStreamGnutls, g_tls_output_stream_gnutls, G_TYPE_OUTPUT_STREAM,
-                        G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, g_tls_output_stream_gnutls_pollable_iface_init)
-                        )
+                         G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, g_tls_output_stream_gnutls_pollable_iface_init)
+                         )
 
-struct _GTlsOutputStreamGnutlsPrivate
+static void
+g_tls_output_stream_gnutls_dispose (GObject *object)
 {
-  GTlsConnectionGnutls *conn;
+  GTlsOutputStreamGnutls *stream = G_TLS_OUTPUT_STREAM_GNUTLS (object);
 
-  /* pending operation metadata */
-  GCancellable *cancellable;
-  gconstpointer buffer;
-  gsize count;
-};
+  g_weak_ref_set (&stream->weak_conn, NULL);
+
+  G_OBJECT_CLASS (g_tls_output_stream_gnutls_parent_class)->dispose (object);
+}
 
 static void
 g_tls_output_stream_gnutls_finalize (GObject *object)
 {
   GTlsOutputStreamGnutls *stream = G_TLS_OUTPUT_STREAM_GNUTLS (object);
 
-  if (stream->priv->conn)
-    g_object_unref (stream->priv->conn);
+  g_weak_ref_clear (&stream->weak_conn);
 
   G_OBJECT_CLASS (g_tls_output_stream_gnutls_parent_class)->finalize (object);
 }
 
 static gssize
 g_tls_output_stream_gnutls_write (GOutputStream  *stream,
-                                 const void     *buffer,
-                                 gsize           count,
-                                 GCancellable   *cancellable,
-                                 GError        **error)
+                                  const void     *buffer,
+                                  gsize           count,
+                                  GCancellable   *cancellable,
+                                  GError        **error)
 {
   GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (stream);
+  GTlsConnectionGnutls *conn;
+  gssize ret;
 
-  return g_tls_connection_gnutls_write (tls_stream->priv->conn,
-                                       buffer, count, TRUE,
-                                       cancellable, error);
+  conn = g_weak_ref_get (&tls_stream->weak_conn);
+  if (conn == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                           _("Connection is closed"));
+      return -1;
+    }
+
+  ret = g_tls_connection_gnutls_write (conn, buffer, count, -1  /* blocking */,
+                                       cancellable, error);
+  g_object_unref (conn);
+  return ret;
 }
 
 static gboolean
-g_tls_output_stream_gnutls_write_ready (GIOStreamAdapter *adapter,
-                                       gpointer          user_data)
+g_tls_output_stream_gnutls_pollable_is_writable (GPollableOutputStream *pollable)
 {
-  GTlsOutputStreamGnutls *tls_stream;
-  GSimpleAsyncResult *simple = user_data;
-  gssize nwrote;
-  GError *error = NULL;
+  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+  GTlsConnectionGnutls *conn;
+  gboolean ret;
+
+  conn = g_weak_ref_get (&tls_stream->weak_conn);
+  if (conn == NULL)
+    return FALSE;
+
+  ret = g_tls_connection_gnutls_check (conn, G_IO_OUT);
+
+  g_object_unref (conn);
+
+  return ret;
+}
 
-  tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
-  g_object_unref (tls_stream);
+static GSource *
+g_tls_output_stream_gnutls_pollable_create_source (GPollableOutputStream *pollable,
+                                                   GCancellable         *cancellable)
+{
+  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+  GTlsConnectionGnutls *conn;
+  GSource *ret;
 
-  nwrote = g_tls_connection_gnutls_write (tls_stream->priv->conn,
-                                         tls_stream->priv->buffer,
-                                         tls_stream->priv->count, FALSE,
-                                         tls_stream->priv->cancellable,
-                                         &error);
-  if (nwrote == -1 &&
-      g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+  conn = g_weak_ref_get (&tls_stream->weak_conn);
+  if (conn == NULL)
     {
-      g_error_free (error);
-      return TRUE;
+      ret = g_idle_source_new ();
+      g_source_set_name (ret, "[glib-networking] g_tls_output_stream_gnutls_pollable_create_source dummy source");
+      return ret;
     }
 
-  if (error)
+  ret = g_tls_connection_gnutls_create_source (conn,
+                                               G_IO_OUT,
+                                               cancellable);
+  g_object_unref (conn);
+  return ret;
+}
+
+static gssize
+g_tls_output_stream_gnutls_pollable_write_nonblocking (GPollableOutputStream  *pollable,
+                                                       const void             *buffer,
+                                                       gsize                   size,
+                                                       GError                **error)
+{
+  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+  GTlsConnectionGnutls *conn;
+  gssize ret;
+
+  conn = g_weak_ref_get (&tls_stream->weak_conn);
+  if (conn == NULL)
     {
-      g_simple_async_result_set_from_error (simple, error);
-      g_error_free (error);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                           _("Connection is closed"));
+      return -1;
     }
-  else
-    g_simple_async_result_set_op_res_gssize (simple, nwrote);
 
-  if (tls_stream->priv->cancellable)
-    g_object_unref (tls_stream->priv->cancellable);
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
+  ret = g_tls_connection_gnutls_write (conn, buffer, size,
+                                       0  /* non-blocking */, NULL, error);
 
-  return FALSE;
+  g_object_unref (conn);
+  return ret;
 }
 
-static void
-g_tls_output_stream_gnutls_write_async (GOutputStream        *stream,
-                                       const void           *buffer,
-                                       gsize                 count,
-                                       gint                  io_priority,
-                                       GCancellable         *cancellable,
-                                       GAsyncReadyCallback   callback,
-                                       gpointer              user_data)
+static gboolean
+g_tls_output_stream_gnutls_close (GOutputStream            *stream,
+                                  GCancellable             *cancellable,
+                                  GError                  **error)
 {
   GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (stream);
-  GSimpleAsyncResult *simple;
-  gssize nwrote;
-  GError *error = NULL;
-  GSource *source;
+  GIOStream *conn;
+  gboolean ret;
 
-  simple = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
-                                     g_tls_output_stream_gnutls_write_async);
-  nwrote = g_tls_connection_gnutls_write (tls_stream->priv->conn,
-                                         buffer, count, FALSE,
-                                         cancellable, &error);
+  conn = g_weak_ref_get (&tls_stream->weak_conn);
+  if (conn == NULL)
+    return TRUE;
 
-  if (nwrote >= 0 ||
-      !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
-    {
-      if (error)
-       {
-         g_simple_async_result_set_from_error (simple, error);
-         g_error_free (error);
-       }
-      else
-       g_simple_async_result_set_op_res_gssize (simple, nwrote);
-      g_simple_async_result_complete_in_idle (simple);
-      g_object_unref (simple);
-      return;
-    }
+  ret = g_tls_connection_gnutls_close_internal (conn, G_TLS_DIRECTION_WRITE,
+                                                -1,  /* blocking */
+                                                cancellable, error);
 
-  tls_stream->priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-  tls_stream->priv->buffer = buffer;
-  tls_stream->priv->count = count;
-
-  source = g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
-                                                 G_IO_OUT,
-                                                 tls_stream->priv->cancellable);
-  g_source_set_callback (source,
-                        (GSourceFunc) g_tls_output_stream_gnutls_write_ready,
-                        simple, NULL);
-  g_source_attach (source, g_main_context_get_thread_default ());
-  g_source_unref (source);
+  g_object_unref (conn);
+  return ret;
 }
 
-static gssize
-g_tls_output_stream_gnutls_write_finish (GOutputStream  *stream,
-                                        GAsyncResult   *result,
-                                        GError        **error)
+/* We do async close as synchronous-in-a-thread so we don't need to
+ * implement G_IO_IN/G_IO_OUT flip-flopping just for this one case
+ * (since handshakes are also done synchronously now).
+ */
+static void
+close_thread (GTask        *task,
+              gpointer      object,
+              gpointer      task_data,
+              GCancellable *cancellable)
 {
-  g_return_val_if_fail (G_IS_TLS_OUTPUT_STREAM_GNUTLS (stream), -1);
-  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (stream), g_tls_output_stream_gnutls_write_async), -1);
+  GTlsOutputStreamGnutls *tls_stream = object;
+  GError *error = NULL;
+  GIOStream *conn;
 
-  return g_simple_async_result_get_op_res_gssize (G_SIMPLE_ASYNC_RESULT (result));
-}
+  conn = g_weak_ref_get (&tls_stream->weak_conn);
 
-static gboolean
-g_tls_output_stream_gnutls_pollable_is_writable (GPollableOutputStream *pollable)
-{
-  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+  if (conn && !g_tls_connection_gnutls_close_internal (conn,
+                                                       G_TLS_DIRECTION_WRITE,
+                                                       -1,  /* blocking */
+                                                       cancellable, &error))
+    g_task_return_error (task, error);
+  else
+    g_task_return_boolean (task, TRUE);
 
-  return g_tls_connection_gnutls_check (tls_stream->priv->conn, G_IO_OUT); 
+  if (conn)
+    g_object_unref (conn);
 }
 
-static GSource *
-g_tls_output_stream_gnutls_pollable_create_source (GPollableOutputStream *pollable,
-                                                  GCancellable         *cancellable)
+
+static void
+g_tls_output_stream_gnutls_close_async (GOutputStream            *stream,
+                                        int                       io_priority,
+                                        GCancellable             *cancellable,
+                                        GAsyncReadyCallback       callback,
+                                        gpointer                  user_data)
 {
-  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+  GTask *task;
 
-  return g_tls_connection_gnutls_create_source (tls_stream->priv->conn,
-                                               G_IO_OUT,
-                                               cancellable);
+  task = g_task_new (stream, cancellable, callback, user_data);
+  g_task_set_source_tag (task, g_tls_output_stream_gnutls_close_async);
+  g_task_set_priority (task, io_priority);
+  g_task_run_in_thread (task, close_thread);
+  g_object_unref (task);
 }
 
-static gssize
-g_tls_output_stream_gnutls_pollable_write_nonblocking (GPollableOutputStream  *pollable,
-                                                      const void             *buffer,
-                                                      gsize                   size,
-                                                      GError                **error)
+static gboolean
+g_tls_output_stream_gnutls_close_finish (GOutputStream            *stream,
+                                         GAsyncResult             *result,
+                                         GError                  **error)
 {
-  GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (pollable);
+  g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
+  g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
+                        g_tls_output_stream_gnutls_close_async, FALSE);
 
-  return g_tls_connection_gnutls_write (tls_stream->priv->conn,
-                                       buffer, size, FALSE,
-                                       NULL, error);
+  return g_task_propagate_boolean (G_TASK (result), error);
 }
 
 static void
@@ -201,13 +235,13 @@ g_tls_output_stream_gnutls_class_init (GTlsOutputStreamGnutlsClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass);
 
-  g_type_class_add_private (klass, sizeof (GTlsOutputStreamGnutlsPrivate));
-
+  gobject_class->dispose = g_tls_output_stream_gnutls_dispose;
   gobject_class->finalize = g_tls_output_stream_gnutls_finalize;
 
   output_stream_class->write_fn = g_tls_output_stream_gnutls_write;
-  output_stream_class->write_async = g_tls_output_stream_gnutls_write_async;
-  output_stream_class->write_finish = g_tls_output_stream_gnutls_write_finish;
+  output_stream_class->close_fn = g_tls_output_stream_gnutls_close;
+  output_stream_class->close_async = g_tls_output_stream_gnutls_close_async;
+  output_stream_class->close_finish = g_tls_output_stream_gnutls_close_finish;
 }
 
 static void
@@ -221,7 +255,6 @@ g_tls_output_stream_gnutls_pollable_iface_init (GPollableOutputStreamInterface *
 static void
 g_tls_output_stream_gnutls_init (GTlsOutputStreamGnutls *stream)
 {
-  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, GTlsOutputStreamGnutlsPrivate);
 }
 
 GOutputStream *
@@ -230,6 +263,7 @@ g_tls_output_stream_gnutls_new (GTlsConnectionGnutls *conn)
   GTlsOutputStreamGnutls *tls_stream;
 
   tls_stream = g_object_new (G_TYPE_TLS_OUTPUT_STREAM_GNUTLS, NULL);
-  tls_stream->priv->conn = g_object_ref (conn);
+  g_weak_ref_init (&tls_stream->weak_conn, conn);
+
   return G_OUTPUT_STREAM (tls_stream);
 }