| |
1 /* |
| |
2 * gaim |
| |
3 * |
| |
4 * Gaim is the legal property of its developers, whose names are too numerous |
| |
5 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
6 * source distribution. |
| |
7 * |
| |
8 * This program is free software; you can redistribute it and/or modify |
| |
9 * it under the terms of the GNU General Public License as published by |
| |
10 * the Free Software Foundation; either version 2 of the License, or |
| |
11 * (at your option) any later version. |
| |
12 * |
| |
13 * This program is distributed in the hope that it will be useful, |
| |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
16 * GNU General Public License for more details. |
| |
17 * |
| |
18 * You should have received a copy of the GNU General Public License |
| |
19 * along with this program; if not, write to the Free Software |
| |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
21 * |
| |
22 */ |
| |
23 |
| |
24 #define DBUS_API_SUBJECT_TO_CHANGE |
| |
25 |
| |
26 #include <stdio.h> |
| |
27 #include <stdlib.h> |
| |
28 #include <string.h> |
| |
29 |
| |
30 #include "account.h" |
| |
31 #include "blist.h" |
| |
32 #include "conversation.h" |
| |
33 #include "dbus-gaim.h" |
| |
34 #include "dbus-server.h" |
| |
35 #include "dbus-useful.h" |
| |
36 #include "dbus-bindings.h" |
| |
37 #include "debug.h" |
| |
38 #include "core.h" |
| |
39 #include "internal.h" |
| |
40 #include "savedstatuses.h" |
| |
41 #include "util.h" |
| |
42 #include "value.h" |
| |
43 #include "xmlnode.h" |
| |
44 |
| |
45 |
| |
46 /**************************************************************************/ |
| |
47 /** @name Gaim DBUS pointer registration mechanism */ |
| |
48 /**************************************************************************/ |
| |
49 |
| |
50 /* |
| |
51 * Here we include the list of #GAIM_DBUS_DEFINE_TYPE statements for |
| |
52 * all structs defined in gaim. This file has been generated by the |
| |
53 * #dbus-analyze-types.py script. |
| |
54 */ |
| |
55 |
| |
56 #include "dbus-types.c" |
| |
57 |
| |
58 /* |
| |
59 * The following three hashtables map are used to translate between |
| |
60 * pointers (nodes) and the corresponding handles (ids). |
| |
61 */ |
| |
62 |
| |
63 static GHashTable *map_node_id; |
| |
64 static GHashTable *map_id_node; |
| |
65 static GHashTable *map_id_type; |
| |
66 |
| |
67 static gchar *init_error; |
| |
68 |
| |
69 /** |
| |
70 * This function initializes the pointer-id traslation system. It |
| |
71 * creates the three above hashtables and defines parents of some types. |
| |
72 */ |
| |
73 void |
| |
74 gaim_dbus_init_ids(void) |
| |
75 { |
| |
76 map_id_node = g_hash_table_new(g_direct_hash, g_direct_equal); |
| |
77 map_id_type = g_hash_table_new(g_direct_hash, g_direct_equal); |
| |
78 map_node_id = g_hash_table_new(g_direct_hash, g_direct_equal); |
| |
79 |
| |
80 GAIM_DBUS_TYPE(GaimBuddy)->parent = GAIM_DBUS_TYPE(GaimBlistNode); |
| |
81 GAIM_DBUS_TYPE(GaimContact)->parent = GAIM_DBUS_TYPE(GaimBlistNode); |
| |
82 GAIM_DBUS_TYPE(GaimChat)->parent = GAIM_DBUS_TYPE(GaimBlistNode); |
| |
83 GAIM_DBUS_TYPE(GaimGroup)->parent = GAIM_DBUS_TYPE(GaimBlistNode); |
| |
84 } |
| |
85 |
| |
86 void |
| |
87 gaim_dbus_register_pointer(gpointer node, GaimDBusType *type) |
| |
88 { |
| |
89 static gint last_id = 0; |
| |
90 |
| |
91 g_return_if_fail(map_node_id); |
| |
92 g_return_if_fail(g_hash_table_lookup(map_node_id, node) == NULL); |
| |
93 |
| |
94 last_id++; |
| |
95 g_hash_table_insert(map_node_id, node, GINT_TO_POINTER(last_id)); |
| |
96 g_hash_table_insert(map_id_node, GINT_TO_POINTER(last_id), node); |
| |
97 g_hash_table_insert(map_id_type, GINT_TO_POINTER(last_id), type); |
| |
98 } |
| |
99 |
| |
100 void |
| |
101 gaim_dbus_unregister_pointer(gpointer node) |
| |
102 { |
| |
103 gpointer id = g_hash_table_lookup(map_node_id, node); |
| |
104 |
| |
105 g_hash_table_remove(map_node_id, node); |
| |
106 g_hash_table_remove(map_id_node, GINT_TO_POINTER(id)); |
| |
107 g_hash_table_remove(map_id_type, GINT_TO_POINTER(id)); |
| |
108 } |
| |
109 |
| |
110 gint |
| |
111 gaim_dbus_pointer_to_id(gpointer node) |
| |
112 { |
| |
113 gint id = GPOINTER_TO_INT(g_hash_table_lookup(map_node_id, node)); |
| |
114 if ((id == 0) && (node != NULL)) |
| |
115 { |
| |
116 gaim_debug_warning("dbus", |
| |
117 "Need to register an object with the dbus subsystem.\n"); |
| |
118 g_return_val_if_reached(0); |
| |
119 } |
| |
120 return id; |
| |
121 } |
| |
122 |
| |
123 gpointer |
| |
124 gaim_dbus_id_to_pointer(gint id, GaimDBusType *type) |
| |
125 { |
| |
126 GaimDBusType *objtype; |
| |
127 |
| |
128 objtype = (GaimDBusType*)g_hash_table_lookup(map_id_type, |
| |
129 GINT_TO_POINTER(id)); |
| |
130 |
| |
131 while (objtype != type && objtype != NULL) |
| |
132 objtype = objtype->parent; |
| |
133 |
| |
134 if (objtype == type) |
| |
135 return g_hash_table_lookup(map_id_node, GINT_TO_POINTER(id)); |
| |
136 else |
| |
137 return NULL; |
| |
138 } |
| |
139 |
| |
140 gint |
| |
141 gaim_dbus_pointer_to_id_error(gpointer ptr, DBusError *error) |
| |
142 { |
| |
143 gint id = gaim_dbus_pointer_to_id(ptr); |
| |
144 |
| |
145 if (ptr != NULL && id == 0) |
| |
146 dbus_set_error(error, "net.sf.gaim.ObjectNotFound", |
| |
147 "The return object is not mapped (this is a Gaim error)"); |
| |
148 |
| |
149 return id; |
| |
150 } |
| |
151 |
| |
152 gpointer |
| |
153 gaim_dbus_id_to_pointer_error(gint id, GaimDBusType *type, |
| |
154 const char *typename, DBusError *error) |
| |
155 { |
| |
156 gpointer ptr = gaim_dbus_id_to_pointer(id, type); |
| |
157 |
| |
158 if (ptr == NULL && id != 0) |
| |
159 dbus_set_error(error, "net.sf.gaim.InvalidHandle", |
| |
160 "%s object with ID = %i not found", typename, id); |
| |
161 |
| |
162 return ptr; |
| |
163 } |
| |
164 |
| |
165 |
| |
166 /**************************************************************************/ |
| |
167 /** @name Modified versions of some DBus functions */ |
| |
168 /**************************************************************************/ |
| |
169 |
| |
170 dbus_bool_t |
| |
171 gaim_dbus_message_get_args(DBusMessage *message, |
| |
172 DBusError *error, int first_arg_type, ...) |
| |
173 { |
| |
174 dbus_bool_t retval; |
| |
175 va_list var_args; |
| |
176 |
| |
177 va_start(var_args, first_arg_type); |
| |
178 retval = gaim_dbus_message_get_args_valist(message, error, first_arg_type, var_args); |
| |
179 va_end(var_args); |
| |
180 |
| |
181 return retval; |
| |
182 } |
| |
183 |
| |
184 dbus_bool_t |
| |
185 gaim_dbus_message_get_args_valist(DBusMessage *message, |
| |
186 DBusError *error, int first_arg_type, va_list var_args) |
| |
187 { |
| |
188 DBusMessageIter iter; |
| |
189 |
| |
190 dbus_message_iter_init(message, &iter); |
| |
191 return gaim_dbus_message_iter_get_args_valist(&iter, error, first_arg_type, var_args); |
| |
192 } |
| |
193 |
| |
194 dbus_bool_t |
| |
195 gaim_dbus_message_iter_get_args(DBusMessageIter *iter, |
| |
196 DBusError *error, int first_arg_type, ...) |
| |
197 { |
| |
198 dbus_bool_t retval; |
| |
199 va_list var_args; |
| |
200 |
| |
201 va_start(var_args, first_arg_type); |
| |
202 retval = gaim_dbus_message_iter_get_args_valist(iter, error, first_arg_type, var_args); |
| |
203 va_end(var_args); |
| |
204 |
| |
205 return retval; |
| |
206 } |
| |
207 |
| |
208 #define TYPE_IS_CONTAINER(typecode) \ |
| |
209 ((typecode) == DBUS_TYPE_STRUCT || \ |
| |
210 (typecode) == DBUS_TYPE_DICT_ENTRY || \ |
| |
211 (typecode) == DBUS_TYPE_VARIANT || \ |
| |
212 (typecode) == DBUS_TYPE_ARRAY) |
| |
213 |
| |
214 |
| |
215 dbus_bool_t |
| |
216 gaim_dbus_message_iter_get_args_valist(DBusMessageIter *iter, |
| |
217 DBusError *error, int first_arg_type, va_list var_args) |
| |
218 { |
| |
219 int spec_type, msg_type, i; |
| |
220 |
| |
221 spec_type = first_arg_type; |
| |
222 |
| |
223 for (i = 0; spec_type != DBUS_TYPE_INVALID; i++) |
| |
224 { |
| |
225 msg_type = dbus_message_iter_get_arg_type(iter); |
| |
226 |
| |
227 if (msg_type != spec_type) |
| |
228 { |
| |
229 dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, |
| |
230 "Argument %d is specified to be of type \"%i\", but " |
| |
231 "is actually of type \"%i\"\n", i, |
| |
232 spec_type, msg_type); |
| |
233 return FALSE; |
| |
234 } |
| |
235 |
| |
236 if (!TYPE_IS_CONTAINER(spec_type)) |
| |
237 { |
| |
238 gpointer ptr; |
| |
239 ptr = va_arg (var_args, gpointer); |
| |
240 dbus_message_iter_get_basic(iter, ptr); |
| |
241 } |
| |
242 else |
| |
243 { |
| |
244 DBusMessageIter *sub; |
| |
245 sub = va_arg (var_args, DBusMessageIter*); |
| |
246 dbus_message_iter_recurse(iter, sub); |
| |
247 gaim_debug_info("dbus", "subiter %p:%p\n", sub, * (gpointer*) sub); |
| |
248 break; /* for testing only! */ |
| |
249 } |
| |
250 |
| |
251 spec_type = va_arg(var_args, int); |
| |
252 if (!dbus_message_iter_next(iter) && spec_type != DBUS_TYPE_INVALID) |
| |
253 { |
| |
254 dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, |
| |
255 "Message has only %d arguments, but more were expected", i); |
| |
256 return FALSE; |
| |
257 } |
| |
258 } |
| |
259 |
| |
260 return TRUE; |
| |
261 } |
| |
262 |
| |
263 |
| |
264 |
| |
265 /**************************************************************************/ |
| |
266 /** @name Useful functions */ |
| |
267 /**************************************************************************/ |
| |
268 |
| |
269 const char *empty_to_null(const char *str) |
| |
270 { |
| |
271 if (str == NULL || str[0] == 0) |
| |
272 return NULL; |
| |
273 else |
| |
274 return str; |
| |
275 } |
| |
276 |
| |
277 const char * |
| |
278 null_to_empty(const char *s) |
| |
279 { |
| |
280 if (s) |
| |
281 return s; |
| |
282 else |
| |
283 return ""; |
| |
284 } |
| |
285 |
| |
286 dbus_int32_t * |
| |
287 gaim_dbusify_GList(GList *list, gboolean free_memory, dbus_int32_t *len) |
| |
288 { |
| |
289 dbus_int32_t *array; |
| |
290 int i; |
| |
291 GList *elem; |
| |
292 |
| |
293 *len = g_list_length(list); |
| |
294 array = g_new0(dbus_int32_t, g_list_length(list)); |
| |
295 for (i = 0, elem = list; elem != NULL; elem = elem->next, i++) |
| |
296 array[i] = gaim_dbus_pointer_to_id(elem->data); |
| |
297 |
| |
298 if (free_memory) |
| |
299 g_list_free(list); |
| |
300 |
| |
301 return array; |
| |
302 } |
| |
303 |
| |
304 dbus_int32_t * |
| |
305 gaim_dbusify_GSList(GSList *list, gboolean free_memory, dbus_int32_t *len) |
| |
306 { |
| |
307 dbus_int32_t *array; |
| |
308 int i; |
| |
309 GSList *elem; |
| |
310 |
| |
311 *len = g_slist_length(list); |
| |
312 array = g_new0(dbus_int32_t, g_slist_length(list)); |
| |
313 for (i = 0, elem = list; elem != NULL; elem = elem->next, i++) |
| |
314 array[i] = gaim_dbus_pointer_to_id(elem->data); |
| |
315 |
| |
316 if (free_memory) |
| |
317 g_slist_free(list); |
| |
318 |
| |
319 return array; |
| |
320 } |
| |
321 |
| |
322 gpointer * |
| |
323 gaim_GList_to_array(GList *list, gboolean free_memory, dbus_int32_t *len) |
| |
324 { |
| |
325 gpointer *array; |
| |
326 int i; |
| |
327 GList *elem; |
| |
328 |
| |
329 *len = g_list_length(list); |
| |
330 array = g_new0(gpointer, g_list_length(list)); |
| |
331 for (i = 0, elem = list; elem != NULL; elem = elem->next, i++) |
| |
332 array[i] = elem->data; |
| |
333 |
| |
334 if (free_memory) |
| |
335 g_list_free(list); |
| |
336 |
| |
337 return array; |
| |
338 } |
| |
339 |
| |
340 gpointer * |
| |
341 gaim_GSList_to_array(GSList *list, gboolean free_memory, dbus_int32_t *len) |
| |
342 { |
| |
343 gpointer *array; |
| |
344 int i; |
| |
345 GSList *elem; |
| |
346 |
| |
347 *len = g_slist_length(list); |
| |
348 array = g_new0(gpointer, g_slist_length(list)); |
| |
349 for (i = 0, elem = list; elem != NULL; elem = elem->next, i++) |
| |
350 array[i] = elem->data; |
| |
351 |
| |
352 if (free_memory) |
| |
353 g_slist_free(list); |
| |
354 |
| |
355 return array; |
| |
356 } |
| |
357 |
| |
358 GHashTable * |
| |
359 gaim_dbus_iter_hash_table(DBusMessageIter *iter, DBusError *error) |
| |
360 { |
| |
361 GHashTable *hash; |
| |
362 |
| |
363 /* we do not need to destroy strings because they are part of the message */ |
| |
364 hash = g_hash_table_new(g_str_hash, g_str_equal); |
| |
365 |
| |
366 do { |
| |
367 char *key, *value; |
| |
368 DBusMessageIter subiter; |
| |
369 |
| |
370 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) |
| |
371 goto error; |
| |
372 /* With all due respect to Dijkstra, |
| |
373 * this goto is for exception |
| |
374 * handling, and it is ok because it |
| |
375 * avoids duplication of the code |
| |
376 * responsible for destroying the hash |
| |
377 * table. Exceptional instructions |
| |
378 * for exceptional situations. |
| |
379 */ |
| |
380 |
| |
381 dbus_message_iter_recurse(iter, &subiter); |
| |
382 if (!gaim_dbus_message_iter_get_args(&subiter, error, |
| |
383 DBUS_TYPE_STRING, &key, |
| |
384 DBUS_TYPE_STRING, &value, |
| |
385 DBUS_TYPE_INVALID)) |
| |
386 goto error; /* same here */ |
| |
387 |
| |
388 g_hash_table_insert(hash, key, value); |
| |
389 } while (dbus_message_iter_next(iter)); |
| |
390 |
| |
391 return hash; |
| |
392 |
| |
393 error: |
| |
394 g_hash_table_destroy(hash); |
| |
395 return NULL; |
| |
396 } |
| |
397 |
| |
398 /**************************************************************/ |
| |
399 /* DBus bindings ... */ |
| |
400 /**************************************************************/ |
| |
401 |
| |
402 static DBusConnection *gaim_dbus_connection; |
| |
403 |
| |
404 DBusConnection * |
| |
405 gaim_dbus_get_connection(void) |
| |
406 { |
| |
407 return gaim_dbus_connection; |
| |
408 } |
| |
409 |
| |
410 #include "dbus-bindings.c" |
| |
411 |
| |
412 static gboolean |
| |
413 gaim_dbus_dispatch_cb(DBusConnection *connection, |
| |
414 DBusMessage *message, void *user_data) |
| |
415 { |
| |
416 const char *name; |
| |
417 GaimDBusBinding *bindings; |
| |
418 int i; |
| |
419 |
| |
420 bindings = (GaimDBusBinding*) user_data; |
| |
421 |
| |
422 if (!dbus_message_has_path(message, DBUS_PATH_GAIM)) |
| |
423 return FALSE; |
| |
424 |
| |
425 name = dbus_message_get_member(message); |
| |
426 |
| |
427 if (name == NULL) |
| |
428 return FALSE; |
| |
429 |
| |
430 if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) |
| |
431 return FALSE; |
| |
432 |
| |
433 for (i = 0; bindings[i].name; i++) |
| |
434 if (!strcmp(name, bindings[i].name)) |
| |
435 { |
| |
436 DBusMessage *reply; |
| |
437 DBusError error; |
| |
438 |
| |
439 dbus_error_init(&error); |
| |
440 |
| |
441 reply = bindings[i].handler(message, &error); |
| |
442 |
| |
443 if (reply == NULL && dbus_error_is_set(&error)) |
| |
444 reply = dbus_message_new_error (message, |
| |
445 error.name, error.message); |
| |
446 |
| |
447 if (reply != NULL) |
| |
448 { |
| |
449 dbus_connection_send(connection, reply, NULL); |
| |
450 dbus_message_unref(reply); |
| |
451 } |
| |
452 |
| |
453 return TRUE; /* return reply! */ |
| |
454 } |
| |
455 |
| |
456 return FALSE; |
| |
457 } |
| |
458 |
| |
459 |
| |
460 static const char * |
| |
461 dbus_gettext(const char **ptr) |
| |
462 { |
| |
463 const char *text = *ptr; |
| |
464 *ptr += strlen(text) + 1; |
| |
465 return text; |
| |
466 } |
| |
467 |
| |
468 static void |
| |
469 gaim_dbus_introspect_cb(GList **bindings_list, void *bindings) |
| |
470 { |
| |
471 *bindings_list = g_list_prepend(*bindings_list, bindings); |
| |
472 } |
| |
473 |
| |
474 static DBusMessage *gaim_dbus_introspect(DBusMessage *message) |
| |
475 { |
| |
476 DBusMessage *reply; |
| |
477 GString *str; |
| |
478 GList *bindings_list, *node; |
| |
479 |
| |
480 str = g_string_sized_new(0x1000); /* TODO: why this size? */ |
| |
481 |
| |
482 g_string_append(str, "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n"); |
| |
483 g_string_append_printf(str, "<node name='%s'>\n", DBUS_PATH_GAIM); |
| |
484 g_string_append_printf(str, "<interface name='%s'>\n", DBUS_INTERFACE_GAIM); |
| |
485 |
| |
486 bindings_list = NULL; |
| |
487 gaim_signal_emit(gaim_dbus_get_handle(), "dbus-introspect", &bindings_list); |
| |
488 |
| |
489 for (node = bindings_list; node; node = node->next) |
| |
490 { |
| |
491 GaimDBusBinding *bindings; |
| |
492 int i; |
| |
493 |
| |
494 bindings = (GaimDBusBinding*)node->data; |
| |
495 |
| |
496 for (i = 0; bindings[i].name; i++) |
| |
497 { |
| |
498 const char *text; |
| |
499 |
| |
500 g_string_append_printf(str, "<method name='%s'>\n", bindings[i].name); |
| |
501 |
| |
502 text = bindings[i].parameters; |
| |
503 while (*text) |
| |
504 { |
| |
505 const char *name, *direction, *type; |
| |
506 |
| |
507 direction = dbus_gettext(&text); |
| |
508 type = dbus_gettext(&text); |
| |
509 name = dbus_gettext(&text); |
| |
510 |
| |
511 g_string_append_printf(str, |
| |
512 "<arg name='%s' type='%s' direction='%s'/>\n", |
| |
513 name, type, direction); |
| |
514 } |
| |
515 g_string_append(str, "</method>\n"); |
| |
516 } |
| |
517 } |
| |
518 |
| |
519 g_string_append(str, "</interface>\n</node>\n"); |
| |
520 |
| |
521 reply = dbus_message_new_method_return(message); |
| |
522 dbus_message_append_args(reply, DBUS_TYPE_STRING, &(str->str), |
| |
523 DBUS_TYPE_INVALID); |
| |
524 g_string_free(str, TRUE); |
| |
525 g_list_free(bindings_list); |
| |
526 |
| |
527 return reply; |
| |
528 } |
| |
529 |
| |
530 static DBusHandlerResult |
| |
531 gaim_dbus_dispatch(DBusConnection *connection, |
| |
532 DBusMessage *message, void *user_data) |
| |
533 { |
| |
534 if (gaim_signal_emit_return_1(gaim_dbus_get_handle(), |
| |
535 "dbus-method-called", connection, message)) |
| |
536 return DBUS_HANDLER_RESULT_HANDLED; |
| |
537 |
| |
538 if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL && |
| |
539 dbus_message_has_path(message, DBUS_PATH_GAIM) && |
| |
540 dbus_message_has_interface(message, DBUS_INTERFACE_INTROSPECTABLE) && |
| |
541 dbus_message_has_member(message, "Introspect")) |
| |
542 { |
| |
543 DBusMessage *reply; |
| |
544 reply = gaim_dbus_introspect(message); |
| |
545 dbus_connection_send (connection, reply, NULL); |
| |
546 dbus_message_unref(reply); |
| |
547 return DBUS_HANDLER_RESULT_HANDLED; |
| |
548 } |
| |
549 |
| |
550 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
551 } |
| |
552 |
| |
553 void |
| |
554 gaim_dbus_register_bindings(void *handle, GaimDBusBinding *bindings) |
| |
555 { |
| |
556 gaim_signal_connect(gaim_dbus_get_handle(), "dbus-method-called", |
| |
557 handle, |
| |
558 GAIM_CALLBACK(gaim_dbus_dispatch_cb), |
| |
559 bindings); |
| |
560 gaim_signal_connect(gaim_dbus_get_handle(), "dbus-introspect", |
| |
561 handle, |
| |
562 GAIM_CALLBACK(gaim_dbus_introspect_cb), |
| |
563 bindings); |
| |
564 } |
| |
565 |
| |
566 static void |
| |
567 gaim_dbus_dispatch_init(void) |
| |
568 { |
| |
569 static DBusObjectPathVTable vtable = {NULL, &gaim_dbus_dispatch, NULL, NULL, NULL, NULL}; |
| |
570 DBusError error; |
| |
571 int result; |
| |
572 |
| |
573 dbus_error_init(&error); |
| |
574 gaim_dbus_connection = dbus_bus_get(DBUS_BUS_STARTER, &error); |
| |
575 |
| |
576 if (gaim_dbus_connection == NULL) |
| |
577 { |
| |
578 init_error = g_strdup_printf(N_("Failed to get connection: %s"), error.message); |
| |
579 dbus_error_free(&error); |
| |
580 return; |
| |
581 } |
| |
582 |
| |
583 /* Do not allow libdbus to exit on connection failure (This may |
| |
584 work around random exit(1) on SIGPIPE errors) */ |
| |
585 dbus_connection_set_exit_on_disconnect (gaim_dbus_connection, FALSE); |
| |
586 |
| |
587 if (!dbus_connection_register_object_path(gaim_dbus_connection, |
| |
588 DBUS_PATH_GAIM, &vtable, NULL)) |
| |
589 { |
| |
590 init_error = g_strdup_printf(N_("Failed to get name: %s"), error.name); |
| |
591 dbus_error_free(&error); |
| |
592 return; |
| |
593 } |
| |
594 |
| |
595 result = dbus_bus_request_name(gaim_dbus_connection, |
| |
596 DBUS_SERVICE_GAIM, 0, &error); |
| |
597 |
| |
598 if (dbus_error_is_set(&error)) |
| |
599 { |
| |
600 dbus_connection_unref(gaim_dbus_connection); |
| |
601 dbus_error_free(&error); |
| |
602 gaim_dbus_connection = NULL; |
| |
603 init_error = g_strdup_printf(N_("Failed to get serv name: %s"), error.name); |
| |
604 return; |
| |
605 } |
| |
606 |
| |
607 dbus_connection_setup_with_g_main(gaim_dbus_connection, NULL); |
| |
608 |
| |
609 gaim_debug_misc("dbus", "okkk\n"); |
| |
610 |
| |
611 gaim_signal_register(gaim_dbus_get_handle(), "dbus-method-called", |
| |
612 gaim_marshal_BOOLEAN__POINTER_POINTER, |
| |
613 gaim_value_new(GAIM_TYPE_BOOLEAN), 2, |
| |
614 gaim_value_new(GAIM_TYPE_POINTER), |
| |
615 gaim_value_new(GAIM_TYPE_POINTER)); |
| |
616 |
| |
617 gaim_signal_register(gaim_dbus_get_handle(), "dbus-introspect", |
| |
618 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
619 gaim_value_new_outgoing(GAIM_TYPE_POINTER)); |
| |
620 |
| |
621 GAIM_DBUS_REGISTER_BINDINGS(gaim_dbus_get_handle()); |
| |
622 } |
| |
623 |
| |
624 |
| |
625 |
| |
626 /**************************************************************************/ |
| |
627 /** @name Signals */ |
| |
628 /**************************************************************************/ |
| |
629 |
| |
630 |
| |
631 |
| |
632 static char * |
| |
633 gaim_dbus_convert_signal_name(const char *gaim_name) |
| |
634 { |
| |
635 int gaim_index, g_index; |
| |
636 char *g_name = g_new(char, strlen(gaim_name) + 1); |
| |
637 gboolean capitalize_next = TRUE; |
| |
638 |
| |
639 for (gaim_index = g_index = 0; gaim_name[gaim_index]; gaim_index++) |
| |
640 if (gaim_name[gaim_index] != '-' && gaim_name[gaim_index] != '_') |
| |
641 { |
| |
642 if (capitalize_next) |
| |
643 g_name[g_index++] = g_ascii_toupper(gaim_name[gaim_index]); |
| |
644 else |
| |
645 g_name[g_index++] = gaim_name[gaim_index]; |
| |
646 capitalize_next = FALSE; |
| |
647 } else |
| |
648 capitalize_next = TRUE; |
| |
649 |
| |
650 g_name[g_index] = 0; |
| |
651 |
| |
652 return g_name; |
| |
653 } |
| |
654 |
| |
655 #define my_arg(type) (ptr != NULL ? * ((type *)ptr) : va_arg(data, type)) |
| |
656 |
| |
657 static void |
| |
658 gaim_dbus_message_append_gaim_values(DBusMessageIter *iter, |
| |
659 int number, GaimValue **gaim_values, va_list data) |
| |
660 { |
| |
661 int i; |
| |
662 |
| |
663 for (i = 0; i < number; i++) |
| |
664 { |
| |
665 const char *str; |
| |
666 int id; |
| |
667 gint xint; |
| |
668 guint xuint; |
| |
669 gboolean xboolean; |
| |
670 gpointer ptr = NULL; |
| |
671 |
| |
672 if (gaim_value_is_outgoing(gaim_values[i])) |
| |
673 { |
| |
674 ptr = my_arg(gpointer); |
| |
675 g_return_if_fail(ptr); |
| |
676 } |
| |
677 |
| |
678 switch (gaim_values[i]->type) |
| |
679 { |
| |
680 case GAIM_TYPE_INT: |
| |
681 xint = my_arg(gint); |
| |
682 dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &xint); |
| |
683 break; |
| |
684 case GAIM_TYPE_UINT: |
| |
685 xuint = my_arg(guint); |
| |
686 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &xuint); |
| |
687 break; |
| |
688 case GAIM_TYPE_BOOLEAN: |
| |
689 xboolean = my_arg(gboolean); |
| |
690 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &xboolean); |
| |
691 break; |
| |
692 case GAIM_TYPE_STRING: |
| |
693 str = null_to_empty(my_arg(char*)); |
| |
694 if (!g_utf8_validate(str, -1, NULL)) { |
| |
695 gchar *tmp; |
| |
696 gaim_debug_error("dbus", "Invalid UTF-8 string passed to signal, emitting salvaged string!\n"); |
| |
697 tmp = gaim_utf8_salvage(str); |
| |
698 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &tmp); |
| |
699 g_free(tmp); |
| |
700 } else { |
| |
701 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); |
| |
702 } |
| |
703 break; |
| |
704 case GAIM_TYPE_SUBTYPE: /* registered pointers only! */ |
| |
705 case GAIM_TYPE_POINTER: |
| |
706 case GAIM_TYPE_OBJECT: |
| |
707 case GAIM_TYPE_BOXED: |
| |
708 id = gaim_dbus_pointer_to_id(my_arg(gpointer)); |
| |
709 dbus_message_iter_append_basic(iter, |
| |
710 (sizeof(void *) == 4) ? DBUS_TYPE_UINT32 : DBUS_TYPE_UINT64, &id); |
| |
711 break; |
| |
712 default: /* no conversion implemented */ |
| |
713 g_return_if_reached(); |
| |
714 } |
| |
715 } |
| |
716 } |
| |
717 |
| |
718 #undef my_arg |
| |
719 |
| |
720 void |
| |
721 gaim_dbus_signal_emit_gaim(const char *name, int num_values, |
| |
722 GaimValue **values, va_list vargs) |
| |
723 { |
| |
724 DBusMessage *signal; |
| |
725 DBusMessageIter iter; |
| |
726 char *newname; |
| |
727 |
| |
728 #if 0 /* this is noisy with no dbus connection */ |
| |
729 g_return_if_fail(gaim_dbus_connection); |
| |
730 #else |
| |
731 if (gaim_dbus_connection == NULL) |
| |
732 return; |
| |
733 #endif |
| |
734 |
| |
735 |
| |
736 /* |
| |
737 * The test below is a hack that prevents our "dbus-method-called" |
| |
738 * signal from being propagated to dbus. What we really need is a |
| |
739 * flag for each signal that states whether this signal is to be |
| |
740 * dbus-propagated or not. |
| |
741 */ |
| |
742 if (!strcmp(name, "dbus-method-called")) |
| |
743 return; |
| |
744 |
| |
745 newname = gaim_dbus_convert_signal_name(name); |
| |
746 signal = dbus_message_new_signal(DBUS_PATH_GAIM, DBUS_INTERFACE_GAIM, newname); |
| |
747 dbus_message_iter_init_append(signal, &iter); |
| |
748 |
| |
749 gaim_dbus_message_append_gaim_values(&iter, num_values, values, vargs); |
| |
750 |
| |
751 dbus_connection_send(gaim_dbus_connection, signal, NULL); |
| |
752 |
| |
753 g_free(newname); |
| |
754 dbus_message_unref(signal); |
| |
755 } |
| |
756 |
| |
757 const char * |
| |
758 gaim_dbus_get_init_error(void) |
| |
759 { |
| |
760 return init_error; |
| |
761 } |
| |
762 |
| |
763 void * |
| |
764 gaim_dbus_get_handle(void) |
| |
765 { |
| |
766 static int handle; |
| |
767 |
| |
768 return &handle; |
| |
769 } |
| |
770 |
| |
771 void |
| |
772 gaim_dbus_init(void) |
| |
773 { |
| |
774 gaim_dbus_init_ids(); |
| |
775 |
| |
776 g_free(init_error); |
| |
777 init_error = NULL; |
| |
778 gaim_dbus_dispatch_init(); |
| |
779 if (init_error != NULL) |
| |
780 gaim_debug_error("dbus", "%s\n", init_error); |
| |
781 } |
| |
782 |
| |
783 void |
| |
784 gaim_dbus_uninit(void) |
| |
785 { |
| |
786 /* Surely we must do SOME kind of uninitialization? */ |
| |
787 |
| |
788 g_free(init_error); |
| |
789 init_error = NULL; |
| |
790 } |