# HG changeset patch # User Piotr Zielinski # Date 1120751028 0 # Node ID 2eca9ed49469dc9c70cd7ae0e2d9426734b0b34a # Parent 2507d20c3d0ba4cd4572ea3c8e8790f22bdaf96f [gaim-migrate @ 13048] Modified configure.ac so that it rejects dbus builds with the dbus library older than 0.34 Added a simple object registration system to the dbus implementation so that it is possible to query object properties remotely (eg. give me property "name" of buddy with id = 5). committer: Tailor Script diff -r 2507d20c3d0b -r 2eca9ed49469 COPYRIGHT --- a/COPYRIGHT Thu Jul 07 15:43:05 2005 +0000 +++ b/COPYRIGHT Thu Jul 07 15:43:48 2005 +0000 @@ -230,4 +230,5 @@ Timmy Yee Nickolai Zeldovich Marco Ziech +Piotr Zielinski Jaroen Zwartepoorte diff -r 2507d20c3d0b -r 2eca9ed49469 README.dbus --- a/README.dbus Thu Jul 07 15:43:05 2005 +0000 +++ b/README.dbus Thu Jul 07 15:43:48 2005 +0000 @@ -34,7 +34,7 @@ executable. -4. Start DBUS if you haven't done it already +4. Start Session DBUS if you haven't done it already eval `dbus-launch --session` export DBUS_SESSION_BUS_ADDRESS DBUS_SESSION_BUS_PID @@ -42,12 +42,7 @@ These commands will set the two above shell variables. These variables must be set before running any dbus-aware programs. - -Now you can start gaim as usual. A new program, gaim-client can be -used to issue commands to a running gaim process +Start gaim as usual. To communicate with it, use "gaim-send". When +you execute gaim-send, the dbus system will automatically start a gaim +process if one is not running already. - gaim-client command1 command2 ... - -At the moment there are only three commands: ping, quit, and connect. -When you execute gaim-client, the dbus system will automatically start -a gaim process if one is not running already. diff -r 2507d20c3d0b -r 2eca9ed49469 configure.ac --- a/configure.ac Thu Jul 07 15:43:05 2005 +0000 +++ b/configure.ac Thu Jul 07 15:43:48 2005 +0000 @@ -318,14 +318,19 @@ if test "x$enable_dbus" = "xyes"; then PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.34 dbus-glib-1 >= 0.34], [ - AC_DEFINE(HAVE_DBUS, 1, [Define if we're using DBUS.]) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) - echo "Building with DBUS support" + ], + [ + enable_dbus=no ]) +fi + +if test "x$enable_dbus" = "xyes"; then + AC_DEFINE(HAVE_DBUS, 1, [Define if we're using DBUS.]) + echo "Building with DBUS support" else echo "Building without DBUS support" - enable_dbus=no fi AM_CONDITIONAL(ENABLE_DBUS, test "x$enable_dbus" = "xyes") diff -r 2507d20c3d0b -r 2eca9ed49469 src/Makefile.am --- a/src/Makefile.am Thu Jul 07 15:43:05 2005 +0000 +++ b/src/Makefile.am Thu Jul 07 15:43:48 2005 +0000 @@ -148,7 +148,9 @@ gaim_coresources += dbus-server.c gaim_coreheaders += dbus-server.h -bin_PROGRAMS += gaim-client + +# do not use gaim-client, use gaim-send instead +# bin_PROGRAMS += gaim-client dbus-client-bindings.c: dbus-service.xml dbus-binding-tool --prefix=gaim_object --mode=glib-client --output=$@ $< diff -r 2507d20c3d0b -r 2eca9ed49469 src/account.c --- a/src/account.c Thu Jul 07 15:43:05 2005 +0000 +++ b/src/account.c Thu Jul 07 15:43:48 2005 +0000 @@ -25,6 +25,7 @@ #include "internal.h" #include "account.h" #include "core.h" +#include "dbus-maybe.h" #include "debug.h" #include "notify.h" #include "pounce.h" @@ -701,6 +702,7 @@ gaim_presence_set_status_active(account->presence, "offline", TRUE); + GAIM_DBUS_REGISTER_POINTER(account, DBUS_POINTER_ACCOUNT); return account; } @@ -742,6 +744,7 @@ if(account->system_log) gaim_log_free(account->system_log); + GAIM_DBUS_UNREGISTER_POINTER(account); g_free(account); } diff -r 2507d20c3d0b -r 2eca9ed49469 src/blist.c --- a/src/blist.c Thu Jul 07 15:43:05 2005 +0000 +++ b/src/blist.c Thu Jul 07 15:43:48 2005 +0000 @@ -23,6 +23,7 @@ #include "internal.h" #include "blist.h" #include "conversation.h" +#include "dbus-maybe.h" #include "debug.h" #include "notify.h" #include "prefs.h" @@ -1053,6 +1054,7 @@ if (ops != NULL && ops->new_node != NULL) ops->new_node((GaimBlistNode *)chat); + GAIM_DBUS_REGISTER_POINTER(chat, DBUS_POINTER_CHAT); return chat; } @@ -1078,6 +1080,7 @@ if (ops && ops->new_node) ops->new_node((GaimBlistNode *)buddy); + GAIM_DBUS_REGISTER_POINTER(buddy, DBUS_POINTER_BUDDY); return buddy; } @@ -1346,6 +1349,7 @@ if (ops && ops->new_node) ops->new_node((GaimBlistNode *)contact); + GAIM_DBUS_REGISTER_POINTER(contact, DBUS_POINTER_CONTACT); return contact; } @@ -1425,6 +1429,7 @@ if (ops && ops->new_node) ops->new_node((GaimBlistNode *)group); + GAIM_DBUS_REGISTER_POINTER(group, DBUS_POINTER_GROUP); return group; } @@ -1681,6 +1686,7 @@ /* Delete the node */ g_hash_table_destroy(contact->node.settings); + GAIM_DBUS_UNREGISTER_POINTER(contact); g_free(contact); } } @@ -1753,6 +1759,8 @@ g_free(buddy->name); g_free(buddy->alias); g_free(buddy->server_alias); + + GAIM_DBUS_UNREGISTER_POINTER(buddy); g_free(buddy); /* If the contact is empty then remove it */ @@ -1797,6 +1805,7 @@ g_hash_table_destroy(chat->components); g_hash_table_destroy(chat->node.settings); g_free(chat->alias); + GAIM_DBUS_UNREGISTER_POINTER(chat); g_free(chat); } @@ -1859,6 +1868,7 @@ /* Delete the node */ g_hash_table_destroy(group->node.settings); g_free(group->name); + GAIM_DBUS_UNREGISTER_POINTER(group); g_free(group); } diff -r 2507d20c3d0b -r 2eca9ed49469 src/dbus-client.c --- a/src/dbus-client.c Thu Jul 07 15:43:05 2005 +0000 +++ b/src/dbus-client.c Thu Jul 07 15:43:48 2005 +0000 @@ -21,6 +21,8 @@ * */ +/* Do not use this program. Use gaim-send instead. */ + #define DBUS_API_SUBJECT_TO_CHANGE #include diff -r 2507d20c3d0b -r 2eca9ed49469 src/dbus-maybe.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dbus-maybe.h Thu Jul 07 15:43:48 2005 +0000 @@ -0,0 +1,23 @@ +/* This file contains macros that wrap calls to the gaim dbus module. + These macros call the appropriate functions if the build includes + dbus support and do nothing otherwise. See "dbus-server.h" for + documentation. */ + +#ifndef _GAIM_DBUS_MAYBE_H_ +#define _GAIM_DBUS_MAYBE_H_ + +#ifdef HAVE_DBUS + +#include "dbus-server.h" + +#define GAIM_DBUS_REGISTER_POINTER(ptr, type) gaim_dbus_register_pointer(ptr, type) +#define GAIM_DBUS_UNREGISTER_POINTER(ptr) gaim_dbus_unregister_pointer(ptr) + +#else /* !HAVE_DBUS */ + +#define GAIM_DBUS_REGISTER_POINTER(ptr, type) +#define GAIM_DBUS_UNREGISTER_POINTER(ptr) + +#endif /* HAVE_DBUS */ + +#endif diff -r 2507d20c3d0b -r 2eca9ed49469 src/dbus-server.c --- a/src/dbus-server.c Thu Jul 07 15:43:05 2005 +0000 +++ b/src/dbus-server.c Thu Jul 07 15:43:48 2005 +0000 @@ -31,12 +31,17 @@ #include #include "account.h" +#include "blist.h" +#include "conversation.h" #include "dbus-gaim.h" #include "dbus-server.h" #include "debug.h" #include "core.h" +#include "value.h" -/* GaimObject, basic definitions*/ +/**************************************************************************/ +/** @name Lots of GObject crap I don't understand */ +/**************************************************************************/ typedef struct GaimObject GaimObject; typedef struct GaimObjectClass GaimObjectClass; @@ -72,10 +77,79 @@ } static GObject *gaim_object; +static GQuark gaim_object_error_quark; + -/* Implementations of remote DBUS calls */ +/**************************************************************************/ +/** @name Utility functions */ +/**************************************************************************/ + +#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; \ + } + +#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; \ + } + +static const char* null_to_empty(const char *s) { + if (s) + return s; + else + return ""; +} + +typedef gboolean (*GaimNodeFilter)(GaimBlistNode *node, gpointer *user_data); + +static gboolean +filter_is_buddy(GaimBlistNode *node, gpointer *user_data) +{ + return GAIM_BLIST_NODE_IS_BUDDY(node); +} +static gboolean +filter_is_online_buddy(GaimBlistNode *node, + gpointer *user_data) +{ + return GAIM_BLIST_NODE_IS_BUDDY(node) && + GAIM_BUDDY_IS_ONLINE((GaimBuddy *)node); +} + + +static GList* +get_buddy_list (GList *created_list, /**< can be NULL */ + GaimBlistNode *gaim_buddy_list, /**< can be NULL */ + GaimNodeFilter filter, + gpointer user_data) +{ + GaimBlistNode *node; + + for(node = gaim_buddy_list; node; node = node->next) + { + if ((*filter)(node, user_data)) + created_list = g_list_prepend(created_list, node); + + created_list = get_buddy_list(created_list, node->child, + filter, user_data); + } + + return created_list; +} + + + +/**************************************************************************/ +/** @name Implementations of remote DBUS calls */ +/**************************************************************************/ static gboolean gaim_object_ping(GaimObject *obj, GError **error) @@ -101,19 +175,269 @@ return TRUE; } + + +static gboolean +gaim_object_get_buddy_list (GaimObject *obj, GArray **out_buddy_ids, + GError **error) +{ + GList *node; + GList *buddy_list = get_buddy_list(NULL, gaim_get_blist()->root, + &filter_is_buddy, NULL); + GArray *buddy_ids = g_array_new(FALSE, TRUE, sizeof(gint32));; + + buddy_list = g_list_reverse(buddy_list); + + for (node = buddy_list; node; node = node->next) { + gint32 id = gaim_dbus_pointer_to_id(node->data); + g_array_append_val(buddy_ids, id); + } + + g_list_free(buddy_list); + *out_buddy_ids = buddy_ids; + + return TRUE; +} + + + + + +static gboolean +gaim_object_find_account(GaimObject *unused, + const char *account_name, const char *protocol_name, + gint *account_id, GError **error) +{ + GaimAccount *account = gaim_accounts_find(account_name, protocol_name); + + error_unless_2(account, "Account '%s' with protocol '%s' not found", + account_name, protocol_name); + + *account_id = gaim_dbus_pointer_to_id(account); + return TRUE; +} + +static gboolean +gaim_object_find_buddy(GaimObject *unused, gint account_id, const char *buddy_name, + gint *buddy_id, GError **error) +{ + GaimAccount *account; + GaimBuddy *buddy; + + account = gaim_dbus_id_to_pointer(account_id, DBUS_POINTER_ACCOUNT); + error_unless_1(account, "Invalid account id: %i", account_id); + + buddy = gaim_find_buddy(account, buddy_name); + error_unless_1(account, "Buddy '%s' not found.", buddy_name); + + *buddy_id = gaim_dbus_pointer_to_id(buddy); + return TRUE; +} + +static gboolean +gaim_object_start_im_conversation (GaimObject *obj, gint buddy_id, GError **error) +{ + GaimBuddy *buddy = (GaimBuddy*) gaim_dbus_id_to_pointer(buddy_id, + DBUS_POINTER_BUDDY); + + error_unless_1(buddy, "Invalid buddy id: %i", buddy_id); + + gaim_conversation_new(GAIM_CONV_IM, buddy->account, buddy->name); + + return TRUE; +} + + + +/**************************************************************************/ +/** @name Gaim DBUS property handling functions */ +/**************************************************************************/ + + +typedef struct _GaimDBusProperty { + char *name; + gint offset; + GaimType type; +} GaimDBusProperty; + +/* change GAIM_TYPE into G_TYPE */ + +static gboolean +gaim_dbus_get_property(GaimDBusProperty *list, int list_len, + gpointer data, const char *name, + GValue *value, GError **error) +{ + int i; + + for(i=0; imessage); g_error_free (error); return FALSE; } - /* Instantiate the gaim dbus object and register it */ gaim_object = g_object_new (GAIM_TYPE_OBJECT, NULL); - dbus_g_object_type_install_info (GAIM_TYPE_OBJECT, + + dbus_g_object_type_install_info (GAIM_TYPE_OBJECT, &dbus_glib_gaim_object_object_info); - dbus_g_connection_register_g_object (connection, DBUS_PATH_GAIM, + dbus_g_connection_register_g_object (connection, DBUS_PATH_GAIM, gaim_object); @@ -146,11 +470,12 @@ 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, - 0, &request_name_ret, &error); + DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT, &request_name_ret, &error); if (error) { gaim_debug_error("dbus", "Failed to get name: %s\n", error->message); @@ -160,7 +485,7 @@ if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - gaim_debug_error ("dbus", "Got result code %u from requesting name\n", + gaim_debug_error ("dbus", "Got result code %u from requesting name\n", request_name_ret); return FALSE; } @@ -168,6 +493,6 @@ gaim_debug_misc ("dbus", "GLib test service has name '%s'\n", DBUS_SERVICE_GAIM); gaim_debug_misc ("dbus", "GLib test service entering main loop\n"); - + return TRUE; } diff -r 2507d20c3d0b -r 2eca9ed49469 src/dbus-server.h --- a/src/dbus-server.h Thu Jul 07 15:43:05 2005 +0000 +++ b/src/dbus-server.h Thu Jul 07 15:43:48 2005 +0000 @@ -27,8 +27,28 @@ #ifndef _GAIM_DBUS_SERVER_H_ #define _GAIM_DBUS_SERVER_H_ + G_BEGIN_DECLS +/* These are the categories of codes used by gaim dbus implementation + for remote calls. In practice, they don't matter + */ +typedef enum { + DBUS_ERROR_NONE = 0, + DBUS_ERROR_NOT_FOUND = 1 +} DbusErrorCodes; + +/* Types of pointers that can be registered with the gaim dbus pointer + registration engine. See below */ +typedef enum { + DBUS_POINTER_GROUP, + DBUS_POINTER_CONTACT, + DBUS_POINTER_BUDDY, + DBUS_POINTER_CHAT, + DBUS_POINTER_ACCOUNT +} GaimDBusPointerType; + + /** * Starts the gaim DBUS server. It is responsible for handling DBUS * requests from other applications. @@ -37,6 +57,52 @@ */ gboolean dbus_server_init(void); +/** + Initializes gaim dbus pointer registration engine. + + Remote dbus applications need a way of addressing objects exposed + by gaim to the outside world. In gaim itself, these objects (such + as GaimBuddy and company) are identified by pointers. The gaim + dbus pointer registration engine converts pointers to handles and + back. + + In order for an object to participate in the scheme, it must + register itself and its type with the engine. This registration + allocates an integer id which can be resolved to the pointer and + back. + + Handles are not persistent. They are reissued every time gaim is + started. This is not good; external applications that use gaim + should work even whether gaim was restarted in the middle of the + interaction. + + Pointer registration is only a temporary solution. When GaimBuddy + and similar structures have been converted into gobjects, this + registration will be done automatically by objects themselves. + + By the way, this kind of object-handle translation should be so + common that there must be a library (maybe even glib) that + implements it. I feel a bit like reinventing the wheel here. +*/ +void gaim_dbus_init_ids(void); + +/** + Registers a typed pointer. + + @node The pointer to register. + @type Type of that pointer. + */ +void gaim_dbus_register_pointer(gpointer node, GaimDBusPointerType type); + +/** + Unregisters a pointer previously registered with + gaim_dbus_register_pointer. + + @node The pointer to register. + */ +void gaim_dbus_unregister_pointer(gpointer node); + + G_END_DECLS #endif /* _GAIM_DBUS_SERVER_H_ */ diff -r 2507d20c3d0b -r 2eca9ed49469 src/dbus-service.xml --- a/src/dbus-service.xml Thu Jul 07 15:43:05 2005 +0000 +++ b/src/dbus-service.xml Thu Jul 07 15:43:48 2005 +0000 @@ -8,6 +8,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 2507d20c3d0b -r 2eca9ed49469 src/gaim-send --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gaim-send Thu Jul 07 15:43:48 2005 +0000 @@ -0,0 +1,35 @@ +#!/bin/bash +# +# A little shell script for communicating with gaim using dbus + +METHOD_NAME=$1 + +if test -z "$METHOD_NAME" +then + cat <