If we get an EOF trying to read a uint or a time value, throw an exception.
[beagle.git] / libbeagle / beagle / beagle-request.c
blobea288dc66e4c347b39851efc855a231fb6ecf70a
1 /*
2 * beagle-request.c
4 * Copyright (C) 2005 Novell, Inc.
6 */
8 /*
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
38 #include "beagle-error-response.h"
39 #include "beagle-marshal.h"
40 #include "beagle-parser.h"
41 #include "beagle-request.h"
42 #include "beagle-private.h"
43 #include "beagle-util.h"
45 typedef struct {
46 char *path;
47 GIOChannel *channel;
48 guint io_watch;
49 BeagleParserContext *ctx;
50 #ifdef ENABLE_XML_DUMP
51 GString *data;
52 #endif
53 } BeagleRequestPrivate;
55 #define BEAGLE_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BEAGLE_TYPE_REQUEST, BeagleRequestPrivate))
57 enum {
58 CLOSED,
59 RESPONSE,
60 ERROR,
61 LAST_SIGNAL
64 static GObjectClass *parent_class = NULL;
65 static guint signals [LAST_SIGNAL] = { 0 };
67 G_DEFINE_TYPE (BeagleRequest, beagle_request, G_TYPE_OBJECT)
69 static void
70 beagle_request_finalize (GObject *obj)
72 BeagleRequestPrivate *priv = BEAGLE_REQUEST_GET_PRIVATE (obj);
74 g_free (priv->path);
76 if (priv->io_watch != 0) {
77 g_source_remove (priv->io_watch);
78 priv->io_watch = 0;
81 if (priv->channel) {
82 g_io_channel_unref (priv->channel);
83 priv->channel = NULL;
86 if (G_OBJECT_CLASS (parent_class)->finalize)
87 G_OBJECT_CLASS (parent_class)->finalize (obj);
90 static void
91 beagle_request_class_init (BeagleRequestClass *klass)
93 GObjectClass *obj_class = G_OBJECT_CLASS (klass);
95 parent_class = g_type_class_peek_parent (klass);
97 obj_class->finalize = beagle_request_finalize;
99 signals [CLOSED] = g_signal_new ("closed",
100 G_TYPE_FROM_CLASS (klass),
101 G_SIGNAL_RUN_LAST,
102 G_STRUCT_OFFSET (BeagleRequestClass, closed),
103 NULL, NULL,
104 beagle_marshal_VOID__VOID,
105 G_TYPE_NONE, 0);
106 signals [RESPONSE] = g_signal_new ("response",
107 G_TYPE_FROM_CLASS (klass),
108 G_SIGNAL_RUN_LAST,
109 G_STRUCT_OFFSET (BeagleRequestClass, response),
110 NULL, NULL,
111 g_cclosure_marshal_VOID__OBJECT,
112 G_TYPE_NONE, 1,
113 BEAGLE_TYPE_RESPONSE);
114 signals [ERROR] = g_signal_new ("error",
115 G_TYPE_FROM_CLASS (klass),
116 G_SIGNAL_RUN_LAST,
117 G_STRUCT_OFFSET (BeagleRequestClass, error),
118 NULL, NULL,
119 g_cclosure_marshal_VOID__POINTER,
120 G_TYPE_NONE, 1,
121 G_TYPE_POINTER);
123 g_type_class_add_private (klass, sizeof (BeagleRequestPrivate));
125 klass->response_types = g_hash_table_new_full (g_str_hash,
126 g_str_equal,
127 g_free,
128 NULL);
130 g_hash_table_insert (klass->response_types,
131 g_strdup ("ErrorResponse"),
132 (gpointer) BEAGLE_TYPE_ERROR_RESPONSE);
135 static void
136 beagle_request_init (BeagleRequest *request)
140 void
141 _beagle_request_class_set_response_types (BeagleRequestClass *klass,
142 const char *beagle_type,
143 GType gobject_type,
144 ...)
146 va_list args;
147 const char *arg;
149 g_hash_table_replace (klass->response_types,
150 g_strdup (beagle_type),
151 (gpointer) gobject_type);
153 va_start (args, gobject_type);
154 arg = va_arg (args, const char *);
156 while (arg != NULL) {
157 GType gtype = va_arg (args, GType);
159 g_hash_table_replace (klass->response_types,
160 g_strdup (arg),
161 (gpointer) gtype);
163 arg = va_arg (args, const char *);
166 va_end (args);
169 static gboolean
170 request_connect (BeagleRequest *request, const char *path, GError **err)
172 BeagleRequestPrivate *priv;
173 int sockfd;
174 struct sockaddr_un sun;
176 priv = BEAGLE_REQUEST_GET_PRIVATE (request);
178 sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
179 if (sockfd < 0) {
180 g_set_error (err, BEAGLE_ERROR, BEAGLE_ERROR,
181 "Unable to create connection");
182 return FALSE;
185 bzero (&sun, sizeof (sun));
186 sun.sun_family = AF_UNIX;
187 snprintf (sun.sun_path, sizeof (sun.sun_path), path);
189 if (connect (sockfd, (struct sockaddr *) &sun, sizeof (sun)) < 0) {
190 g_set_error (err, BEAGLE_ERROR, BEAGLE_ERROR,
191 "Unable to connect to Beagle daemon");
192 return FALSE;
195 g_free (priv->path);
196 priv->path = g_strdup (path);
198 priv->channel = g_io_channel_unix_new (sockfd);
200 g_io_channel_set_encoding (priv->channel, NULL, NULL);
201 g_io_channel_set_buffered (priv->channel, FALSE);
202 g_io_channel_set_close_on_unref (priv->channel, TRUE);
204 return TRUE;
207 static void
208 request_close (BeagleRequest *request)
210 BeagleRequestPrivate *priv;
212 priv = BEAGLE_REQUEST_GET_PRIVATE (request);
214 g_return_if_fail (priv->channel != NULL);
216 g_free (priv->path);
217 priv->path = NULL;
219 g_io_channel_unref (priv->channel);
220 priv->channel = NULL;
222 g_source_remove (priv->io_watch);
223 priv->io_watch = 0;
225 g_signal_emit (request, signals [CLOSED], 0);
228 static gboolean
229 request_send (BeagleRequest *request, const char *socket_path, GError **err)
231 BeagleRequestPrivate *priv;
232 GString *buffer;
233 gsize bytes_written, total_written;
234 char eom_marker = 0xff;
235 GIOStatus status;
237 if (!request_connect (request, socket_path, err))
238 return FALSE;
240 priv = BEAGLE_REQUEST_GET_PRIVATE (request);
242 buffer = BEAGLE_REQUEST_GET_CLASS (request)->to_xml (request, err);
243 if (buffer == NULL)
244 return FALSE;
246 #ifdef ENABLE_XML_DUMP
247 printf ("Sending request:\n");
248 printf ("%*s\n\n", buffer->len, buffer->str);
249 #endif
251 /* Send the data over the wire */
252 total_written = 0;
253 do {
254 status = g_io_channel_write_chars (priv->channel,
255 buffer->str + total_written,
256 buffer->len - total_written,
257 &bytes_written,
258 err);
259 total_written += bytes_written;
260 } while ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_AGAIN)
261 && total_written < buffer->len);
263 if (status == G_IO_STATUS_ERROR)
264 return FALSE;
266 /* And send the end-of-message marker */
267 do {
268 status = g_io_channel_write_chars (priv->channel,
269 &eom_marker, 1,
270 &bytes_written,
271 err);
272 } while ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_AGAIN)
273 && total_written == 0);
275 if (status == G_IO_STATUS_ERROR)
276 return FALSE;
278 return TRUE;
281 static gboolean
282 request_io_cb (GIOChannel *channel, GIOCondition condition, gpointer user_data)
284 BeagleRequestPrivate *priv = BEAGLE_REQUEST_GET_PRIVATE (user_data);
285 gsize bytes_read;
286 char buf[4096];
287 GIOStatus status;
288 char *marker;
289 int start, to_parse;
290 BeagleResponse *response;
292 if (condition & G_IO_IN) {
293 do {
294 GError *error = NULL;
296 status = g_io_channel_read_chars (priv->channel,
297 buf, 4096,
298 &bytes_read,
299 &error);
301 if (status == G_IO_STATUS_ERROR) {
302 g_signal_emit (user_data, signals[ERROR], 0, error);
303 g_error_free (error);
304 error = NULL;
307 if (bytes_read > 0) {
308 start = 0;
310 #ifdef ENABLE_XML_DUMP
311 if (priv->data == NULL)
312 priv->data = g_string_new (NULL);
313 #endif
315 while (start < bytes_read) {
316 marker = memchr (buf + start, 0xff, bytes_read - start);
318 if (!priv->ctx) {
319 priv->ctx = _beagle_parser_context_new ();
322 if (marker != NULL) {
323 to_parse = (marker - buf) - start;
325 if (to_parse > 0) {
326 #ifdef ENABLE_XML_DUMP
327 priv->data = g_string_append_len (priv->data, buf + start, to_parse);
328 #endif
329 _beagle_parser_context_parse_chunk (priv->ctx, buf + start, to_parse);
332 #ifdef ENABLE_XML_DUMP
333 printf ("Received async response:\n");
334 printf ("%s\n\n", priv->data->str);
335 g_string_free (priv->data, TRUE);
336 priv->data = NULL;
337 #endif
339 /* Finish the context */
340 response = _beagle_parser_context_finished (priv->ctx);
341 g_assert (response != NULL);
343 if (BEAGLE_IS_ERROR_RESPONSE (response)) {
344 _beagle_error_response_to_g_error (BEAGLE_ERROR_RESPONSE (response), &error);
345 g_signal_emit (BEAGLE_REQUEST (user_data),
346 signals[ERROR], 0, error);
347 g_error_free (error);
348 error = NULL;
349 } else {
350 g_signal_emit (BEAGLE_REQUEST (user_data),
351 signals[RESPONSE], 0, response);
353 g_object_unref (response);
355 /* Move past the 0xff marker */
356 start += to_parse + 1;
358 priv->ctx = NULL;
360 else {
361 #ifdef ENABLE_XML_DUMP
362 priv->data = g_string_append_len (priv->data, buf + start, bytes_read - start);
363 #endif
364 _beagle_parser_context_parse_chunk (priv->ctx, buf + start, bytes_read - start);
365 break;
369 } while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN);
372 if (condition & G_IO_HUP ||
373 condition & G_IO_ERR) {
374 request_close (BEAGLE_REQUEST (user_data));
377 return TRUE;
380 gboolean
381 _beagle_request_send_async (BeagleRequest *request,
382 const char *socket_path,
383 GError **err)
385 BeagleRequestPrivate *priv = BEAGLE_REQUEST_GET_PRIVATE (request);
387 if (!request_send (request, socket_path, err))
388 return FALSE;
390 priv->io_watch = g_io_add_watch (priv->channel,
391 G_IO_IN | G_IO_HUP | G_IO_ERR,
392 request_io_cb,
393 request);
395 return TRUE;
398 BeagleResponse *
399 _beagle_request_send (BeagleRequest *request, const char *socket_path, GError **err)
401 BeagleRequestPrivate *priv;
402 BeagleParserContext *ctx;
403 char buf [4096];
404 gsize bytes_read;
405 GIOStatus status;
406 char *marker = NULL;
407 BeagleResponse *response;
409 if (!request_send (request, socket_path, err))
410 return FALSE;
412 priv = BEAGLE_REQUEST_GET_PRIVATE (request);
414 ctx = _beagle_parser_context_new ();
416 #ifdef ENABLE_XML_DUMP
417 priv->data = g_string_new (NULL);
418 #endif
420 do {
421 gsize to_parse;
423 status = g_io_channel_read_chars (priv->channel,
424 buf, 4096,
425 &bytes_read,
426 err);
428 if (bytes_read > 0) {
429 marker = memchr (buf, 0xff, bytes_read);
431 if (marker != NULL)
432 to_parse = marker - buf;
433 else
434 to_parse = bytes_read;
436 #ifdef ENABLE_XML_DUMP
437 priv->data = g_string_append_len (priv->data, buf, to_parse);
438 #endif
439 _beagle_parser_context_parse_chunk (ctx, buf, to_parse);
441 } while ((status == G_IO_STATUS_NORMAL || G_IO_STATUS_AGAIN) &&
442 marker == NULL);
444 #ifdef ENABLE_XML_DUMP
445 printf ("Received sync response:\n");
446 printf ("%s\n\n", priv->data->str);
447 g_string_free (priv->data, TRUE);
448 priv->data = NULL;
449 #endif
451 response = _beagle_parser_context_finished (ctx);
453 g_io_channel_unref (priv->channel);
454 priv->channel = NULL;
456 if (BEAGLE_IS_ERROR_RESPONSE (response)) {
457 _beagle_error_response_to_g_error (BEAGLE_ERROR_RESPONSE (response), err);
458 g_object_unref (response);
459 return NULL;
462 return response;
465 void
466 _beagle_request_append_standard_header (GString *data, const char *xsi_type)
468 const char header[] =
469 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
470 "<RequestWrapper xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
471 "<Message xsi:type=\"";
473 g_string_append_len (data, header, sizeof (header) - 1);
474 g_string_append (data, xsi_type);
475 g_string_append_len (data, "\">", 2);
478 void
479 _beagle_request_append_standard_footer (GString *data)
481 const char footer[] = "</Message></RequestWrapper>";
483 g_string_append_len (data, footer, sizeof (footer) - 1);