/* dbus-plugin.c - hexchat 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "../hexchat-plugin.h"
#define PNAME _("remote access")
#define PDESC _("plugin for remote access using DBUS")
#define PVERSION ""
#define DBUS_SERVICE "org.hexchat.service"
#define DBUS_OBJECT_PATH "/org/hexchat"
static hexchat_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;
hexchat_context *context;
char *dbus_path;
char *filename;
GHashTable *hooks;
GHashTable *lists;
void *handle;
};
struct RemoteObjectClass
{
GObjectClass parent;
};
typedef struct
{
guint id;
int return_value;
hexchat_hook *hook;
RemoteObject *obj;
} HookInfo;
typedef struct
{
guint id;
hexchat_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 (hexchat_context *context);
static hexchat_context* context_list_find_context (guint id);
/* Remote Object */
static void
hook_info_destroy (gpointer data)
{
HookInfo *info = (HookInfo*)data;
if (info == NULL) {
return;
}
hexchat_unhook (ph, info->hook);
g_free (info);
}
static void
list_info_destroy (gpointer data)
{
hexchat_list_free (ph, (hexchat_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);
hexchat_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 = hexchat_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 = hexchat_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 (hexchat_set_context (ph, obj->context)) {
hexchat_command (ph, command);
}
return TRUE;
}
static gboolean
remote_object_print (RemoteObject *obj,
const char *text,
GError **error)
{
if (hexchat_set_context (ph, obj->context)) {
hexchat_print (ph, text);
}
return TRUE;
}
static gboolean
remote_object_find_context (RemoteObject *obj,
const char *server,
const char *channel,
guint *ret_id,
GError **error)
{
hexchat_context *context;
if (*server == '\0') {
server = NULL;
}
if (*channel == '\0') {
channel = NULL;
}
context = hexchat_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)
{
hexchat_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 (!hexchat_set_context (ph, obj->context) ||
g_str_equal (id, "win_ptr")) {
*ret_info = NULL;
return TRUE;
}
*ret_info = g_strdup (hexchat_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 (!hexchat_set_context (ph, obj->context)) {
*ret_type = 0;
return TRUE;
}
*ret_type = hexchat_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 = hexchat_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 = hexchat_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 = hexchat_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 = hexchat_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 = hexchat_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 = hexchat_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)
{
hexchat_list *xlist;
guint *id;
if (!hexchat_set_context (ph, obj->context)) {
*ret_id = 0;
return TRUE;
}
xlist = hexchat_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)
{
hexchat_list *xlist;
xlist = g_hash_table_lookup (obj->lists, &id);
if (xlist == NULL) {
*ret = FALSE;
return TRUE;
}
*ret = hexchat_list_next (ph, xlist);
return TRUE;
}
static gboolean
remote_object_list_str (RemoteObject *obj,
guint id,
const char *name,
char **ret_str,
GError **error)
{
hexchat_list *xlist;
xlist = g_hash_table_lookup (obj->lists, &id);
if (xlist == NULL && !hexchat_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 (hexchat_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)
{
hexchat_list *xlist;
xlist = g_hash_table_lookup (obj->lists, &id);
if (xlist == NULL && !hexchat_set_context (ph, obj->context)) {
*ret_int = -1;
return TRUE;
}
if (g_str_equal (name, "context")) {
hexchat_context *context;
context = (hexchat_context*)hexchat_list_str (ph, xlist, name);
*ret_int = context_list_find_id (context);
} else {
*ret_int = hexchat_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)
{
hexchat_list *xlist;
xlist = g_hash_table_lookup (obj->lists, &id);
if (xlist == NULL) {
*ret_time = (guint64) -1;
return TRUE;
}
*ret_time = hexchat_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**)hexchat_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 = hexchat_set_context (ph, obj->context);
if (*ret) {
*ret = hexchat_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)
{
hexchat_set_context (ph, obj->context);
*ret = hexchat_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 = hexchat_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 (hexchat_set_context (ph, obj->context)) {
hexchat_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) {
hexchat_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)) {
hexchat_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;
}
/* hexchat_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 (hexchat_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 hexchat_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 = hexchat_get_context (ph);
contexts = g_list_prepend (contexts, info);
return HEXCHAT_EAT_NONE;
}
static int
close_context_cb (char *word[],
void *userdata)
{
GList *l;
hexchat_context *context = hexchat_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 HEXCHAT_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 HEXCHAT_EAT_ALL;
}
return HEXCHAT_EAT_NONE;
}
int
dbus_plugin_init (hexchat_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()) {
/*hexchat_printf (ph, _("%s loaded successfully!\n"), PNAME);*/
clients = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
g_object_unref);
hexchat_hook_print (ph, "Open Context",
HEXCHAT_PRI_NORM,
open_context_cb,
NULL);
hexchat_hook_print (ph, "Close Context",
HEXCHAT_PRI_NORM,
close_context_cb,
NULL);
hexchat_hook_command (ph, "unload",
HEXCHAT_PRI_HIGHEST,
unload_plugin_cb, NULL, NULL);
}
return TRUE;
}