Files
zoitechat/src/common/dbus/dbus-plugin.c

1133 lines
30 KiB
C
Raw Normal View History

2026-01-05 23:12:38 -07:00
/* dbus-plugin.c - zoitechat plugin for remote access using D-Bus
2011-02-24 04:14:30 +01:00
* 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
2012-12-23 11:36:54 -08:00
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
2011-02-24 04:14:30 +01:00
*
* Claessens Xavier
* xclaesse@gmail.com
*/
#include <config.h>
2026-04-27 11:04:04 -06:00
#include <gio/gio.h>
2011-02-24 04:14:30 +01:00
#include <glib/gi18n.h>
2026-01-05 23:12:38 -07:00
#include "zoitechat-plugin.h"
#include "dbus-plugin.h"
2011-02-24 04:14:30 +01:00
#define PNAME _("remote access")
#define PDESC _("plugin for remote access using DBUS")
#define PVERSION ""
2026-01-05 23:12:38 -07:00
#define DBUS_OBJECT_PATH "/org/zoitechat"
2026-04-27 11:04:04 -06:00
#define DBUS_INTERFACE_CONNECTION "org.zoitechat.connection"
#define DBUS_INTERFACE_PLUGIN "org.zoitechat.plugin"
#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
static const char introspection_xml[] =
"<node>"
" <interface name='org.zoitechat.connection'>"
" <method name='Connect'>"
" <arg type='s' name='filename' direction='in'/>"
" <arg type='s' name='name' direction='in'/>"
" <arg type='s' name='desc' direction='in'/>"
" <arg type='s' name='version' direction='in'/>"
" <arg type='s' name='path' direction='out'/>"
" </method>"
" <method name='Disconnect'/>"
" </interface>"
" <interface name='org.zoitechat.plugin'>"
" <method name='Command'><arg type='s' direction='in'/></method>"
" <method name='Print'><arg type='s' direction='in'/></method>"
" <method name='FindContext'><arg type='s' direction='in'/><arg type='s' direction='in'/><arg type='u' direction='out'/></method>"
" <method name='GetContext'><arg type='u' direction='out'/></method>"
" <method name='SetContext'><arg type='u' direction='in'/><arg type='b' direction='out'/></method>"
" <method name='GetInfo'><arg type='s' direction='in'/><arg type='s' direction='out'/></method>"
" <method name='GetPrefs'><arg type='s' direction='in'/><arg type='i' direction='out'/><arg type='s' direction='out'/><arg type='i' direction='out'/></method>"
" <method name='HookCommand'><arg type='s' direction='in'/><arg type='i' direction='in'/><arg type='s' direction='in'/><arg type='i' direction='in'/><arg type='u' direction='out'/></method>"
" <method name='HookServer'><arg type='s' direction='in'/><arg type='i' direction='in'/><arg type='i' direction='in'/><arg type='u' direction='out'/></method>"
" <method name='HookPrint'><arg type='s' direction='in'/><arg type='i' direction='in'/><arg type='i' direction='in'/><arg type='u' direction='out'/></method>"
" <method name='Unhook'><arg type='u' direction='in'/></method>"
" <method name='ListGet'><arg type='s' direction='in'/><arg type='u' direction='out'/></method>"
" <method name='ListNext'><arg type='u' direction='in'/><arg type='b' direction='out'/></method>"
" <method name='ListStr'><arg type='u' direction='in'/><arg type='s' direction='in'/><arg type='s' direction='out'/></method>"
" <method name='ListInt'><arg type='u' direction='in'/><arg type='s' direction='in'/><arg type='i' direction='out'/></method>"
" <method name='ListTime'><arg type='u' direction='in'/><arg type='s' direction='in'/><arg type='t' direction='out'/></method>"
" <method name='ListFields'><arg type='s' direction='in'/><arg type='as' direction='out'/></method>"
" <method name='ListFree'><arg type='u' direction='in'/></method>"
" <method name='EmitPrint'><arg type='s' direction='in'/><arg type='as' direction='in'/><arg type='b' direction='out'/></method>"
" <method name='Nickcmp'><arg type='s' direction='in'/><arg type='s' direction='in'/><arg type='i' direction='out'/></method>"
" <method name='Strip'><arg type='s' direction='in'/><arg type='i' direction='in'/><arg type='i' direction='in'/><arg type='s' direction='out'/></method>"
" <method name='SendModes'><arg type='as' direction='in'/><arg type='i' direction='in'/><arg type='y' direction='in'/><arg type='y' direction='in'/></method>"
" <signal name='CommandSignal'><arg type='as'/><arg type='as'/><arg type='u'/><arg type='u'/></signal>"
" <signal name='ServerSignal'><arg type='as'/><arg type='as'/><arg type='u'/><arg type='u'/></signal>"
" <signal name='PrintSignal'><arg type='as'/><arg type='u'/><arg type='u'/></signal>"
" <signal name='UnloadSignal'/>"
" </interface>"
"</node>";
2011-02-24 04:14:30 +01:00
2026-01-05 23:12:38 -07:00
static zoitechat_plugin *ph;
2026-04-27 11:04:04 -06:00
static guint last_context_id;
static GList *contexts;
static GHashTable *clients;
static GDBusConnection *connection;
static GDBusNodeInfo *introspection_data;
static guint name_owner_subscription;
static guint object_count;
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
typedef struct
2011-02-24 04:14:30 +01:00
{
guint id;
int return_value;
2026-01-05 23:12:38 -07:00
zoitechat_hook *hook;
2026-04-27 11:04:04 -06:00
struct _RemoteObject *obj;
2011-02-24 04:14:30 +01:00
} HookInfo;
typedef struct
{
guint id;
2026-01-05 23:12:38 -07:00
zoitechat_context *context;
2011-02-24 04:14:30 +01:00
} ContextInfo;
2026-04-27 11:04:04 -06:00
typedef struct _RemoteObject
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
guint registration_id;
guint last_hook_id;
guint last_list_id;
zoitechat_context *context;
char *dbus_path;
char *filename;
void *handle;
GHashTable *hooks;
GHashTable *lists;
} RemoteObject;
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
static char **build_list (char *word[]);
static guint context_list_find_id (zoitechat_context *context);
static zoitechat_context *context_list_find_context (guint id);
static gboolean emit_signal (RemoteObject *obj, const char *name, GVariant *params);
static const GDBusInterfaceVTable connection_vtable;
2011-02-24 04:14:30 +01:00
static void
hook_info_destroy (gpointer data)
{
2026-04-27 11:04:04 -06:00
HookInfo *info = (HookInfo *)data;
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
if (!info)
2011-02-24 04:14:30 +01:00
return;
2026-04-27 11:04:04 -06:00
2026-01-05 23:12:38 -07:00
zoitechat_unhook (ph, info->hook);
2011-02-24 04:14:30 +01:00
g_free (info);
}
static void
list_info_destroy (gpointer data)
{
2026-04-27 11:04:04 -06:00
zoitechat_list_free (ph, (zoitechat_list *)data);
2011-02-24 04:14:30 +01:00
}
2026-04-27 11:04:04 -06:00
static RemoteObject *
remote_object_new (const char *path)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
RemoteObject *obj = g_new0 (RemoteObject, 1);
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
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 = g_strdup (path);
2026-01-05 23:12:38 -07:00
obj->context = zoitechat_get_context (ph);
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
return obj;
2011-02-24 04:14:30 +01:00
}
2026-04-27 11:04:04 -06:00
static void
remote_object_free (RemoteObject *obj)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
if (!obj)
return;
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
if (connection && obj->registration_id)
g_dbus_connection_unregister_object (connection, obj->registration_id);
g_hash_table_destroy (obj->lists);
g_hash_table_destroy (obj->hooks);
g_free (obj->dbus_path);
g_free (obj->filename);
if (obj->handle)
zoitechat_plugingui_remove (ph, obj->handle);
g_free (obj);
2011-02-24 04:14:30 +01:00
}
static gboolean
remote_object_command (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *command)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
if (zoitechat_set_context (ph, obj->context))
2026-01-05 23:12:38 -07:00
zoitechat_command (ph, command);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
remote_object_print (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *text)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
if (zoitechat_set_context (ph, obj->context))
2026-01-05 23:12:38 -07:00
zoitechat_print (ph, text);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
remote_object_find_context (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *server,
const char *channel,
guint *ret_id)
2011-02-24 04:14:30 +01:00
{
2026-01-05 23:12:38 -07:00
zoitechat_context *context;
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
if (*server == '\0')
2011-02-24 04:14:30 +01:00
server = NULL;
2026-04-27 11:04:04 -06:00
if (*channel == '\0')
2011-02-24 04:14:30 +01:00
channel = NULL;
2026-01-05 23:12:38 -07:00
context = zoitechat_find_context (ph, server, channel);
2011-02-24 04:14:30 +01:00
*ret_id = context_list_find_id (context);
return TRUE;
}
static gboolean
remote_object_get_context (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
guint *ret_id)
2011-02-24 04:14:30 +01:00
{
*ret_id = context_list_find_id (obj->context);
return TRUE;
}
static gboolean
remote_object_set_context (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
guint id,
gboolean *ret)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
zoitechat_context *context = context_list_find_context (id);
if (!context)
{
2011-02-24 04:14:30 +01:00
*ret = FALSE;
return TRUE;
}
2026-04-27 11:04:04 -06:00
2011-02-24 04:14:30 +01:00
obj->context = context;
*ret = TRUE;
return TRUE;
}
static gboolean
remote_object_get_info (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *id,
char **ret_info)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
if (!zoitechat_set_context (ph, obj->context) || g_str_equal (id, "win_ptr"))
{
2011-02-24 04:14:30 +01:00
*ret_info = NULL;
return TRUE;
}
2026-04-27 11:04:04 -06:00
2026-01-05 23:12:38 -07:00
*ret_info = g_strdup (zoitechat_get_info (ph, id));
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
remote_object_get_prefs (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *name,
int *ret_type,
char **ret_str,
int *ret_int)
2011-02-24 04:14:30 +01:00
{
const char *str;
2026-04-27 11:04:04 -06:00
if (!zoitechat_set_context (ph, obj->context))
{
2011-02-24 04:14:30 +01:00
*ret_type = 0;
2026-04-27 11:04:04 -06:00
*ret_str = NULL;
*ret_int = 0;
2011-02-24 04:14:30 +01:00
return TRUE;
}
2026-04-27 11:04:04 -06:00
2026-01-05 23:12:38 -07:00
*ret_type = zoitechat_get_prefs (ph, name, &str, ret_int);
2011-02-24 04:14:30 +01:00
*ret_str = g_strdup (str);
return TRUE;
}
static int
server_hook_cb (char *word[],
char *word_eol[],
void *userdata)
{
2026-04-27 11:04:04 -06:00
HookInfo *info = (HookInfo *)userdata;
char **arg1 = build_list (word + 1);
char **arg2 = build_list (word_eol + 1);
guint context_id;
GVariant *params;
2011-02-24 04:14:30 +01:00
2026-01-05 23:12:38 -07:00
info->obj->context = zoitechat_get_context (ph);
2026-04-27 11:04:04 -06:00
context_id = context_list_find_id (info->obj->context);
params = g_variant_new ("(^as^asuu)", arg1 ? arg1 : (char *[]){ NULL }, arg2 ? arg2 : (char *[]){ NULL }, info->id, context_id);
emit_signal (info->obj, "ServerSignal", params);
2011-02-24 04:14:30 +01:00
g_strfreev (arg1);
g_strfreev (arg2);
return info->return_value;
}
static int
command_hook_cb (char *word[],
char *word_eol[],
void *userdata)
{
2026-04-27 11:04:04 -06:00
HookInfo *info = (HookInfo *)userdata;
char **arg1 = build_list (word + 1);
char **arg2 = build_list (word_eol + 1);
guint context_id;
GVariant *params;
2011-02-24 04:14:30 +01:00
2026-01-05 23:12:38 -07:00
info->obj->context = zoitechat_get_context (ph);
2026-04-27 11:04:04 -06:00
context_id = context_list_find_id (info->obj->context);
params = g_variant_new ("(^as^asuu)", arg1 ? arg1 : (char *[]){ NULL }, arg2 ? arg2 : (char *[]){ NULL }, info->id, context_id);
emit_signal (info->obj, "CommandSignal", params);
2011-02-24 04:14:30 +01:00
g_strfreev (arg1);
g_strfreev (arg2);
return info->return_value;
}
static int
print_hook_cb (char *word[],
void *userdata)
{
2026-04-27 11:04:04 -06:00
HookInfo *info = (HookInfo *)userdata;
char **arg1 = build_list (word + 1);
guint context_id;
GVariant *params;
2011-02-24 04:14:30 +01:00
2026-01-05 23:12:38 -07:00
info->obj->context = zoitechat_get_context (ph);
2026-04-27 11:04:04 -06:00
context_id = context_list_find_id (info->obj->context);
params = g_variant_new ("(^asuu)", arg1 ? arg1 : (char *[]){ NULL }, info->id, context_id);
emit_signal (info->obj, "PrintSignal", params);
2011-02-24 04:14:30 +01:00
g_strfreev (arg1);
return info->return_value;
}
static gboolean
remote_object_hook_command (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *name,
int priority,
const char *help_text,
int return_value,
guint *ret_id)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
HookInfo *info = g_new0 (HookInfo, 1);
2011-02-24 04:14:30 +01:00
info->obj = obj;
info->return_value = return_value;
info->id = ++obj->last_hook_id;
2026-04-27 11:04:04 -06:00
info->hook = zoitechat_hook_command (ph, name, priority, command_hook_cb, help_text, info);
2011-02-24 04:14:30 +01:00
g_hash_table_insert (obj->hooks, &info->id, info);
*ret_id = info->id;
return TRUE;
}
static gboolean
remote_object_hook_server (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *name,
int priority,
int return_value,
guint *ret_id)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
HookInfo *info = g_new0 (HookInfo, 1);
2011-02-24 04:14:30 +01:00
info->obj = obj;
info->return_value = return_value;
info->id = ++obj->last_hook_id;
2026-04-27 11:04:04 -06:00
info->hook = zoitechat_hook_server (ph, name, priority, server_hook_cb, info);
2011-02-24 04:14:30 +01:00
g_hash_table_insert (obj->hooks, &info->id, info);
*ret_id = info->id;
return TRUE;
}
static gboolean
remote_object_hook_print (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *name,
int priority,
int return_value,
guint *ret_id)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
HookInfo *info = g_new0 (HookInfo, 1);
2011-02-24 04:14:30 +01:00
info->obj = obj;
info->return_value = return_value;
info->id = ++obj->last_hook_id;
2026-04-27 11:04:04 -06:00
info->hook = zoitechat_hook_print (ph, name, priority, print_hook_cb, info);
2011-02-24 04:14:30 +01:00
g_hash_table_insert (obj->hooks, &info->id, info);
*ret_id = info->id;
return TRUE;
}
static gboolean
remote_object_unhook (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
guint id)
2011-02-24 04:14:30 +01:00
{
g_hash_table_remove (obj->hooks, &id);
return TRUE;
}
static gboolean
remote_object_list_get (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *name,
guint *ret_id)
2011-02-24 04:14:30 +01:00
{
2026-01-05 23:12:38 -07:00
zoitechat_list *xlist;
2011-02-24 04:14:30 +01:00
guint *id;
2026-04-27 11:04:04 -06:00
if (!zoitechat_set_context (ph, obj->context))
{
2011-02-24 04:14:30 +01:00
*ret_id = 0;
return TRUE;
}
2026-04-27 11:04:04 -06:00
2026-01-05 23:12:38 -07:00
xlist = zoitechat_list_get (ph, name);
2026-04-27 11:04:04 -06:00
if (!xlist)
{
2011-02-24 04:14:30 +01:00
*ret_id = 0;
return TRUE;
}
2026-04-27 11:04:04 -06:00
2011-02-24 04:14:30 +01:00
id = g_new (guint, 1);
*id = ++obj->last_list_id;
*ret_id = *id;
2026-04-27 11:04:04 -06:00
g_hash_table_insert (obj->lists, id, xlist);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
2026-04-27 11:04:04 -06:00
remote_object_list_next (RemoteObject *obj,
guint id,
gboolean *ret)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
zoitechat_list *xlist = g_hash_table_lookup (obj->lists, &id);
if (!xlist)
{
2011-02-24 04:14:30 +01:00
*ret = FALSE;
return TRUE;
}
2026-04-27 11:04:04 -06:00
*ret = zoitechat_list_next (ph, xlist);
2011-02-24 04:14:30 +01:00
return TRUE;
2026-04-27 11:04:04 -06:00
}
2011-02-24 04:14:30 +01:00
static gboolean
remote_object_list_str (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
guint id,
const char *name,
char **ret_str)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
zoitechat_list *xlist = g_hash_table_lookup (obj->lists, &id);
if (xlist == NULL && !zoitechat_set_context (ph, obj->context))
{
2011-02-24 04:14:30 +01:00
*ret_str = NULL;
return TRUE;
}
2026-04-27 11:04:04 -06:00
if (g_str_equal (name, "context"))
{
2011-02-24 04:14:30 +01:00
*ret_str = NULL;
return TRUE;
}
2026-04-27 11:04:04 -06:00
*ret_str = g_strdup (zoitechat_list_str (ph, xlist, name));
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
remote_object_list_int (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
guint id,
const char *name,
int *ret_int)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
zoitechat_list *xlist = g_hash_table_lookup (obj->lists, &id);
if (xlist == NULL && !zoitechat_set_context (ph, obj->context))
{
2011-02-24 04:14:30 +01:00
*ret_int = -1;
return TRUE;
}
2026-04-27 11:04:04 -06:00
if (g_str_equal (name, "context"))
{
zoitechat_context *context = (zoitechat_context *)zoitechat_list_str (ph, xlist, name);
2011-02-24 04:14:30 +01:00
*ret_int = context_list_find_id (context);
}
2026-04-27 11:04:04 -06:00
else
*ret_int = zoitechat_list_int (ph, xlist, name);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
remote_object_list_time (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
guint id,
const char *name,
guint64 *ret_time)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
zoitechat_list *xlist = g_hash_table_lookup (obj->lists, &id);
if (!xlist)
{
*ret_time = (guint64)-1;
2011-02-24 04:14:30 +01:00
return TRUE;
}
2026-04-27 11:04:04 -06:00
2026-01-05 23:12:38 -07:00
*ret_time = zoitechat_list_time (ph, xlist, name);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
2026-04-27 11:04:04 -06:00
remote_object_list_fields (const char *name,
char ***ret)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
*ret = g_strdupv ((char **)zoitechat_list_fields (ph, name));
if (*ret == NULL)
*ret = g_new0 (char *, 1);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
remote_object_list_free (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
guint id)
2011-02-24 04:14:30 +01:00
{
g_hash_table_remove (obj->lists, &id);
return TRUE;
}
static gboolean
remote_object_emit_print (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *event_name,
const char *args[],
gboolean *ret)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
const char *argv[4] = { NULL, NULL, NULL, NULL };
2011-02-24 04:14:30 +01:00
int i;
2026-04-27 11:04:04 -06:00
for (i = 0; i < 4 && args[i] != NULL; i++)
2011-02-24 04:14:30 +01:00
argv[i] = args[i];
2026-01-05 23:12:38 -07:00
*ret = zoitechat_set_context (ph, obj->context);
2026-04-27 11:04:04 -06:00
if (*ret)
*ret = zoitechat_emit_print (ph, event_name, argv[0], argv[1], argv[2], argv[3]);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
remote_object_nickcmp (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *nick1,
const char *nick2,
int *ret)
2011-02-24 04:14:30 +01:00
{
2026-01-05 23:12:38 -07:00
zoitechat_set_context (ph, obj->context);
*ret = zoitechat_nickcmp (ph, nick1, nick2);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
2026-04-27 11:04:04 -06:00
remote_object_strip (const char *str,
int len,
int flag,
char **ret_str)
2011-02-24 04:14:30 +01:00
{
2026-01-05 23:12:38 -07:00
*ret_str = zoitechat_strip (ph, str, len, flag);
2011-02-24 04:14:30 +01:00
return TRUE;
}
static gboolean
remote_object_send_modes (RemoteObject *obj,
2026-04-27 11:04:04 -06:00
const char *targets[],
int modes_per_line,
char sign,
char mode)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
if (zoitechat_set_context (ph, obj->context))
{
2026-01-05 23:12:38 -07:00
zoitechat_send_modes (ph, targets,
2026-04-27 11:04:04 -06:00
g_strv_length ((char **)targets),
modes_per_line,
sign,
mode);
2011-02-24 04:14:30 +01:00
}
2026-04-27 11:04:04 -06:00
2011-02-24 04:14:30 +01:00
return TRUE;
}
2026-04-27 11:04:04 -06:00
static gboolean
emit_signal (RemoteObject *obj, const char *name, GVariant *params)
{
if (!connection)
return FALSE;
return g_dbus_connection_emit_signal (connection,
NULL,
obj->dbus_path,
DBUS_INTERFACE_PLUGIN,
name,
params,
NULL);
}
static gboolean
clients_find_path_foreach (gpointer key,
gpointer value,
gpointer user_data)
{
RemoteObject *obj = value;
return g_str_equal (obj->dbus_path, (const char *)user_data);
}
static RemoteObject *
find_remote_by_path (const char *object_path)
{
if (g_str_equal (object_path, DBUS_OBJECT_PATH "/Remote"))
return g_hash_table_lookup (clients, "__root__");
return g_hash_table_find (clients, clients_find_path_foreach, (gpointer)object_path);
}
2011-02-24 04:14:30 +01:00
static void
2026-04-27 11:04:04 -06:00
method_call_cb (GDBusConnection *conn,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
RemoteObject *obj;
if (g_str_equal (interface_name, DBUS_INTERFACE_CONNECTION))
{
if (g_str_equal (method_name, "Connect"))
{
const char *filename;
const char *name;
const char *desc;
const char *version;
GError *error = NULL;
char *path;
gchar count_buffer[15];
g_variant_get (parameters, "(&s&s&s&s)", &filename, &name, &desc, &version);
obj = g_hash_table_lookup (clients, sender);
if (obj)
{
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", obj->dbus_path));
return;
}
g_snprintf (count_buffer, sizeof count_buffer, "%u", object_count++);
path = g_build_filename (DBUS_OBJECT_PATH, count_buffer, NULL);
obj = remote_object_new (path);
obj->filename = g_path_get_basename (filename);
obj->handle = zoitechat_plugingui_add (ph, obj->filename, name, desc, version, NULL);
obj->registration_id = g_dbus_connection_register_object (
conn,
obj->dbus_path,
introspection_data->interfaces[1],
&connection_vtable,
NULL,
NULL,
&error);
if (!obj->registration_id)
{
g_dbus_method_invocation_return_dbus_error (invocation, "org.zoitechat.Error", error->message);
g_error_free (error);
remote_object_free (obj);
g_free (path);
return;
}
g_hash_table_insert (clients, g_strdup (sender), obj);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", obj->dbus_path));
g_free (path);
return;
}
if (g_str_equal (method_name, "Disconnect"))
{
g_hash_table_remove (clients, sender);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
return;
}
}
obj = find_remote_by_path (object_path);
if (!obj)
{
g_dbus_method_invocation_return_dbus_error (invocation, "org.zoitechat.Error", "No such object");
return;
}
if (g_str_equal (method_name, "Command"))
{
const char *command;
g_variant_get (parameters, "(&s)", &command);
remote_object_command (obj, command);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
}
else if (g_str_equal (method_name, "Print"))
{
const char *text;
g_variant_get (parameters, "(&s)", &text);
remote_object_print (obj, text);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
}
else if (g_str_equal (method_name, "FindContext"))
{
const char *server;
const char *channel;
guint id;
g_variant_get (parameters, "(&s&s)", &server, &channel);
remote_object_find_context (obj, server, channel, &id);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", id));
}
else if (g_str_equal (method_name, "GetContext"))
{
guint id;
remote_object_get_context (obj, &id);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", id));
}
else if (g_str_equal (method_name, "SetContext"))
{
guint id;
gboolean ret;
g_variant_get (parameters, "(u)", &id);
remote_object_set_context (obj, id, &ret);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
}
else if (g_str_equal (method_name, "GetInfo"))
{
const char *id;
char *ret_info;
g_variant_get (parameters, "(&s)", &id);
remote_object_get_info (obj, id, &ret_info);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ret_info ? ret_info : ""));
g_free (ret_info);
}
else if (g_str_equal (method_name, "GetPrefs"))
{
const char *name;
int ret_type;
char *ret_str;
int ret_int;
g_variant_get (parameters, "(&s)", &name);
remote_object_get_prefs (obj, name, &ret_type, &ret_str, &ret_int);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(isi)", ret_type, ret_str ? ret_str : "", ret_int));
g_free (ret_str);
}
else if (g_str_equal (method_name, "HookCommand"))
{
const char *name;
int priority;
const char *help;
int rv;
guint id;
g_variant_get (parameters, "(&si&si)", &name, &priority, &help, &rv);
remote_object_hook_command (obj, name, priority, help, rv, &id);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", id));
}
else if (g_str_equal (method_name, "HookServer"))
{
const char *name;
int priority;
int rv;
guint id;
g_variant_get (parameters, "(&sii)", &name, &priority, &rv);
remote_object_hook_server (obj, name, priority, rv, &id);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", id));
}
else if (g_str_equal (method_name, "HookPrint"))
{
const char *name;
int priority;
int rv;
guint id;
g_variant_get (parameters, "(&sii)", &name, &priority, &rv);
remote_object_hook_print (obj, name, priority, rv, &id);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", id));
}
else if (g_str_equal (method_name, "Unhook"))
{
guint id;
g_variant_get (parameters, "(u)", &id);
remote_object_unhook (obj, id);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
2011-02-24 04:14:30 +01:00
}
2026-04-27 11:04:04 -06:00
else if (g_str_equal (method_name, "ListGet"))
{
const char *name;
guint id;
g_variant_get (parameters, "(&s)", &name);
remote_object_list_get (obj, name, &id);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", id));
}
else if (g_str_equal (method_name, "ListNext"))
{
guint id;
gboolean ret;
g_variant_get (parameters, "(u)", &id);
remote_object_list_next (obj, id, &ret);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
}
else if (g_str_equal (method_name, "ListStr"))
{
guint id;
const char *name;
char *ret_str;
g_variant_get (parameters, "(u&s)", &id, &name);
remote_object_list_str (obj, id, name, &ret_str);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ret_str ? ret_str : ""));
g_free (ret_str);
}
else if (g_str_equal (method_name, "ListInt"))
{
guint id;
const char *name;
int ret;
g_variant_get (parameters, "(u&s)", &id, &name);
remote_object_list_int (obj, id, name, &ret);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", ret));
}
else if (g_str_equal (method_name, "ListTime"))
{
guint id;
const char *name;
guint64 ret;
g_variant_get (parameters, "(u&s)", &id, &name);
remote_object_list_time (obj, id, name, &ret);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(t)", ret));
}
else if (g_str_equal (method_name, "ListFields"))
{
const char *name;
char **ret;
g_variant_get (parameters, "(&s)", &name);
remote_object_list_fields (name, &ret);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(^as)", ret));
g_strfreev (ret);
}
else if (g_str_equal (method_name, "ListFree"))
{
guint id;
g_variant_get (parameters, "(u)", &id);
remote_object_list_free (obj, id);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
}
else if (g_str_equal (method_name, "EmitPrint"))
{
const char *event_name;
GVariant *args;
char **strv;
gboolean ret;
g_variant_get (parameters, "(&s@as)", &event_name, &args);
strv = g_variant_dup_strv (args, NULL);
remote_object_emit_print (obj, event_name, (const char **)strv, &ret);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
g_strfreev (strv);
g_variant_unref (args);
}
else if (g_str_equal (method_name, "Nickcmp"))
{
const char *n1;
const char *n2;
int ret;
g_variant_get (parameters, "(&s&s)", &n1, &n2);
remote_object_nickcmp (obj, n1, n2, &ret);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", ret));
}
else if (g_str_equal (method_name, "Strip"))
{
const char *str;
int len;
int flag;
char *ret;
g_variant_get (parameters, "(&sii)", &str, &len, &flag);
remote_object_strip (str, len, flag, &ret);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ret ? ret : ""));
g_free (ret);
}
else if (g_str_equal (method_name, "SendModes"))
{
GVariant *targets;
char **strv;
int modes_per_line;
guchar sign;
guchar mode;
g_variant_get (parameters, "(@asiyy)", &targets, &modes_per_line, &sign, &mode);
strv = g_variant_dup_strv (targets, NULL);
remote_object_send_modes (obj, (const char **)strv, modes_per_line, (char)sign, (char)mode);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
g_strfreev (strv);
g_variant_unref (targets);
}
else
g_dbus_method_invocation_return_dbus_error (invocation, "org.zoitechat.Error", "Unknown method");
}
static const GDBusInterfaceVTable connection_vtable = {
method_call_cb,
NULL,
NULL
};
static void
name_owner_changed_cb (GDBusConnection *conn,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
const char *name;
const char *old_owner;
const char *new_owner;
g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner);
if (*new_owner == '\0')
g_hash_table_remove (clients, name);
2011-02-24 04:14:30 +01:00
}
static gboolean
init_dbus (void)
{
GError *error = NULL;
2026-04-27 11:04:04 -06:00
GVariant *request_name_result;
RemoteObject *root_remote;
guint registration_id;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (!connection)
{
zoitechat_printf (ph, _("Couldn't connect to session bus: %s\n"), error->message);
2011-02-24 04:14:30 +01:00
g_error_free (error);
return FALSE;
}
2026-04-27 11:04:04 -06:00
introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, &error);
if (!introspection_data)
{
zoitechat_printf (ph, _("Couldn't parse DBus introspection data: %s\n"), error->message);
2011-02-24 04:14:30 +01:00
g_error_free (error);
return FALSE;
}
2026-04-27 11:04:04 -06:00
request_name_result = g_dbus_connection_call_sync (
connection,
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS,
"RequestName",
g_variant_new ("(su)", DBUS_SERVICE, 1u),
G_VARIANT_TYPE ("(u)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (!request_name_result)
{
zoitechat_printf (ph, _("Failed to acquire %s: %s\n"), DBUS_SERVICE, error->message);
g_error_free (error);
return FALSE;
}
g_variant_unref (request_name_result);
registration_id = g_dbus_connection_register_object (
connection,
DBUS_OBJECT_PATH,
introspection_data->interfaces[0],
&connection_vtable,
NULL,
NULL,
&error);
if (!registration_id)
{
zoitechat_printf (ph, _("Failed to register %s: %s\n"), DBUS_OBJECT_PATH, error->message);
g_error_free (error);
return FALSE;
}
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
root_remote = remote_object_new (DBUS_OBJECT_PATH "/Remote");
root_remote->registration_id = g_dbus_connection_register_object (
connection,
root_remote->dbus_path,
introspection_data->interfaces[1],
&connection_vtable,
NULL,
NULL,
&error);
if (!root_remote->registration_id)
{
zoitechat_printf (ph, _("Failed to register %s: %s\n"), root_remote->dbus_path, error->message);
g_error_free (error);
remote_object_free (root_remote);
return FALSE;
}
g_hash_table_insert (clients, g_strdup ("__root__"), root_remote);
name_owner_subscription = g_dbus_connection_signal_subscribe (
connection,
DBUS_SERVICE_DBUS,
DBUS_INTERFACE_DBUS,
"NameOwnerChanged",
DBUS_PATH_DBUS,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
name_owner_changed_cb,
NULL,
NULL);
2011-02-24 04:14:30 +01:00
return TRUE;
}
2026-04-27 11:04:04 -06:00
static char **
2011-02-24 04:14:30 +01:00
build_list (char *word[])
{
guint i;
guint num = 0;
char **result;
2026-04-27 11:04:04 -06:00
if (!word)
2011-02-24 04:14:30 +01:00
return NULL;
2026-04-27 11:04:04 -06:00
while (word[num] && word[num][0])
2011-02-24 04:14:30 +01:00
num++;
2026-04-27 11:04:04 -06:00
result = g_new0 (char *, num + 1);
for (i = 0; i < num; i++)
2011-02-24 04:14:30 +01:00
result[i] = g_strdup (word[i]);
return result;
}
static guint
2026-01-05 23:12:38 -07:00
context_list_find_id (zoitechat_context *context)
2011-02-24 04:14:30 +01:00
{
2026-04-27 11:04:04 -06:00
GList *l;
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
for (l = contexts; l != NULL; l = l->next)
{
if (((ContextInfo *)l->data)->context == context)
return ((ContextInfo *)l->data)->id;
2011-02-24 04:14:30 +01:00
}
return 0;
}
2026-04-27 11:04:04 -06:00
static zoitechat_context *
2011-02-24 04:14:30 +01:00
context_list_find_context (guint id)
{
2026-04-27 11:04:04 -06:00
GList *l;
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
for (l = contexts; l != NULL; l = l->next)
{
if (((ContextInfo *)l->data)->id == id)
return ((ContextInfo *)l->data)->context;
2011-02-24 04:14:30 +01:00
}
return NULL;
}
static int
open_context_cb (char *word[],
void *userdata)
{
2026-04-27 11:04:04 -06:00
ContextInfo *info = g_new0 (ContextInfo, 1);
2011-02-24 04:14:30 +01:00
info->id = ++last_context_id;
2026-01-05 23:12:38 -07:00
info->context = zoitechat_get_context (ph);
2011-02-24 04:14:30 +01:00
contexts = g_list_prepend (contexts, info);
2026-01-25 16:13:47 -07:00
return ZOITECHAT_EAT_NONE;
2011-02-24 04:14:30 +01:00
}
static int
close_context_cb (char *word[],
void *userdata)
{
GList *l;
2026-01-05 23:12:38 -07:00
zoitechat_context *context = zoitechat_get_context (ph);
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
for (l = contexts; l != NULL; l = l->next)
{
if (((ContextInfo *)l->data)->context == context)
{
2011-02-24 04:14:30 +01:00
g_free (l->data);
contexts = g_list_delete_link (contexts, l);
break;
}
}
2026-01-25 16:13:47 -07:00
return ZOITECHAT_EAT_NONE;
2011-02-24 04:14:30 +01:00
}
static gboolean
clients_find_filename_foreach (gpointer key,
gpointer value,
gpointer user_data)
{
2026-04-27 11:04:04 -06:00
RemoteObject *obj = value;
return g_str_equal (obj->filename, (char *)user_data);
2011-02-24 04:14:30 +01:00
}
static int
unload_plugin_cb (char *word[], char *word_eol[], void *userdata)
{
2026-04-27 11:04:04 -06:00
RemoteObject *obj = g_hash_table_find (clients, clients_find_filename_foreach, word[2]);
if (obj != NULL)
{
emit_signal (obj, "UnloadSignal", g_variant_new ("()"));
2026-01-25 16:13:47 -07:00
return ZOITECHAT_EAT_ALL;
2011-02-24 04:14:30 +01:00
}
2026-04-27 11:04:04 -06:00
2026-01-25 16:13:47 -07:00
return ZOITECHAT_EAT_NONE;
2011-02-24 04:14:30 +01:00
}
int
2026-01-05 23:12:38 -07:00
dbus_plugin_init (zoitechat_plugin *plugin_handle,
2011-02-24 04:14:30 +01:00
char **plugin_name,
char **plugin_desc,
char **plugin_version,
char *arg)
{
ph = plugin_handle;
*plugin_name = PNAME;
*plugin_desc = PDESC;
*plugin_version = PVERSION;
2026-04-27 11:04:04 -06:00
clients = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)remote_object_free);
2011-02-24 04:14:30 +01:00
2026-04-27 11:04:04 -06:00
if (init_dbus ())
{
zoitechat_hook_print (ph, "Open Context", ZOITECHAT_PRI_NORM, open_context_cb, NULL);
zoitechat_hook_print (ph, "Close Context", ZOITECHAT_PRI_NORM, close_context_cb, NULL);
zoitechat_hook_command (ph, "unload", ZOITECHAT_PRI_HIGHEST, unload_plugin_cb, NULL, NULL);
2011-02-24 04:14:30 +01:00
}
2026-04-27 11:04:04 -06:00
return TRUE;
2011-02-24 04:14:30 +01:00
}