Merge branch 'master' into errorstate
[cascardo/gnio.git] / gnio / gtcpclient.c
index 599358e..ae8de6d 100644 (file)
@@ -41,12 +41,13 @@ G_DEFINE_TYPE (GTcpClient, g_tcp_client, G_TYPE_OBJECT);
 
 enum
 {
-  PROP_0
+  PROP_0,
+  PROP_ADDRESS
 };
 
 struct _GTcpClientPrivate
 {
-
+  GSocketAddress *address;
 };
 
 static void
@@ -56,6 +57,10 @@ g_tcp_client_get_property (GObject *object, guint prop_id, GValue *value, GParam
 
   switch (prop_id)
     {
+      case PROP_ADDRESS:
+        g_value_set_object (value, client->priv->address);
+        break;
+
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -102,6 +107,13 @@ g_tcp_client_class_init (GTcpClientClass *klass)
   gobject_class->dispose = g_tcp_client_dispose;
   gobject_class->set_property = g_tcp_client_set_property;
   gobject_class->get_property = g_tcp_client_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_ADDRESS,
+                                   g_param_spec_object ("address",
+                                                        "address",
+                                                        "the remote address the socket will connect to",
+                                                        G_TYPE_SOCKET_ADDRESS,
+                                                        G_TYPE_CONSTRUCT_ONLY | G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
 }
 
 static void
@@ -117,6 +129,131 @@ g_tcp_client_new (GInetSocketAddress *address,
   return NULL;
 }
 
+gboolean
+g_tcp_client_connect (GTcpClient *client,
+                      GError **error)
+{
+  return FALSE;
+}
+
+typedef struct {
+  GAsyncReadyCallback  callback;
+  GCancellable        *cancellable;
+  gpointer             user_data;
+  GSocket             *socket;
+  gchar                address_buffer[256];
+  gsize                address_length;
+} ConnectData;
+
+static gboolean
+connect_callback (ConnectData *data,
+                  GIOCondition condition,
+                  gint fd)
+{
+  GSocket *socket;
+  GSimpleAsyncResult *result = NULL;
+  gint sockerr = 0;
+  gsize sockerr_size = 1;
+
+  socket = data->socket;
+
+  if (condition & G_IO_OUT)
+    {
+      result = g_simple_async_result_new (G_OBJECT (socket), data->callback, data->user_data, g_socket_connect_async);
+    }
+  else if (condition & G_IO_ERR)
+    {
+      if (getsockopt (fd, SOL_SOCKET, SO_ERROR, (gpointer) &sockerr, &sockerr_size) < 0)
+        g_warning ("getsockopt: %s", g_strerror (errno));
+
+      if (sockerr != 0)
+        result = g_simple_async_result_new_error (G_OBJECT (socket), data->callback, data->user_data, G_IO_ERROR, g_io_error_from_errno (sockerr), "error connecting: %s", g_strerror (sockerr));
+      else
+        g_warning ("getsockopt SO_ERROR returned no error, with sockerr = %d", sockerr);
+    }
+
+  g_simple_async_result_complete (result);
+
+  g_object_unref (result);
+
+  return FALSE;
+}
+
+void
+g_socket_connect_async (GTcpClient          *client,
+                        GCancellable        *cancellable,
+                        GAsyncReadyCallback  callback,
+                        gpointer             user_data)
+{
+  GSource *source;
+  GSimpleAsyncResult *result;
+  ConnectData *data;
+  gint ret;
+  gchar buffer[256];
+  gsize len;
+
+  g_return_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address));
+
+  if (g_socket_get_blocking (socket))
+    g_socket_set_blocking (socket, FALSE);
+
+  g_socket_address_to_native (address, buffer);
+
+  len = g_socket_address_native_size (address);
+
+  if ((ret = connect (socket->priv->fd, (struct sockaddr *) buffer, len)) < 0)
+    {
+      if (errno == EINPROGRESS)
+        {
+          source = _g_fd_source_new (socket->priv->fd, G_IO_OUT | G_IO_ERR, cancellable);
+
+          data = g_new (ConnectData, 1);
+
+          data->socket = socket;
+          data->callback = callback;
+          data->cancellable = cancellable;
+          data->user_data = user_data;
+          data->address_length = len;
+          memcpy (data->address_buffer, buffer, len);
+
+          g_source_set_callback (source, (GSourceFunc) connect_callback, data, g_free);
+
+          g_source_attach (source, NULL);
+        }
+      else
+        {
+          g_simple_async_report_error_in_idle (G_OBJECT (socket), callback, user_data, G_IO_ERROR, g_io_error_from_errno (errno), "error connecting: %s", g_strerror (errno));
+        }
+    }
+  else
+    {
+      result = g_simple_async_result_new (G_OBJECT (socket), callback, user_data, g_socket_connect_async);
+
+      g_simple_async_result_complete_in_idle (result);
+
+      g_object_unref (result);
+    }
+}
+
+gboolean
+g_socket_connect_finish (GSocket       *socket,
+                         GAsyncResult  *result,
+                         GError       **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_socket_connect_async);
+
+  return TRUE;
+}
+
 void
 g_tcp_client_close (GTcpClient *tcp_client)
 {