From c48dfbe92fa02ecbdace6f7ad608d9ef7d87ec43 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 22 Jun 2013 18:09:26 +0100 Subject: console: split plugin into one file per class --- plugins/Makefile.am | 9 +- plugins/console.c | 678 ---------------------------------------------- plugins/console.h | 88 ------ plugins/console/debug.c | 36 +++ plugins/console/debug.h | 28 ++ plugins/console/plugin.c | 134 +++++++++ plugins/console/plugin.h | 51 ++++ plugins/console/sidecar.c | 548 +++++++++++++++++++++++++++++++++++++ plugins/console/sidecar.h | 54 ++++ 9 files changed, 858 insertions(+), 768 deletions(-) delete mode 100644 plugins/console.c delete mode 100644 plugins/console.h create mode 100644 plugins/console/debug.c create mode 100644 plugins/console/debug.h create mode 100644 plugins/console/plugin.c create mode 100644 plugins/console/plugin.h create mode 100644 plugins/console/sidecar.c create mode 100644 plugins/console/sidecar.h (limited to 'plugins') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index a51160472..159ecb141 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -62,8 +62,13 @@ libgateways_la_SOURCES = \ gateways.h libconsole_la_SOURCES = \ - console.c \ - console.h + console/debug.c \ + console/debug.h \ + console/plugin.c \ + console/plugin.h \ + console/sidecar.c \ + console/sidecar.h \ + $(NULL) AM_CFLAGS = $(ERROR_CFLAGS) \ -I $(top_srcdir) -I $(top_builddir) \ diff --git a/plugins/console.c b/plugins/console.c deleted file mode 100644 index fd49d0bd8..000000000 --- a/plugins/console.c +++ /dev/null @@ -1,678 +0,0 @@ -/* XML console plugin - * - * Copyright © 2011 Collabora Ltd. - * - * 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 "config.h" - -#include "console.h" - -#include - -#include - -#include - -#include "extensions/extensions.h" - -#include - -/************************* - * 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_async ( - GabblePlugin *plugin, - const gchar *sidecar_interface, - GabblePluginConnection *connection, - WockySession *session, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (plugin), - callback, user_data, - gabble_console_plugin_create_sidecar_async); - 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_ERROR, - 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 GabbleSidecar * -gabble_console_plugin_create_sidecar_finish ( - GabblePlugin *plugin, - GAsyncResult *result, - GError **error) -{ - GabbleSidecar *sidecar; - - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), - error)) - return NULL; - - g_return_val_if_fail (g_simple_async_result_is_valid (result, - G_OBJECT (plugin), gabble_console_plugin_create_sidecar_async), NULL); - - sidecar = GABBLE_SIDECAR (g_simple_async_result_get_op_res_gpointer ( - G_SIMPLE_ASYNC_RESULT (result))); - - return g_object_ref (sidecar); -} - -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_async = gabble_console_plugin_create_sidecar_async; - iface->create_sidecar_finish = gabble_console_plugin_create_sidecar_finish; -} - -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, - PROP_SPEW -}; - -struct _GabbleConsoleSidecarPrivate -{ - WockySession *session; - TpBaseConnection *connection; - WockyXmppReader *reader; - WockyXmppWriter *writer; - - /* %TRUE if we should emit signals when sending or receiving stanzas */ - gboolean spew; - /* 0 if spew is FALSE; or a WockyPorter handler id for all incoming stanzas - * if spew is TRUE. */ - guint incoming_handler; - /* 0 if spew is FALSE; a GLib signal handler id for WockyPorter::sending if - * spew is TRUE. - */ - gulong sending_id; -}; - -static void sidecar_iface_init ( - gpointer g_iface, - gpointer data); -static void console_iface_init ( - gpointer g_iface, - gpointer data); -static void gabble_console_sidecar_set_spew ( - GabbleConsoleSidecar *self, - gboolean spew); - -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); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, - tp_dbus_properties_mixin_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_ns ( - WOCKY_XMPP_NS_JABBER_CLIENT); - self->priv->writer = wocky_xmpp_writer_new_no_stream (); -} - -static void -gabble_console_sidecar_get_property ( - GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); - - switch (property_id) - { - case PROP_SPEW: - g_value_set_boolean (value, self->priv->spew); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); - } -} - -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; - - case PROP_SPEW: - gabble_console_sidecar_set_spew (self, g_value_get_boolean (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); - - gabble_console_sidecar_set_spew (self, FALSE); - - 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); - static TpDBusPropertiesMixinPropImpl console_props[] = { - { "SpewStanzas", "spew-stanzas", "spew-stanzas" }, - { NULL }, - }; - static TpDBusPropertiesMixinIfaceImpl interfaces[] = { - { GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, - tp_dbus_properties_mixin_getter_gobject_properties, - /* FIXME: if we were feeling clever, we'd override the setter so that - * we can monitor the bus name of any application which sets - * SpewStanzas to TRUE and flip it back to false when that application - * dies. - * - * Alternatively, we could just replace this sidecar with a channel. - */ - tp_dbus_properties_mixin_setter_gobject_properties, - console_props - }, - { NULL }, - }; - - object_class->get_property = gabble_console_sidecar_get_property; - 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_PLUGIN_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)); - - g_object_class_install_property (object_class, PROP_SPEW, - g_param_spec_boolean ("spew-stanzas", "SpewStanzas", - "If %TRUE, someone wants us to spit out a tonne of stanzas", - FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - klass->props_class.interfaces = interfaces; - tp_dbus_properties_mixin_class_init (object_class, - G_STRUCT_OFFSET (GabbleConsoleSidecarClass, props_class)); -} - -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 gboolean -incoming_cb ( - WockyPorter *porter, - WockyStanza *stanza, - gpointer user_data) -{ - GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (user_data); - const guint8 *body; - gsize length; - - wocky_xmpp_writer_write_stanza (self->priv->writer, stanza, &body, &length); - gabble_svc_gabble_plugin_console_emit_stanza_received (self, - (const gchar *) body); - return FALSE; -} - -static void -sending_cb ( - WockyPorter *porter, - WockyStanza *stanza, - gpointer user_data) -{ - GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (user_data); - - if (stanza != NULL) - { - const guint8 *body; - gsize length; - - wocky_xmpp_writer_write_stanza (self->priv->writer, stanza, &body, - &length); - gabble_svc_gabble_plugin_console_emit_stanza_sent (self, - (const gchar *) body); - } -} - -static void -gabble_console_sidecar_set_spew ( - GabbleConsoleSidecar *self, - gboolean spew) -{ - GabbleConsoleSidecarPrivate *priv = self->priv; - - if (!spew != !priv->spew) - { - WockyPorter *porter = wocky_session_get_porter (self->priv->session); - const gchar *props[] = { "SpewStanzas", NULL }; - - priv->spew = spew; - tp_dbus_properties_mixin_emit_properties_changed (G_OBJECT (self), - GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, props); - - if (spew) - { - g_return_if_fail (priv->incoming_handler == 0); - priv->incoming_handler = wocky_porter_register_handler_from_anyone ( - porter, WOCKY_STANZA_TYPE_NONE, WOCKY_STANZA_SUB_TYPE_NONE, - WOCKY_PORTER_HANDLER_PRIORITY_MAX, incoming_cb, self, NULL); - - g_return_if_fail (priv->sending_id == 0); - priv->sending_id = g_signal_connect (porter, "sending", - (GCallback) sending_cb, self); - } - else - { - g_return_if_fail (priv->incoming_handler != 0); - wocky_porter_unregister_handler (porter, priv->incoming_handler); - priv->incoming_handler = 0; - - g_return_if_fail (priv->sending_id != 0); - g_signal_handler_disconnect (porter, priv->sending_id); - priv->sending_id = 0; - } - } -} - -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_ERROR, 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_ERROR, TP_ERROR_INVALID_ARGUMENT, - "'%s' is not a valid (or empty) JID", *to); - return FALSE; -} - -/* - * @xml: doesn't actually have to be a top-level stanza. It can be the body of - * an IQ or whatever. If it has no namespace, it's assumed to be in - * jabber:client. - */ -static gboolean -parse_me_a_stanza ( - GabbleConsoleSidecar *self, - const gchar *xml, - WockyStanza **stanza_out, - GError **error) -{ - GabbleConsoleSidecarPrivate *priv = self->priv; - WockyStanza *stanza; - - wocky_xmpp_reader_reset (priv->reader); - wocky_xmpp_reader_push (priv->reader, (const guint8 *) xml, strlen (xml)); - - *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_ERROR, TP_ERROR_INVALID_ARGUMENT, - "Incomplete stanza! Bad person."); - return FALSE; - } - - *stanza_out = 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; - WockyStanza *fragment; - GError *error = NULL; - - if (get_iq_type (type_str, &sub_type, &error) && - validate_jid (&to, &error) && - parse_me_a_stanza (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), - WOCKY_NODE_TREE (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_stanza_sent_cb ( - GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - WockyPorter *porter = WOCKY_PORTER (source); - DBusGMethodInvocation *context = user_data; - GError *error = NULL; - - if (wocky_porter_send_finish (porter, result, &error)) - { - gabble_svc_gabble_plugin_console_return_from_send_stanza (context); - } - else - { - dbus_g_method_return_error (context, error); - g_clear_error (&error); - } -} - -static gboolean -stanza_looks_coherent ( - WockyStanza *stanza, - GError **error) -{ - WockyNode *top_node = wocky_stanza_get_top_node (stanza); - WockyStanzaType t; - WockyStanzaSubType st; - - wocky_stanza_get_type_info (stanza, &t, &st); - - if (t == WOCKY_STANZA_TYPE_UNKNOWN) - { - g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "I don't know what a <%s xmlns='%s'/> is", top_node->name, - g_quark_to_string (top_node->ns)); - return FALSE; - } - else if (st == WOCKY_STANZA_SUB_TYPE_UNKNOWN) - { - g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, - "I don't know what type='%s' means", - wocky_node_get_attribute (top_node, "type")); - return FALSE; - } - - return TRUE; -} - -static void -console_send_stanza ( - GabbleSvcGabblePluginConsole *sidecar, - const gchar *xml, - DBusGMethodInvocation *context) -{ - GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (sidecar); - WockyPorter *porter = wocky_session_get_porter (self->priv->session); - WockyStanza *stanza = NULL; - GError *error = NULL; - - if (parse_me_a_stanza (self, xml, &stanza, &error) && - stanza_looks_coherent (stanza, &error)) - { - wocky_porter_send_async (porter, stanza, NULL, console_stanza_sent_cb, - context); - } - else - { - DEBUG ("%s", error->message); - dbus_g_method_return_error (context, error); - g_error_free (error); - } - - tp_clear_object (&stanza); -} - -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); - IMPLEMENT (send_stanza); -#undef IMPLEMENT -} diff --git a/plugins/console.h b/plugins/console.h deleted file mode 100644 index e646d067e..000000000 --- a/plugins/console.h +++ /dev/null @@ -1,88 +0,0 @@ -/* XML console plugin - * - * Copyright © 2011 Collabora Ltd. - * - * 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 - -#include -#include -#include - -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; - - TpDBusPropertiesMixinClass props_class; -}; - -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)) diff --git a/plugins/console/debug.c b/plugins/console/debug.c new file mode 100644 index 000000000..29dcf161b --- /dev/null +++ b/plugins/console/debug.c @@ -0,0 +1,36 @@ +/* XML console plugin + * + * Copyright © 2011 Collabora Ltd. + * + * 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 + +#include "console/debug.h" + +int debug = 0; + +static const GDebugKey debug_keys[] = { + { "console", 1 }, + { NULL, 0 } +}; + +void +gabble_console_debug_init (void) +{ + debug = g_parse_debug_string (g_getenv ("GABBLE_DEBUG"), debug_keys, + G_N_ELEMENTS (debug_keys) - 1); +} diff --git a/plugins/console/debug.h b/plugins/console/debug.h new file mode 100644 index 000000000..78031722d --- /dev/null +++ b/plugins/console/debug.h @@ -0,0 +1,28 @@ +/* XML console plugin + * + * Copyright © 2011 Collabora Ltd. + * + * 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 + */ + +extern int debug; + +#define DEBUG(format, ...) \ +G_STMT_START { \ + if (debug != 0) \ + g_debug ("%s: " format, G_STRFUNC, ## __VA_ARGS__); \ +} G_STMT_END + +void gabble_console_debug_init (void); diff --git a/plugins/console/plugin.c b/plugins/console/plugin.c new file mode 100644 index 000000000..d5f02157f --- /dev/null +++ b/plugins/console/plugin.c @@ -0,0 +1,134 @@ +/* XML console plugin + * + * Copyright © 2011 Collabora Ltd. + * + * 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 "config.h" +#include "console/plugin.h" + +#include +#include +#include +#include "extensions/extensions.h" + +#include "console/debug.h" +#include "console/sidecar.h" + +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_async ( + GabblePlugin *plugin, + const gchar *sidecar_interface, + GabblePluginConnection *connection, + WockySession *session, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result = g_simple_async_result_new (G_OBJECT (plugin), + callback, user_data, + gabble_console_plugin_create_sidecar_async); + 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_ERROR, + 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 GabbleSidecar * +gabble_console_plugin_create_sidecar_finish ( + GabblePlugin *plugin, + GAsyncResult *result, + GError **error) +{ + GabbleSidecar *sidecar; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + error)) + return NULL; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (plugin), gabble_console_plugin_create_sidecar_async), NULL); + + sidecar = GABBLE_SIDECAR (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (result))); + + return g_object_ref (sidecar); +} + +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_async = gabble_console_plugin_create_sidecar_async; + iface->create_sidecar_finish = gabble_console_plugin_create_sidecar_finish; +} + +GabblePlugin * +gabble_plugin_create (void) +{ + gabble_console_debug_init (); + + DEBUG ("loaded"); + + return g_object_new (GABBLE_TYPE_CONSOLE_PLUGIN, + NULL); +} diff --git a/plugins/console/plugin.h b/plugins/console/plugin.h new file mode 100644 index 000000000..153484f91 --- /dev/null +++ b/plugins/console/plugin.h @@ -0,0 +1,51 @@ +/* XML console plugin + * + * Copyright © 2011 Collabora Ltd. + * + * 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 + +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)) diff --git a/plugins/console/sidecar.c b/plugins/console/sidecar.c new file mode 100644 index 000000000..1e01234cd --- /dev/null +++ b/plugins/console/sidecar.c @@ -0,0 +1,548 @@ +/* XML console plugin + * + * Copyright © 2011 Collabora Ltd. + * + * 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 "config.h" +#include "console/sidecar.h" + +#include +#include +#include +#include "extensions/extensions.h" + +#include "console/debug.h" + +enum { + PROP_0, + PROP_CONNECTION, + PROP_SESSION, + PROP_SPEW +}; + +struct _GabbleConsoleSidecarPrivate +{ + WockySession *session; + TpBaseConnection *connection; + WockyXmppReader *reader; + WockyXmppWriter *writer; + + /* %TRUE if we should emit signals when sending or receiving stanzas */ + gboolean spew; + /* 0 if spew is FALSE; or a WockyPorter handler id for all incoming stanzas + * if spew is TRUE. */ + guint incoming_handler; + /* 0 if spew is FALSE; a GLib signal handler id for WockyPorter::sending if + * spew is TRUE. + */ + gulong sending_id; +}; + +static void sidecar_iface_init ( + gpointer g_iface, + gpointer data); +static void console_iface_init ( + gpointer g_iface, + gpointer data); +static void gabble_console_sidecar_set_spew ( + GabbleConsoleSidecar *self, + gboolean spew); + +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); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, + tp_dbus_properties_mixin_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_ns ( + WOCKY_XMPP_NS_JABBER_CLIENT); + self->priv->writer = wocky_xmpp_writer_new_no_stream (); +} + +static void +gabble_console_sidecar_get_property ( + GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); + + switch (property_id) + { + case PROP_SPEW: + g_value_set_boolean (value, self->priv->spew); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +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; + + case PROP_SPEW: + gabble_console_sidecar_set_spew (self, g_value_get_boolean (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); + + gabble_console_sidecar_set_spew (self, FALSE); + + 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); + static TpDBusPropertiesMixinPropImpl console_props[] = { + { "SpewStanzas", "spew-stanzas", "spew-stanzas" }, + { NULL }, + }; + static TpDBusPropertiesMixinIfaceImpl interfaces[] = { + { GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, + tp_dbus_properties_mixin_getter_gobject_properties, + /* FIXME: if we were feeling clever, we'd override the setter so that + * we can monitor the bus name of any application which sets + * SpewStanzas to TRUE and flip it back to false when that application + * dies. + * + * Alternatively, we could just replace this sidecar with a channel. + */ + tp_dbus_properties_mixin_setter_gobject_properties, + console_props + }, + { NULL }, + }; + + object_class->get_property = gabble_console_sidecar_get_property; + 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_PLUGIN_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)); + + g_object_class_install_property (object_class, PROP_SPEW, + g_param_spec_boolean ("spew-stanzas", "SpewStanzas", + "If %TRUE, someone wants us to spit out a tonne of stanzas", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + klass->props_class.interfaces = interfaces; + tp_dbus_properties_mixin_class_init (object_class, + G_STRUCT_OFFSET (GabbleConsoleSidecarClass, props_class)); +} + +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 gboolean +incoming_cb ( + WockyPorter *porter, + WockyStanza *stanza, + gpointer user_data) +{ + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (user_data); + const guint8 *body; + gsize length; + + wocky_xmpp_writer_write_stanza (self->priv->writer, stanza, &body, &length); + gabble_svc_gabble_plugin_console_emit_stanza_received (self, + (const gchar *) body); + return FALSE; +} + +static void +sending_cb ( + WockyPorter *porter, + WockyStanza *stanza, + gpointer user_data) +{ + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (user_data); + + if (stanza != NULL) + { + const guint8 *body; + gsize length; + + wocky_xmpp_writer_write_stanza (self->priv->writer, stanza, &body, + &length); + gabble_svc_gabble_plugin_console_emit_stanza_sent (self, + (const gchar *) body); + } +} + +static void +gabble_console_sidecar_set_spew ( + GabbleConsoleSidecar *self, + gboolean spew) +{ + GabbleConsoleSidecarPrivate *priv = self->priv; + + if (!spew != !priv->spew) + { + WockyPorter *porter = wocky_session_get_porter (self->priv->session); + const gchar *props[] = { "SpewStanzas", NULL }; + + priv->spew = spew; + tp_dbus_properties_mixin_emit_properties_changed (G_OBJECT (self), + GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, props); + + if (spew) + { + g_return_if_fail (priv->incoming_handler == 0); + priv->incoming_handler = wocky_porter_register_handler_from_anyone ( + porter, WOCKY_STANZA_TYPE_NONE, WOCKY_STANZA_SUB_TYPE_NONE, + WOCKY_PORTER_HANDLER_PRIORITY_MAX, incoming_cb, self, NULL); + + g_return_if_fail (priv->sending_id == 0); + priv->sending_id = g_signal_connect (porter, "sending", + (GCallback) sending_cb, self); + } + else + { + g_return_if_fail (priv->incoming_handler != 0); + wocky_porter_unregister_handler (porter, priv->incoming_handler); + priv->incoming_handler = 0; + + g_return_if_fail (priv->sending_id != 0); + g_signal_handler_disconnect (porter, priv->sending_id); + priv->sending_id = 0; + } + } +} + +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_ERROR, 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_ERROR, TP_ERROR_INVALID_ARGUMENT, + "'%s' is not a valid (or empty) JID", *to); + return FALSE; +} + +/* + * @xml: doesn't actually have to be a top-level stanza. It can be the body of + * an IQ or whatever. If it has no namespace, it's assumed to be in + * jabber:client. + */ +static gboolean +parse_me_a_stanza ( + GabbleConsoleSidecar *self, + const gchar *xml, + WockyStanza **stanza_out, + GError **error) +{ + GabbleConsoleSidecarPrivate *priv = self->priv; + WockyStanza *stanza; + + wocky_xmpp_reader_reset (priv->reader); + wocky_xmpp_reader_push (priv->reader, (const guint8 *) xml, strlen (xml)); + + *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_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Incomplete stanza! Bad person."); + return FALSE; + } + + *stanza_out = 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; + WockyStanza *fragment; + GError *error = NULL; + + if (get_iq_type (type_str, &sub_type, &error) && + validate_jid (&to, &error) && + parse_me_a_stanza (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), + WOCKY_NODE_TREE (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_stanza_sent_cb ( + GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + WockyPorter *porter = WOCKY_PORTER (source); + DBusGMethodInvocation *context = user_data; + GError *error = NULL; + + if (wocky_porter_send_finish (porter, result, &error)) + { + gabble_svc_gabble_plugin_console_return_from_send_stanza (context); + } + else + { + dbus_g_method_return_error (context, error); + g_clear_error (&error); + } +} + +static gboolean +stanza_looks_coherent ( + WockyStanza *stanza, + GError **error) +{ + WockyNode *top_node = wocky_stanza_get_top_node (stanza); + WockyStanzaType t; + WockyStanzaSubType st; + + wocky_stanza_get_type_info (stanza, &t, &st); + + if (t == WOCKY_STANZA_TYPE_UNKNOWN) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "I don't know what a <%s xmlns='%s'/> is", top_node->name, + g_quark_to_string (top_node->ns)); + return FALSE; + } + else if (st == WOCKY_STANZA_SUB_TYPE_UNKNOWN) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "I don't know what type='%s' means", + wocky_node_get_attribute (top_node, "type")); + return FALSE; + } + + return TRUE; +} + +static void +console_send_stanza ( + GabbleSvcGabblePluginConsole *sidecar, + const gchar *xml, + DBusGMethodInvocation *context) +{ + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (sidecar); + WockyPorter *porter = wocky_session_get_porter (self->priv->session); + WockyStanza *stanza = NULL; + GError *error = NULL; + + if (parse_me_a_stanza (self, xml, &stanza, &error) && + stanza_looks_coherent (stanza, &error)) + { + wocky_porter_send_async (porter, stanza, NULL, console_stanza_sent_cb, + context); + } + else + { + DEBUG ("%s", error->message); + dbus_g_method_return_error (context, error); + g_error_free (error); + } + + tp_clear_object (&stanza); +} + +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); + IMPLEMENT (send_stanza); +#undef IMPLEMENT +} diff --git a/plugins/console/sidecar.h b/plugins/console/sidecar.h new file mode 100644 index 000000000..b31b2071b --- /dev/null +++ b/plugins/console/sidecar.h @@ -0,0 +1,54 @@ +/* XML console plugin + * + * Copyright © 2011 Collabora Ltd. + * + * 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 +#include + +typedef struct _GabbleConsoleSidecar GabbleConsoleSidecar; +typedef struct _GabbleConsoleSidecarClass GabbleConsoleSidecarClass; +typedef struct _GabbleConsoleSidecarPrivate GabbleConsoleSidecarPrivate; + +struct _GabbleConsoleSidecar { + GObject parent; + GabbleConsoleSidecarPrivate *priv; +}; + +struct _GabbleConsoleSidecarClass { + GObjectClass parent; + + TpDBusPropertiesMixinClass props_class; +}; + +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)) -- cgit v1.2.1