diff options
Diffstat (limited to 'glib')
-rw-r--r-- | glib/.cvsignore | 6 | ||||
-rw-r--r-- | glib/Makefile.am | 70 | ||||
-rw-r--r-- | glib/dbus-gidl.c | 524 | ||||
-rw-r--r-- | glib/dbus-gidl.h | 120 | ||||
-rw-r--r-- | glib/dbus-glib-tool.c | 77 | ||||
-rw-r--r-- | glib/dbus-glib.h | 121 | ||||
-rw-r--r-- | glib/dbus-gloader-expat.c | 262 | ||||
-rw-r--r-- | glib/dbus-gmain.c | 100 | ||||
-rw-r--r-- | glib/dbus-gobject.c | 790 | ||||
-rw-r--r-- | glib/dbus-gparser.c | 670 | ||||
-rw-r--r-- | glib/dbus-gparser.h | 65 | ||||
-rw-r--r-- | glib/dbus-gproxy.c | 1249 | ||||
-rw-r--r-- | glib/dbus-gtest-main.c | 46 | ||||
-rw-r--r-- | glib/dbus-gtest.c | 77 | ||||
-rw-r--r-- | glib/dbus-gtest.h | 35 | ||||
-rw-r--r-- | glib/dbus-gthread.c | 2 | ||||
-rw-r--r-- | glib/dbus-gtool-test.h | 31 | ||||
-rw-r--r-- | glib/dbus-gutils.c | 96 | ||||
-rw-r--r-- | glib/dbus-gutils.h | 40 |
19 files changed, 4345 insertions, 36 deletions
diff --git a/glib/.cvsignore b/glib/.cvsignore index a6e3d2b5..881b4887 100644 --- a/glib/.cvsignore +++ b/glib/.cvsignore @@ -4,11 +4,9 @@ Makefile Makefile.in *.lo *.la -test-dbus-glib +dbus-glib-test +dbus-glib-tool *.bb *.bbg *.da *.gcov -test-thread-client -test-thread-server -test-profile diff --git a/glib/Makefile.am b/glib/Makefile.am index ebdb932f..a45aa593 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) +INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) -DDBUS_COMPILATION=1 dbusincludedir=$(includedir)/dbus-1.0/dbus @@ -9,47 +9,61 @@ dbusinclude_HEADERS= \ libdbus_glib_1_la_SOURCES = \ dbus-gmain.c \ - dbus-gthread.c + dbus-gobject.c \ + dbus-gproxy.c \ + dbus-gtest.c \ + dbus-gtest.h \ + dbus-gthread.c \ + dbus-gutils.c \ + dbus-gutils.h libdbus_glib_1_la_LIBADD= $(DBUS_GLIB_LIBS) $(top_builddir)/dbus/libdbus-1.la +## don't export symbols that start with "_" (we use this +## convention for internal symbols) +libdbus_glib_1_la_LDFLAGS= -export-symbols-regex "^[^_].*" -if DBUS_BUILD_TESTS +# convenience lib used here and by dbus-viewer +noinst_LTLIBRARIES=libdbus-gtool.la -if HAVE_GLIB_THREADS -THREAD_APPS=test-thread-server test-thread-client test-profile +libdbus_gtool_la_SOURCES = \ + dbus-gidl.c \ + dbus-gidl.h \ + dbus-gloader-expat.c \ + dbus-gparser.c \ + dbus-gparser.h \ + dbus-gutils.c \ + dbus-gutils.h -test_thread_server_SOURCES= \ - test-thread-server.c \ - test-thread.h +libdbus_gtool_la_LIBADD = libdbus-glib-1.la -test_thread_server_LDADD= $(DBUS_GLIB_THREADS_LIBS) $(top_builddir)/glib/libdbus-glib-1.la +bin_PROGRAMS=dbus-glib-tool -test_thread_client_SOURCES= \ - test-thread-client.c \ - test-thread.h +dbus_glib_tool_SOURCES = \ + dbus-glib-tool.c \ + dbus-gtool-test.h -test_thread_client_LDADD= $(DBUS_GLIB_THREADS_LIBS) $(top_builddir)/glib/libdbus-glib-1.la -endif +dbus_glib_tool_LDADD= $(DBUS_GLIB_TOOL_LIBS) libdbus-gtool.la + +if DBUS_BUILD_TESTS -noinst_PROGRAMS= test-dbus-glib $(THREAD_APPS) +## we use noinst_PROGRAMS not check_PROGRAMS for TESTS so that we +## build even when not doing "make check" +noinst_PROGRAMS= $(TESTS) -test_dbus_glib_SOURCES= \ - test-dbus-glib.c +## note that TESTS has special meaning (stuff to use in make check) +## so if adding tests not to be run in make check, don't add them to +## TESTS +TESTS_ENVIRONMENT=DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus +TESTS=dbus-glib-test -test_dbus_glib_LDADD= $(top_builddir)/glib/libdbus-glib-1.la +dbus_glib_test_SOURCES= \ + dbus-gtest-main.c + +dbus_glib_test_LDADD= $(top_builddir)/glib/libdbus-glib-1.la else ### not building tests - -if HAVE_GLIB_THREADS -noinst_PROGRAMS=test-profile -endif +TESTS= endif -if HAVE_GLIB_THREADS -test_profile_SOURCES= \ - test-profile.c - -test_profile_LDADD= $(DBUS_GLIB_THREADS_LIBS) $(top_builddir)/glib/libdbus-glib-1.la -endif
\ No newline at end of file diff --git a/glib/dbus-gidl.c b/glib/dbus-gidl.c new file mode 100644 index 00000000..596b43ca --- /dev/null +++ b/glib/dbus-gidl.c @@ -0,0 +1,524 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gidl.c data structure describing an interface, to be generated from IDL + * or something + * + * 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-gidl.h" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +struct BaseInfo +{ + unsigned int refcount : 28; + unsigned int type : 4; + char *name; +}; + +struct NodeInfo +{ + BaseInfo base; + GSList *interfaces; + GSList *nodes; +}; + +struct InterfaceInfo +{ + BaseInfo base; + /* Since we have BaseInfo now these could be one list */ + GSList *methods; + GSList *signals; +}; + +struct MethodInfo +{ + BaseInfo base; + GSList *args; +}; + +struct SignalInfo +{ + BaseInfo base; + GSList *args; +}; + +struct ArgInfo +{ + BaseInfo base; + int type; + ArgDirection direction; +}; + +void +base_info_ref (BaseInfo *info) +{ + g_return_if_fail (info != NULL); + g_return_if_fail (info->refcount > 0); + + info->refcount += 1; +} + +static void +base_info_free (void *ptr) +{ + BaseInfo *info; + + info = ptr; + + g_free (info->name); + g_free (info); +} + +void +base_info_unref (BaseInfo *info) +{ + g_return_if_fail (info != NULL); + g_return_if_fail (info->refcount > 0); + + /* This is sort of bizarre, BaseInfo was tacked on later */ + + switch (info->type) + { + case INFO_TYPE_NODE: + node_info_unref ((NodeInfo*) info); + break; + case INFO_TYPE_INTERFACE: + interface_info_unref ((InterfaceInfo*) info); + break; + case INFO_TYPE_SIGNAL: + signal_info_unref ((SignalInfo*) info); + break; + case INFO_TYPE_METHOD: + method_info_unref ((MethodInfo*) info); + break; + case INFO_TYPE_ARG: + arg_info_unref ((ArgInfo*) info); + break; + } +} + +InfoType +base_info_get_type (BaseInfo *info) +{ + return info->type; +} + +const char* +base_info_get_name (BaseInfo *info) +{ + return info->name; +} + +void +base_info_set_name (BaseInfo *info, + const char *name) +{ + char *old; + + old = info->name; + info->name = g_strdup (name); + g_free (old); +} + +GType +base_info_get_gtype (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static ("BaseInfo", + (GBoxedCopyFunc) base_info_ref, + (GBoxedFreeFunc) base_info_unref); + + return our_type; +} + +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_node_list (GSList **nodes_p) +{ + GSList *tmp; + tmp = *nodes_p; + while (tmp != NULL) + { + node_info_unref (tmp->data); + tmp = tmp->next; + } + g_slist_free (*nodes_p); + *nodes_p = NULL; +} + +static void +free_method_list (GSList **methods_p) +{ + GSList *tmp; + tmp = *methods_p; + while (tmp != NULL) + { + method_info_unref (tmp->data); + tmp = tmp->next; + } + g_slist_free (*methods_p); + *methods_p = NULL; +} + +static void +free_signal_list (GSList **signals_p) +{ + GSList *tmp; + tmp = *signals_p; + while (tmp != NULL) + { + signal_info_unref (tmp->data); + tmp = tmp->next; + } + g_slist_free (*signals_p); + *signals_p = NULL; +} + +NodeInfo* +node_info_new (const char *name) +{ + NodeInfo *info; + + /* name can be NULL */ + + info = g_new0 (NodeInfo, 1); + info->base.refcount = 1; + info->base.name = g_strdup (name); + info->base.type = INFO_TYPE_NODE; + + return info; +} + +void +node_info_ref (NodeInfo *info) +{ + info->base.refcount += 1; +} + +void +node_info_unref (NodeInfo *info) +{ + info->base.refcount -= 1; + if (info->base.refcount == 0) + { + free_interface_list (&info->interfaces); + free_node_list (&info->nodes); + base_info_free (info); + } +} + +const char* +node_info_get_name (NodeInfo *info) +{ + return info->base.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); +} + +GSList* +node_info_get_nodes (NodeInfo *info) +{ + return info->nodes; +} + +void +node_info_add_node (NodeInfo *info, + NodeInfo *node) +{ + node_info_ref (node); + info->nodes = g_slist_append (info->nodes, node); +} + +InterfaceInfo* +interface_info_new (const char *name) +{ + InterfaceInfo *info; + + info = g_new0 (InterfaceInfo, 1); + info->base.refcount = 1; + info->base.name = g_strdup (name); + info->base.type = INFO_TYPE_INTERFACE; + + return info; +} + +void +interface_info_ref (InterfaceInfo *info) +{ + info->base.refcount += 1; +} + +void +interface_info_unref (InterfaceInfo *info) +{ + info->base.refcount -= 1; + if (info->base.refcount == 0) + { + free_method_list (&info->methods); + free_signal_list (&info->signals); + base_info_free (info); + } +} + +const char* +interface_info_get_name (InterfaceInfo *info) +{ + return info->base.name; +} + +GSList* +interface_info_get_methods (InterfaceInfo *info) +{ + return info->methods; +} + +GSList* +interface_info_get_signals (InterfaceInfo *info) +{ + return info->signals; +} + +void +interface_info_add_method (InterfaceInfo *info, + MethodInfo *method) +{ + method_info_ref (method); + info->methods = g_slist_append (info->methods, method); +} + +void +interface_info_add_signal (InterfaceInfo *info, + SignalInfo *signal) +{ + signal_info_ref (signal); + info->signals = g_slist_append (info->signals, signal); +} + +static void +free_arg_list (GSList **args_p) +{ + GSList *tmp; + tmp = *args_p; + while (tmp != NULL) + { + arg_info_unref (tmp->data); + tmp = tmp->next; + } + g_slist_free (*args_p); + *args_p = NULL; +} + +MethodInfo* +method_info_new (const char *name) +{ + MethodInfo *info; + + info = g_new0 (MethodInfo, 1); + info->base.refcount = 1; + info->base.name = g_strdup (name); + info->base.type = INFO_TYPE_METHOD; + + return info; +} + +void +method_info_ref (MethodInfo *info) +{ + info->base.refcount += 1; +} + +void +method_info_unref (MethodInfo *info) +{ + info->base.refcount -= 1; + if (info->base.refcount == 0) + { + free_arg_list (&info->args); + base_info_free (info); + } +} + +const char* +method_info_get_name (MethodInfo *info) +{ + return info->base.name; +} + +GSList* +method_info_get_args (MethodInfo *info) +{ + return info->args; +} + +void +method_info_add_arg (MethodInfo *info, + ArgInfo *arg) +{ + arg_info_ref (arg); + info->args = g_slist_append (info->args, arg); +} + +SignalInfo* +signal_info_new (const char *name) +{ + SignalInfo *info; + + info = g_new0 (SignalInfo, 1); + info->base.refcount = 1; + info->base.name = g_strdup (name); + info->base.type = INFO_TYPE_SIGNAL; + + return info; +} + +void +signal_info_ref (SignalInfo *info) +{ + info->base.refcount += 1; +} + +void +signal_info_unref (SignalInfo *info) +{ + info->base.refcount -= 1; + if (info->base.refcount == 0) + { + free_arg_list (&info->args); + base_info_free (info); + } +} + +const char* +signal_info_get_name (SignalInfo *info) +{ + return info->base.name; +} + +GSList* +signal_info_get_args (SignalInfo *info) +{ + return info->args; +} + +void +signal_info_add_arg (SignalInfo *info, + ArgInfo *arg) +{ + arg_info_ref (arg); + info->args = g_slist_append (info->args, arg); +} + +ArgInfo* +arg_info_new (const char *name, + ArgDirection direction, + int type) +{ + ArgInfo *info; + + info = g_new0 (ArgInfo, 1); + info->base.refcount = 1; + info->base.type = INFO_TYPE_ARG; + + /* name can be NULL */ + info->base.name = g_strdup (name); + info->direction = direction; + info->type = type; + + return info; +} + +void +arg_info_ref (ArgInfo *info) +{ + info->base.refcount += 1; +} + +void +arg_info_unref (ArgInfo *info) +{ + info->base.refcount -= 1; + if (info->base.refcount == 0) + { + base_info_free (info); + } +} +const char* +arg_info_get_name (ArgInfo *info) +{ + return info->base.name; +} + +int +arg_info_get_type (ArgInfo *info) +{ + return info->type; +} + +ArgDirection +arg_info_get_direction (ArgInfo *info) +{ + return info->direction; +} + +#ifdef DBUS_BUILD_TESTS + +/** + * @ingroup DBusGIDL + * Unit test for GLib IDL internals + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_gidl_test (void) +{ + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/glib/dbus-gidl.h b/glib/dbus-gidl.h new file mode 100644 index 00000000..f95abfbd --- /dev/null +++ b/glib/dbus-gidl.h @@ -0,0 +1,120 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gidl.h data structure describing an interface, to be generated from IDL + * or something + * + * 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 + * + */ +#ifndef DBUS_GLIB_IDL_H +#define DBUS_GLIB_IDL_H + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include <dbus/dbus.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct BaseInfo BaseInfo; +typedef struct NodeInfo NodeInfo; +typedef struct InterfaceInfo InterfaceInfo; +typedef struct MethodInfo MethodInfo; +typedef struct SignalInfo SignalInfo; +typedef struct ArgInfo ArgInfo; + +typedef enum +{ + ARG_IN, + ARG_OUT +} ArgDirection; + +typedef enum +{ + INFO_TYPE_NODE, + INFO_TYPE_INTERFACE, + INFO_TYPE_METHOD, + INFO_TYPE_SIGNAL, + INFO_TYPE_ARG + +} InfoType; + +void base_info_ref (BaseInfo *info); +void base_info_unref (BaseInfo *info); +InfoType base_info_get_type (BaseInfo *info); +const char* base_info_get_name (BaseInfo *info); +void base_info_set_name (BaseInfo *info, + const char *name); +GType base_info_get_gtype (void); +#define BASE_INFO_TYPE (base_info_get_gtype ()) + + +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); +GSList* node_info_get_nodes (NodeInfo *info); +void node_info_add_interface (NodeInfo *info, + InterfaceInfo *interface); +void node_info_add_node (NodeInfo *info, + NodeInfo *child); + +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, + MethodInfo *method); +void interface_info_add_signal (InterfaceInfo *info, + SignalInfo *signal); + +MethodInfo* method_info_new (const char *name); +void method_info_ref (MethodInfo *info); +void method_info_unref (MethodInfo *info); + +const char* method_info_get_name (MethodInfo *info); +GSList* method_info_get_args (MethodInfo *info); +void method_info_add_arg (MethodInfo *info, + ArgInfo *arg); + +SignalInfo* signal_info_new (const char *name); +void signal_info_ref (SignalInfo *info); +void signal_info_unref (SignalInfo *info); + +const char* signal_info_get_name (SignalInfo *info); +GSList* signal_info_get_args (SignalInfo *info); +void signal_info_add_arg (SignalInfo *info, + ArgInfo *arg); + +ArgInfo* arg_info_new (const char *name, + ArgDirection direction, + int type); +void arg_info_ref (ArgInfo *info); +void arg_info_unref (ArgInfo *info); +const char* arg_info_get_name (ArgInfo *info); +int arg_info_get_type (ArgInfo *info); +ArgDirection arg_info_get_direction (ArgInfo *info); + +G_END_DECLS + +#endif /* DBUS_GLIB_IDL_H */ + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/glib/dbus-glib-tool.c b/glib/dbus-glib-tool.c new file mode 100644 index 00000000..aaf133d4 --- /dev/null +++ b/glib/dbus-glib-tool.c @@ -0,0 +1,77 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-compiler-main.c main() for GLib stubs/skels generator + * + * 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-gidl.h" +#include <locale.h> + +#ifdef DBUS_BUILD_TESTS +static void run_all_tests (const char *test_data_dir); +#endif + +int +main (int argc, char **argv) +{ + setlocale(LC_ALL, ""); + + return 0; +} + +#ifdef DBUS_BUILD_TESTS +static void +test_die (const char *failure) +{ + fprintf (stderr, "Unit test failed: %s\n", failure); + exit (1); +} + +static void +run_all_tests (const char *test_data_dir) +{ + if (test_data_dir == NULL) + test_data_dir = _dbus_getenv ("DBUS_TEST_DATA"); + + if (test_data_dir != NULL) + printf ("Test data in %s\n", test_data_dir); + else + printf ("No test data!\n"); + + printf ("%s: running gtool tests\n", "dbus-glib-tool"); + if (!_dbus_gtool_test (test_data_dir)) + test_die ("gtool"); + + printf ("%s: completed successfully\n", "dbus-glib-test"); +} + +/** + * @ingroup DBusGTool + * Unit test for GLib utility tool + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_gtool_test (const char *test_data_dir) +{ + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/glib/dbus-glib.h b/glib/dbus-glib.h index c6116c52..46f4555d 100644 --- a/glib/dbus-glib.h +++ b/glib/dbus-glib.h @@ -2,6 +2,7 @@ /* dbus-glib.h GLib integration * * Copyright (C) 2002, 2003 CodeFactory AB + * Copyright (C) 2003 Red Hat, Inc. * * Licensed under the Academic Free License version 1.2 * @@ -24,16 +25,132 @@ #define DBUS_GLIB_H #include <dbus/dbus.h> -#include <glib.h> +#include <glib-object.h> G_BEGIN_DECLS -void dbus_gthread_init (void); +#define DBUS_INSIDE_DBUS_GLIB_H 1 + +GQuark dbus_g_error_quark (void); +#define DBUS_GERROR dbus_g_error_quark () + +#define DBUS_TYPE_CONNECTION (dbus_connection_get_g_type ()) +#define DBUS_TYPE_MESSAGE (dbus_message_get_g_type ()) +GType dbus_connection_get_g_type (void) G_GNUC_CONST; +GType dbus_message_get_g_type (void) G_GNUC_CONST; + +typedef enum +{ + /* FIXME map all the DBUS_ERROR to DBUS_GERROR, should + * probably be automated in some way, perhaps + * via lame perl script + */ + DBUS_GERROR_FAILED +} DBusGError; + +void dbus_set_g_error (GError **gerror, + DBusError *derror); + +void dbus_g_thread_init (void); void dbus_connection_setup_with_g_main (DBusConnection *connection, GMainContext *context); void dbus_server_setup_with_g_main (DBusServer *server, GMainContext *context); +typedef struct DBusGObjectInfo DBusGObjectInfo; +typedef struct DBusGMethodInfo DBusGMethodInfo; + +/** + * Object typically generated by dbus-glib-tool that + * stores a mapping from introspection data to a + * function pointer for a C method to be invoked. + */ +struct DBusGMethodInfo +{ + GCallback function; /**< C method to invoke */ + DBusHandleMessageFunction marshaller; /**< Marshaller to go DBusMessage to C method */ + int data_offset; /**< Offset into the introspection data */ +}; + +/** + * Introspection data for a GObject, normally autogenerated by + * a tool such as dbus-glib-tool. + */ +struct DBusGObjectInfo +{ + const DBusGMethodInfo *infos; /**< Array of method pointers */ + const unsigned char *data; /**< Introspection data */ + void *dbus_internal_padding1; /**< Reserved for expansion */ + void *dbus_internal_padding2; /**< Reserved for expansion */ +}; + +void dbus_g_object_class_install_info (GObjectClass *object_class, + const DBusGObjectInfo *info); +void dbus_connection_register_g_object (DBusConnection *connection, + const char *at_path, + GObject *object); + + +typedef struct DBusGProxy DBusGProxy; +typedef struct DBusGProxyClass DBusGProxyClass; + +typedef void (* DBusGProxySignalHandler) (DBusGProxy *proxy, + DBusMessage *signal, + void *user_data); + +#define DBUS_TYPE_GPROXY (dbus_gproxy_get_type ()) +#define DBUS_GPROXY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), DBUS_TYPE_GPROXY, DBusGProxy)) +#define DBUS_GPROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUS_TYPE_GPROXY, DBusGProxyClass)) +#define DBUS_IS_GPROXY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), DBUS_TYPE_GPROXY)) +#define DBUS_IS_GPROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUS_TYPE_GPROXY)) +#define DBUS_GPROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUS_TYPE_GPROXY, DBusGProxyClass)) + + +GType dbus_gproxy_get_type (void) G_GNUC_CONST; +DBusGProxy* dbus_gproxy_new_for_service (DBusConnection *connection, + const char *service_name, + const char *path_name, + const char *interface_name); +DBusGProxy* dbus_gproxy_new_for_service_owner (DBusConnection *connection, + const char *service_name, + const char *path_name, + const char *interface_name, + GError **error); +DBusGProxy* dbus_gproxy_new_for_peer (DBusConnection *connection, + const char *path_name, + const char *interface_name); +void dbus_gproxy_connect_signal (DBusGProxy *proxy, + const char *signal_name, + DBusGProxySignalHandler handler, + void *data, + GClosureNotify free_data_func); +void dbus_gproxy_disconnect_signal (DBusGProxy *proxy, + const char *signal_name, + DBusGProxySignalHandler handler, + void *data); +DBusPendingCall* dbus_gproxy_begin_call (DBusGProxy *proxy, + const char *method, + int first_arg_type, + ...); +gboolean dbus_gproxy_end_call (DBusGProxy *proxy, + DBusPendingCall *pending, + GError **error, + int first_arg_type, + ...); +void dbus_gproxy_oneway_call (DBusGProxy *proxy, + const char *method, + int first_arg_type, + ...); +void dbus_gproxy_send (DBusGProxy *proxy, + DBusMessage *message, + dbus_uint32_t *client_serial); + + +#undef DBUS_INSIDE_DBUS_GLIB_H + G_END_DECLS #endif /* DBUS_GLIB_H */ + + + diff --git a/glib/dbus-gloader-expat.c b/glib/dbus-gloader-expat.c new file mode 100644 index 00000000..01587d21 --- /dev/null +++ b/glib/dbus-gloader-expat.c @@ -0,0 +1,262 @@ +/* -*- 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 +}; + +/** + * Context for Expat parser for introspection data. + */ +typedef struct +{ + Parser *parser; /**< The parser for the introspection data */ + const char *filename; /**< The filename being loaded */ + GString *content; /**< The content of the current element */ + GError **error; /**< Error return location */ + gboolean failed; /**< True if parse has 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); +} + +NodeInfo* +description_load_from_file (const char *filename, + GError **error) +{ + char *contents; + gsize len; + NodeInfo *nodes; + + contents = NULL; + if (!g_file_get_contents (filename, &contents, &len, error)) + return NULL; + + nodes = description_load_from_string (contents, len, error); + g_free (contents); + + return nodes; +} + +NodeInfo* +description_load_from_string (const char *str, + int len, + GError **error) +{ + XML_Parser expat; + ExpatParseContext context; + NodeInfo *nodes; + + 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); + nodes = parser_get_nodes (context.parser); + node_info_ref (nodes); + parser_unref (context.parser); + return nodes; + + 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; +} + diff --git a/glib/dbus-gmain.c b/glib/dbus-gmain.c index d948b431..68ba22d7 100644 --- a/glib/dbus-gmain.c +++ b/glib/dbus-gmain.c @@ -21,8 +21,13 @@ * */ +#include <config.h> #include "dbus-glib.h" -#include <glib.h> +#include "dbus-gtest.h" + +#include <libintl.h> +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x /** * @defgroup DBusGLib GLib bindings @@ -48,6 +53,9 @@ */ typedef struct DBusGSource DBusGSource; +/** + * A GSource subclass for a DBusConnection. + */ struct DBusGSource { GSource source; /**< the parent GSource */ @@ -530,4 +538,94 @@ dbus_server_setup_with_g_main (DBusServer *server, g_error ("Not enough memory to set up DBusServer for use with GLib"); } +/** + * The implementation of DBUS_GERROR error domain. See documentation + * for GError in GLib reference manual. + * + * @returns the error domain quark for use with GError + */ +GQuark +dbus_g_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("g-exec-error-quark"); + return quark; +} + + +/** + * Set a GError return location from a DBusError. + * + * @todo expand the DBUS_GERROR enum and take advantage of it here + * + * @param gerror location to store a GError, or #NULL + * @param derror the DBusError + */ +void +dbus_set_g_error (GError **gerror, + DBusError *derror) +{ + g_return_if_fail (derror != NULL); + g_return_if_fail (dbus_error_is_set (derror)); + + g_set_error (gerror, DBUS_GERROR, + DBUS_GERROR_FAILED, + _("D-BUS error %s: %s"), + derror->name, derror->message); +} + +/** + * Get the GLib type ID for a DBusConnection boxed type. + * + * @returns GLib type + */ +GType +dbus_connection_get_g_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static ("DBusConnection", + (GBoxedCopyFunc) dbus_connection_ref, + (GBoxedFreeFunc) dbus_connection_unref); + + return our_type; +} + +/** + * Get the GLib type ID for a DBusMessage boxed type. + * + * @returns GLib type + */ +GType +dbus_message_get_g_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = g_boxed_type_register_static ("DBusMessage", + (GBoxedCopyFunc) dbus_message_ref, + (GBoxedFreeFunc) dbus_message_unref); + + return our_type; +} + + /** @} */ /* end of public API */ + +#ifdef DBUS_BUILD_TESTS + +/** + * @ingroup DBusGLibInternals + * Unit test for GLib main loop integration + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_gmain_test (const char *test_data_dir) +{ + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/glib/dbus-gobject.c b/glib/dbus-gobject.c new file mode 100644 index 00000000..6e65770f --- /dev/null +++ b/glib/dbus-gobject.c @@ -0,0 +1,790 @@ +/* -*- 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 "dbus-gutils.h" +#include <string.h> + +/** + * @addtogroup DBusGLibInternals + * @{ + */ + +static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT; +static GHashTable *info_hash = NULL; + +static char* +wincaps_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_wincaps (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); + + /* FIXME */ + +} + +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; + char **path; + char **children; + + if (!dbus_message_get_path_decomposed (message, &path)) + g_error ("Out of memory"); + + if (!dbus_connection_list_registered (connection, (const char**) path, + &children)) + g_error ("Out of memory"); + + 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_wincaps (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); + + /* Append child nodes */ + + i = 0; + while (children[i]) + { + g_string_append_printf (xml, " <node name=\"%s\"/>\n", + children[i]); + ++i; + } + + /* 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); + + dbus_free_string_array (path); + dbus_free_string_array (children); + + 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' && member[3] == '_'); + getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't' && member[3] == '_'); + + if (!(setter || getter)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + s = wincaps_to_uscore (&member[4]); + + 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_g_object() 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_g_object_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); +} + +/** + * 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_g_object (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 = _dbus_gutils_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 +#include <stdlib.h> + +/** + * @ingroup DBusGLibInternals + * Unit test for GLib GObject integration ("skeletons") + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_gobject_test (const char *test_data_dir) +{ + int i; + static struct { const char *wincaps; const char *uscore; } name_pairs[] = { + { "SetFoo", "set_foo" }, + { "Foo", "foo" }, + { "GetFooBar", "get_foo_bar" }, + { "Hello", "hello" } + + /* Impossible-to-handle cases */ + /* { "FrobateUIHandler", "frobate_ui_handler" } */ + }; + + i = 0; + while (i < (int) G_N_ELEMENTS (name_pairs)) + { + char *uscore; + char *wincaps; + + uscore = wincaps_to_uscore (name_pairs[i].wincaps); + wincaps = uscore_to_wincaps (name_pairs[i].uscore); + + if (strcmp (uscore, name_pairs[i].uscore) != 0) + { + g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n", + name_pairs[i].wincaps, name_pairs[i].uscore, + uscore); + exit (1); + } + + if (strcmp (wincaps, name_pairs[i].wincaps) != 0) + { + g_printerr ("\"%s\" should have been converted to \"%s\" not \"%s\"\n", + name_pairs[i].uscore, name_pairs[i].wincaps, + wincaps); + exit (1); + } + + g_free (uscore); + g_free (wincaps); + + ++i; + } + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/glib/dbus-gparser.c b/glib/dbus-gparser.c new file mode 100644 index 00000000..16d17f3d --- /dev/null +++ b/glib/dbus-gparser.c @@ -0,0 +1,670 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gparser.c parse DBus description files + * + * 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 "dbus-gidl.h" +#include <string.h> + +#include <libintl.h> +#define _(x) gettext ((x)) +#define N_(x) x + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#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* +parser_new (void) +{ + Parser *parser; + + parser = g_new0 (Parser, 1); + + parser->refcount = 1; + + return parser; +} + +void +parser_ref (Parser *parser) +{ + parser->refcount += 1; +} + +void +parser_unref (Parser *parser) +{ + parser->refcount -= 1; + if (parser->refcount == 0) + { + if (parser->result) + node_info_unref (parser->result); + + g_free (parser); + } +} + +gboolean +parser_check_doctype (Parser *parser, + const char *doctype, + GError **error) +{ + 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); + return FALSE; + } + else + 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); + + if (parser->node_stack != NULL) + { + node_info_add_node (parser->node_stack->data, + node); + } + + 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, + const char **attribute_names, + const char **attribute_values, + GError **error) +{ + 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; +} + +gboolean +parser_end_element (Parser *parser, + const char *element_name, + GError **error) +{ + 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; +} + +gboolean +parser_content (Parser *parser, + const char *content, + int len, + GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return TRUE; +} + +gboolean +parser_finished (Parser *parser, + GError **error) +{ + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return TRUE; +} + +NodeInfo* +parser_get_nodes (Parser *parser) +{ + return parser->result; +} + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/glib/dbus-gparser.h b/glib/dbus-gparser.h new file mode 100644 index 00000000..cc58e5e0 --- /dev/null +++ b/glib/dbus-gparser.h @@ -0,0 +1,65 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gparser.h parse DBus description files + * + * 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 + * + */ +#ifndef DBUS_GLIB_PARSER_H +#define DBUS_GLIB_PARSER_H + +#include <dbus/dbus.h> +#include <glib.h> +#include "dbus-gidl.h" + +G_BEGIN_DECLS + +typedef struct Parser Parser; + +Parser* parser_new (void); +void parser_ref (Parser *parser); +void parser_unref (Parser *parser); +gboolean parser_check_doctype (Parser *parser, + const char *doctype, + GError **error); +gboolean parser_start_element (Parser *parser, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + GError **error); +gboolean parser_end_element (Parser *parser, + const char *element_name, + GError **error); +gboolean parser_content (Parser *parser, + const char *content, + int len, + GError **error); +gboolean parser_finished (Parser *parser, + GError **error); + +NodeInfo* description_load_from_file (const char *filename, + GError **error); +NodeInfo* description_load_from_string (const char *str, + int len, + GError **error); + +NodeInfo* parser_get_nodes (Parser *parser); + +G_END_DECLS + +#endif /* DBUS_GLIB_GPARSER_H */ diff --git a/glib/dbus-gproxy.c b/glib/dbus-gproxy.c new file mode 100644 index 00000000..99900e4c --- /dev/null +++ b/glib/dbus-gproxy.c @@ -0,0 +1,1249 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gcall.c convenience routines for calling methods, etc. + * + * 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-glib.h" +#include <string.h> + +/** + * @addtogroup DBusGLibInternals + * + * @{ + */ + +typedef struct DBusGProxyManager DBusGProxyManager; + +/** + * Internals of DBusGProxy + */ +struct DBusGProxy +{ + GObject parent; /**< Parent instance */ + + DBusGProxyManager *manager; /**< Proxy manager */ + char *service; /**< Service messages go to or NULL */ + char *path; /**< Path messages go to or NULL */ + char *interface; /**< Interface messages go to or NULL */ +}; + +/** + * Class struct for DBusGProxy + */ +struct DBusGProxyClass +{ + GObjectClass parent_class; +}; + +static void dbus_gproxy_init (DBusGProxy *proxy); +static void dbus_gproxy_class_init (DBusGProxyClass *klass); +static void dbus_gproxy_finalize (GObject *object); +static void dbus_gproxy_dispose (GObject *object); +static void dbus_gproxy_destroy (DBusGProxy *proxy); +static void dbus_gproxy_emit_received (DBusGProxy *proxy, + DBusMessage *message); + + +/** + * A list of proxies with a given service+path+interface, used to route incoming + * signals. + */ +typedef struct +{ + GSList *proxies; + + char name[4]; /**< service (empty string for none), nul byte, + * path, nul byte, + * interface, nul byte + */ + +} DBusGProxyList; + +/** + * DBusGProxyManager's primary task is to route signals to the proxies + * those signals are emitted on. In order to do this it also has to + * track the owners of the services proxies are bound to. + */ +struct DBusGProxyManager +{ + GStaticMutex lock; /**< Thread lock */ + int refcount; /**< Reference count */ + DBusConnection *connection; /**< Connection we're associated with. */ + + GHashTable *proxy_lists; /**< Hash used to route incoming signals + * and iterate over proxies + */ + +}; + +static void dbus_gproxy_manager_ref (DBusGProxyManager *manager); +static DBusHandlerResult dbus_gproxy_manager_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data); + +/** Lock the DBusGProxyManager */ +#define LOCK_MANAGER(mgr) (g_static_mutex_lock (&(mgr)->lock)) +/** Unlock the DBusGProxyManager */ +#define UNLOCK_MANAGER(mgr) (g_static_mutex_unlock (&(mgr)->lock)) + +static int gproxy_manager_slot = -1; + +/* Lock controlling get/set manager as data on each connection */ +static GStaticMutex connection_gproxy_lock = G_STATIC_MUTEX_INIT; + +static DBusGProxyManager* +dbus_gproxy_manager_get (DBusConnection *connection) +{ + DBusGProxyManager *manager; + + dbus_connection_allocate_data_slot (&gproxy_manager_slot); + if (gproxy_manager_slot < 0) + g_error ("out of memory"); + + g_static_mutex_lock (&connection_gproxy_lock); + + manager = dbus_connection_get_data (connection, gproxy_manager_slot); + if (manager != NULL) + { + dbus_connection_free_data_slot (&gproxy_manager_slot); + dbus_gproxy_manager_ref (manager); + g_static_mutex_unlock (&connection_gproxy_lock); + return manager; + } + + manager = g_new0 (DBusGProxyManager, 1); + + manager->refcount = 1; + manager->connection = connection; + + g_static_mutex_init (&manager->lock); + + /* Proxy managers keep the connection alive, which means that + * DBusGProxy indirectly does. To free a connection you have to free + * all the proxies referring to it. + */ + dbus_connection_ref (manager->connection); + + dbus_connection_set_data (connection, gproxy_manager_slot, + manager, NULL); + + dbus_connection_add_filter (connection, dbus_gproxy_manager_filter, + manager, NULL); + + g_static_mutex_unlock (&connection_gproxy_lock); + + return manager; +} + +static void +dbus_gproxy_manager_ref (DBusGProxyManager *manager) +{ + g_assert (manager != NULL); + g_assert (manager->refcount > 0); + + LOCK_MANAGER (manager); + + manager->refcount += 1; + + UNLOCK_MANAGER (manager); +} + +static void +dbus_gproxy_manager_unref (DBusGProxyManager *manager) +{ + g_assert (manager != NULL); + g_assert (manager->refcount > 0); + + LOCK_MANAGER (manager); + manager->refcount -= 1; + if (manager->refcount == 0) + { + UNLOCK_MANAGER (manager); + + if (manager->proxy_lists) + { + /* can't have any proxies left since they hold + * a reference to the proxy manager. + */ + g_assert (g_hash_table_size (manager->proxy_lists) == 0); + + g_hash_table_destroy (manager->proxy_lists); + manager->proxy_lists = NULL; + } + + g_static_mutex_free (&manager->lock); + + g_static_mutex_lock (&connection_gproxy_lock); + + dbus_connection_remove_filter (manager->connection, dbus_gproxy_manager_filter, + manager); + + dbus_connection_set_data (manager->connection, + gproxy_manager_slot, + NULL, NULL); + + g_static_mutex_unlock (&connection_gproxy_lock); + + dbus_connection_unref (manager->connection); + g_free (manager); + + dbus_connection_free_data_slot (&gproxy_manager_slot); + } + else + { + UNLOCK_MANAGER (manager); + } +} + +static guint +tristring_hash (gconstpointer key) +{ + const char *p = key; + guint h = *p; + + if (h) + { + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + } + + /* skip nul and do the next substring */ + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + + /* skip nul again and another substring */ + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + *p; + + return h; +} + +static gboolean +strequal_len (const char *a, + const char *b, + size_t *lenp) +{ + size_t a_len; + size_t b_len; + + a_len = strlen (a); + b_len = strlen (b); + + if (a_len != b_len) + return FALSE; + + if (memcmp (a, b, a_len) != 0) + return FALSE; + + *lenp = a_len; + + return TRUE; +} + +static gboolean +tristring_equal (gconstpointer a, + gconstpointer b) +{ + const char *ap = a; + const char *bp = b; + size_t len; + + if (!strequal_len (ap, bp, &len)) + return FALSE; + + ap += len + 1; + bp += len + 1; + + if (!strequal_len (ap, bp, &len)) + return FALSE; + + ap += len + 1; + bp += len + 1; + + if (strcmp (ap, bp) != 0) + return FALSE; + + return TRUE; +} + +static char* +tristring_alloc_from_strings (size_t padding_before, + const char *service, + const char *path, + const char *interface) +{ + size_t service_len, iface_len, path_len, len; + char *tri; + + if (service) + service_len = strlen (service); + else + service_len = 0; + + path_len = strlen (path); + + iface_len = strlen (interface); + + tri = g_malloc (padding_before + service_len + path_len + iface_len + 3); + + len = padding_before; + + if (service) + memcpy (&tri[len], service, service_len); + + len += service_len; + tri[len] = '\0'; + len += 1; + + g_assert (len == (padding_before + service_len + 1)); + + memcpy (&tri[len], path, path_len); + len += path_len; + tri[len] = '\0'; + len += 1; + + g_assert (len == (padding_before + service_len + path_len + 2)); + + memcpy (&tri[len], interface, iface_len); + len += iface_len; + tri[len] = '\0'; + len += 1; + + g_assert (len == (padding_before + service_len + path_len + iface_len + 3)); + + return tri; +} + +static char* +tristring_from_proxy (DBusGProxy *proxy) +{ + return tristring_alloc_from_strings (0, + proxy->service, + proxy->path, + proxy->interface); +} + +static char* +tristring_from_message (DBusMessage *message) +{ + return tristring_alloc_from_strings (0, + dbus_message_get_sender (message), + dbus_message_get_path (message), + dbus_message_get_interface (message)); +} + +static DBusGProxyList* +gproxy_list_new (DBusGProxy *first_proxy) +{ + DBusGProxyList *list; + + list = (void*) tristring_alloc_from_strings (G_STRUCT_OFFSET (DBusGProxyList, name), + first_proxy->service, + first_proxy->path, + first_proxy->interface); + list->proxies = NULL; + + return list; +} + +static void +gproxy_list_free (DBusGProxyList *list) +{ + /* we don't hold a reference to the proxies in the list, + * as they ref the GProxyManager + */ + g_slist_free (list->proxies); + + g_free (list); +} + +static char* +gproxy_get_match_rule (DBusGProxy *proxy) +{ + /* FIXME Some sort of escaping is required here I think */ + + if (proxy->service) + return g_strdup_printf ("type='signal',service='%s',path='%s',interface='%s'", + proxy->service, proxy->path, proxy->interface); + else + return g_strdup_printf ("type='signal',path='%s',interface='%s'", + proxy->path, proxy->interface); +} + +static void +dbus_gproxy_manager_register (DBusGProxyManager *manager, + DBusGProxy *proxy) +{ + DBusGProxyList *list; + + LOCK_MANAGER (manager); + + if (manager->proxy_lists == NULL) + { + list = NULL; + manager->proxy_lists = g_hash_table_new_full (tristring_hash, + tristring_equal, + NULL, + (GFreeFunc) gproxy_list_free); + } + else + { + char *tri; + + tri = tristring_from_proxy (proxy); + + list = g_hash_table_lookup (manager->proxy_lists, tri); + + g_free (tri); + } + + if (list == NULL) + { + list = gproxy_list_new (proxy); + + g_hash_table_replace (manager->proxy_lists, + list->name, list); + } + + if (list->proxies == NULL) + { + /* We have to add the match rule to the server, + * but FIXME only if the server is a message bus, + * not if it's a peer. + */ + char *rule; + + rule = gproxy_get_match_rule (proxy); + + /* We don't check for errors; it's not like anyone would handle them, + * and we don't want a round trip here. + */ + dbus_bus_add_match (manager->connection, + rule, NULL); + + g_free (rule); + } + + g_assert (g_slist_find (list->proxies, proxy) == NULL); + + list->proxies = g_slist_prepend (list->proxies, proxy); + + UNLOCK_MANAGER (manager); +} + +static void +dbus_gproxy_manager_unregister (DBusGProxyManager *manager, + DBusGProxy *proxy) +{ + DBusGProxyList *list; + char *tri; + + LOCK_MANAGER (manager); + +#ifndef G_DISABLE_CHECKS + if (manager->proxy_lists == NULL) + { + g_warning ("Trying to disconnect a signal on a proxy but none are connected\n"); + return; + } +#endif + + tri = tristring_from_proxy (proxy); + + list = g_hash_table_lookup (manager->proxy_lists, tri); + + g_free (tri); + +#ifndef G_DISABLE_CHECKS + if (list == NULL) + { + g_warning ("Trying to disconnect a signal on a proxy but none are connected\n"); + return; + } +#endif + + g_assert (g_slist_find (list->proxies, proxy) != NULL); + + list->proxies = g_slist_remove (list->proxies, proxy); + + g_assert (g_slist_find (list->proxies, proxy) == NULL); + + if (g_hash_table_size (manager->proxy_lists) == 0) + { + g_hash_table_destroy (manager->proxy_lists); + manager->proxy_lists = NULL; + } + + UNLOCK_MANAGER (manager); +} + +static void +list_proxies_foreach (gpointer key, + gpointer value, + gpointer user_data) +{ + DBusGProxyList *list; + GSList **ret; + GSList *tmp; + + list = value; + ret = user_data; + + tmp = list->proxies; + while (tmp != NULL) + { + DBusGProxy *proxy = DBUS_GPROXY (tmp->data); + + g_object_ref (proxy); + *ret = g_slist_prepend (*ret, proxy); + + tmp = tmp->next; + } +} + +static GSList* +dbus_gproxy_manager_list_all (DBusGProxyManager *manager) +{ + GSList *ret; + + ret = NULL; + + if (manager->proxy_lists) + { + g_hash_table_foreach (manager->proxy_lists, + list_proxies_foreach, + &ret); + } + + return ret; +} + +static DBusHandlerResult +dbus_gproxy_manager_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + DBusGProxyManager *manager; + + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + manager = user_data; + + dbus_gproxy_manager_ref (manager); + + LOCK_MANAGER (manager); + + if (dbus_message_is_signal (message, + DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL, + "Disconnected")) + { + /* Destroy all the proxies, quite possibly resulting in unreferencing + * the proxy manager and the connection as well. + */ + GSList *all; + GSList *tmp; + + all = dbus_gproxy_manager_list_all (manager); + + tmp = all; + while (tmp != NULL) + { + DBusGProxy *proxy; + + proxy = DBUS_GPROXY (tmp->data); + + UNLOCK_MANAGER (manager); + dbus_gproxy_destroy (proxy); + g_object_unref (G_OBJECT (proxy)); + LOCK_MANAGER (manager); + + tmp = tmp->next; + } + + g_slist_free (all); + +#ifndef G_DISABLE_CHECKS + if (manager->proxy_lists != NULL) + g_warning ("Disconnection emitted \"destroy\" on all DBusGProxy, but somehow new proxies were created in response to one of those destroy signals. This will cause a memory leak."); +#endif + } + else + { + char *tri; + DBusGProxyList *list; + + tri = tristring_from_message (message); + + if (manager->proxy_lists) + list = g_hash_table_lookup (manager->proxy_lists, tri); + else + list = NULL; + + g_free (tri); + + /* Emit the signal */ + + if (list != NULL) + { + GSList *tmp; + GSList *copy; + + copy = g_slist_copy (list->proxies); + g_slist_foreach (copy, (GFunc) g_object_ref, NULL); + + tmp = copy; + while (tmp != NULL) + { + DBusGProxy *proxy; + + proxy = DBUS_GPROXY (tmp->data); + + UNLOCK_MANAGER (manager); + dbus_gproxy_emit_received (proxy, message); + g_object_unref (G_OBJECT (proxy)); + LOCK_MANAGER (manager); + + tmp = tmp->next; + } + + g_slist_free (copy); + } + } + + UNLOCK_MANAGER (manager); + dbus_gproxy_manager_unref (manager); + + /* "Handling" signals doesn't make sense, they are for everyone + * who cares + */ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + + +/* ---------- DBusGProxy -------------- */ + + + +enum +{ + DESTROY, + RECEIVED, + LAST_SIGNAL +}; + +static void *parent_class; +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +dbus_gproxy_init (DBusGProxy *proxy) +{ + /* Nothing */ +} + +static void +dbus_gproxy_class_init (DBusGProxyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = dbus_gproxy_finalize; + object_class->dispose = dbus_gproxy_dispose; + + signals[DESTROY] = + g_signal_new ("destroy", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[RECEIVED] = + g_signal_new ("received", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + DBUS_TYPE_MESSAGE); +} + + +static void +dbus_gproxy_dispose (GObject *object) +{ + DBusGProxy *proxy; + + proxy = DBUS_GPROXY (object); + + g_signal_emit (object, signals[DESTROY], 0); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +dbus_gproxy_finalize (GObject *object) +{ + DBusGProxy *proxy; + + proxy = DBUS_GPROXY (object); + + if (proxy->manager) + { + dbus_gproxy_manager_unregister (proxy->manager, proxy); + dbus_gproxy_manager_unref (proxy->manager); + } + + g_free (proxy->service); + g_free (proxy->path); + g_free (proxy->interface); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +dbus_gproxy_destroy (DBusGProxy *proxy) +{ + /* FIXME do we need the GTK_IN_DESTRUCTION style flag + * from GtkObject? + */ + g_object_run_dispose (G_OBJECT (proxy)); +} + +static char* +create_signal_detail (const char *interface, + const char *signal) +{ + GString *str; + + str = g_string_new (interface); + + g_string_append (str, "."); + + g_string_append (str, signal); + + return g_string_free (str, FALSE); +} + +static void +dbus_gproxy_emit_received (DBusGProxy *proxy, + DBusMessage *message) +{ + const char *interface; + const char *signal; + char *detail; + GQuark q; + + interface = dbus_message_get_interface (message); + signal = dbus_message_get_member (message); + + g_assert (interface != NULL); + g_assert (signal != NULL); + + detail = create_signal_detail (interface, signal); + + /* If the quark isn't preexisting, there's no way there + * are any handlers connected. We don't want to create + * extra quarks for every possible signal. + */ + q = g_quark_try_string (detail); + + if (q != 0) + g_signal_emit (G_OBJECT (proxy), + signals[RECEIVED], + q, + message); + + g_free (detail); +} + +/** @} End of DBusGLibInternals */ + +/** @addtogroup DBusGLib + * @{ + */ + +/** + * Standard GObject get_type() function for DBusGProxy. + * + * @returns type ID for DBusGProxy class + */ +GType +dbus_gproxy_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) + { + static const GTypeInfo object_info = + { + sizeof (DBusGProxyClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) dbus_gproxy_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (DBusGProxy), + 0, /* n_preallocs */ + (GInstanceInitFunc) dbus_gproxy_init, + }; + + object_type = g_type_register_static (G_TYPE_OBJECT, + "DBusGProxy", + &object_info, 0); + } + + return object_type; +} + +static DBusGProxy* +dbus_gproxy_new (DBusConnection *connection, + const char *service_name, + const char *path_name, + const char *interface_name) +{ + DBusGProxy *proxy; + + g_assert (connection != NULL); + + proxy = g_object_new (DBUS_TYPE_GPROXY, NULL); + + /* These should all be construct-only mandatory properties, + * for now we just don't let people use g_object_new(). + */ + + proxy->manager = dbus_gproxy_manager_get (connection); + + proxy->service = g_strdup (service_name); + proxy->path = g_strdup (path_name); + proxy->interface = g_strdup (interface_name); + + dbus_gproxy_manager_register (proxy->manager, proxy); + + return proxy; +} + +/** + * Creates a new proxy for a remote interface exported by a service on + * a message bus. Method calls and signal connections over this proxy + * will go to the service owner; the service owner is expected to + * support the given interface name. THE SERVICE OWNER MAY CHANGE OVER + * TIME, for example between two different method calls. If you need a + * fixed owner, you need to request the current owner and bind a proxy + * to that rather than to the generic service name; see + * dbus_gproxy_new_for_service_owner(). + * + * A service-associated proxy only makes sense with a message bus, + * not for app-to-app direct dbus connections. + * + * This proxy will only emit the "destroy" signal if the #DBusConnection + * is disconnected or the proxy is has no remaining references. + * + * @param connection the connection to the remote bus + * @param service_name name of the service on the message bus + * @param path_name name of the object inside the service to call methods on + * @param interface_name name of the interface to call methods on + * @returns new proxy object + */ +DBusGProxy* +dbus_gproxy_new_for_service (DBusConnection *connection, + const char *service_name, + const char *path_name, + const char *interface_name) +{ + DBusGProxy *proxy; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (service_name != NULL, NULL); + g_return_val_if_fail (path_name != NULL, NULL); + g_return_val_if_fail (interface_name != NULL, NULL); + + proxy = dbus_gproxy_new (connection, service_name, + path_name, interface_name); + + return proxy; +} + +/** + * Similar to dbus_gproxy_new_for_service(), but makes a round-trip + * request to the message bus to get the current service owner, then + * binds the proxy specifically to the current owner. As a result, the + * service owner will not change over time, and the proxy will emit + * the "destroy" signal when the owner disappears from the message + * bus. + * + * An example of the difference between dbus_gproxy_new_for_service() + * and dbus_gproxy_new_for_service_owner(): if you pass the service name + * "org.freedesktop.Database" dbus_gproxy_new_for_service() remains bound + * to that name as it changes owner. dbus_gproxy_new_for_service_owner() + * will fail if the service has no owner. If the service has an owner, + * dbus_gproxy_new_for_service_owner() will bind to the unique name + * of that owner rather than the generic service name. + * + * @param connection the connection to the remote bus + * @param service_name name of the service on the message bus + * @param path_name name of the object inside the service to call methods on + * @param interface_name name of the interface to call methods on + * @param error return location for an error + * @returns new proxy object, or #NULL on error + */ +DBusGProxy* +dbus_gproxy_new_for_service_owner (DBusConnection *connection, + const char *service_name, + const char *path_name, + const char *interface_name, + GError **error) +{ + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (service_name != NULL, NULL); + g_return_val_if_fail (path_name != NULL, NULL); + g_return_val_if_fail (interface_name != NULL, NULL); + + +} + +/** + * Creates a proxy for an object in peer application (one + * we're directly connected to). That is, this function is + * intended for use when there's no message bus involved, + * we're doing a simple 1-to-1 communication between two + * applications. + * + * + * @param connection the connection to the peer + * @param path_name name of the object inside the peer to call methods on + * @param interface_name name of the interface to call methods on + * @returns new proxy object + * + */ +DBusGProxy* +dbus_gproxy_new_for_peer (DBusConnection *connection, + const char *path_name, + const char *interface_name) +{ + DBusGProxy *proxy; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (path_name != NULL, NULL); + g_return_val_if_fail (interface_name != NULL, NULL); + + proxy = dbus_gproxy_new (connection, NULL, + path_name, interface_name); + + return proxy; +} + +/** + * Invokes a method on a remote interface. This function does not + * block; instead it returns an opaque #DBusPendingCall object that + * tracks the pending call. The method call will not be sent over the + * wire until the application returns to the main loop, or blocks in + * dbus_connection_flush() to write out pending data. The call will + * be completed after a timeout, or when a reply is received. + * To collect the results of the call (which may be an error, + * or a reply), use dbus_gproxy_end_call(). + * + * @todo this particular function shouldn't die on out of memory, + * since you should be able to do a call with large arguments. + * + * @param proxy a proxy for a remote interface + * @param method the name of the method to invoke + * @param first_arg_type type of the first argument + * + * @returns opaque pending call object + * */ +DBusPendingCall* +dbus_gproxy_begin_call (DBusGProxy *proxy, + const char *method, + int first_arg_type, + ...) +{ + DBusPendingCall *pending; + DBusMessage *message; + va_list args; + + g_return_val_if_fail (DBUS_IS_GPROXY (proxy), NULL); + + message = dbus_message_new_method_call (proxy->service, + proxy->path, + proxy->interface, + method); + if (message == NULL) + goto oom; + + va_start (args, first_arg_type); + if (!dbus_message_append_args_valist (message, first_arg_type, + args)) + goto oom; + va_end (args); + + if (!dbus_connection_send_with_reply (proxy->manager->connection, + message, + &pending, + -1)) + goto oom; + + return pending; + + oom: + /* FIXME we should create a pending call that's + * immediately completed with an error status without + * ever going on the wire. + */ + + g_error ("Out of memory"); + return NULL; +} + +/** + * Collects the results of a method call. The method call was normally + * initiated with dbus_gproxy_end_call(). This function will block if + * the results haven't yet been received; use + * dbus_pending_call_set_notify() to be notified asynchronously that a + * pending call has been completed. Use + * dbus_pending_call_get_completed() to check whether a call has been + * completed. If it's completed, it will not block. + * + * If the call results in an error, the error is set as normal for + * GError and the function returns #FALSE. + * + * Otherwise, the "out" parameters and return value of the + * method are stored in the provided varargs list. + * The list should be terminated with DBUS_TYPE_INVALID. + * + * This function doesn't affect the reference count of the + * #DBusPendingCall, the caller of dbus_gproxy_begin_call() still owns + * a reference. + * + * @param proxy a proxy for a remote interface + * @param pending the pending call from dbus_gproxy_begin_call() + * @param error return location for an error + * @param first_arg_type type of first "out" argument + * @returns #FALSE if an error is set */ +gboolean +dbus_gproxy_end_call (DBusGProxy *proxy, + DBusPendingCall *pending, + GError **error, + int first_arg_type, + ...) +{ + DBusMessage *message; + va_list args; + DBusError derror; + + g_return_val_if_fail (DBUS_IS_GPROXY (proxy), FALSE); + g_return_val_if_fail (pending != NULL, FALSE); + + dbus_pending_call_block (pending); + message = dbus_pending_call_get_reply (pending); + + g_assert (message != NULL); + + dbus_error_init (&derror); + va_start (args, first_arg_type); + if (!dbus_message_get_args_valist (message, &derror, first_arg_type, args)) + { + va_end (args); + goto error; + } + va_end (args); + + return TRUE; + + error: + dbus_set_g_error (error, &derror); + dbus_error_free (&derror); + return FALSE; +} + +/** + * Sends a method call message as with dbus_gproxy_begin_call(), but + * does not ask for a reply or allow you to receive one. + * + * @todo this particular function shouldn't die on out of memory, + * since you should be able to do a call with large arguments. + * + * @param proxy a proxy for a remote interface + * @param method the name of the method to invoke + * @param first_arg_type type of the first argument + */ +void +dbus_gproxy_oneway_call (DBusGProxy *proxy, + const char *method, + int first_arg_type, + ...) +{ + DBusMessage *message; + va_list args; + + g_return_if_fail (DBUS_IS_GPROXY (proxy)); + + message = dbus_message_new_method_call (proxy->service, + proxy->path, + proxy->interface, + method); + if (message == NULL) + goto oom; + + dbus_message_set_no_reply (message, TRUE); + + va_start (args, first_arg_type); + if (!dbus_message_append_args_valist (message, first_arg_type, + args)) + goto oom; + va_end (args); + + if (!dbus_connection_send (proxy->manager->connection, + message, + NULL)) + goto oom; + + oom: + g_error ("Out of memory"); +} + +/** + * Sends a message to the interface we're proxying for. Does not + * block or wait for a reply. The message is only actually written out + * when you return to the main loop or block in + * dbus_connection_flush(). + * + * The message is modified to be addressed to the target interface. + * That is, a destination service field or whatever is needed will be + * added to the message. The basic point of this function is to add + * the necessary header fields, otherwise it's equivalent to + * dbus_connection_send(). + * + * This function adds a reference to the message, so the caller + * still owns its original reference. + * + * @param proxy a proxy for a remote interface + * @param message the message to address and send + * @param client_serial return location for message's serial, or #NULL */ +void +dbus_gproxy_send (DBusGProxy *proxy, + DBusMessage *message, + dbus_uint32_t *client_serial) +{ + g_return_if_fail (DBUS_IS_GPROXY (proxy)); + + if (proxy->service) + { + if (!dbus_message_set_destination (message, proxy->service)) + g_error ("Out of memory"); + } + if (proxy->path) + { + if (!dbus_message_set_path (message, proxy->path)) + g_error ("Out of memory"); + } + if (proxy->interface) + { + if (!dbus_message_set_interface (message, proxy->interface)) + g_error ("Out of memory"); + } + + if (!dbus_connection_send (proxy->manager->connection, message, client_serial)) + g_error ("Out of memory\n"); +} + +void +dbus_gproxy_connect_signal (DBusGProxy *proxy, + const char *signal_name, + DBusGProxySignalHandler handler, + void *data, + GClosureNotify free_data_func) +{ + GClosure *closure; + char *detail; + + g_return_if_fail (DBUS_IS_GPROXY (proxy)); + g_return_if_fail (signal_name != NULL); + g_return_if_fail (handler != NULL); + + detail = create_signal_detail (proxy->interface, signal_name); + + closure = g_cclosure_new (G_CALLBACK (handler), data, free_data_func); + g_signal_connect_closure_by_id (G_OBJECT (proxy), + signals[RECEIVED], + g_quark_from_string (detail), + closure, FALSE); + + g_free (detail); +} + +void +dbus_gproxy_disconnect_signal (DBusGProxy *proxy, + const char *signal_name, + DBusGProxySignalHandler handler, + void *data) +{ + char *detail; + GQuark q; + + g_return_if_fail (DBUS_IS_GPROXY (proxy)); + g_return_if_fail (signal_name != NULL); + g_return_if_fail (handler != NULL); + + detail = create_signal_detail (proxy->interface, signal_name); + q = g_quark_try_string (detail); + g_free (detail); + +#ifndef G_DISABLE_CHECKS + if (q == 0) + { + g_warning ("%s: No signal handlers for %s found on this DBusGProxy", + G_GNUC_FUNCTION, signal_name); + return; + } +#endif + + g_signal_handlers_disconnect_matched (G_OBJECT (proxy), + G_SIGNAL_MATCH_DETAIL | + G_SIGNAL_MATCH_FUNC | + G_SIGNAL_MATCH_DATA, + signals[RECEIVED], + q, + NULL, + G_CALLBACK (handler), data); +} + +/** @} End of DBusGLib public */ + +#ifdef DBUS_BUILD_TESTS + +/** + * @ingroup DBusGLibInternals + * Unit test for GLib proxy functions + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_gproxy_test (void) +{ + + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/glib/dbus-gtest-main.c b/glib/dbus-gtest-main.c new file mode 100644 index 00000000..5cc6cb78 --- /dev/null +++ b/glib/dbus-gtest-main.c @@ -0,0 +1,46 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gtest-main.c Program to run all libdbus-glib tests + * + * 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-gtest.h" +#include <stdio.h> +#include <stdlib.h> +#include <locale.h> + +int +main (int argc, + char **argv) +{ + const char *test_data_dir; + + setlocale(LC_ALL, ""); + + + if (argc > 1) + test_data_dir = argv[1]; + else + test_data_dir = NULL; + + dbus_glib_internal_do_not_use_run_tests (test_data_dir); + + return 0; +} diff --git a/glib/dbus-gtest.c b/glib/dbus-gtest.c new file mode 100644 index 00000000..48cd13f0 --- /dev/null +++ b/glib/dbus-gtest.c @@ -0,0 +1,77 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-test.c Program to run all tests + * + * Copyright (C) 2002, 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-gtest.h" +#include <stdio.h> +#include <stdlib.h> + +#ifdef DBUS_BUILD_TESTS +static void +die (const char *failure) +{ + fprintf (stderr, "Unit test failed: %s\n", failure); + exit (1); +} +#endif /* DBUS_BUILD_TESTS */ + +/** + * An exported symbol to be run in order to execute + * unit tests. Should not be used by + * any app other than our test app, this symbol + * won't exist in some builds of the library. + * (with --enable-tests=no) + * + * @param test_data_dir the directory with test data (test/data normally) + */ +void +dbus_glib_internal_do_not_use_run_tests (const char *test_data_dir) +{ +#ifdef DBUS_BUILD_TESTS + if (test_data_dir == NULL) + test_data_dir = g_getenv ("DBUS_TEST_DATA"); + + if (test_data_dir != NULL) + printf ("Test data in %s\n", test_data_dir); + else + printf ("No test data!\n"); + + printf ("%s: running utils tests\n", "dbus-glib-test"); + if (!_dbus_gutils_test (test_data_dir)) + die ("gutils"); + + printf ("%s: running mainloop integration tests\n", "dbus-glib-test"); + if (!_dbus_gmain_test (test_data_dir)) + die ("gmain"); + + printf ("%s: running GObject tests\n", "dbus-glib-test"); + if (!_dbus_gobject_test (test_data_dir)) + die ("gobject"); + + printf ("%s: completed successfully\n", "dbus-glib-test"); +#else + printf ("Not compiled with unit tests, not running any\n"); +#endif +} + + diff --git a/glib/dbus-gtest.h b/glib/dbus-gtest.h new file mode 100644 index 00000000..1174eb0a --- /dev/null +++ b/glib/dbus-gtest.h @@ -0,0 +1,35 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gtest.h Declarations of test functions. + * + * 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 + * + */ + +#ifndef DBUS_GLIB_TEST_H +#define DBUS_GLIB_TEST_H + +#include "dbus-glib.h" + +dbus_bool_t _dbus_gmain_test (const char *test_data_dir); +dbus_bool_t _dbus_gobject_test (const char *test_data_dir); +dbus_bool_t _dbus_gutils_test (const char *test_data_dir); + +void dbus_glib_internal_do_not_use_run_tests (const char *test_data_dir); + +#endif /* DBUS_GLIB_TEST_H */ diff --git a/glib/dbus-gthread.c b/glib/dbus-gthread.c index 71a3c1f5..eb3e5572 100644 --- a/glib/dbus-gthread.c +++ b/glib/dbus-gthread.c @@ -165,7 +165,7 @@ dbus_gcondvar_wake_all (DBusCondVar *cond) * other function in the D-BUS API. */ void -dbus_gthread_init (void) +dbus_g_thread_init (void) { if (!g_thread_supported ()) g_error ("g_thread_init() must be called before dbus_threads_init()"); diff --git a/glib/dbus-gtool-test.h b/glib/dbus-gtool-test.h new file mode 100644 index 00000000..74a7649f --- /dev/null +++ b/glib/dbus-gtool-test.h @@ -0,0 +1,31 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gtool-test.h Declarations of test functions for dbus-glib-tool. + * + * 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 + * + */ + +#ifndef DBUS_GLIB_TOOL_TEST_H +#define DBUS_GLIB_TOOL_TEST_H + +#include "dbus-glib.h" + +dbus_bool_t _dbus_gtool_test (const char *test_data_dir); + +#endif /* DBUS_GLIB_TEST_H */ diff --git a/glib/dbus-gutils.c b/glib/dbus-gutils.c new file mode 100644 index 00000000..e99286f8 --- /dev/null +++ b/glib/dbus-gutils.c @@ -0,0 +1,96 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gutils.c Utils shared between convenience lib and installed lib + * + * 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-gutils.h" +#include "dbus-gtest.h" +#include <string.h> + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +char** +_dbus_gutils_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; +} + +#ifdef DBUS_BUILD_TESTS + +/** + * @ingroup DBusGLibInternals + * Unit test for GLib utils internals + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_gutils_test (const char *test_data_dir) +{ + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/glib/dbus-gutils.h b/glib/dbus-gutils.h new file mode 100644 index 00000000..af7cee45 --- /dev/null +++ b/glib/dbus-gutils.h @@ -0,0 +1,40 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-gutils.h Utils shared between convenience lib and installed lib + * + * 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 + * + */ + +#ifndef DBUS_GLIB_UTILS_H +#define DBUS_GLIB_UTILS_H + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#include <dbus/dbus.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +char** _dbus_gutils_split_path (const char *path); + +G_END_DECLS + +#endif /* DBUS_GLIB_UTILS_H */ + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ |