diff src/dbus-server.c @ 11171:ebb02ea3c789

[gaim-migrate @ 13272] Moved DBUS init call from gtkmain.c to core.c Reimplemented DBUS bindings mechamism to use low-level GLib bindings as described in my last blog entry. This way plugins can add new DBUS methods on the fly. Also wrote an example plugin that demonstrate how to do it. committer: Tailor Script <tailor@pidgin.im>
author Piotr Zielinski <zielaj>
date Sat, 30 Jul 2005 00:23:21 +0000
parents 1c5398ccbeb0
children 57af14280b5f
line wrap: on
line diff
--- a/src/dbus-server.c	Fri Jul 29 13:38:00 2005 +0000
+++ b/src/dbus-server.c	Sat Jul 30 00:23:21 2005 +0000
@@ -23,16 +23,9 @@
 
 #define DBUS_API_SUBJECT_TO_CHANGE
 
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <dbus/dbus-glib-bindings.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <glib/gi18n.h>
-#include <glib-object.h>
-#include <glib/gquark.h>
-#include <glib.h>
 
 #include "account.h"
 #include "blist.h"
@@ -40,50 +33,122 @@
 #include "dbus-gaim.h"
 #include "dbus-server.h"
 #include "dbus-useful.h"
+#include "dbus-bindings.h"
 #include "debug.h"
 #include "core.h"
 #include "value.h"
 
 
-
 /**************************************************************************/
-/** @name Lots of GObject stuff I don't really understand                 */
+/** @name Gaim DBUS pointer registration mechanism                        */
 /**************************************************************************/
 
-GType gaim_object_get_type(void);
+/* 
+   Here we include the list of #GAIM_DBUS_DEFINE_TYPE statements for
+   all structs defined in gaim.  This file has been generated by the
+   #dbus-analize-types.py script.
+*/
 
-struct _GaimObject {
-    GObject parent;
-    DBusGProxy *proxy;
-};
+#include "dbus-types.c"
 
-typedef struct {
-	GObjectClass parent;
-} GaimObjectClass;
+/* The following three hashtables map are used to translate between
+   pointers (nodes) and the corresponding handles (ids).  */
+
+static GHashTable *map_node_id; 
+static GHashTable *map_id_node; 
+static GHashTable *map_id_type; 
 
 
-#define GAIM_DBUS_TYPE_OBJECT              (gaim_object_get_type ())
-#define GAIM_DBUS_OBJECT(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GAIM_DBUS_TYPE_OBJECT, GaimObject))
-#define GAIM_DBUS_OBJECT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GAIM_DBUS_TYPE_OBJECT, GaimObjectClass))
-#define GAIM_DBUS_IS_OBJECT(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GAIM_DBUS_TYPE_OBJECT))
-#define GAIM_DBUS_IS_OBJECT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIM_DBUS_TYPE_OBJECT))
-#define GAIM_DBUS_OBJECT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIM_DBUS_TYPE_OBJECT, GaimObjectClass))
+/* This function initializes the pointer-id traslation system.  It
+   creates the three above hashtables and defines parents of some types.
+ */
+void gaim_dbus_init_ids(void) {
+    map_id_node = g_hash_table_new (g_direct_hash, g_direct_equal);
+    map_id_type = g_hash_table_new (g_direct_hash, g_direct_equal);
+    map_node_id = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+    GAIM_DBUS_TYPE(GaimBuddy)->parent   = GAIM_DBUS_TYPE(GaimBlistNode);
+    GAIM_DBUS_TYPE(GaimContact)->parent = GAIM_DBUS_TYPE(GaimBlistNode);
+    GAIM_DBUS_TYPE(GaimChat)->parent    = GAIM_DBUS_TYPE(GaimBlistNode);
+    GAIM_DBUS_TYPE(GaimGroup)->parent   = GAIM_DBUS_TYPE(GaimBlistNode);
+}
+
+void gaim_dbus_register_pointer(gpointer node, GaimDBusType *type) 
+{
+    static gint last_id = 0;
+
+    g_assert(map_node_id);
+    g_assert(g_hash_table_lookup(map_node_id, node) == NULL);
+	
+    last_id++;
+    g_hash_table_insert(map_node_id, node, GINT_TO_POINTER(last_id));
+    g_hash_table_insert(map_id_node, GINT_TO_POINTER(last_id), node);
+    g_hash_table_insert(map_id_type, GINT_TO_POINTER(last_id), type);
+}
+
+void gaim_dbus_unregister_pointer(gpointer node) {
+    gpointer id = g_hash_table_lookup(map_node_id, node);
+	
+    g_hash_table_remove(map_node_id, node);
+    g_hash_table_remove(map_id_node, GINT_TO_POINTER(id));
+    g_hash_table_remove(map_id_type, GINT_TO_POINTER(id));
+}
 
-G_DEFINE_TYPE(GaimObject, gaim_object, G_TYPE_OBJECT)
+gint gaim_dbus_pointer_to_id(gpointer node) {
+    gint id = GPOINTER_TO_INT(g_hash_table_lookup(map_node_id, node));
+    g_return_val_if_fail(id, 0);
+    return id;
+}
+	
+gpointer gaim_dbus_id_to_pointer(gint id,  GaimDBusType *type) {
+    GaimDBusType *objtype = 
+	(GaimDBusType*) g_hash_table_lookup(map_id_type, 
+						  GINT_TO_POINTER(id));
+							    
+    while (objtype != type && objtype != NULL)
+	objtype = objtype->parent;
+
+    if (objtype == type)
+	return g_hash_table_lookup(map_id_node, GINT_TO_POINTER(id));
+    else
+	return NULL;
+}
+
+gint  gaim_dbus_pointer_to_id_error(gpointer ptr, DBusError *error) 
+{
+    gint id = gaim_dbus_pointer_to_id(ptr);
 
-GaimObject *gaim_dbus_object;
-static GQuark gaim_object_error_quark;
+    if (ptr != NULL && id == 0)
+	dbus_set_error(error, "org.gaim.ObjectNotFound",
+		       "The return object is not mapped (this is a Gaim error)");
+
+    return id;
+}
+
+gpointer gaim_dbus_id_to_pointer_error(gint id, GaimDBusType *type,
+				       const char *typename, DBusError *error) 
+{
+    gpointer ptr = gaim_dbus_id_to_pointer(id, type);
 
-#define NULLIFY(id) id = empty_to_null(id)
+    if (ptr == NULL && id != 0) 
+	dbus_set_error(error, "org.gaim.InvalidHandle",
+		       "%s object with ID = %i not found", typename, id);	
+    
+    return ptr;
+}
+	
+/**************************************************************************/
+/** @name Useful functions                                                */
+/**************************************************************************/
 
-static const char* empty_to_null(const char *str) {
+const char* empty_to_null(const char *str) {
     if (str == NULL || str[0] == 0)
 	return NULL;
     else
 	return str;
 }
 
-static const char* null_to_empty(const char *s) {
+const char* null_to_empty(const char *s) {
 	if (s)
 		return s;
 	else
@@ -92,128 +157,180 @@
 
 
 
-static void gaim_object_class_init(GaimObjectClass *klass) 
-{ 
-}
+
+dbus_int32_t* gaim_dbusify_GList(GList *list, gboolean free_memory,
+				 dbus_int32_t *len)
+ {
+	dbus_int32_t *array;
+	int i;
+	GList *elem;
 
-static void gaim_object_init(GaimObject *object) 
-{
+	*len = g_list_length(list);
+	array = g_new0(dbus_int32_t, g_list_length(list));
+	for(i = 0, elem = list; elem != NULL; elem = elem->next, i++) 
+		array[i] = gaim_dbus_pointer_to_id(elem->data);
+
+	if (free_memory)
+		g_list_free(list);
+
+	return array;
 }
 
-/**************************************************************************/
-/** @name Gaim DBUS pointer registration mechanism                        */
-/**************************************************************************/
-
-GaimDBusPointerType dbus_type_parent[DBUS_POINTER_LASTTYPE];
-
-static GHashTable *map_id_node; 
-static GHashTable *map_id_type; 
-static GHashTable *map_node_id; 
-
-
-#define DECLARE_TYPE(type, parent) \
-	dbus_type_parent[DBUS_POINTER_##type] = DBUS_POINTER_##parent
-
-void gaim_dbus_init_ids(void) {
+dbus_int32_t* gaim_dbusify_GSList(GSList *list, gboolean free_memory,
+				  dbus_int32_t *len) 
+{
+	dbus_int32_t *array;
 	int i;
-
-	map_id_node = g_hash_table_new (g_direct_hash, g_direct_equal);
-	map_id_type = g_hash_table_new (g_direct_hash, g_direct_equal);
-	map_node_id = g_hash_table_new (g_direct_hash, g_direct_equal);
+	GSList *elem;
 
-	for (i = 0; i < DBUS_POINTER_LASTTYPE; i++)
-		dbus_type_parent[i] = DBUS_POINTER_NONE;
-
-	/* some manual corrections */
-	DECLARE_TYPE(GaimBuddy, GaimBlistNode);
-	DECLARE_TYPE(GaimContact, GaimBlistNode);
-	DECLARE_TYPE(GaimChat, GaimBlistNode);
-	DECLARE_TYPE(GaimGroup, GaimBlistNode);
-}
+	*len = g_slist_length(list);
+	array = g_new0(dbus_int32_t, g_slist_length(list));
+	for(i = 0, elem = list; elem != NULL; elem = elem->next, i++) 
+		array[i] = gaim_dbus_pointer_to_id(elem->data);
 
-void gaim_dbus_register_pointer(gpointer node, GaimDBusPointerType type) 
-{
-	static gint last_id = 0;
+	if (free_memory)
+		g_slist_free(list);
 
-	g_assert(map_node_id);
-	g_assert(g_hash_table_lookup(map_node_id, node) == NULL);
-	
-
-	last_id++;
-	g_hash_table_insert(map_node_id, node, GINT_TO_POINTER(last_id));
-	g_hash_table_insert(map_id_node, GINT_TO_POINTER(last_id), node);
-	g_hash_table_insert(map_id_type, GINT_TO_POINTER(last_id), 
-			    GINT_TO_POINTER(type));
+	return array;
 }
 
 
-void gaim_dbus_unregister_pointer(gpointer node) {
-	gpointer id = g_hash_table_lookup(map_node_id, node);
-	
-	g_hash_table_remove(map_node_id, node);
-	g_hash_table_remove(map_id_node, GINT_TO_POINTER(id));
-	g_hash_table_remove(map_id_node, GINT_TO_POINTER(id));
+/**************************************************************/
+/* DBus bindings ...                                          */
+/**************************************************************/
+
+static DBusConnection *gaim_dbus_connection;
+
+DBusConnection *gaim_dbus_get_connection(void) {
+    return gaim_dbus_connection;
+}
+
+#include "dbus-bindings.c"
+
+void *gaim_dbus_get_handle(void) {
+    static int handle;
+
+    return &handle;
 }
 
-static gint gaim_dbus_pointer_to_id(gpointer node) {
-	gint id = GPOINTER_TO_INT(g_hash_table_lookup(map_node_id, node));
-	g_return_val_if_fail(id, 0);
-	return id;
+static gboolean
+gaim_dbus_dispatch_cb(DBusConnection *connection,
+		   DBusMessage *message,
+		   void *user_data) 
+{
+    const char *name;
+    GaimDBusBinding *bindings;
+    int i;
+
+    bindings = (GaimDBusBinding*) user_data;
+
+    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+	return FALSE;
+
+    if (!dbus_message_has_path(message, DBUS_PATH_GAIM))
+	return FALSE;
+
+    name = dbus_message_get_member(message);
+
+    if (name == NULL)
+	return FALSE;
+
+    for(i=0; bindings[i].name; i++)
+	if (!strcmp(name, bindings[i].name)) {
+	    DBusMessage *reply;
+	    DBusError error;
+
+	    dbus_error_init(&error);
+	    
+	    reply = bindings[i].handler(message, &error);
+    
+	    if (reply == NULL && dbus_error_is_set(&error)) 
+		reply = dbus_message_new_error (message,
+						error.name,
+						error.message);
+	    
+	    if (reply != NULL) {
+		dbus_connection_send (connection, reply, NULL);
+		dbus_message_unref(reply);
+	    }
+	    
+	    return TRUE;		/* return reply! */
+	}
+
+    return FALSE;
 }
-	
-static gpointer gaim_dbus_id_to_pointer(gint id, GaimDBusPointerType type) {
-	GaimDBusPointerType objtype = 
-		GPOINTER_TO_INT(g_hash_table_lookup(map_id_type, 
-						    GINT_TO_POINTER(id)));
-							    
-	while (objtype != type && objtype != DBUS_POINTER_NONE)
-		objtype = dbus_type_parent[objtype];
 
-	if (objtype == type)
-		return g_hash_table_lookup(map_id_node, GINT_TO_POINTER(id));
-	else
-		return NULL;
+static DBusHandlerResult gaim_dbus_dispatch(DBusConnection *connection,
+					    DBusMessage *message,
+					    void *user_data)
+{
+    if (gaim_signal_emit_return_1(gaim_dbus_get_handle(), 
+				  "dbus-method-called", 
+				  connection, message))
+	return DBUS_HANDLER_RESULT_HANDLED;
+    else 
+	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
-	
-/**************************************************************************/
-/** @name Useful functions                                                */
-/**************************************************************************/
+
+void gaim_dbus_register_bindings(void *handle, GaimDBusBinding *bindings) {
+    gaim_signal_connect(gaim_dbus_get_handle(), "dbus-method-called",
+			handle, 
+			GAIM_CALLBACK(gaim_dbus_dispatch_cb),
+			bindings);
+}
 
 
-#define error_unless_1(condition, str, parameter)		\
-	if (!(condition)) {					\
-		    g_set_error(error, gaim_object_error_quark,	\
-				DBUS_ERROR_NOT_FOUND,		\
-				str, parameter);		\
-		    return FALSE;				\
-	    }
+
+gboolean gaim_dbus_dispatch_init(void) 
+{
+    static DBusObjectPathVTable vtable = {NULL, &gaim_dbus_dispatch};
+
+    DBusError error;
+    int result;
+    
+    dbus_error_init (&error);
+    gaim_dbus_connection = dbus_bus_get (DBUS_BUS_STARTER, &error);    
 
-#define error_unless_2(condition, str, a,b)			\
-	if (!(condition)) {					\
-		    g_set_error(error, gaim_object_error_quark,	\
-				DBUS_ERROR_NOT_FOUND,		\
-				str, a,b);			\
-		    return FALSE;				\
-	    }
+    if (gaim_dbus_connection == NULL) {
+	gaim_debug_error("dbus", "Failed to get connection\n");
+	dbus_error_free(&error);
+	return FALSE;
+    }
+
+    if (!dbus_connection_register_object_path (gaim_dbus_connection,
+					       DBUS_PATH_GAIM, &vtable, NULL))
+    {
+	gaim_debug_error("dbus", "Failed to get name: %s\n", error.name);
+        dbus_error_free(&error);
+	return FALSE;
+    }
+	    
 
-#define GAIM_DBUS_ID_TO_POINTER(ptr, id, type)				\
-	G_STMT_START {							\
-		ptr = ((type*) gaim_dbus_id_to_pointer			\
-		       (id, DBUS_POINTER_##type));			\
-		error_unless_2(ptr != NULL || id == 0,			\
-			       "%s object with ID = %i not found",	\
-			       #type, id);				\
-	} G_STMT_END
-	       
+    result = dbus_bus_request_name (gaim_dbus_connection, DBUS_SERVICE_GAIM, 
+				    0, &error);
+    
+    if (dbus_error_is_set (&error)) {
+	dbus_connection_unref(gaim_dbus_connection);
+	dbus_error_free(&error);
+	gaim_debug_error("dbus", "Failed to get serv name: %s\n", error.name);
+	return FALSE;
+    }
+	
+    dbus_connection_setup_with_g_main(gaim_dbus_connection, NULL);
 
-#define GAIM_DBUS_POINTER_TO_ID(id, ptr)				\
-	G_STMT_START {							\
-		gpointer _ptr = ptr;					\
-		id = gaim_dbus_pointer_to_id(_ptr);			\
-		error_unless_1(ptr == NULL || id != 0,			\
-			       "Result object not registered (%i)",	\
-			       id);					\
-	} G_STMT_END
+    gaim_debug_misc ("dbus", "okkk\n");
+
+    gaim_signal_register(gaim_dbus_get_handle(), "dbus-method-called",
+			 gaim_marshal_BOOLEAN__POINTER_POINTER,
+			 gaim_value_new(GAIM_TYPE_BOOLEAN), 2,
+			 gaim_value_new(GAIM_TYPE_POINTER),
+			 gaim_value_new(GAIM_TYPE_POINTER));
+
+    GAIM_DBUS_REGISTER_BINDINGS(gaim_dbus_get_handle());
+
+    return TRUE;
+}
+
 
 
 /**************************************************************************/
@@ -265,22 +382,18 @@
 	
 	switch(gaim_values[i]->type) {
 	case  GAIM_TYPE_INT:  
-	    g_print("appending int\n");
 	    xint = my_arg(gint);
 	    dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &xint);
 	    break;
 	case  GAIM_TYPE_UINT:  
 	    xuint = my_arg(guint);
-	    g_print("appending uint\n");
 	    dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &xuint);
 	    break;
 	case  GAIM_TYPE_BOOLEAN:  
-	    g_print("appending boolean\n");
 	    xboolean = my_arg(gboolean);
 	    dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &xboolean);
 	    break;
 	case GAIM_TYPE_STRING: 
-	    g_print("appending string\n");
 	    str = null_to_empty(my_arg(char*));
 	    dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
 	    break;
@@ -288,7 +401,6 @@
 	case GAIM_TYPE_POINTER:
 	case GAIM_TYPE_OBJECT: 
 	case GAIM_TYPE_BOXED:		
-	    g_print("appending obj\n");
 	    id = gaim_dbus_pointer_to_id(my_arg(gpointer));
 	    dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &id);
 	    break;
@@ -300,7 +412,7 @@
 
 #undef my_arg
 
-void gaim_dbus_signal_emit_gaim(GaimObject *object, char *name, int num_values, 
+void gaim_dbus_signal_emit_gaim(char *name, int num_values, 
 				GaimValue **values, va_list vargs)
 {
 	/* pass name */
@@ -308,8 +420,7 @@
     DBusMessageIter iter;
     char *newname;
 
-    g_print("Emitting %s\n", name);    
-    g_return_if_fail(object->proxy);
+    g_return_if_fail(gaim_dbus_connection);
     
     newname =  gaim_dbus_convert_signal_name(name);
     signal = dbus_message_new_signal(DBUS_PATH_GAIM, DBUS_INTERFACE_GAIM, newname);
@@ -317,157 +428,23 @@
 
     gaim_dbus_message_append_gaim_values(&iter, num_values, values, vargs);
 
-    dbus_g_proxy_send(object->proxy, signal, NULL);
+    dbus_connection_send(gaim_dbus_connection, signal, NULL);
 
     g_free(newname);
     dbus_message_unref(signal);
 }
 
 
-/**************************************************************/
-/* DBus bindings ...                                          */
-/**************************************************************/
-
-
-
-GArray* gaim_dbusify_GList(GList *list, gboolean free_memory) {
-	GArray *array;
-	GList *elem;
-
-	array = g_array_new (FALSE, TRUE, sizeof (guint32));
-	for(elem = list; elem != NULL; elem = elem->next) {
-		int objectid;
-
-		objectid = gaim_dbus_pointer_to_id(elem->data);
-		g_array_append_val(array, objectid);
-	}
-
-	if (free_memory)
-		g_list_free(list);
-
-	return array;
-}
-
-GArray* gaim_dbusify_GSList(GSList *list, gboolean free_memory) {
-	GArray *array;
-	GSList *elem;
-
-	array = g_array_new (FALSE, TRUE, sizeof (guint32));
-	for(elem = list; elem != NULL; elem = elem->next) {
-		int objectid;
-
-		objectid = gaim_dbus_pointer_to_id(elem->data);
-		g_array_append_val(array, objectid);
-	}
-
-	if (free_memory)
-		g_slist_free(list);
-	return array;
-}
-
-#include "dbus-generated-code.c"
-
-#include "dbus-server-bindings.c"
-
-
-
-
-
-/**************************************************************************/
-/** @name Gaim DBUS init function                                         */
-/**************************************************************************/
-
-
-/* fixme: why do we need two functions here instead of one?  */
-
-gboolean gaim_dbus_connect(GaimObject *object) 
-{
-	DBusGConnection *connection;
-	GError *error = NULL;
-	DBusGProxy *driver_proxy;
-	guint32 request_name_ret;
-
-	gaim_debug_misc("dbus", "launching dbus server\n");
-					
-	dbus_g_object_type_install_info (GAIM_DBUS_TYPE_OBJECT,
-					 &dbus_glib_gaim_object_object_info);	/* Connect to the bus */
-
-	error = NULL;
-	connection = dbus_g_bus_get(DBUS_BUS_STARTER, &error);
-
-	if (connection == NULL) {
-		g_assert(error);
-		gaim_debug_error("dbus", "Failed to open connection to bus: %s\n",
-				 error->message);
-		g_error_free (error);
-		return FALSE;
-	}
-
-	/* Instantiate the gaim dbus object and register it */
-	
-
-
-
-	/* Obtain a proxy for the DBus object  */
-
-	driver_proxy = dbus_g_proxy_new_for_name (connection,
-						  DBUS_SERVICE_DBUS,
-						  DBUS_PATH_DBUS,
-						  DBUS_INTERFACE_DBUS);
-
-	g_assert(driver_proxy);
-
-	/* Test whether the registration was successful */
-
-	org_freedesktop_DBus_request_name(driver_proxy, DBUS_SERVICE_GAIM,
-					  DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT, &request_name_ret, &error);
-
-	if (error) {
-		gaim_debug_error("dbus", "Failed to get name: %s\n", error->message);
-		g_error_free (error);
-		return FALSE;
-	}
-
-	if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
-	{
-		gaim_debug_error ("dbus", "Got result code %u from requesting name\n",
-				  request_name_ret);
-		return FALSE;
-	}
-
-	gaim_debug_misc ("dbus", "GLib test service has name '%s'\n", 
-			 DBUS_SERVICE_GAIM);
-
-
-
-	dbus_g_connection_register_g_object (connection, DBUS_PATH_GAIM,
-					     (GObject*) object);
-
-	object->proxy = dbus_g_proxy_new_for_name (connection,
-						  DBUS_SERVICE_GAIM,
-						  DBUS_PATH_GAIM,
-						  DBUS_INTERFACE_GAIM);
-
-	gaim_debug_misc ("dbus", "GLib test service entering main loop\n");
-
-	return TRUE;
-}
 
 
 
 
 gboolean gaim_dbus_init(void) 
 {
-    gaim_dbus_init_ids();
-    gaim_object_error_quark =
-	g_quark_from_static_string("org.gaim.GaimError");
-
+    gaim_debug_register_category("dbus");
 
-
-	gaim_dbus_object = GAIM_DBUS_OBJECT(g_object_new (GAIM_DBUS_TYPE_OBJECT, NULL));
-
-	gaim_dbus_object->proxy = NULL;
-	return TRUE;
+    gaim_dbus_init_ids();
+    return gaim_dbus_dispatch_init() ;
 }