1 /* dbus-plugin.c - xchat plugin for remote access using D-Bus
2 * Copyright (C) 2006 Claessens Xavier
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
22 #define DBUS_API_SUBJECT_TO_CHANGE
25 #include <dbus/dbus-glib.h>
26 #include <dbus/dbus-glib-lowlevel.h>
27 #include <glib/gi18n.h>
28 #include "../xchat-plugin.h"
30 #define PNAME _("remote access")
31 #define PDESC _("plugin for remote access using DBUS")
34 #define DBUS_SERVICE "org.xchat.service"
35 #define DBUS_OBJECT_PATH "/org/xchat"
37 static xchat_plugin
*ph
;
38 static guint last_context_id
= 0;
39 static GList
*contexts
= NULL
;
40 static GHashTable
*clients
= NULL
;
41 static DBusGConnection
*connection
;
43 typedef struct RemoteObject RemoteObject
;
44 typedef struct RemoteObjectClass RemoteObjectClass
;
46 GType
Remote_object_get_type (void);
54 xchat_context
*context
;
62 struct RemoteObjectClass
78 xchat_context
*context
;
90 static guint signals
[LAST_SIGNAL
] = { 0 };
92 #define REMOTE_TYPE_OBJECT (remote_object_get_type ())
93 #define REMOTE_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), REMOTE_TYPE_OBJECT, RemoteObject))
94 #define REMOTE_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), REMOTE_TYPE_OBJECT, RemoteObjectClass))
95 #define REMOTE_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), REMOTE_TYPE_OBJECT))
96 #define REMOTE_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), REMOTE_TYPE_OBJECT))
97 #define REMOTE_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), REMOTE_TYPE_OBJECT, RemoteObjectClass))
98 #define REMOTE_OBJECT_ERROR (remote_object_error_quark ())
99 #define REMOTE_TYPE_ERROR (remote_object_error_get_type ())
101 G_DEFINE_TYPE (RemoteObject
, remote_object
, G_TYPE_OBJECT
)
103 /* Available services */
105 static gboolean
remote_object_connect (RemoteObject
*obj
,
106 const char *filename
,
110 DBusGMethodInvocation
*context
);
112 static gboolean
remote_object_disconnect (RemoteObject
*obj
,
113 DBusGMethodInvocation
*context
);
115 static gboolean
remote_object_command (RemoteObject
*obj
,
119 static gboolean
remote_object_print (RemoteObject
*obj
,
123 static gboolean
remote_object_find_context (RemoteObject
*obj
,
129 static gboolean
remote_object_get_context (RemoteObject
*obj
,
133 static gboolean
remote_object_set_context (RemoteObject
*obj
,
138 static gboolean
remote_object_print (RemoteObject
*obj
,
142 static gboolean
remote_object_get_info (RemoteObject
*obj
,
147 static gboolean
remote_object_get_prefs (RemoteObject
*obj
,
154 static gboolean
remote_object_hook_command (RemoteObject
*obj
,
157 const char *help_text
,
162 static gboolean
remote_object_hook_server (RemoteObject
*obj
,
169 static gboolean
remote_object_hook_print (RemoteObject
*obj
,
176 static gboolean
remote_object_unhook (RemoteObject
*obj
,
180 static gboolean
remote_object_list_get (RemoteObject
*obj
,
185 static gboolean
remote_object_list_next (RemoteObject
*obj
,
190 static gboolean
remote_object_list_str (RemoteObject
*obj
,
196 static gboolean
remote_object_list_int (RemoteObject
*obj
,
202 static gboolean
remote_object_list_time (RemoteObject
*obj
,
208 static gboolean
remote_object_list_fields (RemoteObject
*obj
,
213 static gboolean
remote_object_list_free (RemoteObject
*obj
,
217 static gboolean
remote_object_emit_print (RemoteObject
*obj
,
218 const char *event_name
,
223 static gboolean
remote_object_nickcmp (RemoteObject
*obj
,
229 static gboolean
remote_object_strip (RemoteObject
*obj
,
236 static gboolean
remote_object_send_modes (RemoteObject
*obj
,
237 const char *targets
[],
243 #include "remote-object-glue.h"
244 #include "marshallers.h"
246 /* Useful functions */
248 static char** build_list (char *word
[]);
249 static guint
context_list_find_id (xchat_context
*context
);
250 static xchat_context
* context_list_find_context (guint id
);
255 hook_info_destroy (gpointer data
)
257 HookInfo
*info
= (HookInfo
*)data
;
262 xchat_unhook (ph
, info
->hook
);
267 list_info_destroy (gpointer data
)
269 xchat_list_free (ph
, (xchat_list
*)data
);
273 remote_object_finalize (GObject
*obj
)
275 RemoteObject
*self
= (RemoteObject
*)obj
;
277 g_hash_table_destroy (self
->lists
);
278 g_hash_table_destroy (self
->hooks
);
279 g_free (self
->dbus_path
);
280 g_free (self
->filename
);
281 xchat_plugingui_remove (ph
, self
->handle
);
283 G_OBJECT_CLASS (remote_object_parent_class
)->finalize (obj
);
287 remote_object_init (RemoteObject
*obj
)
290 g_hash_table_new_full (g_int_hash
,
296 g_hash_table_new_full (g_int_hash
,
300 obj
->dbus_path
= NULL
;
301 obj
->filename
= NULL
;
302 obj
->last_hook_id
= 0;
303 obj
->last_list_id
= 0;
304 obj
->context
= xchat_get_context (ph
);
308 remote_object_class_init (RemoteObjectClass
*klass
)
310 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
312 gobject_class
->finalize
= remote_object_finalize
;
314 signals
[SERVER_SIGNAL
] =
315 g_signal_new ("server_signal",
316 G_OBJECT_CLASS_TYPE (klass
),
320 g_cclosure_user_marshal_VOID__POINTER_POINTER_UINT_UINT
,
322 4, G_TYPE_STRV
, G_TYPE_STRV
, G_TYPE_UINT
, G_TYPE_UINT
);
324 signals
[COMMAND_SIGNAL
] =
325 g_signal_new ("command_signal",
326 G_OBJECT_CLASS_TYPE (klass
),
330 g_cclosure_user_marshal_VOID__POINTER_POINTER_UINT_UINT
,
332 4, G_TYPE_STRV
, G_TYPE_STRV
, G_TYPE_UINT
, G_TYPE_UINT
);
334 signals
[PRINT_SIGNAL
] =
335 g_signal_new ("print_signal",
336 G_OBJECT_CLASS_TYPE (klass
),
340 g_cclosure_user_marshal_VOID__POINTER_POINTER_UINT_UINT
,
342 3, G_TYPE_STRV
, G_TYPE_UINT
, G_TYPE_UINT
);
344 signals
[UNLOAD_SIGNAL
] =
345 g_signal_new ("unload_signal",
346 G_OBJECT_CLASS_TYPE (klass
),
350 g_cclosure_marshal_VOID__VOID
,
355 /* Implementation of services */
358 remote_object_connect (RemoteObject
*obj
,
359 const char *filename
,
363 DBusGMethodInvocation
*context
)
365 static guint count
= 0;
367 RemoteObject
*remote_object
;
369 sender
= dbus_g_method_get_sender (context
);
370 remote_object
= g_hash_table_lookup (clients
, sender
);
371 if (remote_object
!= NULL
) {
372 dbus_g_method_return (context
, remote_object
->dbus_path
);
376 path
= g_strdup_printf (DBUS_OBJECT_PATH
"/%d", count
++);
377 remote_object
= g_object_new (REMOTE_TYPE_OBJECT
, NULL
);
378 remote_object
->dbus_path
= path
;
379 remote_object
->filename
= g_path_get_basename (filename
);
380 remote_object
->handle
= xchat_plugingui_add (ph
,
381 remote_object
->filename
,
385 dbus_g_connection_register_g_object (connection
,
387 G_OBJECT (remote_object
));
389 g_hash_table_insert (clients
,
393 dbus_g_method_return (context
, path
);
398 remote_object_disconnect (RemoteObject
*obj
,
399 DBusGMethodInvocation
*context
)
403 sender
= dbus_g_method_get_sender (context
);
404 g_hash_table_remove (clients
, sender
);
407 dbus_g_method_return (context
);
412 remote_object_command (RemoteObject
*obj
,
416 if (xchat_set_context (ph
, obj
->context
)) {
417 xchat_command (ph
, command
);
423 remote_object_print (RemoteObject
*obj
,
427 if (xchat_set_context (ph
, obj
->context
)) {
428 xchat_print (ph
, text
);
434 remote_object_find_context (RemoteObject
*obj
,
440 xchat_context
*context
;
442 if (*server
== '\0') {
445 if (*channel
== '\0') {
448 context
= xchat_find_context (ph
, server
, channel
);
449 *ret_id
= context_list_find_id (context
);
455 remote_object_get_context (RemoteObject
*obj
,
459 *ret_id
= context_list_find_id (obj
->context
);
464 remote_object_set_context (RemoteObject
*obj
,
469 xchat_context
*context
;
471 context
= context_list_find_context (id
);
472 if (context
== NULL
) {
476 obj
->context
= context
;
483 remote_object_get_info (RemoteObject
*obj
,
488 /* win_ptr is a GtkWindow* casted to char* and will crash
489 * D-Bus if we send it as a string */
490 if (!xchat_set_context (ph
, obj
->context
) ||
491 g_str_equal (id
, "win_ptr")) {
495 *ret_info
= g_strdup (xchat_get_info (ph
, id
));
500 remote_object_get_prefs (RemoteObject
*obj
,
509 if (!xchat_set_context (ph
, obj
->context
)) {
513 *ret_type
= xchat_get_prefs (ph
, name
, &str
, ret_int
);
514 *ret_str
= g_strdup (str
);
520 server_hook_cb (char *word
[],
524 HookInfo
*info
= (HookInfo
*)userdata
;
528 arg1
= build_list (word
+ 1);
529 arg2
= build_list (word_eol
+ 1);
530 info
->obj
->context
= xchat_get_context (ph
);
531 g_signal_emit (info
->obj
,
532 signals
[SERVER_SIGNAL
],
534 arg1
, arg2
, info
->id
,
535 context_list_find_id (info
->obj
->context
));
539 return info
->return_value
;
543 command_hook_cb (char *word
[],
547 HookInfo
*info
= (HookInfo
*)userdata
;
551 arg1
= build_list (word
+ 1);
552 arg2
= build_list (word_eol
+ 1);
553 info
->obj
->context
= xchat_get_context (ph
);
554 g_signal_emit (info
->obj
,
555 signals
[COMMAND_SIGNAL
],
557 arg1
, arg2
, info
->id
,
558 context_list_find_id (info
->obj
->context
));
562 return info
->return_value
;
566 print_hook_cb (char *word
[],
569 HookInfo
*info
= (HookInfo
*)userdata
;
572 arg1
= build_list (word
+ 1);
573 info
->obj
->context
= xchat_get_context (ph
);
574 g_signal_emit (info
->obj
,
575 signals
[PRINT_SIGNAL
],
578 context_list_find_id (info
->obj
->context
));
581 return info
->return_value
;
585 remote_object_hook_command (RemoteObject
*obj
,
588 const char *help_text
,
595 info
= g_new0 (HookInfo
, 1);
597 info
->return_value
= return_value
;
598 info
->id
= ++obj
->last_hook_id
;
599 info
->hook
= xchat_hook_command (ph
,
605 g_hash_table_insert (obj
->hooks
, &info
->id
, info
);
612 remote_object_hook_server (RemoteObject
*obj
,
621 info
= g_new0 (HookInfo
, 1);
623 info
->return_value
= return_value
;
624 info
->id
= ++obj
->last_hook_id
;
625 info
->hook
= xchat_hook_server (ph
,
630 g_hash_table_insert (obj
->hooks
, &info
->id
, info
);
637 remote_object_hook_print (RemoteObject
*obj
,
646 info
= g_new0 (HookInfo
, 1);
648 info
->return_value
= return_value
;
649 info
->id
= ++obj
->last_hook_id
;
650 info
->hook
= xchat_hook_print (ph
,
655 g_hash_table_insert (obj
->hooks
, &info
->id
, info
);
662 remote_object_unhook (RemoteObject
*obj
,
666 g_hash_table_remove (obj
->hooks
, &id
);
671 remote_object_list_get (RemoteObject
*obj
,
679 if (!xchat_set_context (ph
, obj
->context
)) {
683 xlist
= xchat_list_get (ph
, name
);
688 id
= g_new (guint
, 1);
689 *id
= ++obj
->last_list_id
;
691 g_hash_table_insert (obj
->lists
,
699 remote_object_list_next (RemoteObject
*obj
,
706 xlist
= g_hash_table_lookup (obj
->lists
, &id
);
711 *ret
= xchat_list_next (ph
, xlist
);
717 remote_object_list_str (RemoteObject
*obj
,
725 xlist
= g_hash_table_lookup (obj
->lists
, &id
);
726 if (xlist
== NULL
&& !xchat_set_context (ph
, obj
->context
)) {
730 if (g_str_equal (name
, "context")) {
734 *ret_str
= g_strdup (xchat_list_str (ph
, xlist
, name
));
740 remote_object_list_int (RemoteObject
*obj
,
748 xlist
= g_hash_table_lookup (obj
->lists
, &id
);
749 if (xlist
== NULL
&& !xchat_set_context (ph
, obj
->context
)) {
753 if (g_str_equal (name
, "context")) {
754 xchat_context
*context
;
755 context
= (xchat_context
*)xchat_list_str (ph
, xlist
, name
);
756 *ret_int
= context_list_find_id (context
);
758 *ret_int
= xchat_list_int (ph
, xlist
, name
);
765 remote_object_list_time (RemoteObject
*obj
,
773 xlist
= g_hash_table_lookup (obj
->lists
, &id
);
775 *ret_time
= (guint64
) -1;
778 *ret_time
= xchat_list_time (ph
, xlist
, name
);
784 remote_object_list_fields (RemoteObject
*obj
,
789 *ret
= g_strdupv ((char**)xchat_list_fields (ph
, name
));
791 *ret
= g_new0 (char*, 1);
797 remote_object_list_free (RemoteObject
*obj
,
801 g_hash_table_remove (obj
->lists
, &id
);
806 remote_object_emit_print (RemoteObject
*obj
,
807 const char *event_name
,
812 const char *argv
[4] = {NULL
, NULL
, NULL
, NULL
};
815 for (i
= 0; i
< 4 && args
[i
] != NULL
; i
++) {
819 *ret
= xchat_set_context (ph
, obj
->context
);
821 *ret
= xchat_emit_print (ph
, event_name
, argv
[0], argv
[1],
829 remote_object_nickcmp (RemoteObject
*obj
,
835 xchat_set_context (ph
, obj
->context
);
836 *ret
= xchat_nickcmp (ph
, nick1
, nick2
);
841 remote_object_strip (RemoteObject
*obj
,
848 *ret_str
= xchat_strip (ph
, str
, len
, flag
);
853 remote_object_send_modes (RemoteObject
*obj
,
854 const char *targets
[],
860 if (xchat_set_context (ph
, obj
->context
)) {
861 xchat_send_modes (ph
, targets
,
862 g_strv_length ((char**)targets
),
872 name_owner_changed (DBusGProxy
*driver_proxy
,
874 const char *old_owner
,
875 const char *new_owner
,
878 if (*new_owner
== '\0') {
879 /* this name has vanished */
880 g_hash_table_remove (clients
, name
);
888 RemoteObject
*remote
;
889 guint request_name_result
;
890 GError
*error
= NULL
;
892 dbus_g_object_type_install_info (REMOTE_TYPE_OBJECT
,
893 &dbus_glib_remote_object_object_info
);
895 connection
= dbus_g_bus_get (DBUS_BUS_SESSION
, &error
);
896 if (connection
== NULL
) {
897 xchat_printf (ph
, _("Couldn't connect to session bus: %s\n"),
899 g_error_free (error
);
903 proxy
= dbus_g_proxy_new_for_name (connection
,
906 DBUS_INTERFACE_DBUS
);
908 if (!dbus_g_proxy_call (proxy
, "RequestName", &error
,
909 G_TYPE_STRING
, DBUS_SERVICE
,
910 G_TYPE_UINT
, DBUS_NAME_FLAG_ALLOW_REPLACEMENT
,
912 G_TYPE_UINT
, &request_name_result
,
914 xchat_printf (ph
, _("Failed to acquire %s: %s\n"),
917 g_error_free (error
);
922 dbus_g_proxy_add_signal (proxy
, "NameOwnerChanged",
927 dbus_g_proxy_connect_signal (proxy
, "NameOwnerChanged",
928 G_CALLBACK (name_owner_changed
),
931 remote
= g_object_new (REMOTE_TYPE_OBJECT
, NULL
);
932 dbus_g_connection_register_g_object (connection
,
933 DBUS_OBJECT_PATH
"/Remote",
939 /* xchat_plugin stuffs */
942 build_list (char *word
[])
952 while (word
[num
] && word
[num
][0]) {
956 result
= g_new0 (char*, num
+ 1);
957 for (i
= 0; i
< num
; i
++) {
958 result
[i
] = g_strdup (word
[i
]);
965 context_list_find_id (xchat_context
*context
)
969 for (l
= contexts
; l
!= NULL
; l
= l
->next
) {
970 if (((ContextInfo
*)l
->data
)->context
== context
) {
971 return ((ContextInfo
*)l
->data
)->id
;
978 static xchat_context
*
979 context_list_find_context (guint id
)
983 for (l
= contexts
; l
!= NULL
; l
= l
->next
) {
984 if (((ContextInfo
*)l
->data
)->id
== id
) {
985 return ((ContextInfo
*)l
->data
)->context
;
993 open_context_cb (char *word
[],
998 info
= g_new0 (ContextInfo
, 1);
999 info
->id
= ++last_context_id
;
1000 info
->context
= xchat_get_context (ph
);
1001 contexts
= g_list_prepend (contexts
, info
);
1003 return XCHAT_EAT_NONE
;
1007 close_context_cb (char *word
[],
1011 xchat_context
*context
= xchat_get_context (ph
);
1013 for (l
= contexts
; l
!= NULL
; l
= l
->next
) {
1014 if (((ContextInfo
*)l
->data
)->context
== context
) {
1016 contexts
= g_list_delete_link (contexts
, l
);
1021 return XCHAT_EAT_NONE
;
1025 clients_find_filename_foreach (gpointer key
,
1029 RemoteObject
*obj
= REMOTE_OBJECT (value
);
1030 return g_str_equal (obj
->filename
, (char*)user_data
);
1034 unload_plugin_cb (char *word
[], char *word_eol
[], void *userdata
)
1038 obj
= g_hash_table_find (clients
,
1039 clients_find_filename_foreach
,
1043 signals
[UNLOAD_SIGNAL
],
1045 return XCHAT_EAT_ALL
;
1048 return XCHAT_EAT_NONE
;
1052 dbus_plugin_init (xchat_plugin
*plugin_handle
,
1055 char **plugin_version
,
1059 *plugin_name
= PNAME
;
1060 *plugin_desc
= PDESC
;
1061 *plugin_version
= PVERSION
;
1064 /*xchat_printf (ph, _("%s loaded successfully!\n"), PNAME);*/
1066 clients
= g_hash_table_new_full (g_str_hash
,
1071 xchat_hook_print (ph
, "Open Context",
1076 xchat_hook_print (ph
, "Close Context",
1081 xchat_hook_command (ph
, "unload",
1083 unload_plugin_cb
, NULL
, NULL
);