TcpClient seems to be working for sync connects, but connect()
[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 <gnio/gnio.h>
28
29 #include <string.h>
30 #include <errno.h>
31
32 G_DEFINE_TYPE (GTcpClient, g_tcp_client, G_TYPE_OBJECT);
33
34 enum
35 {
36   PROP_0,
37   PROP_ADDRESS,
38   PROP_HOSTNAME,
39   PROP_PORT
40 };
41
42 struct _GTcpClientPrivate
43 {
44   GInetSocketAddress *address;
45   gchar              *hostname;
46   gushort             port;
47   GSocket            *socket;
48 };
49
50 static void
51 g_tcp_client_constructed (GObject *object)
52 {
53   GTcpClient *client = G_TCP_CLIENT (object);
54
55   if (client->priv->address)
56     {
57       // we've been constructed with an address, extract hostname+port
58       client->priv->hostname = g_inet_address_to_string (g_inet_socket_address_get_address (client->priv->address));
59       client->priv->port = g_inet_socket_address_get_port (client->priv->address);
60       return;
61     }
62 }
63
64 static void
65 g_tcp_client_get_property (GObject    *object,
66                            guint       prop_id,
67                            GValue     *value,
68                            GParamSpec *pspec)
69 {
70   GTcpClient *client = G_TCP_CLIENT (object);
71
72   switch (prop_id)
73     {
74       case PROP_ADDRESS:
75         g_value_set_object (value, client->priv->address);
76         break;
77
78       case PROP_HOSTNAME:
79         g_value_set_string (value, client->priv->hostname);
80         break;
81
82       case PROP_PORT:
83         g_value_set_uint (value, client->priv->port);
84         break;
85
86       default:
87         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
88     }
89 }
90
91 static void
92 g_tcp_client_set_property (GObject      *object,
93                            guint         prop_id,
94                            const GValue *value,
95                            GParamSpec   *pspec)
96 {
97   GTcpClient *client = G_TCP_CLIENT (object);
98
99   switch (prop_id)
100     {
101       case PROP_ADDRESS:
102         // sink the address' floating reference
103         client->priv->address = G_INET_SOCKET_ADDRESS (g_value_get_object (value));
104         if (client->priv->address)
105           g_object_ref_sink (client->priv->address);
106         break;
107
108       case PROP_HOSTNAME:
109         client->priv->hostname = g_value_dup_string (value);
110         break;
111
112       case PROP_PORT:
113         client->priv->port = g_value_get_uint (value);
114         break;
115
116       default:
117         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118     }
119 }
120
121 static void
122 g_tcp_client_finalize (GObject *object)
123 {
124   GTcpClient *client = G_TCP_CLIENT (object);
125
126   g_object_unref (client->priv->address);
127
128   if (G_OBJECT_CLASS (g_tcp_client_parent_class)->finalize)
129     (*G_OBJECT_CLASS (g_tcp_client_parent_class)->finalize) (object);
130 }
131
132 static void
133 g_tcp_client_dispose (GObject *object)
134 {
135   GTcpClient *client = G_TCP_CLIENT (object);
136
137   g_free (client->priv->hostname);
138
139   if (G_OBJECT_CLASS (g_tcp_client_parent_class)->dispose)
140     (*G_OBJECT_CLASS (g_tcp_client_parent_class)->dispose) (object);
141 }
142
143 static void
144 g_tcp_client_class_init (GTcpClientClass *klass)
145 {
146   GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
147
148   g_type_class_add_private (klass, sizeof (GTcpClientPrivate));
149
150   gobject_class->finalize = g_tcp_client_finalize;
151   gobject_class->dispose = g_tcp_client_dispose;
152   gobject_class->constructed = g_tcp_client_constructed;
153   gobject_class->set_property = g_tcp_client_set_property;
154   gobject_class->get_property = g_tcp_client_get_property;
155
156   g_object_class_install_property (gobject_class, PROP_ADDRESS,
157                                    g_param_spec_object ("address",
158                                                         "address",
159                                                         "the remote address the socket will connect to",
160                                                         G_TYPE_INET_SOCKET_ADDRESS,
161                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
162
163   g_object_class_install_property (gobject_class, PROP_HOSTNAME,
164                                    g_param_spec_string ("hostname",
165                                                         "hostname",
166                                                         "the hostname of the remote address the socket will connect to",
167                                                         NULL,
168                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
169
170   g_object_class_install_property (gobject_class, PROP_PORT,
171                                    g_param_spec_uint ("port",
172                                                       "port",
173                                                       "the remote port the socket will connect to",
174                                                       0,
175                                                       G_MAXUSHORT,
176                                                       0,
177                                                       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
178 }
179
180 static void
181 g_tcp_client_init (GTcpClient *client)
182 {
183   client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, G_TYPE_TCP_CLIENT, GTcpClientPrivate);
184
185   client->priv->address = NULL;
186   client->priv->hostname = NULL;
187   client->priv->port = 0;
188   client->priv->socket = NULL;
189 }
190
191 GTcpClient *
192 g_tcp_client_new (const gchar *hostname,
193                   gushort      port)
194 {
195   return G_TCP_CLIENT (g_object_new (G_TYPE_TCP_CLIENT, "hostname", hostname, "port", port, NULL));
196 }
197
198 GTcpClient *
199 g_tcp_client_new_from_address (GInetSocketAddress *address)
200 {
201   return G_TCP_CLIENT (g_object_new (G_TYPE_TCP_CLIENT, "address", address, NULL));
202 }
203
204 gboolean
205 g_tcp_client_connect (GTcpClient    *client,
206                       GCancellable  *cancellable,
207                       GError       **error)
208 {
209   GInetAddress *address;
210
211   g_return_val_if_fail (G_IS_TCP_CLIENT (client), FALSE);
212
213   if (!client->priv->address)
214     {
215       // we've been constructed with just hostname+port, resolve
216       GResolver *resolver = g_resolver_new ();
217
218       address = g_resolver_resolve (resolver, client->priv->hostname, cancellable, error);
219
220       if (!address)
221         return FALSE;
222
223       client->priv->address = g_inet_socket_address_new (address, client->priv->port);
224
225       g_object_unref (resolver);
226
227       g_object_ref_sink (client->priv->address);
228     }
229   else
230     {
231      address = g_inet_socket_address_get_address (client->priv->address);
232     }
233
234   if (G_IS_INET4_ADDRESS (address))
235     client->priv->socket = g_socket_new (G_SOCKET_DOMAIN_INET, G_SOCKET_TYPE_STREAM, NULL, error);
236   else if (G_IS_INET6_ADDRESS (address))
237     client->priv->socket = g_socket_new (G_SOCKET_DOMAIN_INET6, G_SOCKET_TYPE_STREAM, NULL, error);
238   else
239     {
240       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported address domain");
241       return FALSE;
242     }
243
244   if (!client->priv->socket)
245     return FALSE;
246
247   if (g_cancellable_set_error_if_cancelled (cancellable, error))
248     return FALSE;
249
250   if (!g_socket_connect (client->priv->socket, G_SOCKET_ADDRESS (client->priv->address), error))
251     return FALSE;
252
253   return TRUE;
254 }
255
256 typedef struct {
257   GAsyncReadyCallback  callback;
258   GCancellable        *cancellable;
259   gpointer             user_data;
260   GTcpClient          *client;
261   gchar                address_buffer[256];
262   gsize                address_length;
263 } ConnectData;
264
265 static gboolean
266 connect_callback (ConnectData *data,
267                   GIOCondition condition,
268                   gint fd)
269 {
270   return FALSE;
271 }
272
273 void
274 g_tcp_client_connect_async (GTcpClient          *client,
275                             GCancellable        *cancellable,
276                             GAsyncReadyCallback  callback,
277                             gpointer             user_data)
278 {
279
280 }
281
282 gboolean
283 g_tcp_client_connect_finish (GTcpClient    *client,
284                              GAsyncResult  *result,
285                              GError       **error)
286 {
287   GSimpleAsyncResult *simple;
288
289   g_return_val_if_fail (G_IS_TCP_CLIENT (client), FALSE);
290
291   simple = G_SIMPLE_ASYNC_RESULT (result);
292
293   if (g_simple_async_result_propagate_error (simple, error))
294     return FALSE;
295
296   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_tcp_client_connect_async);
297
298   return TRUE;
299 }
300
301 void
302 g_tcp_client_close (GTcpClient *tcp_client)
303 {
304   g_return_if_fail (G_IS_TCP_CLIENT (tcp_client));
305 }