Starting on TcpClient, some formatting fixes
[cascardo/gnio.git] / gnio / gtcpclient.c
1 /* GNIO - GLib Network Layer of GIO
2  *
3  * Copyright (C) 2008 Christian Kellner, Samuel Cormier-Iijima
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Christian Kellner <gicmo@gnome.org>
21  *          Samuel Cormier-Iijima <sciyoshi@gmail.com>
22  */
23
24 #include <config.h>
25 #include <glib.h>
26 #include <gio/gio.h>
27 #include <gio/gasynchelper.h>
28
29 #include <string.h>
30 #include <errno.h>
31
32 #include "ginetaddress.h"
33 #include "ginet4address.h"
34 #include "ginet6address.h"
35 #include "gsocket.h"
36 #include "gtcpclient.h"
37 #include "gnioerror.h"
38 #include "ginetsocketaddress.h"
39
40 G_DEFINE_TYPE (GTcpClient, g_tcp_client, G_TYPE_OBJECT);
41
42 enum
43 {
44   PROP_0,
45   PROP_ADDRESS
46 };
47
48 struct _GTcpClientPrivate
49 {
50   GSocketAddress *address;
51 };
52
53 static void
54 g_tcp_client_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
55 {
56   GTcpClient *client = G_TCP_CLIENT (object);
57
58   switch (prop_id)
59     {
60       case PROP_ADDRESS:
61         g_value_set_object (value, client->priv->address);
62         break;
63
64       default:
65         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
66     }
67 }
68
69 static void
70 g_tcp_client_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
71 {
72   GTcpClient *client = G_TCP_CLIENT (object);
73
74   switch (prop_id)
75     {
76       default:
77         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
78     }
79 }
80
81 static void
82 g_tcp_client_finalize (GObject *object)
83 {
84   GTcpClient *client = G_TCP_CLIENT (object);
85
86   if (G_OBJECT_CLASS (g_tcp_client_parent_class)->finalize)
87     (*G_OBJECT_CLASS (g_tcp_client_parent_class)->finalize) (object);
88 }
89
90 static void
91 g_tcp_client_dispose (GObject *object)
92 {
93   GTcpClient *client = G_TCP_CLIENT (object);
94
95   if (G_OBJECT_CLASS (g_tcp_client_parent_class)->dispose)
96     (*G_OBJECT_CLASS (g_tcp_client_parent_class)->dispose) (object);
97 }
98
99 static void
100 g_tcp_client_class_init (GTcpClientClass *klass)
101 {
102   GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
103
104   g_type_class_add_private (klass, sizeof (GTcpClientPrivate));
105
106   gobject_class->finalize = g_tcp_client_finalize;
107   gobject_class->dispose = g_tcp_client_dispose;
108   gobject_class->set_property = g_tcp_client_set_property;
109   gobject_class->get_property = g_tcp_client_get_property;
110
111   g_object_class_install_property (gobject_class, PROP_ADDRESS,
112                                    g_param_spec_object ("address",
113                                                         "address",
114                                                         "the remote address the socket will connect to",
115                                                         G_TYPE_SOCKET_ADDRESS,
116                                                         G_TYPE_CONSTRUCT_ONLY | G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
117 }
118
119 static void
120 g_tcp_client_init (GTcpClient *client)
121 {
122   client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, G_TYPE_TCP_CLIENT, GTcpClientPrivate);
123 }
124
125 GTcpClient *
126 g_tcp_client_new (GInetSocketAddress *address,
127                   GError **error)
128 {
129   return NULL;
130 }
131
132 gboolean
133 g_tcp_client_connect (GTcpClient *client,
134                       GError **error)
135 {
136   return FALSE;
137 }
138
139 typedef struct {
140   GAsyncReadyCallback  callback;
141   GCancellable        *cancellable;
142   gpointer             user_data;
143   GSocket             *socket;
144   gchar                address_buffer[256];
145   gsize                address_length;
146 } ConnectData;
147
148 static gboolean
149 connect_callback (ConnectData *data,
150                   GIOCondition condition,
151                   gint fd)
152 {
153   GSocket *socket;
154   GSimpleAsyncResult *result = NULL;
155   gint sockerr = 0;
156   gsize sockerr_size = 1;
157
158   socket = data->socket;
159
160   if (condition & G_IO_OUT)
161     {
162       result = g_simple_async_result_new (G_OBJECT (socket), data->callback, data->user_data, g_socket_connect_async);
163     }
164   else if (condition & G_IO_ERR)
165     {
166       if (getsockopt (fd, SOL_SOCKET, SO_ERROR, (gpointer) &sockerr, &sockerr_size) < 0)
167         g_warning ("getsockopt: %s", g_strerror (errno));
168
169       if (sockerr != 0)
170         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));
171       else
172         g_warning ("getsockopt SO_ERROR returned no error, with sockerr = %d", sockerr);
173     }
174
175   g_simple_async_result_complete (result);
176
177   g_object_unref (result);
178
179   return FALSE;
180 }
181
182 void
183 g_socket_connect_async (GTcpClient          *client,
184                         GCancellable        *cancellable,
185                         GAsyncReadyCallback  callback,
186                         gpointer             user_data)
187 {
188   GSource *source;
189   GSimpleAsyncResult *result;
190   ConnectData *data;
191   gint ret;
192   gchar buffer[256];
193   gsize len;
194
195   g_return_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address));
196
197   if (g_socket_get_blocking (socket))
198     g_socket_set_blocking (socket, FALSE);
199
200   g_socket_address_to_native (address, buffer);
201
202   len = g_socket_address_native_size (address);
203
204   if ((ret = connect (socket->priv->fd, (struct sockaddr *) buffer, len)) < 0)
205     {
206       if (errno == EINPROGRESS)
207         {
208           source = _g_fd_source_new (socket->priv->fd, G_IO_OUT | G_IO_ERR, cancellable);
209
210           data = g_new (ConnectData, 1);
211
212           data->socket = socket;
213           data->callback = callback;
214           data->cancellable = cancellable;
215           data->user_data = user_data;
216           data->address_length = len;
217           memcpy (data->address_buffer, buffer, len);
218
219           g_source_set_callback (source, (GSourceFunc) connect_callback, data, g_free);
220
221           g_source_attach (source, NULL);
222         }
223       else
224         {
225           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));
226         }
227     }
228   else
229     {
230       result = g_simple_async_result_new (G_OBJECT (socket), callback, user_data, g_socket_connect_async);
231
232       g_simple_async_result_complete_in_idle (result);
233
234       g_object_unref (result);
235     }
236 }
237
238 gboolean
239 g_socket_connect_finish (GSocket       *socket,
240                          GAsyncResult  *result,
241                          GError       **error)
242 {
243   GSimpleAsyncResult *simple;
244
245   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
246
247   simple = G_SIMPLE_ASYNC_RESULT (result);
248
249   if (g_simple_async_result_propagate_error (simple, error))
250     return FALSE;
251
252   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_socket_connect_async);
253
254   return TRUE;
255 }
256
257 void
258 g_tcp_client_close (GTcpClient *tcp_client)
259 {
260   g_return_if_fail (G_IS_TCP_CLIENT (tcp_client));
261 }