Dont add null mimetypes. Fixes bgo# 337431. The patch hasnt been officially accepted...
[beagle.git] / libbeagle / beagle / beagle-request.c
blob7b40a8a6fff535b89a88c8b6786c9d9ac2558eb2
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 } BeagleRequestPrivate;
52 #define BEAGLE_REQUEST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), BEAGLE_TYPE_REQUEST, BeagleRequestPrivate))
54 enum {
55 CLOSED,
56 RESPONSE,
57 ERROR,
58 LAST_SIGNAL
61 static GObjectClass *parent_class = NULL;
62 static guint signals [LAST_SIGNAL] = { 0 };
64 G_DEFINE_TYPE (BeagleRequest, beagle_request, G_TYPE_OBJECT)
66 static void
67 beagle_request_finalize (GObject *obj)
69 BeagleRequestPrivate *priv = BEAGLE_REQUEST_GET_PRIVATE (obj);
71 g_free (priv->path);
73 if (priv->io_watch != 0) {
74 g_source_remove (priv->io_watch);
75 priv->io_watch = 0;
78 if (priv->channel) {
79 g_io_channel_unref (priv->channel);
80 priv->channel = NULL;
83 if (G_OBJECT_CLASS (parent_class)->finalize)
84 G_OBJECT_CLASS (parent_class)->finalize (obj);
87 static void
88 beagle_request_class_init (BeagleRequestClass *klass)
90 GObjectClass *obj_class = G_OBJECT_CLASS (klass);
92 parent_class = g_type_class_peek_parent (klass);
94 obj_class->finalize = beagle_request_finalize;
96 signals [CLOSED] = g_signal_new ("closed",
97 G_TYPE_FROM_CLASS (klass),
98 G_SIGNAL_RUN_LAST,
99 G_STRUCT_OFFSET (BeagleRequestClass, closed),
100 NULL, NULL,
101 beagle_marshal_VOID__VOID,
102 G_TYPE_NONE, 0);
103 signals [RESPONSE] = g_signal_new ("response",
104 G_TYPE_FROM_CLASS (klass),
105 G_SIGNAL_RUN_LAST,
106 G_STRUCT_OFFSET (BeagleRequestClass, response),
107 NULL, NULL,
108 g_cclosure_marshal_VOID__OBJECT,
109 G_TYPE_NONE, 1,
110 BEAGLE_TYPE_RESPONSE);
111 signals [ERROR] = g_signal_new ("error",
112 G_TYPE_FROM_CLASS (klass),
113 G_SIGNAL_RUN_LAST,
114 G_STRUCT_OFFSET (BeagleRequestClass, error),
115 NULL, NULL,
116 g_cclosure_marshal_VOID__POINTER,
117 G_TYPE_NONE, 1,
118 G_TYPE_POINTER);
120 g_type_class_add_private (klass, sizeof (BeagleRequestPrivate));
122 klass->response_types = g_hash_table_new_full (g_str_hash,
123 g_str_equal,
124 g_free,
125 NULL);
127 g_hash_table_insert (klass->response_types,
128 g_strdup ("ErrorResponse"),
129 (gpointer) BEAGLE_TYPE_ERROR_RESPONSE);
132 static void
133 beagle_request_init (BeagleRequest *request)
137 void
138 _beagle_request_class_set_response_types (BeagleRequestClass *klass,
139 const char *beagle_type,
140 GType gobject_type,
141 ...)
143 va_list args;
144 const char *arg;
146 g_hash_table_replace (klass->response_types,
147 g_strdup (beagle_type),
148 (gpointer) gobject_type);
150 va_start (args, gobject_type);
151 arg = va_arg (args, const char *);
153 while (arg != NULL) {
154 GType gtype = va_arg (args, GType);
156 g_hash_table_replace (klass->response_types,
157 g_strdup (arg),
158 (gpointer) gtype);
160 arg = va_arg (args, const char *);
163 va_end (args);
166 static gboolean
167 request_connect (BeagleRequest *request, const char *path, GError **err)
169 BeagleRequestPrivate *priv;
170 int sockfd;
171 struct sockaddr_un sun;
173 priv = BEAGLE_REQUEST_GET_PRIVATE (request);
175 sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
176 if (sockfd < 0) {
177 g_set_error (err, BEAGLE_ERROR, BEAGLE_ERROR,
178 "Unable to create connection");
179 return FALSE;
182 bzero (&sun, sizeof (sun));
183 sun.sun_family = AF_UNIX;
184 snprintf (sun.sun_path, sizeof (sun.sun_path), path);
186 if (connect (sockfd, (struct sockaddr *) &sun, sizeof (sun)) < 0) {
187 g_set_error (err, BEAGLE_ERROR, BEAGLE_ERROR,
188 "Unable to connect to Beagle daemon");
189 return FALSE;
192 g_free (priv->path);
193 priv->path = g_strdup (path);
195 priv->channel = g_io_channel_unix_new (sockfd);
197 g_io_channel_set_encoding (priv->channel, NULL, NULL);
198 g_io_channel_set_buffered (priv->channel, FALSE);
199 g_io_channel_set_close_on_unref (priv->channel, TRUE);
201 return TRUE;
204 static void
205 request_close (BeagleRequest *request)
207 BeagleRequestPrivate *priv;
209 priv = BEAGLE_REQUEST_GET_PRIVATE (request);
211 g_return_if_fail (priv->channel != NULL);
213 g_free (priv->path);
214 priv->path = NULL;
216 g_io_channel_unref (priv->channel);
217 priv->channel = NULL;
219 g_source_remove (priv->io_watch);
220 priv->io_watch = 0;
222 g_signal_emit (request, signals [CLOSED], 0);
225 static gboolean
226 request_send (BeagleRequest *request, const char *socket_path, GError **err)
228 BeagleRequestPrivate *priv;
229 GString *buffer;
230 gsize bytes_written, total_written;
231 char eom_marker = 0xff;
232 GIOStatus status;
234 if (!request_connect (request, socket_path, err))
235 return FALSE;
237 priv = BEAGLE_REQUEST_GET_PRIVATE (request);
239 buffer = BEAGLE_REQUEST_GET_CLASS (request)->to_xml (request, err);
240 if (buffer == NULL)
241 return FALSE;
243 #ifdef ENABLE_XML_DUMP
244 printf ("Sending request:\n");
245 printf ("%*s\n\n", buffer->len, buffer->str);
246 #endif
248 /* Send the data over the wire */
249 total_written = 0;
250 do {
251 status = g_io_channel_write_chars (priv->channel,
252 buffer->str + total_written,
253 buffer->len - total_written,
254 &bytes_written,
255 err);
256 total_written += bytes_written;
257 } while ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_AGAIN)
258 && total_written < buffer->len);
260 if (status == G_IO_STATUS_ERROR)
261 return FALSE;
263 /* And send the end-of-message marker */
264 do {
265 status = g_io_channel_write_chars (priv->channel,
266 &eom_marker, 1,
267 &bytes_written,
268 err);
269 } while ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_AGAIN)
270 && total_written == 0);
272 if (status == G_IO_STATUS_ERROR)
273 return FALSE;
275 return TRUE;
278 static gboolean
279 request_io_cb (GIOChannel *channel, GIOCondition condition, gpointer user_data)
281 BeagleRequestPrivate *priv = BEAGLE_REQUEST_GET_PRIVATE (user_data);
282 gsize bytes_read;
283 char buf[4096];
284 GIOStatus status;
285 char *marker;
286 int start, to_parse;
287 BeagleResponse *response;
289 if (condition & G_IO_IN) {
290 do {
291 GError *error = NULL;
293 status = g_io_channel_read_chars (priv->channel,
294 buf, 4096,
295 &bytes_read,
296 &error);
298 if (status == G_IO_STATUS_ERROR) {
299 g_signal_emit (user_data, signals[ERROR], 0, error);
300 g_error_free (error);
301 error = NULL;
304 if (bytes_read > 0) {
305 start = 0;
307 while (start < bytes_read) {
308 marker = memchr (buf + start, 0xff, bytes_read - start);
310 if (!priv->ctx) {
311 priv->ctx = _beagle_parser_context_new ();
314 if (marker != NULL) {
315 to_parse = (marker - buf) - start;
317 if (to_parse > 0) {
318 _beagle_parser_context_parse_chunk (priv->ctx, buf + start, to_parse);
321 /* Finish the context */
322 response = _beagle_parser_context_finished (priv->ctx);
323 g_assert (response != NULL);
325 if (BEAGLE_IS_ERROR_RESPONSE (response)) {
326 _beagle_error_response_to_g_error (BEAGLE_ERROR_RESPONSE (response), &error);
327 g_signal_emit (BEAGLE_REQUEST (user_data),
328 signals[ERROR], 0, error);
329 g_error_free (error);
330 error = NULL;
331 } else {
332 g_signal_emit (BEAGLE_REQUEST (user_data),
333 signals[RESPONSE], 0, response);
335 g_object_unref (response);
337 /* Move past the 0xff marker */
338 start += to_parse + 1;
340 priv->ctx = NULL;
342 else {
343 _beagle_parser_context_parse_chunk (priv->ctx, buf + start, bytes_read - start);
344 break;
348 } while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN);
351 if (condition & G_IO_HUP ||
352 condition & G_IO_ERR) {
353 request_close (BEAGLE_REQUEST (user_data));
356 return TRUE;
359 gboolean
360 _beagle_request_send_async (BeagleRequest *request,
361 const char *socket_path,
362 GError **err)
364 BeagleRequestPrivate *priv = BEAGLE_REQUEST_GET_PRIVATE (request);
366 if (!request_send (request, socket_path, err))
367 return FALSE;
369 priv->io_watch = g_io_add_watch (priv->channel,
370 G_IO_IN | G_IO_HUP | G_IO_ERR,
371 request_io_cb,
372 request);
374 return TRUE;
377 BeagleResponse *
378 _beagle_request_send (BeagleRequest *request, const char *socket_path, GError **err)
380 BeagleRequestPrivate *priv;
381 BeagleParserContext *ctx;
382 char buf [4096];
383 gsize bytes_read;
384 GIOStatus status;
385 char *marker = NULL;
386 BeagleResponse *response;
388 if (!request_send (request, socket_path, err))
389 return FALSE;
391 priv = BEAGLE_REQUEST_GET_PRIVATE (request);
393 ctx = _beagle_parser_context_new ();
395 do {
396 gsize to_parse;
398 status = g_io_channel_read_chars (priv->channel,
399 buf, 4096,
400 &bytes_read,
401 err);
403 if (bytes_read > 0) {
404 marker = memchr (buf, 0xff, bytes_read);
406 if (marker != NULL)
407 to_parse = marker - buf;
408 else
409 to_parse = bytes_read;
411 _beagle_parser_context_parse_chunk (ctx, buf, to_parse);
413 } while ((status == G_IO_STATUS_NORMAL || G_IO_STATUS_AGAIN) &&
414 marker == NULL);
416 response = _beagle_parser_context_finished (ctx);
418 g_io_channel_unref (priv->channel);
419 priv->channel = NULL;
421 if (BEAGLE_IS_ERROR_RESPONSE (response)) {
422 _beagle_error_response_to_g_error (BEAGLE_ERROR_RESPONSE (response), err);
423 g_object_unref (response);
424 return NULL;
427 return response;
430 void
431 _beagle_request_append_standard_header (GString *data, const char *xsi_type)
433 const char header[] =
434 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
435 "<RequestWrapper xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
436 "<Message xsi:type=\"";
438 g_string_append_len (data, header, sizeof (header) - 1);
439 g_string_append (data, xsi_type);
440 g_string_append_len (data, "\">", 2);
443 void
444 _beagle_request_append_standard_footer (GString *data)
446 const char footer[] = "</Message></RequestWrapper>";
448 g_string_append_len (data, footer, sizeof (footer) - 1);