diff options
author | Will Thompson <will.thompson@collabora.co.uk> | 2011-06-22 14:33:52 +0100 |
---|---|---|
committer | Will Thompson <will.thompson@collabora.co.uk> | 2011-11-18 10:43:05 +0000 |
commit | 33f739934f7a8f01aaec71400afd584be24d4e72 (patch) | |
tree | 46c7a49165d599e1fd4b9093bdd816743f79b75c /gabble | |
parent | c48ab1e75a698bda268a4331ed4d793af98ae3b8 (diff) |
Add an XMPP console sidecar
It currently only supports sending an arbitrary IQ and getting the
reply. Obviously this is for developer use only.
Diffstat (limited to 'gabble')
-rw-r--r-- | gabble/extensions/Gabble_Plugin_Console.xml | 66 | ||||
-rw-r--r-- | gabble/extensions/Makefile.am | 1 | ||||
-rw-r--r-- | gabble/extensions/all.xml | 1 | ||||
-rw-r--r-- | gabble/plugins/Makefile.am | 5 | ||||
-rw-r--r-- | gabble/plugins/console.c | 430 | ||||
-rw-r--r-- | gabble/plugins/console.h | 85 |
6 files changed, 588 insertions, 0 deletions
diff --git a/gabble/extensions/Gabble_Plugin_Console.xml b/gabble/extensions/Gabble_Plugin_Console.xml new file mode 100644 index 000000000..f00a24ad3 --- /dev/null +++ b/gabble/extensions/Gabble_Plugin_Console.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" ?> +<node name="/Gabble_Plugin_Console" + xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright>Copyright © 2011 Collabora Ltd.</tp:copyright> + <tp:license xmlns="http://www.w3.org/1999/xhtml"> + <p>This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version.</p> + + <p>This library 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 + Lesser General Public License for more details.</p> + + <p>You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA.</p> + </tp:license> + + <interface name="org.freedesktop.Telepathy.Gabble.Plugin.Console"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A sidecar interface providing an XMPP console.</p> + </tp:docstring> + + <method name="SendIQ" tp:name-for-bindings="Send_IQ"> + <arg direction="in" name="Type" type="s"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + The type of IQ request to send; either <code>"get"</code> or + <code>"set"</code>. + </tp:docstring> + </arg> + <arg direction="in" name="To" type="s"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + The recipient for the IQ; or the empty string to send an IQ to the + server (with no recipient specified). + </tp:docstring> + </arg> + <arg direction="in" name="Body" type="s"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + An XML fragment for the body of the IQ, which MUST have exactly one + top-level element. + </tp:docstring> + </arg> + <arg direction="out" name="Reply_Type" type="s"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + The type of IQ reply; either <code>"result"</code> or + <code>"error"</code>. + </tp:docstring> + </arg> + <arg direction="out" name="Body" type="s"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + An XML fragment for the body of the response, which MUST have between + zero and two top-level elements (the query element, an error element, + both, or neither). + </tp:docstring> + </arg> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + Hai. + </tp:docstring> + </method> + + </interface> +</node> +<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/gabble/extensions/Makefile.am b/gabble/extensions/Makefile.am index feee4e790..10cab4308 100644 --- a/gabble/extensions/Makefile.am +++ b/gabble/extensions/Makefile.am @@ -5,6 +5,7 @@ EXTRA_DIST = \ Channel_Type_FileTransfer_Future.xml \ Connection_Future.xml \ Connection_Interface_Gabble_Decloak.xml \ + Gabble_Plugin_Console.xml \ Gabble_Plugin_Gateways.xml \ Gabble_Plugin_Test.xml \ OLPC_Activity_Properties.xml \ diff --git a/gabble/extensions/all.xml b/gabble/extensions/all.xml index cb368ef9e..90f52a89b 100644 --- a/gabble/extensions/all.xml +++ b/gabble/extensions/all.xml @@ -40,6 +40,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p> <xi:include href="Connection_Interface_Gabble_Decloak.xml"/> <xi:include href="Connection_Future.xml"/> +<xi:include href="Gabble_Plugin_Console.xml"/> <xi:include href="Gabble_Plugin_Gateways.xml"/> <xi:include href="Gabble_Plugin_Test.xml"/> diff --git a/gabble/plugins/Makefile.am b/gabble/plugins/Makefile.am index 97f87a4c5..5d4715be1 100644 --- a/gabble/plugins/Makefile.am +++ b/gabble/plugins/Makefile.am @@ -1,6 +1,7 @@ plugindir = $(libdir)/telepathy/gabble-0 installable_plugins = \ + console.la \ gateways.la test_only_plugins = \ @@ -45,6 +46,10 @@ gateways_la_SOURCES = \ gateways.c \ gateways.h +console_la_SOURCES = \ + console.c \ + console.h + AM_CFLAGS = $(ERROR_CFLAGS) \ -I $(top_srcdir) -I $(top_builddir) \ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ @TP_GLIB_CFLAGS@ \ diff --git a/gabble/plugins/console.c b/gabble/plugins/console.c new file mode 100644 index 000000000..47375f774 --- /dev/null +++ b/gabble/plugins/console.c @@ -0,0 +1,430 @@ +/* XML console plugin + * + * Copyright © 2011 Collabora Ltd. <http://www.collabora.co.uk/> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "console.h" + +#include "config.h" + +#include <string.h> + +#include <telepathy-glib/telepathy-glib.h> + +#include <wocky/wocky-xmpp-error.h> +#include <wocky/wocky-utils.h> +#include <wocky/wocky-xmpp-reader.h> +#include <wocky/wocky-xmpp-writer.h> + +#include "extensions/extensions.h" + +#include <gabble/gabble.h> + +/************************* + * Plugin implementation * + *************************/ + +static guint debug = 0; + +#define DEBUG(format, ...) \ +G_STMT_START { \ + if (debug != 0) \ + g_debug ("%s: " format, G_STRFUNC, ## __VA_ARGS__); \ +} G_STMT_END + +static const GDebugKey debug_keys[] = { + { "console", 1 }, + { NULL, 0 } +}; + +static void plugin_iface_init ( + gpointer g_iface, + gpointer data); + +static const gchar * const sidecar_interfaces[] = { + GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, + NULL +}; + +G_DEFINE_TYPE_WITH_CODE (GabbleConsolePlugin, gabble_console_plugin, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GABBLE_TYPE_PLUGIN, plugin_iface_init); + ) + +static void +gabble_console_plugin_init (GabbleConsolePlugin *self) +{ +} + +static void +gabble_console_plugin_class_init (GabbleConsolePluginClass *klass) +{ +} + +static void +gabble_console_plugin_create_sidecar ( + GabblePlugin *plugin, + const gchar *sidecar_interface, + GabbleConnection *connection, + WockySession *session, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (plugin), + callback, user_data, + /* sic: all plugins share gabble_plugin_create_sidecar_finish() so we + * need to use the same source tag. + */ + gabble_plugin_create_sidecar); + GabbleSidecar *sidecar = NULL; + + if (!tp_strdiff (sidecar_interface, GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE)) + { + sidecar = g_object_new (GABBLE_TYPE_CONSOLE_SIDECAR, + "connection", connection, + "session", session, + NULL); + } + else + { + g_simple_async_result_set_error (result, TP_ERRORS, + TP_ERROR_NOT_IMPLEMENTED, "'%s' not implemented", sidecar_interface); + } + + if (sidecar != NULL) + g_simple_async_result_set_op_res_gpointer (result, sidecar, + g_object_unref); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +static void +plugin_iface_init ( + gpointer g_iface, + gpointer data G_GNUC_UNUSED) +{ + GabblePluginInterface *iface = g_iface; + + iface->name = "XMPP console"; + iface->version = PACKAGE_VERSION; + iface->sidecar_interfaces = sidecar_interfaces; + iface->create_sidecar = gabble_console_plugin_create_sidecar; +} + +GabblePlugin * +gabble_plugin_create (void) +{ + debug = g_parse_debug_string (g_getenv ("GABBLE_DEBUG"), debug_keys, + G_N_ELEMENTS (debug_keys) - 1); + DEBUG ("loaded"); + + return g_object_new (GABBLE_TYPE_CONSOLE_PLUGIN, + NULL); +} + +/************************** + * Sidecar implementation * + **************************/ + +enum { + PROP_0, + PROP_CONNECTION, + PROP_SESSION +}; + +struct _GabbleConsoleSidecarPrivate +{ + WockySession *session; + TpBaseConnection *connection; + WockyXmppReader *reader; + WockyXmppWriter *writer; +}; + +static void sidecar_iface_init ( + gpointer g_iface, + gpointer data); + +static void console_iface_init ( + gpointer g_iface, + gpointer data); + +G_DEFINE_TYPE_WITH_CODE (GabbleConsoleSidecar, gabble_console_sidecar, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SIDECAR, sidecar_iface_init); + G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_GABBLE_PLUGIN_CONSOLE, + console_iface_init); + ) + +static void +gabble_console_sidecar_init (GabbleConsoleSidecar *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CONSOLE_SIDECAR, + GabbleConsoleSidecarPrivate); + self->priv->reader = wocky_xmpp_reader_new_no_stream (); + self->priv->writer = wocky_xmpp_writer_new_no_stream (); +} + +static void +gabble_console_sidecar_set_property ( + GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); + + switch (property_id) + { + case PROP_CONNECTION: + g_assert (self->priv->connection == NULL); /* construct-only */ + self->priv->connection = g_value_dup_object (value); + break; + + case PROP_SESSION: + g_assert (self->priv->session == NULL); /* construct-only */ + self->priv->session = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +gabble_console_sidecar_dispose (GObject *object) +{ + void (*chain_up) (GObject *) = + G_OBJECT_CLASS (gabble_console_sidecar_parent_class)->dispose; + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); + + tp_clear_object (&self->priv->connection); + tp_clear_object (&self->priv->reader); + tp_clear_object (&self->priv->writer); + tp_clear_object (&self->priv->session); + + if (chain_up != NULL) + chain_up (object); +} + +static void +gabble_console_sidecar_class_init (GabbleConsoleSidecarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gabble_console_sidecar_set_property; + object_class->dispose = gabble_console_sidecar_dispose; + + g_type_class_add_private (klass, sizeof (GabbleConsoleSidecarPrivate)); + + g_object_class_install_property (object_class, PROP_CONNECTION, + g_param_spec_object ("connection", "Connection", + "Gabble connection", + GABBLE_TYPE_CONNECTION, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_SESSION, + g_param_spec_object ("session", "Session", + "Wocky session", + WOCKY_TYPE_SESSION, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} + +static void sidecar_iface_init ( + gpointer g_iface, + gpointer data) +{ + GabbleSidecarInterface *iface = g_iface; + + iface->interface = GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE; + iface->get_immutable_properties = NULL; +} + +static void +return_from_send_iq ( + GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (source); + DBusGMethodInvocation *context = user_data; + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + GError *error = NULL; + + if (g_simple_async_result_propagate_error (simple, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + } + else + { + WockyStanza *reply = g_simple_async_result_get_op_res_gpointer (simple); + WockyStanzaSubType sub_type; + const guint8 *body; + gsize length; + + wocky_stanza_get_type_info (reply, NULL, &sub_type); + wocky_xmpp_writer_write_stanza (self->priv->writer, reply, &body, &length); + + /* woop woop */ + gabble_svc_gabble_plugin_console_return_from_send_iq (context, + sub_type == WOCKY_STANZA_SUB_TYPE_RESULT ? "result" : "error", + (const gchar *) body); + } +} + +static void +console_iq_reply_cb ( + GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + WockyPorter *porter = WOCKY_PORTER (source); + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GError *error = NULL; + WockyStanza *reply = wocky_porter_send_iq_finish (porter, result, &error); + + if (reply != NULL) + { + g_simple_async_result_set_op_res_gpointer (simple, reply, g_object_unref); + } + else + { + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + } + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static gboolean +get_iq_type (const gchar *type_str, + WockyStanzaSubType *sub_type_out, + GError **error) +{ + if (!wocky_strdiff (type_str, "get")) + { + *sub_type_out = WOCKY_STANZA_SUB_TYPE_GET; + return TRUE; + } + + if (!wocky_strdiff (type_str, "set")) + { + *sub_type_out = WOCKY_STANZA_SUB_TYPE_SET; + return TRUE; + } + + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Type must be 'get' or 'set', not '%s'", type_str); + return FALSE; +} + +static gboolean +validate_jid (const gchar **to, + GError **error) +{ + if (tp_str_empty (*to)) + { + *to = NULL; + return TRUE; + } + + if (wocky_decode_jid (*to, NULL, NULL, NULL)) + return TRUE; + + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "'%s' is not a valid (or empty) JID", *to); + return FALSE; +} + +static gboolean +parse_body_fragment ( + GabbleConsoleSidecar *self, + const gchar *body, + WockyNodeTree **fragment_out, + GError **error) +{ + GabbleConsoleSidecarPrivate *priv = self->priv; + WockyStanza *stanza; + + wocky_xmpp_reader_reset (priv->reader); + wocky_xmpp_reader_push (priv->reader, (const guint8 *) body, strlen (body)); + + *error = wocky_xmpp_reader_get_error (priv->reader); + + if (*error != NULL) + return FALSE; + + stanza = wocky_xmpp_reader_pop_stanza (priv->reader); + + if (stanza == NULL) + { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Incomplete stanza! Bad person."); + return FALSE; + } + + *fragment_out = (WockyNodeTree *) stanza; + return TRUE; +} + +static void +console_send_iq ( + GabbleSvcGabblePluginConsole *sidecar, + const gchar *type_str, + const gchar *to, + const gchar *body, + DBusGMethodInvocation *context) +{ + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (sidecar); + WockyPorter *porter = wocky_session_get_porter (self->priv->session); + WockyStanzaSubType sub_type; + WockyNodeTree *fragment; + GError *error = NULL; + + if (get_iq_type (type_str, &sub_type, &error) && + validate_jid (&to, &error) && + parse_body_fragment (self, body, &fragment, &error)) + { + GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (self), + return_from_send_iq, context, console_send_iq); + WockyStanza *stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, sub_type, + NULL, to, NULL); + + wocky_node_add_node_tree (wocky_stanza_get_top_node (stanza), fragment); + wocky_porter_send_iq_async (porter, stanza, NULL, console_iq_reply_cb, simple); + g_object_unref (fragment); + } + else + { + DEBUG ("%s", error->message); + dbus_g_method_return_error (context, error); + g_error_free (error); + } +} + +static void +console_iface_init ( + gpointer klass, + gpointer data G_GNUC_UNUSED) +{ +#define IMPLEMENT(x) gabble_svc_gabble_plugin_console_implement_##x (\ + klass, console_##x) + IMPLEMENT (send_iq); +#undef IMPLEMENT +} diff --git a/gabble/plugins/console.h b/gabble/plugins/console.h new file mode 100644 index 000000000..e3e2c0304 --- /dev/null +++ b/gabble/plugins/console.h @@ -0,0 +1,85 @@ +/* XML console plugin + * + * Copyright © 2011 Collabora Ltd. <http://www.collabora.co.uk/> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <glib-object.h> + +#include <gio/gio.h> +#include <wocky/wocky-session.h> + +typedef struct _GabbleConsolePlugin GabbleConsolePlugin; +typedef struct _GabbleConsolePluginClass GabbleConsolePluginClass; +typedef struct _GabbleConsolePluginPrivate GabbleConsolePluginPrivate; + +struct _GabbleConsolePlugin { + GObject parent; + GabbleConsolePluginPrivate *priv; +}; + +struct _GabbleConsolePluginClass { + GObjectClass parent; +}; + +GType gabble_console_plugin_get_type (void); + +#define GABBLE_TYPE_CONSOLE_PLUGIN \ + (gabble_console_plugin_get_type ()) +#define GABBLE_CONSOLE_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_CONSOLE_PLUGIN, \ + GabbleConsolePlugin)) +#define GABBLE_CONSOLE_PLUGIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_CONSOLE_PLUGIN, \ + GabbleConsolePluginClass)) +#define GABBLE_IS_CONSOLE_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_CONSOLE_PLUGIN)) +#define GABBLE_IS_CONSOLE_PLUGIN_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_CONSOLE_PLUGIN)) +#define GABBLE_CONSOLE_PLUGIN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONSOLE_PLUGIN, \ + GabbleConsolePluginClass)) + +typedef struct _GabbleConsoleSidecar GabbleConsoleSidecar; +typedef struct _GabbleConsoleSidecarClass GabbleConsoleSidecarClass; +typedef struct _GabbleConsoleSidecarPrivate GabbleConsoleSidecarPrivate; + +struct _GabbleConsoleSidecar { + GObject parent; + GabbleConsoleSidecarPrivate *priv; +}; + +struct _GabbleConsoleSidecarClass { + GObjectClass parent; +}; + +GType gabble_console_sidecar_get_type (void); + +#define GABBLE_TYPE_CONSOLE_SIDECAR \ + (gabble_console_sidecar_get_type ()) +#define GABBLE_CONSOLE_SIDECAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_CONSOLE_SIDECAR, \ + GabbleConsoleSidecar)) +#define GABBLE_CONSOLE_SIDECAR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_CONSOLE_SIDECAR, \ + GabbleConsoleSidecarClass)) +#define GABBLE_IS_CONSOLE_SIDECAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_CONSOLE_SIDECAR)) +#define GABBLE_IS_CONSOLE_SIDECAR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_CONSOLE_SIDECAR)) +#define GABBLE_CONSOLE_SIDECAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONSOLE_SIDECAR, \ + GabbleConsoleSidecarClass)) |