2003-09-01 Havoc Pennington <hp@pobox.com>

* glib/dbus-gparser.c: implement

	* glib/dbus-gobject.c: start implementing skeletons support

	* configure.in: when disabling checks/assert, also define
	G_DISABLE_ASSERT and G_DISABLE_CHECKS
This commit is contained in:
Havoc Pennington 2003-09-03 02:08:25 +00:00
parent 7b4ac5de11
commit d021cfae66
12 changed files with 1740 additions and 28 deletions

View file

@ -1,3 +1,12 @@
2003-09-01 Havoc Pennington <hp@pobox.com>
* glib/dbus-gparser.c: implement
* glib/dbus-gobject.c: start implementing skeletons support
* configure.in: when disabling checks/assert, also define
G_DISABLE_ASSERT and G_DISABLE_CHECKS
2003-09-01 Havoc Pennington <hp@pobox.com>
* glib/Makefile.am: rearrange a bunch of files and get "make

View file

@ -54,9 +54,11 @@ if test x$enable_verbose_mode = xyes; then
fi
if test x$enable_asserts = xno; then
AC_DEFINE(DBUS_DISABLE_ASSERT,1,[Disable assertion checking])
AC_DEFINE(G_DISABLE_ASSERT,1,[Disable GLib assertion macros])
fi
if test x$enable_checks = xno; then
AC_DEFINE(DBUS_DISABLE_CHECKS,1,[Disable public API sanity checking])
AC_DEFINE(G_DISABLE_CHECKS,1,[Disable GLib public API sanity checking])
fi
#### gcc warning flags
@ -545,7 +547,7 @@ AC_SUBST(DBUS_TEST_CFLAGS)
AC_SUBST(DBUS_TEST_LIBS)
# Glib detection
PKG_CHECK_MODULES(DBUS_GLIB, glib-2.0, have_glib=yes, have_glib=no)
PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0, have_glib=yes, have_glib=no)
PKG_CHECK_MODULES(DBUS_GLIB_THREADS, glib-2.0 gthread-2.0, have_glib_threads=yes, have_glib_threads=no)
if test x$have_glib = xno ; then
@ -570,6 +572,11 @@ AC_SUBST(DBUS_GLIB_CFLAGS)
AC_SUBST(DBUS_GLIB_LIBS)
AC_SUBST(DBUS_GLIB_THREADS_LIBS)
DBUS_GLIB_TOOL_CFLAGS=$XML_CFLAGS
DBUS_GLIB_TOOL_LIBS=$XML_LIBS
AC_SUBST(DBUS_GLIB_TOOL_CFLAGS)
AC_SUBST(DBUS_GLIB_TOOL_LIBS)
# Qt detection
have_qt=no
AC_MSG_CHECKING([for qglobal.h])

View file

@ -1473,7 +1473,8 @@ _dbus_demarshal_string_array (const DBusString *str,
#define VERBOSE_DECOMPOSE 0
/**
* Demarshals an object path.
* Demarshals an object path. A path of just "/" is
* represented as an empty vector of strings.
*
* @param str the string containing the data
* @param byte_order the byte order
@ -1556,7 +1557,6 @@ _dbus_demarshal_object_path (const DBusString *str,
i = j;
}
_dbus_assert (i == len);
_dbus_assert (retval[0] != NULL);
*path = retval;
if (path_len)

View file

@ -87,6 +87,18 @@ extern "C" {
#define DBUS_PATH_ORG_FREEDESKTOP_DBUS "/org/freedesktop/DBus"
#define DBUS_PATH_ORG_FREEDESKTOP_LOCAL "/org/freedesktop/Local"
/* Interfaces, these #define don't do much other than
* catch typos at compile time
*/
#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus"
#define DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE "org.freedesktop.Introspectable"
/* This is a special interface whose methods can only be invoked
* by the local implementation (messages from remote apps aren't
* allowed to specify this interface).
*/
#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local"
/* Service owner flags */
#define DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT 0x1
#define DBUS_SERVICE_FLAG_REPLACE_EXISTING 0x2
@ -101,17 +113,6 @@ extern "C" {
#define DBUS_ACTIVATION_REPLY_ACTIVATED 0x0
#define DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE 0x1
/* Interfaces, these #define don't do much other than
* catch typos at compile time
*/
#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS "org.freedesktop.DBus"
/* This is a special interface whose methods can only be invoked
* by the local implementation (messages from remote apps aren't
* allowed to specify this interface).
*/
#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local"
#ifdef __cplusplus
}
#endif

View file

@ -1,4 +1,4 @@
INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) -DDBUS_COMPILATION=1
INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) -DDBUS_COMPILATION=1
dbusincludedir=$(includedir)/dbus-1.0/dbus
@ -10,6 +10,7 @@ dbusinclude_HEADERS= \
libdbus_glib_1_la_SOURCES = \
dbus-gmain.c \
dbus-gobject.c \
dbus-gproxy.c \
dbus-gtest.c \
dbus-gtest.h \
@ -23,12 +24,13 @@ dbus_glib_tool_SOURCES = \
dbus-gidl.c \
dbus-gidl.h \
dbus-glib-tool.c \
dbus-gloader-expat.c \
dbus-gparser.c \
dbus-gparser.h \
dbus-gtool-test.h
dbus_glib_tool_LDADD= libdbus-glib-1.la $(DBUS_GLIB_LIBS) $(top_builddir)/dbus/libdbus-1.la
dbus_glib_tool_LDADD= $(DBUS_GLIB_TOOL_LIBS) libdbus-glib-1.la $(top_builddir)/dbus/libdbus-1.la
if DBUS_BUILD_TESTS

View file

@ -24,6 +24,13 @@
#include "dbus-gidl.h"
struct NodeInfo
{
int refcount;
char *name;
GSList *interfaces;
};
struct InterfaceInfo
{
int refcount;
@ -54,6 +61,20 @@ struct ArgInfo
ArgDirection direction;
};
static void
free_interface_list (GSList **interfaces_p)
{
GSList *tmp;
tmp = *interfaces_p;
while (tmp != NULL)
{
interface_info_unref (tmp->data);
tmp = tmp->next;
}
g_slist_free (*interfaces_p);
*interfaces_p = NULL;
}
static void
free_method_list (GSList **methods_p)
{
@ -82,6 +103,59 @@ free_signal_list (GSList **signals_p)
*signals_p = NULL;
}
NodeInfo*
node_info_new (const char *name)
{
NodeInfo *info;
/* name can be NULL */
info = g_new0 (NodeInfo, 1);
info->refcount = 1;
info->name = g_strdup (name);
return info;
}
void
node_info_ref (NodeInfo *info)
{
info->refcount += 1;
}
void
node_info_unref (NodeInfo *info)
{
info->refcount -= 1;
if (info->refcount == 0)
{
free_interface_list (&info->interfaces);
g_free (info->name);
g_free (info);
}
}
const char*
node_info_get_name (NodeInfo *info)
{
return info->name;
}
GSList*
node_info_get_interfaces (NodeInfo *info)
{
return info->interfaces;
}
void
node_info_add_interface (NodeInfo *info,
InterfaceInfo *interface)
{
interface_info_ref (interface);
info->interfaces = g_slist_append (info->interfaces, interface);
}
InterfaceInfo*
interface_info_new (const char *name)
{
@ -90,7 +164,7 @@ interface_info_new (const char *name)
info = g_new0 (InterfaceInfo, 1);
info->refcount = 1;
info->name = g_strdup (name);
return info;
}
@ -113,6 +187,12 @@ interface_info_unref (InterfaceInfo *info)
}
}
const char*
interface_info_get_name (InterfaceInfo *info)
{
return info->name;
}
GSList*
interface_info_get_methods (InterfaceInfo *info)
{
@ -163,7 +243,7 @@ method_info_new (const char *name)
info = g_new0 (MethodInfo, 1);
info->refcount = 1;
info->name = g_strdup (name);
return info;
}
@ -213,7 +293,7 @@ signal_info_new (const char *name)
info = g_new0 (SignalInfo, 1);
info->refcount = 1;
info->name = g_strdup (name);
return info;
}
@ -264,10 +344,12 @@ arg_info_new (const char *name,
info = g_new0 (ArgInfo, 1);
info->refcount = 1;
/* name can be NULL */
info->name = g_strdup (name);
info->direction = direction;
info->type = type;
return info;
}

View file

@ -29,6 +29,7 @@
G_BEGIN_DECLS
typedef struct NodeInfo NodeInfo;
typedef struct InterfaceInfo InterfaceInfo;
typedef struct MethodInfo MethodInfo;
typedef struct SignalInfo SignalInfo;
@ -40,9 +41,18 @@ typedef enum
ARG_OUT
} ArgDirection;
NodeInfo* node_info_new (const char *name);
void node_info_ref (NodeInfo *info);
void node_info_unref (NodeInfo *info);
const char* node_info_get_name (NodeInfo *info);
GSList* node_info_get_interfaces (NodeInfo *info);
void node_info_add_interface (NodeInfo *info,
InterfaceInfo *interface);
InterfaceInfo* interface_info_new (const char *name);
void interface_info_ref (InterfaceInfo *info);
void interface_info_unref (InterfaceInfo *info);
const char* interface_info_get_name (InterfaceInfo *info);
GSList* interface_info_get_methods (InterfaceInfo *info);
GSList* interface_info_get_signals (InterfaceInfo *info);
void interface_info_add_method (InterfaceInfo *info,

View file

@ -24,7 +24,7 @@
#define DBUS_GLIB_H
#include <dbus/dbus.h>
#include <glib.h>
#include <glib-object.h>
G_BEGIN_DECLS
@ -36,6 +36,30 @@ void dbus_connection_setup_with_g_main (DBusConnection *connection,
void dbus_server_setup_with_g_main (DBusServer *server,
GMainContext *context);
typedef struct DBusGObjectInfo DBusGObjectInfo;
typedef struct DBusGMethodInfo DBusGMethodInfo;
struct DBusGMethodInfo
{
GCallback function;
DBusHandleMessageFunction marshaller;
int data_offset;
};
struct DBusGObjectInfo
{
const DBusGMethodInfo *infos;
const unsigned char *data;
void *dbus_internal_padding1;
void *dbus_internal_padding2;
};
void dbus_gobject_class_install_info (GObjectClass *object_class,
const DBusGObjectInfo *info);
void dbus_connection_register_gobject (DBusConnection *connection,
const char *at_path,
GObject *object);
#undef DBUS_INSIDE_DBUS_GLIB_H
G_END_DECLS

254
glib/dbus-gloader-expat.c Normal file
View file

@ -0,0 +1,254 @@
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-gloader-expat.c expat XML loader
*
* Copyright (C) 2003 Red Hat, Inc.
*
* Licensed under the Academic Free License version 1.2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "dbus-gparser.h"
#include <expat.h>
static void*
expat_g_malloc (size_t sz)
{
return g_malloc (sz);
}
static void*
expat_g_realloc (void *mem, size_t sz)
{
return g_realloc (mem, sz);
}
static XML_Memory_Handling_Suite memsuite =
{
expat_g_malloc,
expat_g_realloc,
g_free
};
typedef struct
{
Parser *parser;
const char *filename;
GString *content;
GError **error;
gboolean failed;
} ExpatParseContext;
static dbus_bool_t
process_content (ExpatParseContext *context)
{
if (context->failed)
return FALSE;
if (context->content->len > 0)
{
if (!parser_content (context->parser,
context->content->str,
context->content->len,
context->error))
{
context->failed = TRUE;
return FALSE;
}
g_string_set_size (context->content, 0);
}
return TRUE;
}
static void
expat_StartElementHandler (void *userData,
const XML_Char *name,
const XML_Char **atts)
{
ExpatParseContext *context = userData;
int i;
char **names;
char **values;
/* Expat seems to suck and can't abort the parse if we
* throw an error. Expat 2.0 is supposed to fix this.
*/
if (context->failed)
return;
if (!process_content (context))
return;
/* "atts" is key, value, key, value, NULL */
for (i = 0; atts[i] != NULL; ++i)
; /* nothing */
g_assert (i % 2 == 0);
names = g_new0 (char *, i / 2 + 1);
values = g_new0 (char *, i / 2 + 1);
i = 0;
while (atts[i] != NULL)
{
g_assert (i % 2 == 0);
names [i / 2] = (char*) atts[i];
values[i / 2] = (char*) atts[i+1];
i += 2;
}
if (!parser_start_element (context->parser,
name,
(const char **) names,
(const char **) values,
context->error))
{
g_free (names);
g_free (values);
context->failed = TRUE;
return;
}
g_free (names);
g_free (values);
}
static void
expat_EndElementHandler (void *userData,
const XML_Char *name)
{
ExpatParseContext *context = userData;
if (!process_content (context))
return;
if (!parser_end_element (context->parser,
name,
context->error))
{
context->failed = TRUE;
return;
}
}
/* s is not 0 terminated. */
static void
expat_CharacterDataHandler (void *userData,
const XML_Char *s,
int len)
{
ExpatParseContext *context = userData;
if (context->failed)
return;
g_string_append_len (context->content,
s, len);
}
Parser*
description_load_from_file (const char *filename,
GError **error)
{
char *contents;
gsize len;
Parser *parser;
contents = NULL;
if (!g_file_get_contents (filename, &contents, &len, error))
return NULL;
parser = description_load_from_string (contents, len, error);
g_free (contents);
return parser;
}
Parser*
description_load_from_string (const char *str,
int len,
GError **error)
{
XML_Parser expat;
ExpatParseContext context;
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
expat = NULL;
context.parser = NULL;
context.error = error;
context.failed = FALSE;
expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL);
if (expat == NULL)
g_error ("No memory to create XML parser\n");
context.parser = parser_new ();
context.content = g_string_new (NULL);
XML_SetUserData (expat, &context);
XML_SetElementHandler (expat,
expat_StartElementHandler,
expat_EndElementHandler);
XML_SetCharacterDataHandler (expat,
expat_CharacterDataHandler);
if (!XML_Parse (expat, str, len, TRUE))
{
if (context.error != NULL &&
*context.error == NULL)
{
enum XML_Error e;
e = XML_GetErrorCode (expat);
if (e == XML_ERROR_NO_MEMORY)
g_error ("Not enough memory to parse XML document");
else
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
"Error in D-BUS description XML, line %d, column %d: %s\n",
XML_GetCurrentLineNumber (expat),
XML_GetCurrentColumnNumber (expat),
XML_ErrorString (e));
}
goto failed;
}
if (context.failed)
goto failed;
if (!parser_finished (context.parser, error))
goto failed;
XML_ParserFree (expat);
g_string_free (context.content, TRUE);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return context.parser;
failed:
g_return_val_if_fail (error == NULL || *error != NULL, NULL);
g_string_free (context.content, TRUE);
if (expat)
XML_ParserFree (expat);
if (context.parser)
parser_unref (context.parser);
return NULL;
}

780
glib/dbus-gobject.c Normal file
View file

@ -0,0 +1,780 @@
/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-gobject.c Exporting a GObject remotely
*
* Copyright (C) 2003 Red Hat, Inc.
*
* Licensed under the Academic Free License version 1.2
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <config.h>
#include "dbus-glib.h"
#include "dbus-gtest.h"
#include <string.h>
/**
* @addtogroup DBusGLibInternals
* @{
*/
static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
static GHashTable *info_hash = NULL;
static char*
javacaps_to_uscore (const char *caps)
{
const char *p;
GString *str;
str = g_string_new (NULL);
p = caps;
while (*p)
{
if (g_ascii_isupper (*p))
{
if (str->len > 0 &&
(str->len < 2 || str->str[str->len-2] != '_'))
g_string_append_c (str, '_');
g_string_append_c (str, g_ascii_tolower (*p));
}
else
{
g_string_append_c (str, *p);
}
++p;
}
return g_string_free (str, FALSE);
}
static char*
uscore_to_javacaps (const char *uscore)
{
const char *p;
GString *str;
gboolean last_was_uscore;
last_was_uscore = TRUE;
str = g_string_new (NULL);
p = uscore;
while (*p)
{
if (*p == '-' || *p == '_')
{
last_was_uscore = TRUE;
}
else
{
if (last_was_uscore)
{
g_string_append_c (str, g_ascii_toupper (*p));
last_was_uscore = FALSE;
}
else
g_string_append_c (str, *p);
}
++p;
}
return g_string_free (str, FALSE);
}
static void
gobject_unregister_function (DBusConnection *connection,
void *user_data)
{
GObject *object;
object = G_OBJECT (user_data);
}
static int
gtype_to_dbus_type (GType type)
{
switch (type)
{
case G_TYPE_CHAR:
case G_TYPE_UCHAR:
return DBUS_TYPE_BYTE;
case G_TYPE_BOOLEAN:
return DBUS_TYPE_BOOLEAN;
/* long gets cut to 32 bits so the remote API is consistent
* on all architectures
*/
case G_TYPE_LONG:
case G_TYPE_INT:
return DBUS_TYPE_INT32;
case G_TYPE_ULONG:
case G_TYPE_UINT:
return DBUS_TYPE_UINT32;
case G_TYPE_INT64:
return DBUS_TYPE_INT64;
case G_TYPE_UINT64:
return DBUS_TYPE_UINT64;
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
return DBUS_TYPE_DOUBLE;
case G_TYPE_STRING:
return DBUS_TYPE_STRING;
default:
return DBUS_TYPE_INVALID;
}
}
static const char *
dbus_type_to_string (int type)
{
switch (type)
{
case DBUS_TYPE_INVALID:
return "invalid";
case DBUS_TYPE_NIL:
return "nil";
case DBUS_TYPE_BOOLEAN:
return "boolean";
case DBUS_TYPE_INT32:
return "int32";
case DBUS_TYPE_UINT32:
return "uint32";
case DBUS_TYPE_DOUBLE:
return "double";
case DBUS_TYPE_STRING:
return "string";
case DBUS_TYPE_NAMED:
return "named";
case DBUS_TYPE_ARRAY:
return "array";
case DBUS_TYPE_DICT:
return "dict";
default:
return "unknown";
}
}
static DBusHandlerResult
handle_introspect (DBusConnection *connection,
DBusMessage *message,
GObject *object)
{
GString *xml;
GParamSpec **specs;
unsigned int n_specs;
unsigned int i;
GType last_type;
DBusMessage *ret;
xml = g_string_new (NULL);
g_string_append (xml, "<node>\n");
last_type = G_TYPE_INVALID;
specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
&n_specs);
i = 0;
while (i < n_specs)
{
GParamSpec *spec = specs[i];
gboolean can_set;
gboolean can_get;
char *s;
int dbus_type;
dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
if (dbus_type == DBUS_TYPE_INVALID)
goto next;
if (spec->owner_type != last_type)
{
if (last_type != G_TYPE_INVALID)
g_string_append (xml, " </interface>\n");
/* FIXME what should the namespace on the interface be in
* general? should people be able to set it for their
* objects?
*/
g_string_append (xml, " <interface name=\"org.gtk.objects.");
g_string_append (xml, g_type_name (spec->owner_type));
g_string_append (xml, "\">\n");
last_type = spec->owner_type;
}
can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
(spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
can_get = (spec->flags & G_PARAM_READABLE) != 0;
s = uscore_to_javacaps (spec->name);
if (can_set)
{
g_string_append (xml, " <method name=\"set");
g_string_append (xml, s);
g_string_append (xml, "\">\n");
g_string_append (xml, " <arg type=\"");
g_string_append (xml, dbus_type_to_string (dbus_type));
g_string_append (xml, "\"/>\n");
}
if (can_get)
{
g_string_append (xml, " <method name=\"get");
g_string_append (xml, s);
g_string_append (xml, "\">\n");
g_string_append (xml, " <arg type=\"");
g_string_append (xml, dbus_type_to_string (dbus_type));
g_string_append (xml, "\" direction=\"out\"/>\n");
}
g_free (s);
next:
++i;
}
if (last_type != G_TYPE_INVALID)
g_string_append (xml, " </interface>\n");
g_free (specs);
/* Close the XML, and send it to the requesting app */
g_string_append (xml, "</node>\n");
ret = dbus_message_new_method_return (message);
if (ret == NULL)
g_error ("out of memory");
dbus_message_append_args (message,
DBUS_TYPE_STRING, xml->str,
DBUS_TYPE_INVALID);
dbus_connection_send (connection, message, NULL);
dbus_message_unref (message);
g_string_free (xml, TRUE);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusMessage*
set_object_property (DBusConnection *connection,
DBusMessage *message,
GObject *object,
GParamSpec *pspec)
{
GValue value;
DBusMessageIter iter;
int type;
gboolean can_set;
DBusMessage *ret;
dbus_message_iter_init (message, &iter);
type = dbus_message_get_type (message);
can_set = TRUE;
switch (type)
{
case DBUS_TYPE_BYTE:
{
unsigned char b;
b = dbus_message_iter_get_byte (&iter);
g_value_init (&value, G_TYPE_UCHAR);
g_value_set_uchar (&value, b);
}
break;
case DBUS_TYPE_BOOLEAN:
{
gboolean b;
b = dbus_message_iter_get_boolean (&iter);
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, b);
}
break;
case DBUS_TYPE_INT32:
{
gint32 i;
i = dbus_message_iter_get_int32 (&iter);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, i);
}
break;
case DBUS_TYPE_UINT32:
{
guint32 i;
i = dbus_message_iter_get_uint32 (&iter);
g_value_init (&value, G_TYPE_UINT);
g_value_set_uint (&value, i);
}
break;
case DBUS_TYPE_INT64:
{
gint64 i;
i = dbus_message_iter_get_int64 (&iter);
g_value_init (&value, G_TYPE_INT64);
g_value_set_int64 (&value, i);
}
break;
case DBUS_TYPE_UINT64:
{
guint64 i;
i = dbus_message_iter_get_uint64 (&iter);
g_value_init (&value, G_TYPE_UINT64);
g_value_set_uint64 (&value, i);
}
break;
case DBUS_TYPE_DOUBLE:
{
double d;
d = dbus_message_iter_get_double (&iter);
g_value_init (&value, G_TYPE_DOUBLE);
g_value_set_double (&value, d);
}
break;
case DBUS_TYPE_STRING:
{
char *s;
/* FIXME use a const string accessor */
s = dbus_message_iter_get_string (&iter);
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, s);
g_free (s);
}
break;
/* FIXME array and other types, especially byte array
* converted to G_TYPE_STRING
*/
default:
can_set = FALSE;
break;
}
/* The g_object_set_property() will transform some types, e.g. it
* will let you use a uchar to set an int property etc. Note that
* any error in value range or value conversion will just
* g_warning(). These GObject skels are not for secure applications.
*/
if (can_set)
{
g_object_set_property (object,
pspec->name,
&value);
ret = dbus_message_new_method_return (message);
if (ret == NULL)
g_error ("out of memory");
g_value_unset (&value);
}
else
{
ret = dbus_message_new_error (message,
DBUS_ERROR_INVALID_ARGS,
"Argument's D-BUS type can't be converted to a GType");
if (ret == NULL)
g_error ("out of memory");
}
return ret;
}
static DBusMessage*
get_object_property (DBusConnection *connection,
DBusMessage *message,
GObject *object,
GParamSpec *pspec)
{
GType value_type;
gboolean can_get;
DBusMessage *ret;
GValue value;
DBusMessageIter iter;
value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
ret = dbus_message_new_method_return (message);
if (ret == NULL)
g_error ("out of memory");
can_get = TRUE;
g_value_init (&value, value_type);
g_object_get_property (object, pspec->name, &value);
value_type = G_VALUE_TYPE (&value);
dbus_message_append_iter_init (message, &iter);
switch (value_type)
{
case G_TYPE_CHAR:
dbus_message_iter_append_byte (&iter,
g_value_get_char (&value));
break;
case G_TYPE_UCHAR:
dbus_message_iter_append_byte (&iter,
g_value_get_uchar (&value));
break;
case G_TYPE_BOOLEAN:
dbus_message_iter_append_boolean (&iter,
g_value_get_boolean (&value));
break;
case G_TYPE_INT:
dbus_message_iter_append_int32 (&iter,
g_value_get_int (&value));
break;
case G_TYPE_UINT:
dbus_message_iter_append_uint32 (&iter,
g_value_get_uint (&value));
break;
/* long gets cut to 32 bits so the remote API is consistent
* on all architectures
*/
case G_TYPE_LONG:
dbus_message_iter_append_int32 (&iter,
g_value_get_long (&value));
break;
case G_TYPE_ULONG:
dbus_message_iter_append_uint32 (&iter,
g_value_get_ulong (&value));
break;
case G_TYPE_INT64:
dbus_message_iter_append_int64 (&iter,
g_value_get_int64 (&value));
break;
case G_TYPE_UINT64:
dbus_message_iter_append_uint64 (&iter,
g_value_get_uint64 (&value));
break;
case G_TYPE_FLOAT:
dbus_message_iter_append_double (&iter,
g_value_get_float (&value));
break;
case G_TYPE_DOUBLE:
dbus_message_iter_append_double (&iter,
g_value_get_double (&value));
break;
case G_TYPE_STRING:
/* FIXME, the GValue string may not be valid UTF-8 */
dbus_message_iter_append_string (&iter,
g_value_get_string (&value));
break;
default:
can_get = FALSE;
break;
}
g_value_unset (&value);
if (!can_get)
{
dbus_message_unref (ret);
ret = dbus_message_new_error (message,
DBUS_ERROR_UNKNOWN_METHOD,
"Can't convert GType of object property to a D-BUS type");
}
return ret;
}
static DBusHandlerResult
gobject_message_function (DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
const DBusGObjectInfo *info;
GParamSpec *pspec;
GObject *object;
const char *member;
gboolean setter;
gboolean getter;
char *s;
object = G_OBJECT (user_data);
if (dbus_message_is_method_call (message,
DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
"Introspect"))
return handle_introspect (connection, message, object);
member = dbus_message_get_member (message);
/* Try the metainfo, which lets us invoke methods */
g_static_mutex_lock (&info_hash_mutex);
/* FIXME this needs to walk up the inheritance tree, not
* just look at the most-derived class
*/
info = g_hash_table_lookup (info_hash,
G_OBJECT_GET_CLASS (object));
g_static_mutex_unlock (&info_hash_mutex);
if (info != NULL)
{
}
/* If no metainfo, we can still do properties and signals
* via standard GLib introspection
*/
setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't');
getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't');
if (!(setter || getter))
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
s = javacaps_to_uscore (&member[3]);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
s);
g_free (s);
if (pspec != NULL)
{
DBusMessage *ret;
if (setter)
{
ret = set_object_property (connection, message,
object, pspec);
}
else if (getter)
{
ret = get_object_property (connection, message,
object, pspec);
}
else
{
g_assert_not_reached ();
ret = NULL;
}
g_assert (ret != NULL);
dbus_connection_send (connection, ret, NULL);
dbus_message_unref (ret);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static DBusObjectPathVTable gobject_dbus_vtable = {
gobject_unregister_function,
gobject_message_function,
NULL
};
/** @} */ /* end of internals */
/**
* @addtogroup DBusGLib
* @{
*/
/**
* Install introspection information about the given object class
* sufficient to allow methods on the object to be invoked by name.
* The introspection information is normally generated by
* dbus-glib-tool, then this function is called in the
* class_init() for the object class.
*
* Once introspection information has been installed, instances of the
* object registered with dbus_connection_register_gobject() can have
* their methods invoked remotely.
*
* @param object_class class struct of the object
* @param info introspection data generated by dbus-glib-tool
*/
void
dbus_gobject_class_install_info (GObjectClass *object_class,
const DBusGObjectInfo *info)
{
g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
g_static_mutex_lock (&info_hash_mutex);
if (info_hash == NULL)
{
info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
}
g_hash_table_replace (info_hash, object_class, (void*) info);
g_static_mutex_unlock (&info_hash_mutex);
}
static char**
split_path (const char *path)
{
int len;
char **split;
int n_components;
int i, j, comp;
len = strlen (path);
n_components = 0;
i = 0;
while (i < len)
{
if (path[i] == '/')
n_components += 1;
++i;
}
split = g_new0 (char*, n_components + 1);
comp = 0;
i = 0;
while (i < len)
{
if (path[i] == '/')
++i;
j = i;
while (j < len && path[j] != '/')
++j;
/* Now [i, j) is the path component */
g_assert (i < j);
g_assert (path[i] != '/');
g_assert (j == len || path[j] == '/');
split[comp] = g_strndup (&path[i], j - i + 1);
split[comp][j-i] = '\0';
++comp;
i = j;
}
g_assert (i == len);
return split;
}
/**
* Registers a GObject at the given path. Properties, methods, and signals
* of the object can then be accessed remotely. Methods are only available
* if method introspection data has been added to the object's class
* with g_object_class_install_info().
*
* The registration will be cancelled if either the DBusConnection or
* the GObject gets finalized.
*
* @param connection the D-BUS connection
* @param at_path the path where the object will live (the object's name)
* @param object the object
*/
void
dbus_connection_register_gobject (DBusConnection *connection,
const char *at_path,
GObject *object)
{
char **split;
g_return_if_fail (connection != NULL);
g_return_if_fail (at_path != NULL);
g_return_if_fail (G_IS_OBJECT (object));
split = split_path (at_path);
if (!dbus_connection_register_object_path (connection,
(const char**) split,
&gobject_dbus_vtable,
object))
g_error ("Failed to register GObject with DBusConnection");
g_strfreev (split);
/* FIXME set up memory management (so we break the
* registration if object or connection vanishes)
*/
}
/** @} */ /* end of public API */
#ifdef DBUS_BUILD_TESTS
/**
* @ingroup DBusGLibInternals
* Unit test for GLib GObject integration ("skeletons")
* @returns #TRUE on success.
*/
dbus_bool_t
_dbus_gobject_test (const char *test_data_dir)
{
static struct { const char *javacaps; const char *uscore; } name_pairs[] = {
{ "setFoo", "set_foo" },
{ "foo", "foo" },
{ "getFooBar", "get_foo_bar" },
{ "Hello", "hello" },
{ "frobateUIHandler", "frobate_ui_handler" }
};
return TRUE;
}
#endif /* DBUS_BUILD_TESTS */

View file

@ -22,11 +22,152 @@
*/
#include "dbus-gparser.h"
#include "dbus-gidl.h"
#include <string.h>
#include <libintl.h>
#define _(x) gettext ((x))
#define N_(x) x
#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
typedef struct
{
const char *name;
const char **retloc;
} LocateAttr;
static gboolean
locate_attributes (const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error,
const char *first_attribute_name,
const char **first_attribute_retloc,
...)
{
va_list args;
const char *name;
const char **retloc;
int n_attrs;
#define MAX_ATTRS 24
LocateAttr attrs[MAX_ATTRS];
gboolean retval;
int i;
g_return_val_if_fail (first_attribute_name != NULL, FALSE);
g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
retval = TRUE;
n_attrs = 1;
attrs[0].name = first_attribute_name;
attrs[0].retloc = first_attribute_retloc;
*first_attribute_retloc = NULL;
va_start (args, first_attribute_retloc);
name = va_arg (args, const char*);
retloc = va_arg (args, const char**);
while (name != NULL)
{
g_return_val_if_fail (retloc != NULL, FALSE);
g_assert (n_attrs < MAX_ATTRS);
attrs[n_attrs].name = name;
attrs[n_attrs].retloc = retloc;
n_attrs += 1;
*retloc = NULL;
name = va_arg (args, const char*);
retloc = va_arg (args, const char**);
}
va_end (args);
if (!retval)
return retval;
i = 0;
while (attribute_names[i])
{
int j;
gboolean found;
found = FALSE;
j = 0;
while (j < n_attrs)
{
if (strcmp (attrs[j].name, attribute_names[i]) == 0)
{
retloc = attrs[j].retloc;
if (*retloc != NULL)
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Attribute \"%s\" repeated twice on the same <%s> element"),
attrs[j].name, element_name);
retval = FALSE;
goto out;
}
*retloc = attribute_values[i];
found = TRUE;
}
++j;
}
if (!found)
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Attribute \"%s\" is invalid on <%s> element in this context"),
attribute_names[i], element_name);
retval = FALSE;
goto out;
}
++i;
}
out:
return retval;
}
static gboolean
check_no_attributes (const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error)
{
if (attribute_names[0] != NULL)
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Attribute \"%s\" is invalid on <%s> element in this context"),
attribute_names[0], element_name);
return FALSE;
}
return TRUE;
}
struct Parser
{
int refcount;
NodeInfo *result; /* Filled in when we pop the last node */
GSList *node_stack;
InterfaceInfo *interface;
MethodInfo *method;
SignalInfo *signal;
ArgInfo *arg;
};
Parser*
@ -53,7 +194,8 @@ parser_unref (Parser *parser)
parser->refcount -= 1;
if (parser->refcount == 0)
{
if (parser->result)
node_info_unref (parser->result);
g_free (parser);
}
@ -64,11 +206,12 @@ parser_check_doctype (Parser *parser,
const char *doctype,
GError **error)
{
g_return_val_if_fail (error == NULL || *error == NULL);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (strcmp (doctype, "dbus_description") != 0)
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
"D-BUS description file has the wrong document type %s, use dbus_description",
doctype);
@ -78,6 +221,324 @@ parser_check_doctype (Parser *parser,
return TRUE;
}
static gboolean
parse_node (Parser *parser,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error)
{
const char *name;
NodeInfo *node;
if (parser->interface ||
parser->method ||
parser->signal ||
parser->arg)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Can't put a <%s> element here"),
element_name);
return FALSE;
}
name = NULL;
if (!locate_attributes (element_name, attribute_names,
attribute_values, error,
"name", &name,
NULL))
return FALSE;
/* Only the root node can have no name */
if (parser->node_stack != NULL && name == NULL)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("\"%s\" attribute required on <%s> element "),
"name", element_name);
return FALSE;
}
node = node_info_new (name);
parser->node_stack = g_slist_prepend (parser->node_stack,
node);
return TRUE;
}
static gboolean
parse_interface (Parser *parser,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error)
{
const char *name;
InterfaceInfo *iface;
NodeInfo *top;
if (parser->interface ||
parser->method ||
parser->signal ||
parser->arg ||
(parser->node_stack == NULL))
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Can't put a <%s> element here"),
element_name);
return FALSE;
}
name = NULL;
if (!locate_attributes (element_name, attribute_names,
attribute_values, error,
"name", &name,
NULL))
return FALSE;
if (name == NULL)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("\"%s\" attribute required on <%s> element "),
"name", element_name);
return FALSE;
}
top = parser->node_stack->data;
iface = interface_info_new (name);
node_info_add_interface (top, iface);
interface_info_unref (iface);
parser->interface = iface;
return TRUE;
}
static gboolean
parse_method (Parser *parser,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error)
{
const char *name;
MethodInfo *method;
NodeInfo *top;
if (parser->interface == NULL ||
parser->node_stack == NULL ||
parser->method ||
parser->signal ||
parser->arg)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Can't put a <%s> element here"),
element_name);
return FALSE;
}
name = NULL;
if (!locate_attributes (element_name, attribute_names,
attribute_values, error,
"name", &name,
NULL))
return FALSE;
if (name == NULL)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("\"%s\" attribute required on <%s> element "),
"name", element_name);
return FALSE;
}
top = parser->node_stack->data;
method = method_info_new (name);
interface_info_add_method (parser->interface, method);
method_info_unref (method);
parser->method = method;
return TRUE;
}
static gboolean
parse_signal (Parser *parser,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error)
{
const char *name;
SignalInfo *signal;
NodeInfo *top;
if (parser->interface == NULL ||
parser->node_stack == NULL ||
parser->signal ||
parser->signal ||
parser->arg)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Can't put a <%s> element here"),
element_name);
return FALSE;
}
name = NULL;
if (!locate_attributes (element_name, attribute_names,
attribute_values, error,
"name", &name,
NULL))
return FALSE;
if (name == NULL)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("\"%s\" attribute required on <%s> element "),
"name", element_name);
return FALSE;
}
top = parser->node_stack->data;
signal = signal_info_new (name);
interface_info_add_signal (parser->interface, signal);
signal_info_unref (signal);
parser->signal = signal;
return TRUE;
}
static int
basic_type_from_string (const char *str)
{
if (strcmp (str, "string") == 0)
return DBUS_TYPE_STRING;
else if (strcmp (str, "int32") == 0)
return DBUS_TYPE_INT32;
else if (strcmp (str, "uint32") == 0)
return DBUS_TYPE_UINT32;
else if (strcmp (str, "int64") == 0)
return DBUS_TYPE_INT64;
else if (strcmp (str, "uint64") == 0)
return DBUS_TYPE_UINT64;
else if (strcmp (str, "double") == 0)
return DBUS_TYPE_DOUBLE;
else if (strcmp (str, "byte") == 0)
return DBUS_TYPE_BYTE;
else if (strcmp (str, "boolean") == 0)
return DBUS_TYPE_BOOLEAN;
else if (strcmp (str, "byte") == 0)
return DBUS_TYPE_BYTE;
else if (strcmp (str, "object") == 0)
return DBUS_TYPE_OBJECT_PATH;
else
return DBUS_TYPE_INVALID;
}
static int
type_from_string (const char *str)
{
return basic_type_from_string (str);
}
static gboolean
parse_arg (Parser *parser,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
GError **error)
{
const char *name;
const char *type;
const char *direction;
ArgDirection dir;
int t;
ArgInfo *arg;
if (!(parser->method || parser->signal) ||
parser->node_stack == NULL ||
parser->arg)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Can't put a <%s> element here"),
element_name);
return FALSE;
}
name = NULL;
if (!locate_attributes (element_name, attribute_names,
attribute_values, error,
"name", &name,
"type", &type,
"direction", &direction,
NULL))
return FALSE;
/* name can be null for args */
if (type == NULL)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("\"%s\" attribute required on <%s> element "),
"type", element_name);
return FALSE;
}
if (direction == NULL)
{
/* methods default to in, signal to out */
if (parser->method)
direction = "in";
else if (parser->signal)
direction = "out";
else
g_assert_not_reached ();
}
if (strcmp (direction, "in") == 0)
dir = ARG_IN;
else if (strcmp (direction, "out") == 0)
dir = ARG_OUT;
else
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
"direction", element_name);
return FALSE;
}
t = type_from_string (type);
arg = arg_info_new (name, dir, t);
if (parser->method)
method_info_add_arg (parser->method, arg);
else if (parser->signal)
signal_info_add_arg (parser->signal, arg);
else
g_assert_not_reached ();
arg_info_unref (arg);
parser->arg = arg;
return TRUE;
}
gboolean
parser_start_element (Parser *parser,
const char *element_name,
@ -85,8 +546,46 @@ parser_start_element (Parser *parser,
const char **attribute_values,
GError **error)
{
g_return_val_if_fail (error == NULL || *error == NULL);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (ELEMENT_IS ("node"))
{
if (!parse_node (parser, element_name, attribute_names,
attribute_values, error))
return FALSE;
}
else if (ELEMENT_IS ("interface"))
{
if (!parse_interface (parser, element_name, attribute_names,
attribute_values, error))
return FALSE;
}
else if (ELEMENT_IS ("method"))
{
if (!parse_method (parser, element_name, attribute_names,
attribute_values, error))
return FALSE;
}
else if (ELEMENT_IS ("signal"))
{
if (!parse_signal (parser, element_name, attribute_names,
attribute_values, error))
return FALSE;
}
else if (ELEMENT_IS ("arg"))
{
if (!parse_arg (parser, element_name, attribute_names,
attribute_values, error))
return FALSE;
}
else
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_PARSE,
_("Element <%s> not recognized"),
element_name);
}
return TRUE;
}
@ -95,8 +594,40 @@ parser_end_element (Parser *parser,
const char *element_name,
GError **error)
{
g_return_val_if_fail (error == NULL || *error == NULL);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (ELEMENT_IS ("interface"))
{
parser->interface = NULL;
}
else if (ELEMENT_IS ("method"))
{
parser->method = NULL;
}
else if (ELEMENT_IS ("signal"))
{
parser->signal = NULL;
}
else if (ELEMENT_IS ("arg"))
{
parser->arg = NULL;
}
else if (ELEMENT_IS ("node"))
{
NodeInfo *top;
g_assert (parser->node_stack != NULL);
top = parser->node_stack->data;
parser->node_stack = g_slist_remove (parser->node_stack,
top);
if (parser->node_stack == NULL)
parser->result = top; /* We are done, store the result */
}
else
g_assert_not_reached (); /* should have had an error on start_element */
return TRUE;
}
@ -106,7 +637,7 @@ parser_content (Parser *parser,
int len,
GError **error)
{
g_return_val_if_fail (error == NULL || *error == NULL);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return TRUE;
}
@ -115,7 +646,13 @@ gboolean
parser_finished (Parser *parser,
GError **error)
{
g_return_val_if_fail (error == NULL || *error == NULL);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return TRUE;
}
NodeInfo*
parser_get_nodes (Parser *parser)
{
return parser->result;
}

View file

@ -25,10 +25,10 @@
#include <dbus/dbus.h>
#include <glib.h>
#include "dbus-gidl.h"
G_BEGIN_DECLS
typedef struct Parser Parser;
Parser* parser_new (void);
@ -52,7 +52,13 @@ gboolean parser_content (Parser *parser,
gboolean parser_finished (Parser *parser,
GError **error);
Parser* description_load_from_file (const char *filename,
GError **error);
Parser* description_load_from_string (const char *str,
int len,
GError **error);
NodeInfo* parser_get_nodes (Parser *parser);
G_END_DECLS