summary refs log tree commit diff stats
path: root/src/common/dbus/dbus-plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/dbus/dbus-plugin.c')
-rw-r--r--src/common/dbus/dbus-plugin.c1087
1 files changed, 1087 insertions, 0 deletions
diff --git a/src/common/dbus/dbus-plugin.c b/src/common/dbus/dbus-plugin.c
new file mode 100644
index 00000000..012812cc
--- /dev/null
+++ b/src/common/dbus/dbus-plugin.c
@@ -0,0 +1,1087 @@
+/* dbus-plugin.c - xchat plugin for remote access using D-Bus
+ * Copyright (C) 2006 Claessens Xavier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Claessens Xavier
+ * xclaesse@gmail.com
+ */
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+
+#include <config.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib/gi18n.h>
+#include "../xchat-plugin.h"
+
+#define PNAME _("remote access")
+#define PDESC _("plugin for remote access using DBUS")
+#define PVERSION ""
+
+#define DBUS_SERVICE "org.xchat.service"
+#define DBUS_OBJECT_PATH "/org/xchat"
+
+static xchat_plugin *ph;
+static guint last_context_id = 0;
+static GList *contexts = NULL;
+static GHashTable *clients = NULL;
+static DBusGConnection *connection;
+
+typedef struct RemoteObject RemoteObject;
+typedef struct RemoteObjectClass RemoteObjectClass;
+
+GType Remote_object_get_type (void);
+
+struct RemoteObject
+{
+	GObject parent;
+
+	guint last_hook_id;
+	guint last_list_id;
+	xchat_context *context;
+	char *dbus_path;
+	char *filename;
+	GHashTable *hooks;
+	GHashTable *lists;
+	void *handle;
+};
+
+struct RemoteObjectClass
+{
+	GObjectClass parent;
+};
+
+typedef struct 
+{
+	guint id;
+	int return_value;
+	xchat_hook *hook;
+	RemoteObject *obj;
+} HookInfo;
+
+typedef struct
+{
+	guint id;
+	xchat_context *context;
+} ContextInfo;
+
+enum
+{
+	SERVER_SIGNAL,
+	COMMAND_SIGNAL,
+	PRINT_SIGNAL,
+	UNLOAD_SIGNAL,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+#define REMOTE_TYPE_OBJECT              (remote_object_get_type ())
+#define REMOTE_OBJECT(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), REMOTE_TYPE_OBJECT, RemoteObject))
+#define REMOTE_OBJECT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), REMOTE_TYPE_OBJECT, RemoteObjectClass))
+#define REMOTE_IS_OBJECT(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), REMOTE_TYPE_OBJECT))
+#define REMOTE_IS_OBJECT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), REMOTE_TYPE_OBJECT))
+#define REMOTE_OBJECT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), REMOTE_TYPE_OBJECT, RemoteObjectClass))
+#define REMOTE_OBJECT_ERROR (remote_object_error_quark ())
+#define REMOTE_TYPE_ERROR (remote_object_error_get_type ()) 
+
+G_DEFINE_TYPE (RemoteObject, remote_object, G_TYPE_OBJECT)
+
+/* Available services */
+
+static gboolean		remote_object_connect		(RemoteObject *obj,
+							 const char *filename,
+							 const char *name,
+							 const char *desc,
+							 const char *version,
+							 DBusGMethodInvocation *context);
+
+static gboolean		remote_object_disconnect	(RemoteObject *obj,
+							 DBusGMethodInvocation *context);
+
+static gboolean		remote_object_command		(RemoteObject *obj,
+							 const char *command,
+							 GError **error);
+
+static gboolean		remote_object_print		(RemoteObject *obj,
+							 const char *text,
+							 GError **error);
+
+static gboolean		remote_object_find_context	(RemoteObject *obj,
+							 const char *server,
+							 const char *channel,
+							 guint *ret_id,
+							 GError **error);
+
+static gboolean		remote_object_get_context	(RemoteObject *obj,
+							 guint *ret_id,
+							 GError **error);
+
+static gboolean		remote_object_set_context	(RemoteObject *obj,
+							 guint id,
+							 gboolean *ret,
+							 GError **error);
+
+static gboolean		remote_object_print		(RemoteObject *obj,
+							 const char *text,
+							 GError **error);
+
+static gboolean		remote_object_get_info		(RemoteObject *obj,
+							 const char *id,
+							 char **ret_info,
+							 GError **error);
+
+static gboolean		remote_object_get_prefs		(RemoteObject *obj,
+							 const char *name,
+							 int *ret_type,
+							 char **ret_str,
+							 int *ret_int,
+							 GError **error);
+
+static gboolean		remote_object_hook_command	(RemoteObject *obj,
+							 const char *name,
+							 int pri,
+							 const char *help_text,
+							 int return_value,
+							 guint *ret_id,
+							 GError **error);
+
+static gboolean		remote_object_hook_server	(RemoteObject *obj,
+							 const char *name,
+							 int pri,
+							 int return_value,
+							 guint *ret_id,
+							 GError **error);
+
+static gboolean		remote_object_hook_print	(RemoteObject *obj,
+							 const char *name,
+							 int pri,
+							 int return_value,
+							 guint *ret_id,
+							 GError **error);
+
+static gboolean		remote_object_unhook		(RemoteObject *obj,
+							 guint id,
+							 GError **error);
+
+static gboolean		remote_object_list_get		(RemoteObject *obj,
+							 const char *name,
+							 guint *ret_id,
+							 GError **error);
+
+static gboolean		remote_object_list_next		(RemoteObject *obj,
+							 guint id,
+							 gboolean *ret,
+							 GError **error);
+
+static gboolean		remote_object_list_str		(RemoteObject *obj,
+							 guint id,
+							 const char *name,
+							 char **ret_str,
+							 GError **error);
+
+static gboolean		remote_object_list_int		(RemoteObject *obj,
+							 guint id,
+							 const char *name,
+							 int *ret_int,
+							 GError **error);
+
+static gboolean		remote_object_list_time		(RemoteObject *obj,
+							 guint id,
+							 const char *name,
+							 guint64 *ret_time,
+							 GError **error);
+
+static gboolean		remote_object_list_fields	(RemoteObject *obj,
+							 const char *name,
+							 char ***ret,
+							 GError **error);
+
+static gboolean		remote_object_list_free		(RemoteObject *obj,
+							 guint id,
+							 GError **error);
+
+static gboolean		remote_object_emit_print	(RemoteObject *obj,
+							 const char *event_name,
+							 const char *args[],
+							 gboolean *ret,
+							 GError **error);
+
+static gboolean		remote_object_nickcmp		(RemoteObject *obj,
+							 const char *nick1,
+							 const char *nick2,
+							 int *ret,
+							 GError **error);
+
+static gboolean		remote_object_strip		(RemoteObject *obj,
+							 const char *str,
+							 int len,
+							 int flag,
+							 char **ret_str,
+							 GError **error);
+
+static gboolean		remote_object_send_modes	(RemoteObject *obj,
+							 const char *targets[],
+							 int modes_per_line,
+							 char sign,
+							 char mode,
+							 GError **error);
+
+#include "remote-object-glue.h"
+#include "marshallers.h"
+
+/* Useful functions */
+
+static char**		build_list			(char *word[]);
+static guint		context_list_find_id		(xchat_context *context);
+static xchat_context*	context_list_find_context	(guint id);
+
+/* Remote Object */
+
+static void
+hook_info_destroy (gpointer data)
+{
+	HookInfo *info = (HookInfo*)data;
+
+	if (info == NULL) {
+		return;
+	}
+	xchat_unhook (ph, info->hook);
+	g_free (info);
+}
+
+static void
+list_info_destroy (gpointer data)
+{
+	xchat_list_free (ph, (xchat_list*)data);
+}
+
+static void
+remote_object_finalize (GObject *obj)
+{
+	RemoteObject *self = (RemoteObject*)obj;
+
+	g_hash_table_destroy (self->lists);
+	g_hash_table_destroy (self->hooks);
+	g_free (self->dbus_path);
+	g_free (self->filename);
+	xchat_plugingui_remove (ph, self->handle);
+
+	G_OBJECT_CLASS (remote_object_parent_class)->finalize (obj);
+}
+
+static void
+remote_object_init (RemoteObject *obj)
+{
+	obj->hooks =
+		g_hash_table_new_full (g_int_hash,
+				       g_int_equal,
+				       NULL,
+				       hook_info_destroy);
+
+	obj->lists =
+		g_hash_table_new_full (g_int_hash,
+				       g_int_equal,
+				       g_free,
+				       list_info_destroy);
+	obj->dbus_path = NULL;
+	obj->filename = NULL;
+	obj->last_hook_id = 0;
+	obj->last_list_id = 0;
+	obj->context = xchat_get_context (ph);
+}
+
+static void
+remote_object_class_init (RemoteObjectClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->finalize = remote_object_finalize;
+
+	signals[SERVER_SIGNAL] =
+		g_signal_new ("server_signal",
+			      G_OBJECT_CLASS_TYPE (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_user_marshal_VOID__POINTER_POINTER_UINT_UINT,
+			      G_TYPE_NONE,
+			      4, G_TYPE_STRV, G_TYPE_STRV, G_TYPE_UINT, G_TYPE_UINT);
+
+	signals[COMMAND_SIGNAL] =
+		g_signal_new ("command_signal",
+			      G_OBJECT_CLASS_TYPE (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_user_marshal_VOID__POINTER_POINTER_UINT_UINT,
+			      G_TYPE_NONE,
+			      4, G_TYPE_STRV, G_TYPE_STRV, G_TYPE_UINT, G_TYPE_UINT);
+
+	signals[PRINT_SIGNAL] =
+		g_signal_new ("print_signal",
+			      G_OBJECT_CLASS_TYPE (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_user_marshal_VOID__POINTER_POINTER_UINT_UINT,
+			      G_TYPE_NONE,
+			      3, G_TYPE_STRV, G_TYPE_UINT, G_TYPE_UINT);
+
+	signals[UNLOAD_SIGNAL] =
+		g_signal_new ("unload_signal",
+			      G_OBJECT_CLASS_TYPE (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+}
+
+/* Implementation of services */
+
+static gboolean
+remote_object_connect (RemoteObject *obj,
+		       const char *filename,
+		       const char *name,
+		       const char *desc,
+		       const char *version,
+		       DBusGMethodInvocation *context)
+{
+	static guint count = 0;
+	char *sender, *path;
+	RemoteObject *remote_object;
+	
+	sender = dbus_g_method_get_sender (context);
+	remote_object = g_hash_table_lookup (clients, sender);
+	if (remote_object != NULL) {
+		dbus_g_method_return (context, remote_object->dbus_path);
+		g_free (sender);
+		return TRUE;
+	}
+	path = g_strdup_printf (DBUS_OBJECT_PATH"/%d", count++);
+	remote_object = g_object_new (REMOTE_TYPE_OBJECT, NULL);
+	remote_object->dbus_path = path;
+	remote_object->filename = g_path_get_basename (filename);
+	remote_object->handle = xchat_plugingui_add (ph,
+						     remote_object->filename,
+						     name,
+						     desc,
+						     version, NULL);
+	dbus_g_connection_register_g_object (connection,
+					     path,
+					     G_OBJECT (remote_object));
+
+	g_hash_table_insert (clients,
+			     sender,
+			     remote_object);
+
+	dbus_g_method_return (context, path);
+	return TRUE;
+}
+
+static gboolean
+remote_object_disconnect (RemoteObject *obj,
+			  DBusGMethodInvocation *context)
+{
+	char *sender;
+	
+	sender = dbus_g_method_get_sender (context);
+	g_hash_table_remove (clients, sender);
+	g_free (sender);
+
+	dbus_g_method_return (context);
+	return TRUE;
+}
+
+static gboolean
+remote_object_command (RemoteObject *obj,
+		       const char *command,
+		       GError **error)
+{
+	if (xchat_set_context (ph, obj->context)) {
+		xchat_command (ph, command);
+	}
+	return TRUE;
+}
+
+static gboolean
+remote_object_print (RemoteObject *obj,
+		     const char *text,
+		     GError **error)
+{
+	if (xchat_set_context (ph, obj->context)) {
+		xchat_print (ph, text);
+	}
+	return TRUE;
+}
+
+static gboolean
+remote_object_find_context (RemoteObject *obj,
+			    const char *server,
+			    const char *channel,
+			    guint *ret_id,
+			    GError **error)
+{
+	xchat_context *context;
+
+	if (*server == '\0') {
+		server = NULL;
+	}
+	if (*channel == '\0') {
+		channel = NULL;
+	}
+	context = xchat_find_context (ph, server, channel);
+	*ret_id = context_list_find_id (context);
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_get_context (RemoteObject *obj,
+			   guint *ret_id,
+			   GError **error)
+{
+	*ret_id = context_list_find_id (obj->context);
+	return TRUE;
+}
+
+static gboolean
+remote_object_set_context (RemoteObject *obj,
+			   guint id,
+			   gboolean *ret,
+			   GError **error)
+{
+	xchat_context *context;
+	
+	context = context_list_find_context (id);
+	if (context == NULL) {
+		*ret = FALSE;
+		return TRUE;
+	}
+	obj->context = context;
+	*ret = TRUE;
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_get_info (RemoteObject *obj,
+			const char *id,
+			char **ret_info,
+			GError **error)
+{
+	/* win_ptr is a GtkWindow* casted to char* and will crash
+	 * D-Bus if we send it as a string */
+	if (!xchat_set_context (ph, obj->context) ||
+	    g_str_equal (id, "win_ptr")) {
+		*ret_info = NULL;
+		return TRUE;
+	}
+	*ret_info = g_strdup (xchat_get_info (ph, id));
+	return TRUE;
+}
+
+static gboolean
+remote_object_get_prefs (RemoteObject *obj,
+			 const char *name,
+			 int *ret_type,
+			 char **ret_str,
+			 int *ret_int,
+			 GError **error)
+{
+	const char *str;
+
+	if (!xchat_set_context (ph, obj->context)) {
+		*ret_type = 0;
+		return TRUE;
+	}
+	*ret_type = xchat_get_prefs (ph, name, &str, ret_int);
+	*ret_str = g_strdup (str);
+
+	return TRUE;
+}
+
+static int
+server_hook_cb (char *word[],
+		char *word_eol[],
+		void *userdata)
+{
+	HookInfo *info = (HookInfo*)userdata;
+	char **arg1;
+	char **arg2;
+
+	arg1 = build_list (word + 1);
+	arg2 = build_list (word_eol + 1);
+	info->obj->context = xchat_get_context (ph);
+	g_signal_emit (info->obj,
+		       signals[SERVER_SIGNAL],
+		       0,
+		       arg1, arg2, info->id,
+		       context_list_find_id (info->obj->context));
+	g_strfreev (arg1);
+	g_strfreev (arg2);
+  
+	return info->return_value;
+}
+
+static int
+command_hook_cb (char *word[],
+		 char *word_eol[],
+		 void *userdata)
+{
+	HookInfo *info = (HookInfo*)userdata;
+	char **arg1;
+	char **arg2;
+
+	arg1 = build_list (word + 1);
+	arg2 = build_list (word_eol + 1);
+	info->obj->context = xchat_get_context (ph);
+	g_signal_emit (info->obj,
+		       signals[COMMAND_SIGNAL],
+		       0,
+		       arg1, arg2, info->id,
+		       context_list_find_id (info->obj->context));
+	g_strfreev (arg1);
+	g_strfreev (arg2);
+  
+	return info->return_value;
+}
+
+static int
+print_hook_cb (char *word[],
+	       void *userdata)
+{
+	HookInfo *info = (HookInfo*)userdata;
+	char **arg1;
+
+	arg1 = build_list (word + 1);
+	info->obj->context = xchat_get_context (ph);
+	g_signal_emit (info->obj,
+		       signals[PRINT_SIGNAL],
+		       0,
+		       arg1, info->id,
+		       context_list_find_id (info->obj->context));
+	g_strfreev (arg1);
+  
+	return info->return_value;
+}
+
+static gboolean
+remote_object_hook_command (RemoteObject *obj,
+			    const char *name,
+			    int priority,
+			    const char *help_text,
+			    int return_value,
+			    guint *ret_id,
+			    GError **error)
+{
+	HookInfo *info;
+
+	info = g_new0 (HookInfo, 1);
+	info->obj = obj;
+	info->return_value = return_value;
+	info->id = ++obj->last_hook_id;
+	info->hook = xchat_hook_command (ph,
+					 name,
+					 priority,
+					 command_hook_cb,
+					 help_text,
+					 info);
+	g_hash_table_insert (obj->hooks, &info->id, info);
+	*ret_id = info->id;
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_hook_server (RemoteObject *obj,
+			   const char *name,
+			   int priority,
+			   int return_value,
+			   guint *ret_id,
+			   GError **error)
+{
+	HookInfo *info;
+
+	info = g_new0 (HookInfo, 1);
+	info->obj = obj;
+	info->return_value = return_value;
+	info->id = ++obj->last_hook_id;
+	info->hook = xchat_hook_server (ph,
+					name,
+					priority,
+					server_hook_cb,
+					info);
+	g_hash_table_insert (obj->hooks, &info->id, info);
+	*ret_id = info->id;
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_hook_print (RemoteObject *obj,
+			  const char *name,
+			  int priority,
+			  int return_value,
+			  guint *ret_id,
+			  GError **error)
+{
+	HookInfo *info;
+
+	info = g_new0 (HookInfo, 1);
+	info->obj = obj;
+	info->return_value = return_value;
+	info->id = ++obj->last_hook_id;
+	info->hook = xchat_hook_print (ph,
+				       name,
+				       priority,
+				       print_hook_cb,
+				       info);
+	g_hash_table_insert (obj->hooks, &info->id, info);
+	*ret_id = info->id;
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_unhook (RemoteObject *obj,
+		      guint id,
+		      GError **error)
+{
+	g_hash_table_remove (obj->hooks, &id);
+	return TRUE;
+}
+
+static gboolean
+remote_object_list_get (RemoteObject *obj,
+			const char *name,
+			guint *ret_id,
+			GError **error)
+{
+	xchat_list *xlist;
+	guint *id;
+
+	if (!xchat_set_context (ph, obj->context)) {
+		*ret_id = 0;
+		return TRUE;
+	}
+	xlist = xchat_list_get (ph, name);
+	if (xlist == NULL) {
+		*ret_id = 0;
+		return TRUE;
+	}
+	id = g_new (guint, 1);
+	*id = ++obj->last_list_id;
+	*ret_id = *id;
+	g_hash_table_insert (obj->lists,
+			     id,
+			     xlist);
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_list_next	(RemoteObject *obj,
+			 guint id,
+			 gboolean *ret,
+			 GError **error)
+{
+	xchat_list *xlist;
+	
+	xlist = g_hash_table_lookup (obj->lists, &id);
+	if (xlist == NULL) {
+		*ret = FALSE;
+		return TRUE;
+	}
+	*ret = xchat_list_next (ph, xlist);
+
+	return TRUE;
+}			 
+
+static gboolean
+remote_object_list_str (RemoteObject *obj,
+			guint id,
+			const char *name,
+			char **ret_str,
+			GError **error)
+{
+	xchat_list *xlist;
+	
+	xlist = g_hash_table_lookup (obj->lists, &id);
+	if (xlist == NULL && !xchat_set_context (ph, obj->context)) {
+		*ret_str = NULL;
+		return TRUE;
+	}
+	if (g_str_equal (name, "context")) {
+		*ret_str = NULL;
+		return TRUE;
+	}
+	*ret_str = g_strdup (xchat_list_str (ph, xlist, name));
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_list_int (RemoteObject *obj,
+			guint id,
+			const char *name,
+			int *ret_int,
+			GError **error)
+{
+	xchat_list *xlist;
+	
+	xlist = g_hash_table_lookup (obj->lists, &id);
+	if (xlist == NULL && !xchat_set_context (ph, obj->context)) {
+		*ret_int = -1;
+		return TRUE;
+	}
+	if (g_str_equal (name, "context")) {
+		xchat_context *context;
+		context = (xchat_context*)xchat_list_str (ph, xlist, name);
+		*ret_int = context_list_find_id (context);
+	} else {
+		*ret_int = xchat_list_int (ph, xlist, name);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_list_time (RemoteObject *obj,
+			 guint id,
+			 const char *name,
+			 guint64 *ret_time,
+			 GError **error)
+{
+	xchat_list *xlist;
+	
+	xlist = g_hash_table_lookup (obj->lists, &id);
+	if (xlist == NULL) {
+		*ret_time = (guint64) -1;
+		return TRUE;
+	}
+	*ret_time = xchat_list_time (ph, xlist, name);
+	
+	return TRUE;
+}
+
+static gboolean
+remote_object_list_fields (RemoteObject *obj,
+			   const char *name,
+			   char ***ret,
+			   GError **error)
+{
+	*ret = g_strdupv ((char**)xchat_list_fields (ph, name));
+	if (*ret == NULL) {
+		*ret = g_new0 (char*, 1);
+	}
+	return TRUE;
+}
+
+static gboolean
+remote_object_list_free (RemoteObject *obj,
+			 guint id,
+			 GError **error)
+{
+	g_hash_table_remove (obj->lists, &id);
+	return TRUE;
+}
+
+static gboolean
+remote_object_emit_print (RemoteObject *obj,
+			  const char *event_name,
+			  const char *args[],
+			  gboolean *ret,
+			  GError **error)
+{
+	const char *argv[4] = {NULL, NULL, NULL, NULL};
+	int i;
+	
+	for (i = 0; i < 4 && args[i] != NULL; i++) {
+		argv[i] = args[i];
+	}
+
+	*ret = xchat_set_context (ph, obj->context);
+	if (*ret) {
+		*ret = xchat_emit_print (ph, event_name, argv[0], argv[1],
+							 argv[2], argv[3]);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+remote_object_nickcmp (RemoteObject *obj,
+		       const char *nick1,
+		       const char *nick2,
+		       int *ret,
+		       GError **error)
+{
+	xchat_set_context (ph, obj->context);
+	*ret = xchat_nickcmp (ph, nick1, nick2);
+	return TRUE;
+}
+
+static gboolean
+remote_object_strip (RemoteObject *obj,
+		     const char *str,
+		     int len,
+		     int flag,
+		     char **ret_str,
+		     GError **error)
+{
+	*ret_str = xchat_strip (ph, str, len, flag);
+	return TRUE;
+}
+
+static gboolean
+remote_object_send_modes (RemoteObject *obj,
+			  const char *targets[],
+			  int modes_per_line,
+			  char sign,
+			  char mode,
+			  GError **error)
+{
+	if (xchat_set_context (ph, obj->context)) {
+		xchat_send_modes (ph, targets,
+				  g_strv_length ((char**)targets),
+				  modes_per_line,
+				  sign, mode);
+	}
+	return TRUE;
+}
+
+/* DBUS stuffs */
+
+static void
+name_owner_changed (DBusGProxy *driver_proxy,
+		    const char *name,
+		    const char *old_owner,
+		    const char *new_owner,
+		    void       *user_data)
+{
+	if (*new_owner == '\0') {
+		/* this name has vanished */
+		g_hash_table_remove (clients, name);
+	}
+}
+
+static gboolean
+init_dbus (void)
+{
+	DBusGProxy *proxy;
+	RemoteObject *remote;
+	guint request_name_result;
+	GError *error = NULL;
+
+	dbus_g_object_type_install_info (REMOTE_TYPE_OBJECT,
+					 &dbus_glib_remote_object_object_info);
+
+	connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+	if (connection == NULL) {
+		xchat_printf (ph, _("Couldn't connect to session bus: %s\n"),
+			      error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	proxy = dbus_g_proxy_new_for_name (connection,
+					   DBUS_SERVICE_DBUS,
+					   DBUS_PATH_DBUS,
+					   DBUS_INTERFACE_DBUS);
+
+	if (!dbus_g_proxy_call (proxy, "RequestName", &error,
+				G_TYPE_STRING, DBUS_SERVICE,
+				G_TYPE_UINT, DBUS_NAME_FLAG_ALLOW_REPLACEMENT,
+				G_TYPE_INVALID,
+				G_TYPE_UINT, &request_name_result,
+				G_TYPE_INVALID)) {
+		xchat_printf (ph, _("Failed to acquire %s: %s\n"),
+			      DBUS_SERVICE,
+			      error->message);
+		g_error_free (error);
+
+		return FALSE;
+	}
+
+	dbus_g_proxy_add_signal (proxy, "NameOwnerChanged",
+				 G_TYPE_STRING,
+				 G_TYPE_STRING,
+				 G_TYPE_STRING,
+				 G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (proxy, "NameOwnerChanged", 
+				     G_CALLBACK (name_owner_changed),
+				     NULL, NULL);
+
+	remote = g_object_new (REMOTE_TYPE_OBJECT, NULL);
+	dbus_g_connection_register_g_object (connection,
+					     DBUS_OBJECT_PATH"/Remote",
+					     G_OBJECT (remote));
+
+	return TRUE;
+}
+
+/* xchat_plugin stuffs */
+
+static char**
+build_list (char *word[])
+{
+	guint i;
+	guint num = 0;
+	char **result;
+
+	if (word == NULL) {
+		return NULL;
+	}
+  
+	while (word[num] && word[num][0]) {
+		num++;
+	}
+	
+	result = g_new0 (char*, num + 1);
+	for (i = 0; i < num; i++) {
+		result[i] = g_strdup (word[i]);
+	}
+
+	return result;
+}
+
+static guint
+context_list_find_id (xchat_context *context)
+{
+	GList *l = NULL;
+
+	for (l = contexts; l != NULL; l = l->next) {
+		if (((ContextInfo*)l->data)->context == context) {
+			return ((ContextInfo*)l->data)->id;
+		}
+	}
+
+	return 0;
+}
+
+static xchat_context*
+context_list_find_context (guint id)
+{
+	GList *l = NULL;
+
+	for (l = contexts; l != NULL; l = l->next) {
+		if (((ContextInfo*)l->data)->id == id) {
+			return ((ContextInfo*)l->data)->context;
+		}
+	}
+
+	return NULL;
+}
+
+static int
+open_context_cb (char *word[],
+		 void *userdata)
+{
+	ContextInfo *info;
+	
+	info = g_new0 (ContextInfo, 1);
+	info->id = ++last_context_id;
+	info->context = xchat_get_context (ph);
+	contexts = g_list_prepend (contexts, info);
+
+	return XCHAT_EAT_NONE;
+}
+
+static int
+close_context_cb (char *word[],
+		  void *userdata)
+{
+	GList *l;
+	xchat_context *context = xchat_get_context (ph);
+
+	for (l = contexts; l != NULL; l = l->next) {
+		if (((ContextInfo*)l->data)->context == context) {
+			g_free (l->data);
+			contexts = g_list_delete_link (contexts, l);
+			break;
+		}
+	}
+
+	return XCHAT_EAT_NONE;
+}
+
+static gboolean
+clients_find_filename_foreach (gpointer key,
+			       gpointer value,
+			       gpointer user_data)
+{
+	RemoteObject *obj = REMOTE_OBJECT (value);
+	return g_str_equal (obj->filename, (char*)user_data);
+}
+
+static int
+unload_plugin_cb (char *word[], char *word_eol[], void *userdata)
+{
+	RemoteObject *obj;
+	
+	obj = g_hash_table_find (clients,
+				 clients_find_filename_foreach,
+				 word[2]);
+	if (obj != NULL) {
+		g_signal_emit (obj, 
+			       signals[UNLOAD_SIGNAL],
+			       0);
+		return XCHAT_EAT_ALL;
+	}
+	
+	return XCHAT_EAT_NONE;
+}
+
+int
+dbus_plugin_init (xchat_plugin *plugin_handle,
+		  char **plugin_name,
+		  char **plugin_desc,
+		  char **plugin_version,
+		  char *arg)
+{
+	ph = plugin_handle;
+	*plugin_name = PNAME;
+	*plugin_desc = PDESC;
+	*plugin_version = PVERSION;
+
+	if (init_dbus()) {
+		/*xchat_printf (ph, _("%s loaded successfully!\n"), PNAME);*/
+
+		clients = g_hash_table_new_full (g_str_hash,
+						 g_str_equal,
+						 g_free,
+						 g_object_unref);
+
+		xchat_hook_print (ph, "Open Context",
+				  XCHAT_PRI_NORM,
+				  open_context_cb,
+				  NULL);
+
+		xchat_hook_print (ph, "Close Context",
+				  XCHAT_PRI_NORM,
+				  close_context_cb,
+				  NULL);
+
+		xchat_hook_command (ph, "unload",
+				    XCHAT_PRI_HIGHEST,
+				    unload_plugin_cb, NULL, NULL);
+	}
+
+	return TRUE; 
+}