da5da3b2dfe4115b108e1e3198d90e576039a94d
[platform/upstream/dbus.git] / glib / dbus-gobject.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-gobject.c Exporting a GObject remotely
3  *
4  * Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include <config.h>
25 #include <gobject/gvaluecollector.h>
26 #include <dbus/dbus-glib.h>
27 #include <dbus/dbus-glib-lowlevel.h>
28 #include "dbus-gtest.h"
29 #include "dbus-gutils.h"
30 #include "dbus-gobject.h"
31 #include "dbus-gvalue.h"
32 #include "dbus-gmarshal.h"
33 #include "dbus-gvalue-utils.h"
34 #include <string.h>
35
36 /**
37  * @addtogroup DBusGLibInternals
38  * @{
39  */
40
41 static GStaticRWLock globals_lock = G_STATIC_RW_LOCK_INIT;
42 static GHashTable *info_hash = NULL;
43 static GHashTable *marshal_table = NULL;
44
45 static char*
46 uscore_to_wincaps (const char *uscore)
47 {
48   const char *p;
49   GString *str;
50   gboolean last_was_uscore;
51
52   last_was_uscore = TRUE;
53   
54   str = g_string_new (NULL);
55   p = uscore;
56   while (*p)
57     {
58       if (*p == '-' || *p == '_')
59         {
60           last_was_uscore = TRUE;
61         }
62       else
63         {
64           if (last_was_uscore)
65             {
66               g_string_append_c (str, g_ascii_toupper (*p));
67               last_was_uscore = FALSE;
68             }
69           else
70             g_string_append_c (str, *p);
71         }
72       ++p;
73     }
74
75   return g_string_free (str, FALSE);
76 }
77
78 static const char *
79 string_table_next (const char *table)
80 {
81   return (table + (strlen (table) + 1));
82 }
83
84 static const char *
85 string_table_lookup (const char *table, int index)
86 {
87   const char *ret;
88
89   ret = table;
90
91   while (index--)
92     ret = string_table_next (ret);
93
94   return ret;
95 }
96
97 static const char *
98 get_method_data (const DBusGObjectInfo *object,
99                  const DBusGMethodInfo *method)
100 {
101   return object->data + method->data_offset;
102 }
103
104 static char *
105 object_error_domain_prefix_from_object_info (const DBusGObjectInfo *info)
106 {
107   /* FIXME */
108   return NULL;
109 }
110
111 static char *
112 object_error_code_from_object_info (const DBusGObjectInfo *info, GQuark domain, gint code)
113 {
114   /* FIXME */
115   return NULL;
116 }
117
118 static const char *
119 method_interface_from_object_info (const DBusGObjectInfo *object,
120                               const DBusGMethodInfo *method)
121 {
122   return string_table_lookup (get_method_data (object, method), 0);
123 }
124
125 static const char *
126 method_name_from_object_info (const DBusGObjectInfo *object,
127                               const DBusGMethodInfo *method)
128 {
129   return string_table_lookup (get_method_data (object, method), 1);
130 }
131
132 static const char *
133 method_arg_info_from_object_info (const DBusGObjectInfo *object,
134                                   const DBusGMethodInfo *method)
135 {
136   return string_table_lookup (get_method_data (object, method), 3);/*RB was 2*/
137 }
138
139 static const char *
140 arg_iterate (const char *data, const char **name, gboolean *in,
141              const char **type)
142 {
143   *name = data;
144
145   data = string_table_next (data);
146   switch (*data)
147     {
148     case 'I':
149       *in = TRUE;
150       break;
151     case 'O':
152       *in = FALSE;
153       break;
154     default:
155       g_warning ("invalid arg direction");
156       break;
157     }
158   
159   data = string_table_next (data);
160   *type = data;
161
162   return string_table_next (data);
163 }
164
165 static char *
166 method_dir_signature_from_object_info (const DBusGObjectInfo *object,
167                                        const DBusGMethodInfo *method,
168                                        gboolean               in)
169 {
170   const char *arg;
171   GString *ret;
172
173   arg = method_arg_info_from_object_info (object, method);
174
175   ret = g_string_new (NULL);
176
177   while (*arg)
178     {
179       const char *name;
180       gboolean arg_in;
181       const char *type;
182
183       arg = arg_iterate (arg, &name, &arg_in, &type);
184
185       if (arg_in == in)
186         g_string_append (ret, type);
187     }
188
189   return g_string_free (ret, FALSE);
190 }
191
192 static char *
193 method_input_signature_from_object_info (const DBusGObjectInfo *object,
194                                          const DBusGMethodInfo *method)
195 {
196   return method_dir_signature_from_object_info (object, method, TRUE);
197 }
198
199 static char *
200 method_output_signature_from_object_info (const DBusGObjectInfo *object,
201                                           const DBusGMethodInfo *method)
202 {
203   return method_dir_signature_from_object_info (object, method, FALSE);
204 }
205
206 static void
207 gobject_unregister_function (DBusConnection  *connection,
208                              void            *user_data)
209 {
210   GObject *object;
211
212   object = G_OBJECT (user_data);
213
214   /* FIXME */
215
216 }
217
218 static void
219 introspect_properties (GObject *object, GString *xml)
220 {
221   unsigned int i;
222   unsigned int n_specs;
223   GType last_type;
224   GParamSpec **specs;
225
226   last_type = G_TYPE_INVALID;
227   specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
228                                           &n_specs);
229
230   for (i = 0; i < n_specs; i++ )
231     {
232       char *s;
233       const char *dbus_type;
234       gboolean can_set;
235       gboolean can_get;
236       GParamSpec *spec = specs[i];
237       
238       dbus_type = dbus_gtype_to_signature (G_PARAM_SPEC_VALUE_TYPE (spec));
239       if (dbus_type == NULL)
240         continue;
241       
242       if (spec->owner_type != last_type)
243         {
244           if (last_type != G_TYPE_INVALID)
245             g_string_append (xml, "  </interface>\n");
246
247
248           /* FIXME what should the namespace on the interface be in
249            * general?  should people be able to set it for their
250            * objects?
251            */
252           g_string_append (xml, "  <interface name=\"org.gtk.objects.");
253           g_string_append (xml, g_type_name (spec->owner_type));
254           g_string_append (xml, "\">\n");
255
256           last_type = spec->owner_type;
257         }
258
259       can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
260                  (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
261       
262       can_get = (spec->flags & G_PARAM_READABLE) != 0;
263
264       s = uscore_to_wincaps (spec->name);
265       
266       if (can_set || can_get)
267         {
268           g_string_append (xml, "    <property name=\"");
269           g_string_append (xml, s);
270           g_string_append (xml, "\" type=\"");
271           g_string_append (xml, dbus_type);
272           g_string_append (xml, "\" access=\"");
273
274           if (can_set && can_get)
275             g_string_append (xml, "readwrite");
276           else if (can_get)
277             g_string_append (xml, "read");
278           else
279             {
280               g_assert (can_set);
281               g_string_append (xml, "write");
282             }
283           
284           g_string_append (xml, "\"/>\n");
285         }
286       
287       g_free (s);
288     }
289
290   if (last_type != G_TYPE_INVALID)
291     g_string_append (xml, "  </interface>\n");
292
293   g_free (specs);
294 }
295
296 static void
297 introspect_signals (GType type, GString *xml)
298 {
299   guint i;
300   guint *ids, n_ids;
301
302   ids = g_signal_list_ids (type, &n_ids);
303   if (!n_ids)
304     return;
305
306   g_string_append (xml, "  <interface name=\"org.gtk.objects.");
307   g_string_append (xml, g_type_name (type));
308   g_string_append (xml, "\">\n");
309
310   /* FIXME: recurse to parent types ? */
311   for (i = 0; i < n_ids; i++)
312     {
313       guint arg;
314       GSignalQuery query;
315       
316       g_signal_query (ids[i], &query);
317
318       if (query.return_type != G_TYPE_NONE)
319         continue; /* FIXME: these could be listed as methods ? */
320
321       g_string_append (xml, "    <signal name=\"");
322       g_string_append (xml, query.signal_name);
323       g_string_append (xml, "\">\n");
324
325       for (arg = 0; arg < query.n_params; arg++)
326         {
327           const char *dbus_type = dbus_gtype_to_signature (query.param_types[arg]);
328
329           if (!dbus_type)
330             continue;
331           
332           g_string_append (xml, "      <arg type=\"");
333           g_string_append (xml, dbus_type);
334           g_string_append (xml, "\"/>\n");
335         }
336
337       g_string_append (xml, "    </signal>\n");
338     }
339
340   g_string_append (xml, "  </interface>\n");
341 }
342
343 typedef struct
344 {
345   GString *xml;
346   const DBusGObjectInfo *object_info;
347 } DBusGlibWriteIterfaceData;
348
349 static void
350 write_interface (gpointer key, gpointer val, gpointer user_data)
351 {
352   const char *name;
353   GSList *methods;
354   GString *xml;
355   const DBusGObjectInfo *object_info;
356   DBusGlibWriteIterfaceData *data;
357
358   name = key;
359   methods = val;
360   data = user_data;
361   xml = data->xml;
362   object_info = data->object_info;
363
364   g_string_append_printf (xml, "  <interface name=\"%s\">\n", name);
365
366   /* FIXME: recurse to parent types ? */
367   for (; methods; methods = methods->next)
368     {
369       DBusGMethodInfo *method;
370       method = methods->data;
371       const char *args;
372
373       g_string_append_printf (xml, "    <method name=\"%s\">\n",
374                               method_name_from_object_info (object_info, method));
375
376       args = method_arg_info_from_object_info (object_info, method);
377
378       while (*args)
379         {
380           const char *name;
381           gboolean arg_in;
382           const char *type;
383           
384           args = arg_iterate (args, &name, &arg_in, &type);
385
386           /* FIXME - handle container types */
387           g_string_append_printf (xml, "      <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
388                                   name, type, arg_in ? "in" : "out");
389
390         }
391       g_string_append (xml, "    </method>\n");
392     }
393
394   g_string_append (xml, "  </interface>\n");
395 }
396
397 static void
398 introspect_interfaces (GObject *object, GString *xml)
399 {
400   GType classtype;
401
402   g_static_rw_lock_reader_lock (&globals_lock);
403
404   for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype))
405     {
406       const DBusGObjectInfo *info;
407       DBusGlibWriteIterfaceData data;
408
409       info = g_hash_table_lookup (info_hash,
410                                   g_type_class_peek (classtype));
411
412       if (info != NULL && info->format_version == 0)
413         {
414           int i;
415           GHashTable *interfaces;
416
417           /* Gather a list of all interfaces, indexed into their methods */
418           interfaces = g_hash_table_new (g_str_hash, g_str_equal);
419           for (i = 0; i < info->n_infos; i++)
420             {
421               const char *method_name;
422               const char *method_interface;
423               const char *method_args;
424               const DBusGMethodInfo *method;
425               GSList *methods;
426
427               method = &(info->infos[i]);
428
429               method_interface = method_interface_from_object_info (info, method);
430               method_name = method_name_from_object_info (info, method);
431               method_args = method_arg_info_from_object_info (info, method);
432
433               if ((methods = g_hash_table_lookup (interfaces, method_interface)) == NULL)
434                   methods = g_slist_prepend (NULL, (gpointer) method);
435               else
436                   methods = g_slist_prepend (methods, (gpointer) method);
437               g_hash_table_insert (interfaces, (gpointer) method_interface, methods);
438             }
439
440           memset (&data, 0, sizeof (data));
441           data.xml = xml;
442           data.object_info = info;
443           g_hash_table_foreach (interfaces, write_interface, &data);
444
445           g_hash_table_destroy (interfaces);
446         }
447     }
448
449   g_static_rw_lock_reader_unlock (&globals_lock);
450 }
451
452 static DBusHandlerResult
453 handle_introspect (DBusConnection *connection,
454                    DBusMessage    *message,
455                    GObject        *object)
456 {
457   GString *xml;
458   unsigned int i;
459   DBusMessage *ret;
460   char **children;
461   
462   if (!dbus_connection_list_registered (connection, 
463                                         dbus_message_get_path (message),
464                                         &children))
465     g_error ("Out of memory");
466   
467   xml = g_string_new (NULL);
468
469   g_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
470   
471   g_string_append (xml, "<node>\n");
472
473   /* We are introspectable, though I guess that was pretty obvious */
474   g_string_append_printf (xml, "  <interface name=\"%s\">\n", DBUS_INTERFACE_INTROSPECTABLE);
475   g_string_append (xml, "    <method name=\"Introspect\">\n");
476   g_string_append_printf (xml, "      <arg name=\"data\" direction=\"out\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
477   g_string_append (xml, "    </method>\n");
478   g_string_append (xml, "  </interface>\n");
479
480   /* We support get/set properties */
481   g_string_append_printf (xml, "  <interface name=\"%s\">\n", DBUS_INTERFACE_PROPERTIES);
482   g_string_append (xml, "    <method name=\"Get\">\n");
483   g_string_append_printf (xml, "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
484   g_string_append_printf (xml, "      <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
485   g_string_append_printf (xml, "      <arg name=\"value\" direction=\"out\" type=\"%s\"/>\n", DBUS_TYPE_VARIANT_AS_STRING);
486   g_string_append (xml, "    </method>\n");
487   g_string_append (xml, "    <method name=\"Set\">\n");
488   g_string_append_printf (xml, "      <arg name=\"interface\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
489   g_string_append_printf (xml, "      <arg name=\"propname\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING);
490   g_string_append_printf (xml, "      <arg name=\"value\" direction=\"in\" type=\"%s\"/>\n", DBUS_TYPE_VARIANT_AS_STRING);
491   g_string_append (xml, "    </method>\n");
492   g_string_append (xml, "  </interface>\n");
493   
494   introspect_signals (G_OBJECT_TYPE (object), xml);
495   introspect_properties (object, xml);
496   introspect_interfaces (object, xml);
497
498   /* Append child nodes */
499   for (i = 0; children[i]; i++)
500       g_string_append_printf (xml, "  <node name=\"%s\"/>\n",
501                               children[i]);
502   
503   /* Close the XML, and send it to the requesting app */
504   g_string_append (xml, "</node>\n");
505
506   ret = dbus_message_new_method_return (message);
507   if (ret == NULL)
508     g_error ("Out of memory");
509
510   dbus_message_append_args (ret,
511                             DBUS_TYPE_STRING, &xml->str,
512                             DBUS_TYPE_INVALID);
513
514   dbus_connection_send (connection, ret, NULL);
515   dbus_message_unref (ret);
516
517   g_string_free (xml, TRUE);
518
519   dbus_free_string_array (children);
520   
521   return DBUS_HANDLER_RESULT_HANDLED;
522 }
523
524 static DBusMessage*
525 set_object_property (DBusConnection  *connection,
526                      DBusMessage     *message,
527                      DBusMessageIter *iter,
528                      GObject         *object,
529                      GParamSpec      *pspec)
530 {
531   GValue value = { 0, };
532   DBusMessage *ret;
533   DBusMessageIter sub;
534   DBusGValueMarshalCtx context;
535
536   dbus_message_iter_recurse (iter, &sub);
537
538   context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
539   context.proxy = NULL;
540
541   g_value_init (&value, pspec->value_type);
542   if (dbus_gvalue_demarshal (&context, &sub, &value, NULL))
543     {
544       g_object_set_property (object,
545                              pspec->name,
546                              &value);
547
548       g_value_unset (&value);
549
550       ret = dbus_message_new_method_return (message);
551       if (ret == NULL)
552         g_error ("out of memory");
553     }
554   else
555     {
556       ret = dbus_message_new_error (message,
557                                     DBUS_ERROR_INVALID_ARGS,
558                                     "Argument's D-BUS type can't be converted to a GType");
559       if (ret == NULL)
560         g_error ("out of memory");
561     }
562
563   return ret;
564 }
565
566 static DBusMessage*
567 get_object_property (DBusConnection *connection,
568                      DBusMessage    *message,
569                      GObject        *object,
570                      GParamSpec     *pspec)
571 {
572   GType value_type;
573   GValue value = {0, };
574   DBusMessage *ret;
575   DBusMessageIter iter;
576
577   value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
578
579   ret = dbus_message_new_method_return (message);
580   if (ret == NULL)
581     g_error ("out of memory");
582
583   g_value_init (&value, value_type);
584   g_object_get_property (object, pspec->name, &value);
585
586   value_type = G_VALUE_TYPE (&value);
587
588   dbus_message_iter_init_append (message, &iter);
589
590   if (!dbus_gvalue_marshal (&iter, &value))
591     {
592       dbus_message_unref (ret);
593       ret = dbus_message_new_error (message,
594                                     DBUS_ERROR_UNKNOWN_METHOD,
595                                     "Can't convert GType of object property to a D-BUS type");
596     }
597
598   return ret;
599 }
600
601 static gboolean
602 lookup_object_and_method (GObject      *object,
603                           DBusMessage  *message,
604                           const DBusGObjectInfo **object_ret,
605                           const DBusGMethodInfo **method_ret)
606 {
607   GType classtype;
608   const char *interface;
609   const char *member;
610   const char *signature;
611   gboolean ret;
612
613   interface = dbus_message_get_interface (message);
614   member = dbus_message_get_member (message);
615   signature = dbus_message_get_signature (message);
616   ret = FALSE;
617
618   g_static_rw_lock_reader_lock (&globals_lock);
619
620   if (!info_hash)
621     goto out;
622   
623   for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype))
624     {
625       const DBusGObjectInfo *info;
626
627       info = g_hash_table_lookup (info_hash,
628                                   g_type_class_peek (classtype));
629
630       *object_ret = info;
631
632       if (info != NULL && info->format_version == 0)
633         {
634           int i;
635           for (i = 0; i < info->n_infos; i++)
636             {
637               const char *expected_member;
638               const char *expected_interface;
639               char *expected_signature;
640               const DBusGMethodInfo *method;
641
642               method = &(info->infos[i]);
643
644               /* Check method interface/name and input signature */ 
645               expected_interface = method_interface_from_object_info (*object_ret, method);
646               expected_member = method_name_from_object_info (*object_ret, method);
647               expected_signature = method_input_signature_from_object_info (*object_ret, method);
648
649               if ((interface == NULL
650                    || strcmp (expected_interface, interface) == 0)
651                   && strcmp (expected_member, member) == 0
652                   && strcmp (expected_signature, signature) == 0)
653                 {
654                   g_free (expected_signature);
655                   *method_ret = method;
656                   ret = TRUE;
657                   goto out;
658                 }
659               g_free (expected_signature);
660             }
661         }
662     }
663  out:
664   g_static_rw_lock_reader_unlock (&globals_lock);
665   return ret;
666 }
667
668 static char *
669 gerror_domaincode_to_dbus_error_name (const DBusGObjectInfo *object_info,
670                                       GQuark domain, gint code)
671 {
672   const char *domain_str;
673   const char *code_str;
674   GString *dbus_error_name;
675
676   domain_str = object_error_domain_prefix_from_object_info (object_info);
677   code_str = object_error_code_from_object_info (object_info, domain, code);
678
679   if (!domain_str || !code_str)
680     {
681       /* If we can't map it sensibly, make up an error name */
682       char *domain_from_quark;
683       
684       dbus_error_name = g_string_new ("org.freedesktop.DBus.GLib.UnmappedError.");
685
686       domain_from_quark = uscore_to_wincaps (g_quark_to_string (domain));
687       g_string_append (dbus_error_name, domain_from_quark);
688       g_free (domain_from_quark);
689         
690       g_string_append_printf (dbus_error_name, ".Code%d", code);
691     }
692   else
693     {
694       dbus_error_name = g_string_new (domain_str);
695       g_string_append_c (dbus_error_name, '.');
696       g_string_append (dbus_error_name, code_str);
697     }
698
699   return g_string_free (dbus_error_name, FALSE);
700 }
701
702 static DBusMessage *
703 gerror_to_dbus_error_message (const DBusGObjectInfo *object_info,
704                               DBusMessage     *message,
705                               GError          *error)
706 {
707   DBusMessage *reply;
708
709   if (!error)
710     {
711       char *error_msg;
712       
713       error_msg = g_strdup_printf ("Method invoked for %s returned FALSE but did not set error", dbus_message_get_member (message));
714       reply = dbus_message_new_error (message, "org.freedesktop.DBus.GLib.ErrorError", error_msg);
715       g_free (error_msg);
716     }
717   else
718     {
719       char *error_name;
720       error_name = gerror_domaincode_to_dbus_error_name (object_info, error->domain, error->code);
721       reply = dbus_message_new_error (message, error_name, error->message);
722       g_free (error_name); 
723     }
724   return reply;
725 }
726
727 /**
728  * The context of an asynchronous method call.  See dbus_g_method_return() and
729  * dbus_g_method_return_error().
730  */
731 struct DBusGMethodInvocation {
732   DBusGConnection *connection; /**< The connection */
733   DBusGMessage *message; /**< The message which generated the method call */
734   const DBusGObjectInfo *object; /**< The object the method was called on */
735   const DBusGMethodInfo *method; /**< The method called */
736 };
737
738 static DBusHandlerResult
739 invoke_object_method (GObject         *object,
740                       const DBusGObjectInfo *object_info,
741                       const DBusGMethodInfo *method,
742                       DBusConnection  *connection,
743                       DBusMessage     *message)
744 {
745   gboolean had_error, call_only;
746   GError *gerror;
747   GValueArray *value_array;
748   GValue object_value = {0,};
749   GValue error_value = {0,};
750   GValue return_value = {0,};
751   GClosure closure;
752   char *in_signature;
753   char *out_signature = NULL;
754   int current_type;
755   DBusSignatureIter out_signature_iter;
756   GArray *out_param_values = NULL;
757   GValueArray *out_param_gvalues = NULL;
758   int out_param_count;
759   int out_param_pos, out_param_gvalue_pos;
760   DBusHandlerResult result;
761   DBusMessage *reply;
762
763   gerror = NULL;
764
765   if (strcmp (string_table_lookup (get_method_data (object_info, method), 2), "A") == 0)
766     call_only = TRUE;
767   else
768     call_only = FALSE;
769
770   /* This is evil.  We do this to work around the fact that
771    * the generated glib marshallers check a flag in the closure object
772    * which we don't care about.  We don't need/want to create
773    * a new closure for each invocation.
774    */
775   memset (&closure, 0, sizeof (closure));
776
777   in_signature = method_input_signature_from_object_info (object_info, method); 
778   
779   /* Convert method IN parameters to GValueArray */
780   {
781     GArray *types_array;
782     guint n_params;
783     const GType *types;
784     DBusGValueMarshalCtx context;
785     GError *error = NULL;
786     
787     context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
788     context.proxy = NULL;
789
790     types_array = dbus_gtypes_from_arg_signature (in_signature, FALSE);
791     n_params = types_array->len;
792     types = (const GType*) types_array->data;
793
794     value_array = dbus_gvalue_demarshal_message (&context, message, n_params, types, &error);
795     if (value_array == NULL)
796       {
797         g_free (in_signature); 
798         g_array_free (types_array, TRUE);
799         reply = dbus_message_new_error (message, "org.freedesktop.DBus.GLib.ErrorError", error->message);
800         dbus_connection_send (connection, reply, NULL);
801         dbus_message_unref (reply);
802         g_error_free (error);
803         return DBUS_HANDLER_RESULT_HANDLED;
804       }
805     g_array_free (types_array, TRUE);
806   }
807
808   /* Prepend object as first argument */ 
809   g_value_init (&object_value, G_TYPE_OBJECT);
810   g_value_set_object (&object_value, object);
811   g_value_array_prepend (value_array, &object_value);
812
813   if (call_only)
814     {
815       GValue context_value = {0,};
816       DBusGMethodInvocation *context;
817       context = g_new (DBusGMethodInvocation, 1);
818       context->connection = dbus_g_connection_ref (DBUS_G_CONNECTION_FROM_CONNECTION (connection));
819       context->message = dbus_g_message_ref (DBUS_G_MESSAGE_FROM_MESSAGE (message));
820       context->object = object_info;
821       context->method = method;
822       g_value_init (&context_value, G_TYPE_POINTER);
823       g_value_set_pointer (&context_value, context);
824       g_value_array_append (value_array, &context_value);
825     }
826   else
827     {
828       out_signature = method_output_signature_from_object_info (object_info, method); 
829
830       /* Count number of output parameters */
831       dbus_signature_iter_init (&out_signature_iter, out_signature);
832       out_param_count = 0;
833       while ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID)
834         {
835           out_param_count++;
836           dbus_signature_iter_next (&out_signature_iter);
837         }
838
839       /* Create an array to store the actual values of OUT
840        * parameters.  Then, create a GValue boxed POINTER
841        * to each of those values, and append to the invocation,
842        * so the method can return the OUT parameters.
843        */
844       out_param_values = g_array_sized_new (FALSE, TRUE, sizeof (GTypeCValue), out_param_count);
845
846       /* We have a special array of GValues for toplevel GValue return
847        * types.
848        */
849       out_param_gvalues = g_value_array_new (out_param_count);
850       out_param_pos = 0;
851       out_param_gvalue_pos = 0;
852       dbus_signature_iter_init (&out_signature_iter, out_signature);
853       while ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID)
854         {
855           GValue value = {0, };
856           GTypeCValue storage;
857
858           g_value_init (&value, G_TYPE_POINTER);
859
860           /* We special case variants to make method invocation a bit nicer */
861           if (current_type != DBUS_TYPE_VARIANT)
862             {
863               memset (&storage, 0, sizeof (storage));
864               g_array_append_val (out_param_values, storage);
865               g_value_set_pointer (&value, &(g_array_index (out_param_values, GTypeCValue, out_param_pos)));
866               out_param_pos++;
867             }
868           else
869             {
870               g_value_array_append (out_param_gvalues, NULL);
871               g_value_set_pointer (&value, out_param_gvalues->values + out_param_gvalue_pos);
872               out_param_gvalue_pos++;
873             }
874           g_value_array_append (value_array, &value);
875           dbus_signature_iter_next (&out_signature_iter);
876         }
877
878       /* Append GError as final argument */
879       g_value_init (&error_value, G_TYPE_POINTER);
880       g_value_set_pointer (&error_value, &gerror);
881       g_value_array_append (value_array, &error_value);
882     }
883   /* Actually invoke method */
884   g_value_init (&return_value, G_TYPE_BOOLEAN);
885   method->marshaller (&closure, &return_value,
886                       value_array->n_values,
887                       value_array->values,
888                       NULL, method->function);
889   if (call_only)
890     {
891       result = DBUS_HANDLER_RESULT_HANDLED;
892       goto done;
893     }
894   had_error = !g_value_get_boolean (&return_value);
895
896   if (!had_error)
897     {
898       DBusMessageIter iter;
899
900       reply = dbus_message_new_method_return (message);
901       if (reply == NULL)
902         goto nomem;
903
904       /* Append OUT arguments to reply */
905       dbus_message_iter_init_append (reply, &iter);
906       dbus_signature_iter_init (&out_signature_iter, out_signature);
907       out_param_pos = 0;
908       out_param_gvalue_pos = 0;
909       while ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID)
910         {
911           GValue gvalue = {0, };
912           
913           g_value_init (&gvalue, dbus_gtype_from_signature_iter (&out_signature_iter, FALSE));
914           if (current_type != DBUS_TYPE_VARIANT)
915             {
916               if (!dbus_gvalue_take (&gvalue,
917                                      &(g_array_index (out_param_values, GTypeCValue, out_param_pos))))
918                 g_assert_not_reached ();
919               out_param_pos++;
920             }
921           else
922             {
923               g_value_set_static_boxed (&gvalue, out_param_gvalues->values + out_param_gvalue_pos);
924               out_param_gvalue_pos++;
925             }
926               
927           if (!dbus_gvalue_marshal (&iter, &gvalue))
928             goto nomem;
929           /* Here we actually free the allocated value; we
930            * took ownership of it with dbus_gvalue_take.
931            */
932           g_value_unset (&gvalue);
933           dbus_signature_iter_next (&out_signature_iter);
934         }
935     }
936   else
937     reply = gerror_to_dbus_error_message (object_info, message, gerror);
938
939   if (reply)
940     {
941       dbus_connection_send (connection, reply, NULL);
942       dbus_message_unref (reply);
943     }
944
945   result = DBUS_HANDLER_RESULT_HANDLED;
946  done:
947   g_free (in_signature);
948   g_free (out_signature);
949   if (!call_only)
950     {
951       g_array_free (out_param_values, TRUE);
952       g_value_array_free (out_param_gvalues);
953       g_value_unset (&object_value);
954       g_value_unset (&error_value);
955     }
956   g_value_array_free (value_array);
957   g_value_unset (&return_value);
958   return result;
959  nomem:
960   result = DBUS_HANDLER_RESULT_NEED_MEMORY;
961   goto done;
962 }
963
964 static DBusHandlerResult
965 gobject_message_function (DBusConnection  *connection,
966                           DBusMessage     *message,
967                           void            *user_data)
968 {
969   GParamSpec *pspec;
970   GObject *object;
971   gboolean setter;
972   gboolean getter;
973   char *s;
974   const char *wincaps_propname;
975   /* const char *wincaps_propiface; */
976   DBusMessageIter iter;
977   const DBusGMethodInfo *method;
978   const DBusGObjectInfo *object_info;
979
980   object = G_OBJECT (user_data);
981
982   if (dbus_message_is_method_call (message,
983                                    DBUS_INTERFACE_INTROSPECTABLE,
984                                    "Introspect"))
985     return handle_introspect (connection, message, object);
986   
987   /* Try the metainfo, which lets us invoke methods */
988   if (lookup_object_and_method (object, message, &object_info, &method))
989     return invoke_object_method (object, object_info, method, connection, message);
990
991   /* If no metainfo, we can still do properties and signals
992    * via standard GLib introspection
993    */
994   getter = FALSE;
995   setter = FALSE;
996   if (dbus_message_is_method_call (message,
997                                    DBUS_INTERFACE_PROPERTIES,
998                                    "Get"))
999     getter = TRUE;
1000   else if (dbus_message_is_method_call (message,
1001                                         DBUS_INTERFACE_PROPERTIES,
1002                                         "Set"))
1003     setter = TRUE;
1004
1005   if (!(setter || getter))
1006     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1007
1008   dbus_message_iter_init (message, &iter);
1009
1010   if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
1011     {
1012       g_warning ("Property get or set does not have an interface string as first arg\n");
1013       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1014     }
1015   /* We never use the interface name; if we did, we'd need to
1016    * remember that it can be empty string for "pick one for me"
1017    */
1018   /* dbus_message_iter_get_basic (&iter, &wincaps_propiface); */
1019   dbus_message_iter_next (&iter);
1020
1021   if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
1022     {
1023       g_warning ("Property get or set does not have a property name string as second arg\n");
1024       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1025     }
1026   dbus_message_iter_get_basic (&iter, &wincaps_propname);
1027   dbus_message_iter_next (&iter);
1028   
1029   s = _dbus_gutils_wincaps_to_uscore (wincaps_propname);
1030
1031   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
1032                                         s);
1033
1034   g_free (s);
1035
1036   if (pspec != NULL)
1037     {
1038       DBusMessage *ret;
1039
1040       if (setter)
1041         {
1042           if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
1043             {
1044               g_warning ("Property set does not have a variant value as third arg\n");
1045               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1046             }
1047           
1048           ret = set_object_property (connection, message, &iter,
1049                                      object, pspec);
1050           dbus_message_iter_next (&iter);
1051         }
1052       else if (getter)
1053         {     
1054           ret = get_object_property (connection, message,
1055                                      object, pspec);
1056         }
1057       else
1058         {
1059           g_assert_not_reached ();
1060           ret = NULL;
1061         }
1062
1063       g_assert (ret != NULL);
1064
1065       if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
1066         g_warning ("Property get or set had too many arguments\n");
1067       
1068       dbus_connection_send (connection, ret, NULL);
1069       dbus_message_unref (ret);
1070       return DBUS_HANDLER_RESULT_HANDLED;
1071     }
1072
1073   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1074 }
1075
1076 static DBusObjectPathVTable gobject_dbus_vtable = {
1077   gobject_unregister_function,
1078   gobject_message_function,
1079   NULL
1080 };
1081
1082 typedef struct {
1083   GClosure         closure;
1084   DBusGConnection *connection;
1085   GObject         *object;
1086   char            *signame;
1087 } DBusGSignalClosure;
1088
1089 static GClosure *
1090 dbus_g_signal_closure_new (DBusGConnection *connection,
1091                            GObject         *object,
1092                            const char      *signame)
1093 {
1094   DBusGSignalClosure *closure;
1095   
1096   closure = (DBusGSignalClosure*) g_closure_new_simple (sizeof (DBusGSignalClosure), NULL);
1097
1098   closure->connection = dbus_g_connection_ref (connection);
1099   closure->object = object;
1100   closure->signame = g_strdup (signame);
1101   return (GClosure*) closure;
1102 }
1103
1104 static void
1105 dbus_g_signal_closure_finalize (gpointer data,
1106                                 GClosure *closure)
1107 {
1108   DBusGSignalClosure *sigclosure = (DBusGSignalClosure *) closure;
1109
1110   dbus_g_connection_unref (sigclosure->connection);
1111   g_free (sigclosure->signame);
1112 }
1113
1114 static void
1115 signal_emitter_marshaller (GClosure        *closure,
1116                            GValue          *retval,
1117                            guint            n_param_values,
1118                            const GValue    *param_values,
1119                            gpointer         invocation_hint,
1120                            gpointer         marshal_data)
1121 {
1122   DBusGSignalClosure *sigclosure;
1123   DBusMessage *signal;
1124   DBusMessageIter iter;
1125   guint i;
1126   const char *path;
1127
1128   sigclosure = (DBusGSignalClosure *) closure;
1129   
1130   g_assert (retval == NULL);
1131
1132   path = _dbus_gobject_get_path (sigclosure->object);
1133
1134   g_assert (path != NULL);
1135
1136   signal = dbus_message_new_signal (path,
1137                                     "org.gtk.objects",
1138                                     sigclosure->signame);
1139   if (!signal)
1140     {
1141       g_error ("out of memory");
1142       return;
1143     }
1144
1145   dbus_message_iter_init_append (signal, &iter);
1146
1147   /* First argument is the object itself, and we can't marshall that */
1148   for (i = 1; i < n_param_values; i++)
1149     {
1150       if (!dbus_gvalue_marshal (&iter,
1151                                 (GValue *) (&(param_values[i]))))
1152         {
1153           g_warning ("failed to marshal parameter %d for signal %s",
1154                      i, sigclosure->signame);
1155           goto out;
1156         }
1157     }
1158   dbus_connection_send (DBUS_CONNECTION_FROM_G_CONNECTION (sigclosure->connection),
1159                         signal, NULL);
1160  out:
1161   dbus_message_unref (signal);
1162 }
1163
1164 static void
1165 export_signals (DBusGConnection *connection, GObject *object)
1166 {
1167   guint i;
1168   guint *ids, n_ids;
1169
1170   ids = g_signal_list_ids (G_TYPE_FROM_INSTANCE (object), &n_ids);
1171   if (!n_ids)
1172     return;
1173
1174   /* FIXME: recurse to parent types ? */
1175   for (i = 0; i < n_ids; i++)
1176     {
1177       GSignalQuery query;
1178       GClosure *closure;
1179       
1180       g_signal_query (ids[i], &query);
1181
1182       if (query.return_type != G_TYPE_NONE)
1183         {
1184           g_warning("Not exporting signal '%s' as it has a return type %s", query.signal_name, g_type_name (query.return_type));
1185           continue; /* FIXME: these could be listed as methods ? */
1186         }
1187       
1188       closure = dbus_g_signal_closure_new (connection, object, query.signal_name);
1189       g_closure_set_marshal (closure, signal_emitter_marshaller);
1190
1191       g_signal_connect_closure_by_id (object,
1192                                       ids[i],
1193                                       0,
1194                                       closure,
1195                                       FALSE);
1196
1197       g_closure_add_finalize_notifier (closure, NULL,
1198                                        dbus_g_signal_closure_finalize);
1199     }
1200 }
1201
1202 /** @} */ /* end of internals */
1203
1204 /**
1205  * @addtogroup DBusGLib
1206  * @{
1207  */
1208
1209 /**
1210  * Install introspection information about the given object GType
1211  * sufficient to allow methods on the object to be invoked by name.
1212  * The introspection information is normally generated by
1213  * dbus-glib-tool, then this function is called in the
1214  * class_init() for the object class.
1215  *
1216  * Once introspection information has been installed, instances of the
1217  * object registered with dbus_g_connection_register_g_object() can have
1218  * their methods invoked remotely.
1219  *
1220  * @param object_type GType for the object
1221  * @param info introspection data generated by dbus-glib-tool
1222  */
1223 void
1224 dbus_g_object_type_install_info (GType                  object_type,
1225                                  const DBusGObjectInfo *info)
1226 {
1227   GObjectClass *object_class;
1228
1229   g_return_if_fail (G_TYPE_IS_OBJECT (object_type));
1230
1231   dbus_g_value_types_init ();
1232
1233   object_class = g_type_class_peek (object_type);
1234
1235   g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
1236
1237   g_static_rw_lock_writer_lock (&globals_lock);
1238
1239   if (info_hash == NULL)
1240     {
1241       info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
1242     }
1243
1244   g_hash_table_replace (info_hash, object_class, (void*) info);
1245
1246   g_static_rw_lock_writer_unlock (&globals_lock);
1247 }
1248
1249 static void
1250 unregister_gobject (DBusGConnection *connection, GObject *dead)
1251 {
1252   char *path;
1253   path = g_object_steal_data (dead, "dbus_glib_object_path");
1254   dbus_connection_unregister_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection), path);
1255   g_free (path);
1256 }
1257
1258 /**
1259  * Registers a GObject at the given path. Properties, methods, and signals
1260  * of the object can then be accessed remotely. Methods are only available
1261  * if method introspection data has been added to the object's class
1262  * with g_object_class_install_info().
1263  *
1264  * The registration will be cancelled if either the DBusConnection or
1265  * the GObject gets finalized.
1266  *
1267  * @param connection the D-BUS connection
1268  * @param at_path the path where the object will live (the object's name)
1269  * @param object the object
1270  */
1271 void
1272 dbus_g_connection_register_g_object (DBusGConnection       *connection,
1273                                      const char            *at_path,
1274                                      GObject               *object)
1275 {
1276   g_return_if_fail (connection != NULL);
1277   g_return_if_fail (at_path != NULL);
1278   g_return_if_fail (G_IS_OBJECT (object));
1279
1280   if (!dbus_connection_register_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection),
1281                                              at_path,
1282                                              &gobject_dbus_vtable,
1283                                              object))
1284     {
1285       g_error ("Failed to register GObject with DBusConnection");
1286       return;
1287     }
1288
1289   export_signals (connection, object);
1290
1291   g_object_set_data (object, "dbus_glib_object_path", g_strdup (at_path));
1292   g_object_weak_ref (object, (GWeakNotify)unregister_gobject, connection);
1293 }
1294
1295 GObject *
1296 dbus_g_connection_lookup_g_object (DBusGConnection       *connection,
1297                                    const char            *at_path)
1298 {
1299   gpointer ret;
1300   if (!dbus_connection_get_object_path_data (DBUS_CONNECTION_FROM_G_CONNECTION (connection), at_path, &ret))
1301     return NULL;
1302   return ret;
1303 }
1304
1305 typedef struct {
1306   GType    rettype;
1307   guint    n_params;
1308   GType   *params;
1309 } DBusGFuncSignature;
1310
1311 static guint
1312 funcsig_hash (gconstpointer key)
1313 {
1314   const DBusGFuncSignature *sig = key;
1315   GType *types;
1316   guint ret;
1317
1318   ret = sig->rettype;
1319   types = sig->params;
1320
1321   while (*types != G_TYPE_INVALID)
1322     {
1323       ret += (int) (*types);
1324       types++;
1325     }
1326       
1327   return ret;
1328 }
1329
1330 static gboolean
1331 funcsig_equal (gconstpointer aval,
1332                gconstpointer bval)
1333 {
1334   const DBusGFuncSignature *a = aval;
1335   const DBusGFuncSignature *b = bval;
1336   const GType *atypes;
1337   const GType *btypes;
1338
1339   if (a->rettype != b->rettype)
1340     return FALSE;
1341
1342   atypes = a->params;
1343   btypes = b->params;
1344
1345   while (*atypes != G_TYPE_INVALID)
1346     {
1347       if (*btypes != *atypes)
1348         return FALSE;
1349       atypes++;
1350       btypes++;
1351     }
1352   if (*btypes != G_TYPE_INVALID)
1353     return FALSE;
1354       
1355   return TRUE;
1356 }
1357
1358 GClosureMarshal
1359 _dbus_gobject_lookup_marshaller (GType        rettype,
1360                                  guint        n_params,
1361                                  const GType *param_types)
1362 {
1363   GClosureMarshal ret;
1364   DBusGFuncSignature sig;
1365
1366   sig.rettype = rettype;
1367   sig.n_params = n_params;
1368   sig.params = (GType*) param_types;
1369   
1370   g_static_rw_lock_reader_lock (&globals_lock);
1371
1372   if (marshal_table)
1373     ret = g_hash_table_lookup (marshal_table, &sig);
1374   else
1375     ret = NULL;
1376
1377   g_static_rw_lock_reader_unlock (&globals_lock);
1378
1379   if (ret == NULL)
1380     {
1381       if (rettype == G_TYPE_NONE)
1382         {
1383           if (n_params == 0)
1384             ret = g_cclosure_marshal_VOID__VOID;
1385           else if (n_params == 1)
1386             {
1387               switch (param_types[0])
1388                 {
1389                 case G_TYPE_BOOLEAN:
1390                   ret = g_cclosure_marshal_VOID__BOOLEAN;
1391                   break;
1392                 case G_TYPE_UCHAR:
1393                   ret = g_cclosure_marshal_VOID__UCHAR;
1394                   break;
1395                 case G_TYPE_INT:
1396                   ret = g_cclosure_marshal_VOID__INT;
1397                   break;
1398                 case G_TYPE_UINT:
1399                   ret = g_cclosure_marshal_VOID__UINT;
1400                   break;
1401                 case G_TYPE_DOUBLE:
1402                   ret = g_cclosure_marshal_VOID__DOUBLE;
1403                   break;
1404                 case G_TYPE_STRING:
1405                   ret = g_cclosure_marshal_VOID__STRING;
1406                   break;
1407                 }
1408             }
1409         }
1410       else if (n_params == 3
1411                && param_types[0] == G_TYPE_STRING
1412                && param_types[1] == G_TYPE_STRING
1413                && param_types[2] == G_TYPE_STRING)
1414         {
1415           ret = _dbus_g_marshal_NONE__STRING_STRING_STRING;
1416         }
1417     }
1418
1419   return ret;
1420 }
1421
1422 /**
1423  * Register a GClosureMarshal to be used for signal invocations.
1424  * This function will not be needed once GLib includes libffi.
1425  *
1426  * @param rettype a GType for the return type of the function
1427  * @param n_types number of function parameters
1428  * @param param_types a C array of GTypes values
1429  * @param marshaller a GClosureMarshal to be used for invocation
1430  */
1431 void
1432 dbus_g_object_register_marshaller (GType            rettype,
1433                                    guint            n_types,
1434                                    const GType      *param_types,
1435                                    GClosureMarshal   marshaller)
1436 {
1437   DBusGFuncSignature *sig;
1438   
1439   g_static_rw_lock_writer_lock (&globals_lock);
1440
1441   if (marshal_table == NULL)
1442     marshal_table = g_hash_table_new_full (funcsig_hash,
1443                                            funcsig_equal,
1444                                            g_free,
1445                                            NULL);
1446   sig = g_new0 (DBusGFuncSignature, 1);
1447   sig->rettype = rettype;
1448   sig->n_params = n_types;
1449   sig->params = g_new (GType, n_types);
1450   memcpy (sig->params, param_types, n_types * sizeof (GType));
1451
1452   g_hash_table_insert (marshal_table, sig, marshaller);
1453
1454   g_static_rw_lock_writer_unlock (&globals_lock);
1455 }
1456
1457 /**
1458  * Send a return message for a given method invocation, with arguments.
1459  *
1460  * @param context the method context
1461  */
1462 void
1463 dbus_g_method_return (DBusGMethodInvocation *context, ...)
1464 {
1465   DBusMessage *reply;
1466   DBusMessageIter iter;
1467   va_list args;
1468   char *out_sig;
1469   GArray *argsig;
1470   guint i;
1471
1472   reply = dbus_message_new_method_return (dbus_g_message_get_message (context->message));
1473   out_sig = method_output_signature_from_object_info (context->object, context->method);
1474   argsig = dbus_gtypes_from_arg_signature (out_sig, FALSE);
1475
1476   dbus_message_iter_init_append (reply, &iter);
1477
1478   va_start (args, context);
1479   for (i = 0; i < argsig->len; i++)
1480     {
1481       GValue value = {0,};
1482       char *error;
1483       g_value_init (&value, g_array_index (argsig, GType, i));
1484       error = NULL;
1485       G_VALUE_COLLECT (&value, args, 0, &error);
1486       if (error)
1487         {
1488           g_warning(error);
1489           g_free (error);
1490         }
1491       dbus_gvalue_marshal (&iter, &value);
1492     }
1493   va_end (args);
1494
1495   dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
1496   dbus_message_unref (reply);
1497
1498   dbus_g_connection_unref (context->connection);
1499   dbus_g_message_unref (context->message);
1500   g_free (context);
1501 }
1502
1503 /**
1504  * Send a error message for a given method invocation.
1505  *
1506  * @param context the method context
1507  * @param error the error to send.
1508  */
1509 void
1510 dbus_g_method_return_error (DBusGMethodInvocation *context, GError *error)
1511 {
1512   DBusMessage *reply;
1513   reply = gerror_to_dbus_error_message (context->object, dbus_g_message_get_message (context->message), error);
1514   dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
1515   dbus_message_unref (reply);
1516 }
1517
1518 /** @} */ /* end of public API */
1519
1520 const char * _dbus_gobject_get_path (GObject *obj)
1521 {
1522   return g_object_get_data (obj, "dbus_glib_object_path");
1523 }
1524
1525 #ifdef DBUS_BUILD_TESTS
1526 #include <stdlib.h>
1527
1528 /**
1529  * @ingroup DBusGLibInternals
1530  * Unit test for GLib GObject integration ("skeletons")
1531  * @returns #TRUE on success.
1532  */
1533 gboolean
1534 _dbus_gobject_test (const char *test_data_dir)
1535 {
1536   int i;
1537   static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
1538     { "SetFoo", "set_foo" },
1539     { "Foo", "foo" },
1540     { "GetFooBar", "get_foo_bar" },
1541     { "Hello", "hello" }
1542     
1543     /* Impossible-to-handle cases */
1544     /* { "FrobateUIHandler", "frobate_ui_handler" } */
1545   };
1546
1547   i = 0;
1548   while (i < (int) G_N_ELEMENTS (name_pairs))
1549     {
1550       char *uscore;
1551       char *wincaps;
1552
1553       uscore = _dbus_gutils_wincaps_to_uscore (name_pairs[i].wincaps);
1554       wincaps = uscore_to_wincaps (name_pairs[i].uscore);
1555
1556       if (strcmp (uscore, name_pairs[i].uscore) != 0)
1557         {
1558           g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
1559                       name_pairs[i].wincaps, name_pairs[i].uscore,
1560                       uscore);
1561           exit (1);
1562         }
1563       
1564       if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
1565         {
1566           g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
1567                       name_pairs[i].uscore, name_pairs[i].wincaps,
1568                       wincaps);
1569           exit (1);
1570         }
1571       
1572       g_free (uscore);
1573       g_free (wincaps);
1574
1575       ++i;
1576     }
1577   
1578   return TRUE;
1579 }
1580
1581 #endif /* DBUS_BUILD_TESTS */