4 * Copyright (C) 2005 Novell, Inc.
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.
34 #include <sys/types.h>
35 #include <sys/socket.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"
49 BeagleParserContext
*ctx
;
50 #ifdef ENABLE_XML_DUMP
53 } BeagleRequestPrivate
;
55 #define BEAGLE_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BEAGLE_TYPE_REQUEST, BeagleRequestPrivate))
64 static GObjectClass
*parent_class
= NULL
;
65 static guint signals
[LAST_SIGNAL
] = { 0 };
67 G_DEFINE_TYPE (BeagleRequest
, beagle_request
, G_TYPE_OBJECT
)
70 beagle_request_finalize (GObject
*obj
)
72 BeagleRequestPrivate
*priv
= BEAGLE_REQUEST_GET_PRIVATE (obj
);
76 if (priv
->io_watch
!= 0) {
77 g_source_remove (priv
->io_watch
);
82 g_io_channel_unref (priv
->channel
);
86 if (G_OBJECT_CLASS (parent_class
)->finalize
)
87 G_OBJECT_CLASS (parent_class
)->finalize (obj
);
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
),
102 G_STRUCT_OFFSET (BeagleRequestClass
, closed
),
104 beagle_marshal_VOID__VOID
,
106 signals
[RESPONSE
] = g_signal_new ("response",
107 G_TYPE_FROM_CLASS (klass
),
109 G_STRUCT_OFFSET (BeagleRequestClass
, response
),
111 g_cclosure_marshal_VOID__OBJECT
,
113 BEAGLE_TYPE_RESPONSE
);
114 signals
[ERROR
] = g_signal_new ("error",
115 G_TYPE_FROM_CLASS (klass
),
117 G_STRUCT_OFFSET (BeagleRequestClass
, error
),
119 g_cclosure_marshal_VOID__POINTER
,
123 g_type_class_add_private (klass
, sizeof (BeagleRequestPrivate
));
125 klass
->response_types
= g_hash_table_new_full (g_str_hash
,
130 g_hash_table_insert (klass
->response_types
,
131 g_strdup ("ErrorResponse"),
132 (gpointer
) BEAGLE_TYPE_ERROR_RESPONSE
);
136 beagle_request_init (BeagleRequest
*request
)
141 _beagle_request_class_set_response_types (BeagleRequestClass
*klass
,
142 const char *beagle_type
,
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
,
163 arg
= va_arg (args
, const char *);
170 request_connect (BeagleRequest
*request
, const char *path
, GError
**err
)
172 BeagleRequestPrivate
*priv
;
174 struct sockaddr_un sun
;
176 priv
= BEAGLE_REQUEST_GET_PRIVATE (request
);
178 sockfd
= socket (AF_UNIX
, SOCK_STREAM
, 0);
180 g_set_error (err
, BEAGLE_ERROR
, BEAGLE_ERROR
,
181 "Unable to create connection");
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");
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
);
208 request_close (BeagleRequest
*request
)
210 BeagleRequestPrivate
*priv
;
212 priv
= BEAGLE_REQUEST_GET_PRIVATE (request
);
214 g_return_if_fail (priv
->channel
!= NULL
);
219 g_io_channel_unref (priv
->channel
);
220 priv
->channel
= NULL
;
222 g_source_remove (priv
->io_watch
);
225 g_signal_emit (request
, signals
[CLOSED
], 0);
229 request_send (BeagleRequest
*request
, const char *socket_path
, GError
**err
)
231 BeagleRequestPrivate
*priv
;
233 gsize bytes_written
, total_written
;
234 char eom_marker
= 0xff;
237 if (!request_connect (request
, socket_path
, err
))
240 priv
= BEAGLE_REQUEST_GET_PRIVATE (request
);
242 buffer
= BEAGLE_REQUEST_GET_CLASS (request
)->to_xml (request
, err
);
246 #ifdef ENABLE_XML_DUMP
247 printf ("Sending request:\n");
248 printf ("%*s\n\n", buffer
->len
, buffer
->str
);
251 /* Send the data over the wire */
254 status
= g_io_channel_write_chars (priv
->channel
,
255 buffer
->str
+ total_written
,
256 buffer
->len
- total_written
,
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
)
266 /* And send the end-of-message marker */
268 status
= g_io_channel_write_chars (priv
->channel
,
272 } while ((status
== G_IO_STATUS_NORMAL
|| status
== G_IO_STATUS_AGAIN
)
273 && total_written
== 0);
275 if (status
== G_IO_STATUS_ERROR
)
282 request_io_cb (GIOChannel
*channel
, GIOCondition condition
, gpointer user_data
)
284 BeagleRequestPrivate
*priv
= BEAGLE_REQUEST_GET_PRIVATE (user_data
);
290 BeagleResponse
*response
;
292 if (condition
& G_IO_IN
) {
294 GError
*error
= NULL
;
296 status
= g_io_channel_read_chars (priv
->channel
,
301 if (status
== G_IO_STATUS_ERROR
) {
302 g_signal_emit (user_data
, signals
[ERROR
], 0, error
);
303 g_error_free (error
);
307 if (bytes_read
> 0) {
310 #ifdef ENABLE_XML_DUMP
311 if (priv
->data
== NULL
)
312 priv
->data
= g_string_new (NULL
);
315 while (start
< bytes_read
) {
316 marker
= memchr (buf
+ start
, 0xff, bytes_read
- start
);
319 priv
->ctx
= _beagle_parser_context_new ();
322 if (marker
!= NULL
) {
323 to_parse
= (marker
- buf
) - start
;
326 #ifdef ENABLE_XML_DUMP
327 priv
->data
= g_string_append_len (priv
->data
, buf
+ start
, to_parse
);
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
);
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
);
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;
361 #ifdef ENABLE_XML_DUMP
362 priv
->data
= g_string_append_len (priv
->data
, buf
+ start
, bytes_read
- start
);
364 _beagle_parser_context_parse_chunk (priv
->ctx
, buf
+ start
, bytes_read
- start
);
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
));
381 _beagle_request_send_async (BeagleRequest
*request
,
382 const char *socket_path
,
385 BeagleRequestPrivate
*priv
= BEAGLE_REQUEST_GET_PRIVATE (request
);
387 if (!request_send (request
, socket_path
, err
))
390 priv
->io_watch
= g_io_add_watch (priv
->channel
,
391 G_IO_IN
| G_IO_HUP
| G_IO_ERR
,
399 _beagle_request_send (BeagleRequest
*request
, const char *socket_path
, GError
**err
)
401 BeagleRequestPrivate
*priv
;
402 BeagleParserContext
*ctx
;
407 BeagleResponse
*response
;
409 if (!request_send (request
, socket_path
, err
))
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
);
423 status
= g_io_channel_read_chars (priv
->channel
,
428 if (bytes_read
> 0) {
429 marker
= memchr (buf
, 0xff, bytes_read
);
432 to_parse
= marker
- buf
;
434 to_parse
= bytes_read
;
436 #ifdef ENABLE_XML_DUMP
437 priv
->data
= g_string_append_len (priv
->data
, buf
, to_parse
);
439 _beagle_parser_context_parse_chunk (ctx
, buf
, to_parse
);
441 } while ((status
== G_IO_STATUS_NORMAL
|| G_IO_STATUS_AGAIN
) &&
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
);
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
);
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);
479 _beagle_request_append_standard_footer (GString
*data
)
481 const char footer
[] = "</Message></RequestWrapper>";
483 g_string_append_len (data
, footer
, sizeof (footer
) - 1);