Mercurial > pidgin
comparison src/dbus-server.c @ 11067:2eca9ed49469
[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 <tailor@pidgin.im>
| author | Piotr Zielinski <zielaj> |
|---|---|
| date | Thu, 07 Jul 2005 15:43:48 +0000 |
| parents | df0241eb602c |
| children | 9bd0aac996f4 |
comparison
equal
deleted
inserted
replaced
| 11066:2507d20c3d0b | 11067:2eca9ed49469 |
|---|---|
| 29 #include <glib/gi18n.h> | 29 #include <glib/gi18n.h> |
| 30 #include <glib-object.h> | 30 #include <glib-object.h> |
| 31 #include <glib/gquark.h> | 31 #include <glib/gquark.h> |
| 32 | 32 |
| 33 #include "account.h" | 33 #include "account.h" |
| 34 #include "blist.h" | |
| 35 #include "conversation.h" | |
| 34 #include "dbus-gaim.h" | 36 #include "dbus-gaim.h" |
| 35 #include "dbus-server.h" | 37 #include "dbus-server.h" |
| 36 #include "debug.h" | 38 #include "debug.h" |
| 37 #include "core.h" | 39 #include "core.h" |
| 38 | 40 #include "value.h" |
| 39 /* GaimObject, basic definitions*/ | 41 |
| 42 /**************************************************************************/ | |
| 43 /** @name Lots of GObject crap I don't understand */ | |
| 44 /**************************************************************************/ | |
| 40 | 45 |
| 41 typedef struct GaimObject GaimObject; | 46 typedef struct GaimObject GaimObject; |
| 42 typedef struct GaimObjectClass GaimObjectClass; | 47 typedef struct GaimObjectClass GaimObjectClass; |
| 43 | 48 |
| 44 GType gaim_object_get_type(void); | 49 GType gaim_object_get_type(void); |
| 70 gaim_object_class_init(GaimObjectClass *mobject_class) | 75 gaim_object_class_init(GaimObjectClass *mobject_class) |
| 71 { | 76 { |
| 72 } | 77 } |
| 73 | 78 |
| 74 static GObject *gaim_object; | 79 static GObject *gaim_object; |
| 75 | 80 static GQuark gaim_object_error_quark; |
| 76 | 81 |
| 77 /* Implementations of remote DBUS calls */ | 82 |
| 78 | 83 |
| 84 /**************************************************************************/ | |
| 85 /** @name Utility functions */ | |
| 86 /**************************************************************************/ | |
| 87 | |
| 88 #define error_unless_1(condition, str, parameter) \ | |
| 89 if (!(condition)) { \ | |
| 90 g_set_error(error, gaim_object_error_quark, \ | |
| 91 DBUS_ERROR_NOT_FOUND, \ | |
| 92 str, parameter); \ | |
| 93 return FALSE; \ | |
| 94 } | |
| 95 | |
| 96 #define error_unless_2(condition, str, a,b) \ | |
| 97 if (!(condition)) { \ | |
| 98 g_set_error(error, gaim_object_error_quark, \ | |
| 99 DBUS_ERROR_NOT_FOUND, \ | |
| 100 str, a,b); \ | |
| 101 return FALSE; \ | |
| 102 } | |
| 103 | |
| 104 static const char* null_to_empty(const char *s) { | |
| 105 if (s) | |
| 106 return s; | |
| 107 else | |
| 108 return ""; | |
| 109 } | |
| 110 | |
| 111 typedef gboolean (*GaimNodeFilter)(GaimBlistNode *node, gpointer *user_data); | |
| 112 | |
| 113 static gboolean | |
| 114 filter_is_buddy(GaimBlistNode *node, gpointer *user_data) | |
| 115 { | |
| 116 return GAIM_BLIST_NODE_IS_BUDDY(node); | |
| 117 } | |
| 118 | |
| 119 static gboolean | |
| 120 filter_is_online_buddy(GaimBlistNode *node, | |
| 121 gpointer *user_data) | |
| 122 { | |
| 123 return GAIM_BLIST_NODE_IS_BUDDY(node) && | |
| 124 GAIM_BUDDY_IS_ONLINE((GaimBuddy *)node); | |
| 125 } | |
| 126 | |
| 127 | |
| 128 static GList* | |
| 129 get_buddy_list (GList *created_list, /**< can be NULL */ | |
| 130 GaimBlistNode *gaim_buddy_list, /**< can be NULL */ | |
| 131 GaimNodeFilter filter, | |
| 132 gpointer user_data) | |
| 133 { | |
| 134 GaimBlistNode *node; | |
| 135 | |
| 136 for(node = gaim_buddy_list; node; node = node->next) | |
| 137 { | |
| 138 if ((*filter)(node, user_data)) | |
| 139 created_list = g_list_prepend(created_list, node); | |
| 140 | |
| 141 created_list = get_buddy_list(created_list, node->child, | |
| 142 filter, user_data); | |
| 143 } | |
| 144 | |
| 145 return created_list; | |
| 146 } | |
| 147 | |
| 148 | |
| 149 | |
| 150 /**************************************************************************/ | |
| 151 /** @name Implementations of remote DBUS calls */ | |
| 152 /**************************************************************************/ | |
| 79 | 153 |
| 80 static gboolean | 154 static gboolean |
| 81 gaim_object_ping(GaimObject *obj, GError **error) | 155 gaim_object_ping(GaimObject *obj, GError **error) |
| 82 { | 156 { |
| 83 return TRUE; | 157 return TRUE; |
| 99 gaim_account_connect((GaimAccount*) cur->data); | 173 gaim_account_connect((GaimAccount*) cur->data); |
| 100 | 174 |
| 101 return TRUE; | 175 return TRUE; |
| 102 } | 176 } |
| 103 | 177 |
| 178 | |
| 179 | |
| 180 static gboolean | |
| 181 gaim_object_get_buddy_list (GaimObject *obj, GArray **out_buddy_ids, | |
| 182 GError **error) | |
| 183 { | |
| 184 GList *node; | |
| 185 GList *buddy_list = get_buddy_list(NULL, gaim_get_blist()->root, | |
| 186 &filter_is_buddy, NULL); | |
| 187 GArray *buddy_ids = g_array_new(FALSE, TRUE, sizeof(gint32));; | |
| 188 | |
| 189 buddy_list = g_list_reverse(buddy_list); | |
| 190 | |
| 191 for (node = buddy_list; node; node = node->next) { | |
| 192 gint32 id = gaim_dbus_pointer_to_id(node->data); | |
| 193 g_array_append_val(buddy_ids, id); | |
| 194 } | |
| 195 | |
| 196 g_list_free(buddy_list); | |
| 197 *out_buddy_ids = buddy_ids; | |
| 198 | |
| 199 return TRUE; | |
| 200 } | |
| 201 | |
| 202 | |
| 203 | |
| 204 | |
| 205 | |
| 206 static gboolean | |
| 207 gaim_object_find_account(GaimObject *unused, | |
| 208 const char *account_name, const char *protocol_name, | |
| 209 gint *account_id, GError **error) | |
| 210 { | |
| 211 GaimAccount *account = gaim_accounts_find(account_name, protocol_name); | |
| 212 | |
| 213 error_unless_2(account, "Account '%s' with protocol '%s' not found", | |
| 214 account_name, protocol_name); | |
| 215 | |
| 216 *account_id = gaim_dbus_pointer_to_id(account); | |
| 217 return TRUE; | |
| 218 } | |
| 219 | |
| 220 static gboolean | |
| 221 gaim_object_find_buddy(GaimObject *unused, gint account_id, const char *buddy_name, | |
| 222 gint *buddy_id, GError **error) | |
| 223 { | |
| 224 GaimAccount *account; | |
| 225 GaimBuddy *buddy; | |
| 226 | |
| 227 account = gaim_dbus_id_to_pointer(account_id, DBUS_POINTER_ACCOUNT); | |
| 228 error_unless_1(account, "Invalid account id: %i", account_id); | |
| 229 | |
| 230 buddy = gaim_find_buddy(account, buddy_name); | |
| 231 error_unless_1(account, "Buddy '%s' not found.", buddy_name); | |
| 232 | |
| 233 *buddy_id = gaim_dbus_pointer_to_id(buddy); | |
| 234 return TRUE; | |
| 235 } | |
| 236 | |
| 237 static gboolean | |
| 238 gaim_object_start_im_conversation (GaimObject *obj, gint buddy_id, GError **error) | |
| 239 { | |
| 240 GaimBuddy *buddy = (GaimBuddy*) gaim_dbus_id_to_pointer(buddy_id, | |
| 241 DBUS_POINTER_BUDDY); | |
| 242 | |
| 243 error_unless_1(buddy, "Invalid buddy id: %i", buddy_id); | |
| 244 | |
| 245 gaim_conversation_new(GAIM_CONV_IM, buddy->account, buddy->name); | |
| 246 | |
| 247 return TRUE; | |
| 248 } | |
| 249 | |
| 250 | |
| 251 | |
| 252 /**************************************************************************/ | |
| 253 /** @name Gaim DBUS property handling functions */ | |
| 254 /**************************************************************************/ | |
| 255 | |
| 256 | |
| 257 typedef struct _GaimDBusProperty { | |
| 258 char *name; | |
| 259 gint offset; | |
| 260 GaimType type; | |
| 261 } GaimDBusProperty; | |
| 262 | |
| 263 /* change GAIM_TYPE into G_TYPE */ | |
| 264 | |
| 265 static gboolean | |
| 266 gaim_dbus_get_property(GaimDBusProperty *list, int list_len, | |
| 267 gpointer data, const char *name, | |
| 268 GValue *value, GError **error) | |
| 269 { | |
| 270 int i; | |
| 271 | |
| 272 for(i=0; i<list_len; i++) { | |
| 273 if (!strcmp(list[i].name, name)) { | |
| 274 gpointer ptr = G_STRUCT_MEMBER_P(data, list[i].offset); | |
| 275 switch(list[i].type) { | |
| 276 case GAIM_TYPE_STRING: | |
| 277 g_value_init(value, G_TYPE_STRING); | |
| 278 g_value_set_string (value, g_strdup(null_to_empty(*(char **)ptr))); | |
| 279 break; | |
| 280 | |
| 281 case GAIM_TYPE_INT: | |
| 282 g_value_init(value, G_TYPE_INT); | |
| 283 g_value_set_int (value, *(int*) ptr); | |
| 284 break; | |
| 285 | |
| 286 case GAIM_TYPE_BOOLEAN: | |
| 287 g_value_init(value, G_TYPE_BOOLEAN); | |
| 288 g_value_set_int (value, *(gboolean*) ptr); | |
| 289 break; | |
| 290 | |
| 291 case GAIM_TYPE_POINTER: /* registered pointers only! */ | |
| 292 g_value_init(value, G_TYPE_INT); | |
| 293 g_value_set_int (value, | |
| 294 gaim_dbus_pointer_to_id (*(gpointer *)ptr)); | |
| 295 break; | |
| 296 default: | |
| 297 g_assert_not_reached(); | |
| 298 } | |
| 299 return TRUE; | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 g_value_init(value, G_TYPE_INT); | |
| 304 g_value_set_int(value, 0); | |
| 305 error_unless_1(FALSE, "Invalid property '%s'", name); | |
| 306 } | |
| 307 | |
| 308 | |
| 309 #define DECLARE_PROPERTY(maintype, name, type) {#name, G_STRUCT_OFFSET(maintype, name), type} | |
| 310 | |
| 311 GaimDBusProperty buddy_properties [] = { | |
| 312 DECLARE_PROPERTY(GaimBuddy, name, GAIM_TYPE_STRING), | |
| 313 DECLARE_PROPERTY(GaimBuddy, alias, GAIM_TYPE_STRING), | |
| 314 DECLARE_PROPERTY(GaimBuddy, server_alias, GAIM_TYPE_STRING), | |
| 315 DECLARE_PROPERTY(GaimBuddy, account, GAIM_TYPE_POINTER) | |
| 316 }; | |
| 317 | |
| 318 GaimDBusProperty account_properties [] = { | |
| 319 DECLARE_PROPERTY(GaimAccount, username, GAIM_TYPE_STRING), | |
| 320 DECLARE_PROPERTY(GaimAccount, alias, GAIM_TYPE_STRING), | |
| 321 DECLARE_PROPERTY(GaimAccount, user_info, GAIM_TYPE_STRING), | |
| 322 DECLARE_PROPERTY(GaimAccount, protocol_id, GAIM_TYPE_STRING), | |
| 323 }; | |
| 324 | |
| 325 GaimDBusProperty contact_properties [] = { | |
| 326 DECLARE_PROPERTY(GaimContact, alias, GAIM_TYPE_STRING), | |
| 327 DECLARE_PROPERTY(GaimContact, totalsize, GAIM_TYPE_INT), | |
| 328 DECLARE_PROPERTY(GaimContact, currentsize, GAIM_TYPE_INT), | |
| 329 DECLARE_PROPERTY(GaimContact, online, GAIM_TYPE_INT), | |
| 330 DECLARE_PROPERTY(GaimContact, priority, GAIM_TYPE_POINTER), | |
| 331 DECLARE_PROPERTY(GaimContact, priority_valid, GAIM_TYPE_BOOLEAN), | |
| 332 }; | |
| 333 | |
| 334 GaimDBusProperty group_properties [] = { | |
| 335 DECLARE_PROPERTY(GaimGroup, name, GAIM_TYPE_STRING), | |
| 336 DECLARE_PROPERTY(GaimGroup, totalsize, GAIM_TYPE_INT), | |
| 337 DECLARE_PROPERTY(GaimGroup, currentsize, GAIM_TYPE_INT), | |
| 338 DECLARE_PROPERTY(GaimGroup, online, GAIM_TYPE_INT), | |
| 339 }; | |
| 340 | |
| 341 GaimDBusProperty chat_properties [] = { | |
| 342 DECLARE_PROPERTY(GaimChat, alias, GAIM_TYPE_STRING), | |
| 343 DECLARE_PROPERTY(GaimChat, account, GAIM_TYPE_POINTER), | |
| 344 }; | |
| 345 | |
| 346 | |
| 347 #define DECLARE_PROPERTY_HANDLER(type, gaim_type) \ | |
| 348 static gboolean \ | |
| 349 gaim_object_get_##type##_property (GaimObject *unused, \ | |
| 350 gint id, const char *property_name, \ | |
| 351 GValue *value, GError **error) \ | |
| 352 { \ | |
| 353 gpointer object = gaim_dbus_id_to_pointer(id, gaim_type); \ | |
| 354 \ | |
| 355 error_unless_1(object, "Invalid " #type " id: %i", id); \ | |
| 356 \ | |
| 357 return gaim_dbus_get_property(type##_properties, \ | |
| 358 G_N_ELEMENTS(type##_properties), \ | |
| 359 object, property_name, value, error); \ | |
| 360 } | |
| 361 | |
| 362 DECLARE_PROPERTY_HANDLER(buddy, DBUS_POINTER_BUDDY) | |
| 363 DECLARE_PROPERTY_HANDLER(account, DBUS_POINTER_ACCOUNT) | |
| 364 DECLARE_PROPERTY_HANDLER(contact, DBUS_POINTER_ACCOUNT) | |
| 365 DECLARE_PROPERTY_HANDLER(group, DBUS_POINTER_ACCOUNT) | |
| 366 DECLARE_PROPERTY_HANDLER(chat, DBUS_POINTER_ACCOUNT) | |
| 367 | |
| 104 #include "dbus-server-bindings.c" | 368 #include "dbus-server-bindings.c" |
| 369 | |
| 370 | |
| 371 | |
| 372 /**************************************************************************/ | |
| 373 /** @name Gaim DBUS pointer registration mechanism */ | |
| 374 /**************************************************************************/ | |
| 375 | |
| 376 static GHashTable *map_id_node; | |
| 377 static GHashTable *map_id_type; | |
| 378 static GHashTable *map_node_id; | |
| 379 | |
| 380 void gaim_dbus_init_ids(void) { | |
| 381 map_id_node = g_hash_table_new (g_direct_hash, g_direct_equal); | |
| 382 map_id_type = g_hash_table_new (g_direct_hash, g_direct_equal); | |
| 383 map_node_id = g_hash_table_new (g_direct_hash, g_direct_equal); | |
| 384 } | |
| 385 | |
| 386 void gaim_dbus_register_pointer(gpointer node, GaimDBusPointerType type) | |
| 387 { | |
| 388 static gint last_id = 0; | |
| 389 g_assert(g_hash_table_lookup(map_node_id, node) == NULL); | |
| 390 | |
| 391 | |
| 392 last_id++; | |
| 393 g_hash_table_insert(map_node_id, node, GINT_TO_POINTER(last_id)); | |
| 394 g_hash_table_insert(map_id_node, GINT_TO_POINTER(last_id), node); | |
| 395 g_hash_table_insert(map_id_type, GINT_TO_POINTER(last_id), | |
| 396 GINT_TO_POINTER(type)); | |
| 397 } | |
| 398 | |
| 399 | |
| 400 void gaim_dbus_unregister_pointer(gpointer node) { | |
| 401 gpointer id = g_hash_table_lookup(map_node_id, node); | |
| 402 | |
| 403 g_hash_table_remove(map_node_id, node); | |
| 404 g_hash_table_remove(map_id_node, GINT_TO_POINTER(id)); | |
| 405 g_hash_table_remove(map_id_node, GINT_TO_POINTER(id)); | |
| 406 } | |
| 407 | |
| 408 gint gaim_dbus_pointer_to_id(gpointer node) { | |
| 409 gint id = GPOINTER_TO_INT(g_hash_table_lookup(map_node_id, node)); | |
| 410 g_assert(id); | |
| 411 return id; | |
| 412 } | |
| 413 | |
| 414 gpointer gaim_dbus_id_to_pointer(gint id, GaimDBusPointerType type) { | |
| 415 if (type != GPOINTER_TO_INT(g_hash_table_lookup(map_id_type, | |
| 416 GINT_TO_POINTER(id)))) | |
| 417 return NULL; | |
| 418 return g_hash_table_lookup(map_id_node, GINT_TO_POINTER(id)); | |
| 419 } | |
| 420 | |
| 421 | |
| 422 | |
| 423 /**************************************************************************/ | |
| 424 /** @name Gaim DBUS init function */ | |
| 425 /**************************************************************************/ | |
| 105 | 426 |
| 106 | 427 |
| 107 gboolean | 428 gboolean |
| 108 dbus_server_init(void) | 429 dbus_server_init(void) |
| 109 { | 430 { |
| 110 DBusGConnection *connection; | 431 DBusGConnection *connection; |
| 111 GError *error; | 432 GError *error = NULL; |
| 112 DBusGProxy *driver_proxy; | 433 DBusGProxy *driver_proxy; |
| 113 guint32 request_name_ret; | 434 guint32 request_name_ret; |
| 114 | 435 |
| 436 gaim_object_error_quark = | |
| 437 g_quark_from_static_string("org.gaim.GaimError"); | |
| 438 | |
| 115 gaim_debug_misc("dbus", "launching dbus server\n"); | 439 gaim_debug_misc("dbus", "launching dbus server\n"); |
| 116 | 440 |
| 117 /* Connect to the bus */ | 441 /* Connect to the bus */ |
| 118 | 442 |
| 119 error = NULL; | 443 error = NULL; |
| 120 connection = dbus_g_bus_get(DBUS_BUS_STARTER, &error); | 444 connection = dbus_g_bus_get(DBUS_BUS_STARTER, &error); |
| 121 | 445 |
| 122 if (connection == NULL) { | 446 if (connection == NULL) { |
| 123 g_assert(error); | 447 g_assert(error); |
| 124 gaim_debug_error("dbus", "Failed to open connection to bus: %s\n", | 448 gaim_debug_error("dbus", "Failed to open connection to bus: %s\n", |
| 125 error->message); | 449 error->message); |
| 126 g_error_free (error); | 450 g_error_free (error); |
| 127 return FALSE; | 451 return FALSE; |
| 128 } | 452 } |
| 129 | 453 |
| 130 | |
| 131 /* Instantiate the gaim dbus object and register it */ | 454 /* Instantiate the gaim dbus object and register it */ |
| 132 | 455 |
| 133 gaim_object = g_object_new (GAIM_TYPE_OBJECT, NULL); | 456 gaim_object = g_object_new (GAIM_TYPE_OBJECT, NULL); |
| 134 | 457 |
| 135 dbus_g_object_type_install_info (GAIM_TYPE_OBJECT, | 458 |
| 459 dbus_g_object_type_install_info (GAIM_TYPE_OBJECT, | |
| 136 &dbus_glib_gaim_object_object_info); | 460 &dbus_glib_gaim_object_object_info); |
| 137 | 461 |
| 138 dbus_g_connection_register_g_object (connection, DBUS_PATH_GAIM, | 462 dbus_g_connection_register_g_object (connection, DBUS_PATH_GAIM, |
| 139 gaim_object); | 463 gaim_object); |
| 140 | 464 |
| 141 | 465 |
| 142 /* Obtain a proxy for the DBus object */ | 466 /* Obtain a proxy for the DBus object */ |
| 143 | 467 |
| 144 driver_proxy = dbus_g_proxy_new_for_name (connection, | 468 driver_proxy = dbus_g_proxy_new_for_name (connection, |
| 145 DBUS_SERVICE_DBUS, | 469 DBUS_SERVICE_DBUS, |
| 146 DBUS_PATH_DBUS, | 470 DBUS_PATH_DBUS, |
| 147 DBUS_INTERFACE_DBUS); | 471 DBUS_INTERFACE_DBUS); |
| 148 | 472 |
| 473 g_assert(driver_proxy); | |
| 149 | 474 |
| 150 /* Test whether the registration was successful */ | 475 /* Test whether the registration was successful */ |
| 151 | 476 |
| 152 org_freedesktop_DBus_request_name(driver_proxy, DBUS_SERVICE_GAIM, | 477 org_freedesktop_DBus_request_name(driver_proxy, DBUS_SERVICE_GAIM, |
| 153 0, &request_name_ret, &error); | 478 DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT, &request_name_ret, &error); |
| 154 | 479 |
| 155 if (error) { | 480 if (error) { |
| 156 gaim_debug_error("dbus", "Failed to get name: %s\n", error->message); | 481 gaim_debug_error("dbus", "Failed to get name: %s\n", error->message); |
| 157 g_error_free (error); | 482 g_error_free (error); |
| 158 return FALSE; | 483 return FALSE; |
| 159 } | 484 } |
| 160 | 485 |
| 161 if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) | 486 if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) |
| 162 { | 487 { |
| 163 gaim_debug_error ("dbus", "Got result code %u from requesting name\n", | 488 gaim_debug_error ("dbus", "Got result code %u from requesting name\n", |
| 164 request_name_ret); | 489 request_name_ret); |
| 165 return FALSE; | 490 return FALSE; |
| 166 } | 491 } |
| 167 | 492 |
| 168 gaim_debug_misc ("dbus", "GLib test service has name '%s'\n", | 493 gaim_debug_misc ("dbus", "GLib test service has name '%s'\n", |
| 169 DBUS_SERVICE_GAIM); | 494 DBUS_SERVICE_GAIM); |
| 170 gaim_debug_misc ("dbus", "GLib test service entering main loop\n"); | 495 gaim_debug_misc ("dbus", "GLib test service entering main loop\n"); |
| 171 | 496 |
| 172 return TRUE; | 497 return TRUE; |
| 173 } | 498 } |
