Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

dbus-gobject.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* dbus-gobject.c Exporting a GObject remotely
00003  *
00004  * Copyright (C) 2003 Red Hat, Inc.
00005  *
00006  * Licensed under the Academic Free License version 1.2
00007  *
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 
00024 #include <config.h>
00025 #include "dbus-glib.h"
00026 #include "dbus-gtest.h"
00027 #include "dbus-gutils.h"
00028 #include <string.h>
00029 
00035 static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
00036 static GHashTable *info_hash = NULL;
00037 
00038 static char*
00039 wincaps_to_uscore (const char *caps)
00040 {
00041   const char *p;
00042   GString *str;
00043 
00044   str = g_string_new (NULL);
00045   p = caps;
00046   while (*p)
00047     {
00048       if (g_ascii_isupper (*p))
00049         {
00050           if (str->len > 0 &&
00051               (str->len < 2 || str->str[str->len-2] != '_'))
00052             g_string_append_c (str, '_');
00053           g_string_append_c (str, g_ascii_tolower (*p));
00054         }
00055       else
00056         {
00057           g_string_append_c (str, *p);
00058         }
00059       ++p;
00060     }
00061 
00062   return g_string_free (str, FALSE);
00063 }
00064 
00065 static char*
00066 uscore_to_wincaps (const char *uscore)
00067 {
00068   const char *p;
00069   GString *str;
00070   gboolean last_was_uscore;
00071 
00072   last_was_uscore = TRUE;
00073   
00074   str = g_string_new (NULL);
00075   p = uscore;
00076   while (*p)
00077     {
00078       if (*p == '-' || *p == '_')
00079         {
00080           last_was_uscore = TRUE;
00081         }
00082       else
00083         {
00084           if (last_was_uscore)
00085             {
00086               g_string_append_c (str, g_ascii_toupper (*p));
00087               last_was_uscore = FALSE;
00088             }
00089           else
00090             g_string_append_c (str, *p);
00091         }
00092       ++p;
00093     }
00094 
00095   return g_string_free (str, FALSE);
00096 }
00097 
00098 static void
00099 gobject_unregister_function (DBusConnection  *connection,
00100                              void            *user_data)
00101 {
00102   GObject *object;
00103 
00104   object = G_OBJECT (user_data);
00105 
00106   /* FIXME */
00107 
00108 }
00109 
00110 static int
00111 gtype_to_dbus_type (GType type)
00112 {
00113   switch (type)
00114     {
00115     case G_TYPE_CHAR:
00116     case G_TYPE_UCHAR:
00117       return DBUS_TYPE_BYTE;
00118       
00119     case G_TYPE_BOOLEAN:
00120       return DBUS_TYPE_BOOLEAN;
00121 
00122       /* long gets cut to 32 bits so the remote API is consistent
00123        * on all architectures
00124        */
00125       
00126     case G_TYPE_LONG:
00127     case G_TYPE_INT:
00128       return DBUS_TYPE_INT32;
00129     case G_TYPE_ULONG:
00130     case G_TYPE_UINT:
00131       return DBUS_TYPE_UINT32;
00132 
00133     case G_TYPE_INT64:
00134       return DBUS_TYPE_INT64;
00135 
00136     case G_TYPE_UINT64:
00137       return DBUS_TYPE_UINT64;
00138       
00139     case G_TYPE_FLOAT:
00140     case G_TYPE_DOUBLE:
00141       return DBUS_TYPE_DOUBLE;
00142 
00143     case G_TYPE_STRING:
00144       return DBUS_TYPE_STRING;
00145 
00146     default:
00147       return DBUS_TYPE_INVALID;
00148     }
00149 }
00150 
00151 static const char *
00152 dbus_type_to_string (int type)
00153 {
00154   switch (type)
00155     {
00156     case DBUS_TYPE_INVALID:
00157       return "invalid";
00158     case DBUS_TYPE_NIL:
00159       return "nil";
00160     case DBUS_TYPE_BOOLEAN:
00161       return "boolean";
00162     case DBUS_TYPE_INT32:
00163       return "int32";
00164     case DBUS_TYPE_UINT32:
00165       return "uint32";
00166     case DBUS_TYPE_DOUBLE:
00167       return "double";
00168     case DBUS_TYPE_STRING:
00169       return "string";
00170     case DBUS_TYPE_CUSTOM:
00171       return "custom";
00172     case DBUS_TYPE_ARRAY:
00173       return "array";
00174     case DBUS_TYPE_DICT:
00175       return "dict";
00176     default:
00177       return "unknown";
00178     }
00179 }
00180 
00181 static DBusHandlerResult
00182 handle_introspect (DBusConnection *connection,
00183                    DBusMessage    *message,
00184                    GObject        *object)
00185 {
00186   GString *xml;
00187   GParamSpec **specs;
00188   unsigned int n_specs;
00189   unsigned int i;
00190   GType last_type;
00191   DBusMessage *ret;
00192   char **path;
00193   char **children;
00194   
00195   if (!dbus_message_get_path_decomposed (message, &path))
00196     g_error ("Out of memory");
00197 
00198   if (!dbus_connection_list_registered (connection, (const char**) path,
00199                                         &children))
00200     g_error ("Out of memory");
00201   
00202   xml = g_string_new (NULL);
00203 
00204   g_string_append (xml, "<node>\n");
00205 
00206   last_type = G_TYPE_INVALID;
00207 
00208   specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
00209                                           &n_specs);
00210 
00211   i = 0;
00212   while (i < n_specs)
00213     {
00214       GParamSpec *spec = specs[i];
00215       gboolean can_set;
00216       gboolean can_get;
00217       char *s;
00218       int dbus_type;
00219       
00220       dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
00221       if (dbus_type == DBUS_TYPE_INVALID)
00222         goto next;
00223       
00224       if (spec->owner_type != last_type)
00225         {
00226           if (last_type != G_TYPE_INVALID)
00227             g_string_append (xml, "  </interface>\n");
00228 
00229 
00230           /* FIXME what should the namespace on the interface be in
00231            * general?  should people be able to set it for their
00232            * objects?
00233            */
00234           
00235           g_string_append (xml, "  <interface name=\"org.gtk.objects.");
00236           g_string_append (xml, g_type_name (spec->owner_type));
00237           g_string_append (xml, "\">\n");
00238 
00239           last_type = spec->owner_type;
00240         }
00241 
00242       can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
00243                     (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
00244 
00245       can_get = (spec->flags & G_PARAM_READABLE) != 0;
00246 
00247       s = uscore_to_wincaps (spec->name);
00248       
00249       if (can_set)
00250         {
00251           g_string_append (xml, "    <method name=\"set_");
00252           g_string_append (xml, s);
00253           g_string_append (xml, "\">\n");
00254           
00255           g_string_append (xml, "      <arg type=\"");
00256           g_string_append (xml, dbus_type_to_string (dbus_type));
00257           g_string_append (xml, "\"/>\n");
00258         }
00259 
00260       if (can_get)
00261         {
00262           g_string_append (xml, "    <method name=\"get_");
00263           g_string_append (xml, s);
00264           g_string_append (xml, "\">\n");
00265           
00266           g_string_append (xml, "      <arg type=\"");
00267           g_string_append (xml, dbus_type_to_string (dbus_type));
00268           g_string_append (xml, "\" direction=\"out\"/>\n");
00269         }
00270 
00271       g_free (s);
00272 
00273     next:
00274       ++i;
00275     }
00276 
00277   if (last_type != G_TYPE_INVALID)
00278     g_string_append (xml, "  </interface>\n");
00279 
00280   g_free (specs);
00281 
00282   /* Append child nodes */
00283   
00284   i = 0;
00285   while (children[i])
00286     {
00287       g_string_append_printf (xml, "  <node name=\"%s\"/>\n",
00288                               children[i]);
00289       ++i;
00290     }
00291   
00292   /* Close the XML, and send it to the requesting app */
00293 
00294   g_string_append (xml, "</node>\n");
00295 
00296   ret = dbus_message_new_method_return (message);
00297   if (ret == NULL)
00298     g_error ("Out of memory");
00299 
00300   dbus_message_append_args (message,
00301                             DBUS_TYPE_STRING, xml->str,
00302                             DBUS_TYPE_INVALID);
00303 
00304   dbus_connection_send (connection, message, NULL);
00305   dbus_message_unref (message);
00306 
00307   g_string_free (xml, TRUE);
00308 
00309   dbus_free_string_array (path);
00310   dbus_free_string_array (children);
00311   
00312   return DBUS_HANDLER_RESULT_HANDLED;
00313 }
00314 
00315 static DBusMessage*
00316 set_object_property (DBusConnection *connection,
00317                      DBusMessage    *message,
00318                      GObject        *object,
00319                      GParamSpec     *pspec)
00320 {
00321   GValue value;
00322   DBusMessageIter iter;
00323   int type;
00324   gboolean can_set;
00325   DBusMessage *ret;
00326 
00327   dbus_message_iter_init (message, &iter);
00328   type = dbus_message_get_type (message);
00329 
00330   can_set = TRUE;
00331   switch (type)
00332     {
00333     case DBUS_TYPE_BYTE:
00334       {
00335         unsigned char b;
00336 
00337         b = dbus_message_iter_get_byte (&iter);
00338 
00339         g_value_init (&value, G_TYPE_UCHAR);
00340 
00341         g_value_set_uchar (&value, b);
00342       }
00343       break;
00344     case DBUS_TYPE_BOOLEAN:
00345       {
00346         gboolean b;
00347 
00348         b = dbus_message_iter_get_boolean (&iter);
00349 
00350         g_value_init (&value, G_TYPE_BOOLEAN);
00351 
00352         g_value_set_boolean (&value, b);
00353       }
00354       break;
00355     case DBUS_TYPE_INT32:
00356       {
00357         gint32 i;
00358 
00359         i = dbus_message_iter_get_int32 (&iter);
00360 
00361         g_value_init (&value, G_TYPE_INT);
00362 
00363         g_value_set_int (&value, i);
00364       }
00365       break;
00366     case DBUS_TYPE_UINT32:
00367       {
00368         guint32 i;
00369 
00370         i = dbus_message_iter_get_uint32 (&iter);
00371 
00372         g_value_init (&value, G_TYPE_UINT);
00373 
00374         g_value_set_uint (&value, i);
00375       }
00376       break;
00377     case DBUS_TYPE_INT64:
00378       {
00379         gint64 i;
00380 
00381         i = dbus_message_iter_get_int64 (&iter);
00382 
00383         g_value_init (&value, G_TYPE_INT64);
00384 
00385         g_value_set_int64 (&value, i);
00386       }
00387       break;
00388     case DBUS_TYPE_UINT64:
00389       {
00390         guint64 i;
00391 
00392         i = dbus_message_iter_get_uint64 (&iter);
00393 
00394         g_value_init (&value, G_TYPE_UINT64);
00395 
00396         g_value_set_uint64 (&value, i);
00397       }
00398       break;
00399     case DBUS_TYPE_DOUBLE:
00400       {
00401         double d;
00402 
00403         d = dbus_message_iter_get_double (&iter);
00404 
00405         g_value_init (&value, G_TYPE_DOUBLE);
00406 
00407         g_value_set_double (&value, d);
00408       }
00409       break;
00410     case DBUS_TYPE_STRING:
00411       {
00412         char *s;
00413 
00414         /* FIXME use a const string accessor */
00415 
00416         s = dbus_message_iter_get_string (&iter);
00417 
00418         g_value_init (&value, G_TYPE_STRING);
00419 
00420         g_value_set_string (&value, s);
00421 
00422         g_free (s);
00423       }
00424       break;
00425 
00426       /* FIXME array and other types, especially byte array
00427        * converted to G_TYPE_STRING
00428        */
00429 
00430     default:
00431       can_set = FALSE;
00432       break;
00433     }
00434 
00435   /* The g_object_set_property() will transform some types, e.g. it
00436    * will let you use a uchar to set an int property etc. Note that
00437    * any error in value range or value conversion will just
00438    * g_warning(). These GObject skels are not for secure applications.
00439    */
00440 
00441   if (can_set)
00442     {
00443       g_object_set_property (object,
00444                              pspec->name,
00445                              &value);
00446 
00447       ret = dbus_message_new_method_return (message);
00448       if (ret == NULL)
00449         g_error ("out of memory");
00450 
00451       g_value_unset (&value);
00452     }
00453   else
00454     {
00455       ret = dbus_message_new_error (message,
00456                                     DBUS_ERROR_INVALID_ARGS,
00457                                     "Argument's D-BUS type can't be converted to a GType");
00458       if (ret == NULL)
00459         g_error ("out of memory");
00460     }
00461 
00462   return ret;
00463 }
00464 
00465 static DBusMessage*
00466 get_object_property (DBusConnection *connection,
00467                      DBusMessage    *message,
00468                      GObject        *object,
00469                      GParamSpec     *pspec)
00470 {
00471   GType value_type;
00472   gboolean can_get;
00473   DBusMessage *ret;
00474   GValue value;
00475   DBusMessageIter iter;
00476 
00477   value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
00478 
00479   ret = dbus_message_new_method_return (message);
00480   if (ret == NULL)
00481     g_error ("out of memory");
00482 
00483   can_get = TRUE;
00484   g_value_init (&value, value_type);
00485   g_object_get_property (object, pspec->name, &value);
00486 
00487   value_type = G_VALUE_TYPE (&value);
00488 
00489   dbus_message_append_iter_init (message, &iter);
00490   
00491   switch (value_type)
00492     {
00493     case G_TYPE_CHAR:
00494       dbus_message_iter_append_byte (&iter,
00495                                      g_value_get_char (&value));
00496       break;
00497     case G_TYPE_UCHAR:
00498       dbus_message_iter_append_byte (&iter,
00499                                      g_value_get_uchar (&value));
00500       break;
00501     case G_TYPE_BOOLEAN:
00502       dbus_message_iter_append_boolean (&iter,
00503                                         g_value_get_boolean (&value));
00504       break;
00505     case G_TYPE_INT:
00506       dbus_message_iter_append_int32 (&iter,
00507                                       g_value_get_int (&value));
00508       break;
00509     case G_TYPE_UINT:
00510       dbus_message_iter_append_uint32 (&iter,
00511                                        g_value_get_uint (&value));
00512       break;
00513       /* long gets cut to 32 bits so the remote API is consistent
00514        * on all architectures
00515        */
00516     case G_TYPE_LONG:
00517       dbus_message_iter_append_int32 (&iter,
00518                                       g_value_get_long (&value));
00519       break;
00520     case G_TYPE_ULONG:
00521       dbus_message_iter_append_uint32 (&iter,
00522                                        g_value_get_ulong (&value));
00523       break;
00524     case G_TYPE_INT64:
00525       dbus_message_iter_append_int64 (&iter,
00526                                       g_value_get_int64 (&value));
00527       break;
00528     case G_TYPE_UINT64:
00529       dbus_message_iter_append_uint64 (&iter,
00530                                        g_value_get_uint64 (&value));
00531       break;
00532     case G_TYPE_FLOAT:
00533       dbus_message_iter_append_double (&iter,
00534                                        g_value_get_float (&value));
00535       break;
00536     case G_TYPE_DOUBLE:
00537       dbus_message_iter_append_double (&iter,
00538                                        g_value_get_double (&value));
00539       break;
00540     case G_TYPE_STRING:
00541       /* FIXME, the GValue string may not be valid UTF-8 */
00542       dbus_message_iter_append_string (&iter,
00543                                        g_value_get_string (&value));
00544       break;
00545     default:
00546       can_get = FALSE;
00547       break;
00548     }
00549 
00550   g_value_unset (&value);
00551 
00552   if (!can_get)
00553     {
00554       dbus_message_unref (ret);
00555       ret = dbus_message_new_error (message,
00556                                     DBUS_ERROR_UNKNOWN_METHOD,
00557                                     "Can't convert GType of object property to a D-BUS type");
00558     }
00559 
00560   return ret;
00561 }
00562 
00563 static DBusHandlerResult
00564 gobject_message_function (DBusConnection  *connection,
00565                           DBusMessage     *message,
00566                           void            *user_data)
00567 {
00568   const DBusGObjectInfo *info;
00569   GParamSpec *pspec;
00570   GObject *object;
00571   const char *member;
00572   gboolean setter;
00573   gboolean getter;
00574   char *s;
00575 
00576   object = G_OBJECT (user_data);
00577 
00578   if (dbus_message_is_method_call (message,
00579                                    DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
00580                                    "Introspect"))
00581     return handle_introspect (connection, message, object);
00582 
00583   member = dbus_message_get_member (message);
00584 
00585   /* Try the metainfo, which lets us invoke methods */
00586 
00587   g_static_mutex_lock (&info_hash_mutex);
00588   /* FIXME this needs to walk up the inheritance tree, not
00589    * just look at the most-derived class
00590    */
00591   info = g_hash_table_lookup (info_hash,
00592                               G_OBJECT_GET_CLASS (object));
00593   g_static_mutex_unlock (&info_hash_mutex);
00594 
00595   if (info != NULL)
00596     {
00597 
00598 
00599 
00600     }
00601 
00602   /* If no metainfo, we can still do properties and signals
00603    * via standard GLib introspection
00604    */
00605   setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
00606   getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't' && member[3] == '_');
00607 
00608   if (!(setter || getter))
00609     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00610 
00611   s = wincaps_to_uscore (&member[4]);
00612 
00613   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
00614                                         s);
00615 
00616   g_free (s);
00617 
00618   if (pspec != NULL)
00619     {
00620       DBusMessage *ret;
00621 
00622       if (setter)
00623         {
00624           ret = set_object_property (connection, message,
00625                                      object, pspec);
00626         }
00627       else if (getter)
00628         {
00629           ret = get_object_property (connection, message,
00630                                      object, pspec);
00631         }
00632       else
00633         {
00634           g_assert_not_reached ();
00635           ret = NULL;
00636         }
00637 
00638       g_assert (ret != NULL);
00639 
00640       dbus_connection_send (connection, ret, NULL);
00641       dbus_message_unref (ret);
00642       return DBUS_HANDLER_RESULT_HANDLED;
00643     }
00644 
00645   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00646 }
00647 
00648 static DBusObjectPathVTable gobject_dbus_vtable = {
00649   gobject_unregister_function,
00650   gobject_message_function,
00651   NULL
00652 };
00653  /* end of internals */
00655 
00675 void
00676 dbus_g_object_class_install_info (GObjectClass          *object_class,
00677                                   const DBusGObjectInfo *info)
00678 {
00679   g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
00680 
00681   g_static_mutex_lock (&info_hash_mutex);
00682 
00683   if (info_hash == NULL)
00684     {
00685       info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
00686     }
00687 
00688   g_hash_table_replace (info_hash, object_class, (void*) info);
00689 
00690   g_static_mutex_unlock (&info_hash_mutex);
00691 }
00692 
00706 void
00707 dbus_connection_register_g_object (DBusConnection        *connection,
00708                                    const char            *at_path,
00709                                    GObject               *object)
00710 {
00711   char **split;
00712 
00713   g_return_if_fail (connection != NULL);
00714   g_return_if_fail (at_path != NULL);
00715   g_return_if_fail (G_IS_OBJECT (object));
00716 
00717   split = _dbus_gutils_split_path (at_path);
00718 
00719   if (!dbus_connection_register_object_path (connection,
00720                                              (const char**) split,
00721                                              &gobject_dbus_vtable,
00722                                              object))
00723     g_error ("Failed to register GObject with DBusConnection");
00724 
00725   g_strfreev (split);
00726 
00727   /* FIXME set up memory management (so we break the
00728    * registration if object or connection vanishes)
00729    */
00730 }
00731  /* end of public API */
00733 
00734 #ifdef DBUS_BUILD_TESTS
00735 #include <stdlib.h>
00736 
00742 dbus_bool_t
00743 _dbus_gobject_test (const char *test_data_dir)
00744 {
00745   int i;
00746   static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
00747     { "SetFoo", "set_foo" },
00748     { "Foo", "foo" },
00749     { "GetFooBar", "get_foo_bar" },
00750     { "Hello", "hello" }
00751     
00752     /* Impossible-to-handle cases */
00753     /* { "FrobateUIHandler", "frobate_ui_handler" } */
00754   };
00755 
00756   i = 0;
00757   while (i < (int) G_N_ELEMENTS (name_pairs))
00758     {
00759       char *uscore;
00760       char *wincaps;
00761 
00762       uscore = wincaps_to_uscore (name_pairs[i].wincaps);
00763       wincaps = uscore_to_wincaps (name_pairs[i].uscore);
00764 
00765       if (strcmp (uscore, name_pairs[i].uscore) != 0)
00766         {
00767           g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
00768                       name_pairs[i].wincaps, name_pairs[i].uscore,
00769                       uscore);
00770           exit (1);
00771         }
00772       
00773       if (strcmp (wincaps, name_pairs[i].wincaps) != 0)
00774         {
00775           g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n",
00776                       name_pairs[i].uscore, name_pairs[i].wincaps,
00777                       wincaps);
00778           exit (1);
00779         }
00780       
00781       g_free (uscore);
00782       g_free (wincaps);
00783 
00784       ++i;
00785     }
00786   
00787   return TRUE;
00788 }
00789 
00790 #endif /* DBUS_BUILD_TESTS */

Generated on Tue Feb 10 18:14:02 2004 for D-BUS by doxygen 1.3.5