mirror of
https://github.com/moparisthebest/hexchat
synced 2024-11-11 11:55:07 -05:00
1088 lines
24 KiB
C
1088 lines
24 KiB
C
|
/* 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;
|
||
|
}
|