From 8c3585c858fb41ea404af29b6f3c507edbda8249 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 22 Jun 2013 19:16:55 +0100 Subject: plugin-loader: clear GError after g_dir_open fails --- src/plugin-loader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugin-loader.c b/src/plugin-loader.c index d308437ee..2e14c0781 100644 --- a/src/plugin-loader.c +++ b/src/plugin-loader.c @@ -108,7 +108,6 @@ plugin_loader_try_to_load ( static void gabble_plugin_loader_probe (GabblePluginLoader *self) { - GError *error = NULL; const gchar *directory_names = g_getenv ("GABBLE_PLUGIN_DIR"); gchar **dir_array; gchar **ptr; @@ -132,13 +131,15 @@ gabble_plugin_loader_probe (GabblePluginLoader *self) for (ptr = dir_array ; *ptr != NULL ; ptr++) { + GError *error = NULL; + DEBUG ("probing %s", *ptr); d = g_dir_open (*ptr, 0, &error); if (d == NULL) { DEBUG ("%s", error->message); - g_error_free (error); + g_clear_error (&error); continue; } -- cgit v1.2.3 From ebc3a9794c4a87092cab0a1e5f2d8c83ddafa5de Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 22 Jun 2013 19:02:40 +0100 Subject: plugin-loader: let get_sidecar_interfaces() return NULL --- src/plugin-loader.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugin-loader.c b/src/plugin-loader.c index 2e14c0781..392715966 100644 --- a/src/plugin-loader.c +++ b/src/plugin-loader.c @@ -89,13 +89,18 @@ plugin_loader_try_to_load ( } else { - gchar *sidecars = g_strjoinv (", ", - (gchar **) gabble_plugin_get_sidecar_interfaces (plugin)); + const gchar * const *interfaces = gabble_plugin_get_sidecar_interfaces (plugin); const gchar *version = gabble_plugin_get_version (plugin); + gchar *sidecars; if (version == NULL) version = "(unspecified)"; + if (interfaces != NULL) + sidecars = g_strjoinv (", ", (gchar **) interfaces); + else + sidecars = g_strdup ("none (maybe it implements some channels instead?)"); + DEBUG ("loaded '%s' version %s (%s), implementing these sidecars: %s", gabble_plugin_get_name (plugin), version, path, sidecars); -- cgit v1.2.3 From c53a7c69b9b6615a73a392879458f6a4314ca1a8 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Mon, 13 Feb 2012 12:13:24 +0000 Subject: console UI: quit cleanly if connection has gone away The console UI doesn't watch for NameOwnerChanged to notice the connection going away from beneath it. No big deal, it's a tool for nerds, but if you tried to quit it, it would not close cleanly: the call to self.snoopy.teardown() (which tries to turn off the connection dumping all stanzas over D-Bus) throws an exception, and so Gtk.main_quit() never got called. --- plugins/telepathy-gabble-xmpp-console | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/telepathy-gabble-xmpp-console b/plugins/telepathy-gabble-xmpp-console index 8b96469d8..731929ba1 100755 --- a/plugins/telepathy-gabble-xmpp-console +++ b/plugins/telepathy-gabble-xmpp-console @@ -347,7 +347,11 @@ plugin installed.""" % locals() self.connect('destroy', Window.__destroy_cb) def __destroy_cb(self): - self.snoopy.teardown() + try: + self.snoopy.teardown() + except GLib.GError, e: + print "Couldn't turn off the monitor (maybe the connection went away?)" + print e Gtk.main_quit() GABBLE_PREFIX = 'org.freedesktop.Telepathy.Connection.gabble.jabber.' -- cgit v1.2.3 From 9ffab2b80a0395dd681dc740dc29ddf20f0ff841 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Mon, 13 Feb 2012 12:47:07 +0000 Subject: console UI: list possible account identifiers --- plugins/telepathy-gabble-xmpp-console | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/plugins/telepathy-gabble-xmpp-console b/plugins/telepathy-gabble-xmpp-console index 731929ba1..7e473d468 100755 --- a/plugins/telepathy-gabble-xmpp-console +++ b/plugins/telepathy-gabble-xmpp-console @@ -357,20 +357,35 @@ plugin installed.""" % locals() GABBLE_PREFIX = 'org.freedesktop.Telepathy.Connection.gabble.jabber.' AM_BUS_NAME = 'org.freedesktop.Telepathy.AccountManager' +AM_PATH = '/org/freedesktop/Telepathy/AccountManager' +AM_IFACE = 'org.freedesktop.Telepathy.AccountManager' ACCOUNT_PREFIX = '/org/freedesktop/Telepathy/Account' ACCOUNT_IFACE = 'org.freedesktop.Telepathy.Account' -def usage(): +def usage(bus): + am_proxy = Gio.DBusProxy.new_sync(bus, 0, None, + AM_BUS_NAME, AM_PATH, AM_IFACE, None) + valid_accounts = am_proxy.get_cached_property('ValidAccounts').get_objv() + xmpp_accounts = sorted( + path[len(ACCOUNT_PREFIX + '/'):] + for path in valid_accounts + if path.startswith(ACCOUNT_PREFIX + '/gabble/') + ) + print """ Usage: %(arg0)s gabble/jabber/blahblah %(arg0)s %(prefix)sblahblah -List account identifiers using `mc-tool list | grep gabble`. +Here are some account identifiers: + + %(accounts)s + List connection bus names using `qdbus | grep gabble`. """ % { 'arg0': sys.argv[0], 'prefix': GABBLE_PREFIX, + 'accounts': '\n '.join(xmpp_accounts), } raise SystemExit(1) @@ -378,7 +393,7 @@ if __name__ == '__main__': bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) if len(sys.argv) != 2: - usage() + usage(bus) thing = sys.argv[1] @@ -395,7 +410,7 @@ if __name__ == '__main__': thing = nameify(path) if not re.match('^%s[a-zA-Z0-9_]+$' % GABBLE_PREFIX, thing): - usage() + usage(bus) win = Window(bus, thing) win.show_all() -- cgit v1.2.3 From 0c8fadad44e3b4acc9a2086f59df9d653128dbd8 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Mon, 13 Feb 2012 13:04:09 +0000 Subject: console UI: throw up a message when the connection dies --- plugins/telepathy-gabble-xmpp-console | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/plugins/telepathy-gabble-xmpp-console b/plugins/telepathy-gabble-xmpp-console index 7e473d468..d54d52ac7 100755 --- a/plugins/telepathy-gabble-xmpp-console +++ b/plugins/telepathy-gabble-xmpp-console @@ -324,10 +324,14 @@ plugin installed.""" % locals() self.console_proxy = Gio.DBusProxy.new_sync(bus, 0, None, connection_bus_name, sidecar_path, CONSOLE_IFACE, None) + self.console_proxy.connect('notify::g-name-owner', self.__console_noc_cb) # Build up the UI + self.grid = Gtk.Grid() + self.add(self.grid) + self.nb = Gtk.Notebook() - self.add(self.nb) + self.grid.attach(self.nb, 0, 0, 1, 1) self.iq = IQPage(self.console_proxy) self.nb.insert_page(self.iq, @@ -344,8 +348,28 @@ plugin installed.""" % locals() Gtk.Label.new_with_mnemonic("_Monitor network traffic"), self.SNOOPY_PAGE) + self.infobar = Gtk.InfoBar() + self.infobar.set_message_type(Gtk.MessageType.WARNING) + self.infobar.set_no_show_all(True) + label = Gtk.Label("The connection went away! Time to leave.") + label.show() + self.infobar.get_content_area().add(label) + self.infobar_close_button = self.infobar.add_button("Close", Gtk.ResponseType.CLOSE) + self.infobar.connect('response', lambda infobar, response: Gtk.main_quit()) + self.infobar.connect('close', lambda infobar: Gtk.main_quit()) + + self.grid.attach_next_to(self.infobar, self.nb, + Gtk.PositionType.BOTTOM, 1, 1) + self.connect('destroy', Window.__destroy_cb) + def __console_noc_cb(self, *args): + if self.console_proxy.get_name_owner() is None: + self.infobar.show() + self.infobar_close_button.grab_focus() + self.nb.set_sensitive(False) + # TODO: reconnect if the connection comes back. + def __destroy_cb(self): try: self.snoopy.teardown() -- cgit v1.2.3 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 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.3 From 1bc93dbe2b02b6f44aa5b1a75aeec4a4a0e33ea9 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 22 Jun 2013 18:20:06 +0100 Subject: console: make the sidecar subclass TpBaseChannel --- plugins/console/sidecar.c | 46 +++++++++++++--------------------------------- plugins/console/sidecar.h | 7 +++---- 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/plugins/console/sidecar.c b/plugins/console/sidecar.c index 1e01234cd..5484a3ca0 100644 --- a/plugins/console/sidecar.c +++ b/plugins/console/sidecar.c @@ -29,7 +29,6 @@ enum { PROP_0, - PROP_CONNECTION, PROP_SESSION, PROP_SPEW }; @@ -37,7 +36,6 @@ enum { struct _GabbleConsoleSidecarPrivate { WockySession *session; - TpBaseConnection *connection; WockyXmppReader *reader; WockyXmppWriter *writer; @@ -63,7 +61,7 @@ static void gabble_console_sidecar_set_spew ( gboolean spew); G_DEFINE_TYPE_WITH_CODE (GabbleConsoleSidecar, gabble_console_sidecar, - G_TYPE_OBJECT, + TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SIDECAR, sidecar_iface_init); G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_GABBLE_PLUGIN_CONSOLE, console_iface_init); @@ -112,11 +110,6 @@ gabble_console_sidecar_set_property ( 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); @@ -140,7 +133,6 @@ gabble_console_sidecar_dispose (GObject *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); @@ -157,21 +149,6 @@ gabble_console_sidecar_class_init (GabbleConsoleSidecarClass *klass) { "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; @@ -179,12 +156,6 @@ gabble_console_sidecar_class_init (GabbleConsoleSidecarClass *klass) 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", @@ -197,9 +168,18 @@ gabble_console_sidecar_class_init (GabbleConsoleSidecarClass *klass) 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)); + tp_dbus_properties_mixin_implement_interface (object_class, + GABBLE_IFACE_QUARK_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); } static void sidecar_iface_init ( diff --git a/plugins/console/sidecar.h b/plugins/console/sidecar.h index b31b2071b..9045b1f02 100644 --- a/plugins/console/sidecar.h +++ b/plugins/console/sidecar.h @@ -25,14 +25,13 @@ typedef struct _GabbleConsoleSidecarClass GabbleConsoleSidecarClass; typedef struct _GabbleConsoleSidecarPrivate GabbleConsoleSidecarPrivate; struct _GabbleConsoleSidecar { - GObject parent; + TpBaseChannel parent; + GabbleConsoleSidecarPrivate *priv; }; struct _GabbleConsoleSidecarClass { - GObjectClass parent; - - TpDBusPropertiesMixinClass props_class; + TpBaseChannelClass parent; }; GType gabble_console_sidecar_get_type (void); -- cgit v1.2.3 From e996dd8b1f69eb1367582916b92e702426dfdf25 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 22 Jun 2013 18:38:05 +0100 Subject: console: add a channel manager I am already losing the will to live at the thought of implementing the actual requesting and closing bits. --- plugins/Makefile.am | 2 + plugins/console/channel-manager.c | 125 ++++++++++++++++++++++++++++++++++++++ plugins/console/channel-manager.h | 51 ++++++++++++++++ plugins/console/plugin.c | 16 +++++ tests/twisted/console.py | 11 ++++ 5 files changed, 205 insertions(+) create mode 100644 plugins/console/channel-manager.c create mode 100644 plugins/console/channel-manager.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 159ecb141..8cdbb423c 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -62,6 +62,8 @@ libgateways_la_SOURCES = \ gateways.h libconsole_la_SOURCES = \ + console/channel-manager.c \ + console/channel-manager.h \ console/debug.c \ console/debug.h \ console/plugin.c \ diff --git a/plugins/console/channel-manager.c b/plugins/console/channel-manager.c new file mode 100644 index 000000000..44f4a8b6d --- /dev/null +++ b/plugins/console/channel-manager.c @@ -0,0 +1,125 @@ +/* XML console plugin + * + * Copyright © 2011–2013 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/channel-manager.h" + +#include "extensions/extensions.h" + +static void channel_manager_iface_init (gpointer, gpointer); + +G_DEFINE_TYPE_WITH_CODE (GabbleConsoleChannelManager, gabble_console_channel_manager, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, channel_manager_iface_init) + G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, NULL); + ) + +enum { + PROP_CONNECTION = 1, +}; + +static void +gabble_console_channel_manager_init (GabbleConsoleChannelManager *self) +{ +} + +static void +gabble_console_channel_manager_set_property ( + GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (object); + + switch (property_id) + { + case PROP_CONNECTION: + /* Not reffing this: the connection owns all channel managers, so it + * must outlive us. Taking a reference leads to a cycle. + */ + self->plugin_connection = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +gabble_console_channel_manager_get_property ( + GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (object); + + switch (property_id) + { + case PROP_CONNECTION: + g_value_set_object (value, self->plugin_connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +gabble_console_channel_manager_class_init (GabbleConsoleChannelManagerClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + oclass->set_property = gabble_console_channel_manager_set_property; + oclass->get_property = gabble_console_channel_manager_get_property; + + g_object_class_install_property (oclass, PROP_CONNECTION, + g_param_spec_object ("plugin-connection", "Gabble Plugin Connection", + "Gabble Plugin Connection", + GABBLE_TYPE_PLUGIN_CONNECTION, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} + +static void +gabble_console_channel_manager_type_foreach_channel_class (GType type, + TpChannelManagerTypeChannelClassFunc func, + gpointer user_data) +{ + GHashTable *table = tp_asv_new ( + TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_NONE, + NULL); + + func (type, table, NULL, user_data); + + g_hash_table_unref (table); +} + +static void +channel_manager_iface_init (gpointer g_iface, + gpointer iface_data) +{ + TpChannelManagerIface *iface = g_iface; + + iface->type_foreach_channel_class = gabble_console_channel_manager_type_foreach_channel_class; + + /* not requestable. */ + iface->ensure_channel = NULL; + iface->create_channel = NULL; + iface->request_channel = NULL; +} diff --git a/plugins/console/channel-manager.h b/plugins/console/channel-manager.h new file mode 100644 index 000000000..a876a7b14 --- /dev/null +++ b/plugins/console/channel-manager.h @@ -0,0 +1,51 @@ +/* XML console plugin + * + * Copyright © 2011–2013 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 _GabbleConsoleChannelManager GabbleConsoleChannelManager; +typedef struct _GabbleConsoleChannelManagerClass GabbleConsoleChannelManagerClass; + +struct _GabbleConsoleChannelManagerClass { + GObjectClass parent_class; +}; + +struct _GabbleConsoleChannelManager { + GObject parent; + + GabblePluginConnection *plugin_connection; +}; + +GType gabble_console_channel_manager_get_type (void); + +#define GABBLE_TYPE_CONSOLE_CHANNEL_MANAGER \ + (gabble_console_channel_manager_get_type ()) +#define GABBLE_CONSOLE_CHANNEL_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CONSOLE_CHANNEL_MANAGER, GabbleConsoleChannelManager)) +#define GABBLE_CONSOLE_CHANNEL_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CONSOLE_CHANNEL_MANAGER,\ + GabbleConsoleChannelManagerClass)) +#define GABBLE_IS_CONSOLE_CHANNEL_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CONSOLE_CHANNEL_MANAGER)) +#define GABBLE_IS_CONSOLE_CHANNEL_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CONSOLE_CHANNEL_MANAGER)) +#define GABBLE_CONSOLE_CHANNEL_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONSOLE_CHANNEL_MANAGER,\ + GabbleConsoleChannelManagerClass)) diff --git a/plugins/console/plugin.c b/plugins/console/plugin.c index d5f02157f..222c58378 100644 --- a/plugins/console/plugin.c +++ b/plugins/console/plugin.c @@ -25,6 +25,7 @@ #include #include "extensions/extensions.h" +#include "console/channel-manager.h" #include "console/debug.h" #include "console/sidecar.h" @@ -108,6 +109,20 @@ gabble_console_plugin_create_sidecar_finish ( return g_object_ref (sidecar); } +static GPtrArray * +gabble_console_plugin_create_channel_managers (GabblePlugin *plugin, + GabblePluginConnection *plugin_connection) +{ + GPtrArray *ret = g_ptr_array_new (); + + g_ptr_array_add (ret, + g_object_new (GABBLE_TYPE_CONSOLE_CHANNEL_MANAGER, + "plugin-connection", plugin_connection, + NULL)); + + return ret; +} + static void plugin_iface_init ( gpointer g_iface, @@ -120,6 +135,7 @@ plugin_iface_init ( 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; + iface->create_channel_managers = gabble_console_plugin_create_channel_managers; } GabblePlugin * diff --git a/tests/twisted/console.py b/tests/twisted/console.py index 95c76a30e..8aab65f76 100644 --- a/tests/twisted/console.py +++ b/tests/twisted/console.py @@ -10,6 +10,7 @@ from gabbletest import exec_test, acknowledge_iq, elem, elem_iq from config import PLUGINS_ENABLED from twisted.words.xish import domish import ns +import constants as cs CONSOLE_PLUGIN_IFACE = "org.freedesktop.Telepathy.Gabble.Plugin.Console" STACY = 'stacy@pilgrim.lit' @@ -27,6 +28,16 @@ def send_unrecognised_get(q, stream): return q.expect('stream-iq', iq_type='error') def test(q, bus, conn, stream): + rccs = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, + 'RequestableChannelClasses') + + fixed = { + cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE, + cs.TARGET_HANDLE_TYPE: cs.HT_NONE, + } + allowed = [] + assertContains((fixed, allowed), rccs) + path, _ = conn.Future.EnsureSidecar(CONSOLE_PLUGIN_IFACE) console = ProxyWrapper(bus.get_object(conn.bus_name, path), CONSOLE_PLUGIN_IFACE) -- cgit v1.2.3 From ddb451a71569bdd6b7d9a673d12e8e83eddb818e Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 22 Jun 2013 19:10:10 +0100 Subject: console: turn it into an actual channel --- plugins/console/channel-manager.c | 103 ++++++++++++++++++++++++++++++++++++-- plugins/console/channel-manager.h | 1 + plugins/console/plugin.c | 59 ---------------------- plugins/console/sidecar.c | 61 +++++++++++----------- tests/twisted/console.py | 7 ++- 5 files changed, 134 insertions(+), 97 deletions(-) diff --git a/plugins/console/channel-manager.c b/plugins/console/channel-manager.c index 44f4a8b6d..b2a17902d 100644 --- a/plugins/console/channel-manager.c +++ b/plugins/console/channel-manager.c @@ -21,6 +21,7 @@ #include "console/channel-manager.h" #include "extensions/extensions.h" +#include "console/sidecar.h" static void channel_manager_iface_init (gpointer, gpointer); @@ -80,6 +81,23 @@ gabble_console_channel_manager_get_property ( } } + +static void +gabble_console_channel_manager_dispose ( + GObject *object) +{ + GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (object); + TpBaseChannel *channel; + + while ((channel = g_queue_peek_head (&self->console_channels)) != NULL) + { + tp_base_channel_close (channel); + } + + G_OBJECT_CLASS (gabble_console_channel_manager_parent_class)->dispose (object); +} + + static void gabble_console_channel_manager_class_init (GabbleConsoleChannelManagerClass *klass) { @@ -87,6 +105,7 @@ gabble_console_channel_manager_class_init (GabbleConsoleChannelManagerClass *kla oclass->set_property = gabble_console_channel_manager_set_property; oclass->get_property = gabble_console_channel_manager_get_property; + oclass->dispose = gabble_console_channel_manager_dispose; g_object_class_install_property (oclass, PROP_CONNECTION, g_param_spec_object ("plugin-connection", "Gabble Plugin Connection", @@ -95,6 +114,13 @@ gabble_console_channel_manager_class_init (GabbleConsoleChannelManagerClass *kla G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } + +static const gchar * const allowed[] = { + TP_PROP_CHANNEL_CHANNEL_TYPE, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, + NULL +}; + static void gabble_console_channel_manager_type_foreach_channel_class (GType type, TpChannelManagerTypeChannelClassFunc func, @@ -110,6 +136,77 @@ gabble_console_channel_manager_type_foreach_channel_class (GType type, g_hash_table_unref (table); } + +static void +console_channel_closed_cb ( + GabbleConsoleSidecar *channel, + gpointer user_data) +{ + GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (user_data); + + tp_channel_manager_emit_channel_closed_for_object (self, + TP_EXPORTABLE_CHANNEL (channel)); + + if (g_queue_remove (&self->console_channels, channel)) + { + g_object_unref (channel); + } +} + + +static gboolean +gabble_console_channel_manager_create_channel ( + TpChannelManager *manager, + gpointer request_token, + GHashTable *request_properties) +{ + GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (manager); + TpBaseChannel *channel = NULL; + GError *error = NULL; + GSList *request_tokens; + + if (tp_strdiff (tp_asv_get_string (request_properties, + TP_IFACE_CHANNEL ".ChannelType"), + GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE)) + return FALSE; + + if (tp_asv_get_uint32 (request_properties, + TP_IFACE_CHANNEL ".TargetHandleType", NULL) != 0) + { + g_set_error (&error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "Console channels can't have a target handle"); + goto error; + } + + if (tp_channel_manager_asv_has_unknown_properties (request_properties, + allowed, + allowed, + &error)) + goto error; + + channel = g_object_new (GABBLE_TYPE_CONSOLE_SIDECAR, + "connection", self->plugin_connection, + NULL); + tp_base_channel_register (channel); + g_signal_connect (channel, "closed", (GCallback) console_channel_closed_cb, + self); + g_queue_push_tail (&self->console_channels, channel); + + request_tokens = g_slist_prepend (NULL, request_token); + tp_channel_manager_emit_new_channel (self, + TP_EXPORTABLE_CHANNEL (channel), request_tokens); + g_slist_free (request_tokens); + + return TRUE; + +error: + tp_channel_manager_emit_request_failed (self, request_token, + error->domain, error->code, error->message); + g_error_free (error); + return TRUE; +} + + static void channel_manager_iface_init (gpointer g_iface, gpointer iface_data) @@ -117,9 +214,5 @@ channel_manager_iface_init (gpointer g_iface, TpChannelManagerIface *iface = g_iface; iface->type_foreach_channel_class = gabble_console_channel_manager_type_foreach_channel_class; - - /* not requestable. */ - iface->ensure_channel = NULL; - iface->create_channel = NULL; - iface->request_channel = NULL; + iface->create_channel = gabble_console_channel_manager_create_channel; } diff --git a/plugins/console/channel-manager.h b/plugins/console/channel-manager.h index a876a7b14..8ec4adc0a 100644 --- a/plugins/console/channel-manager.h +++ b/plugins/console/channel-manager.h @@ -31,6 +31,7 @@ struct _GabbleConsoleChannelManager { GObject parent; GabblePluginConnection *plugin_connection; + GQueue console_channels; }; GType gabble_console_channel_manager_get_type (void); diff --git a/plugins/console/plugin.c b/plugins/console/plugin.c index 222c58378..52386016d 100644 --- a/plugins/console/plugin.c +++ b/plugins/console/plugin.c @@ -53,62 +53,6 @@ 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 GPtrArray * gabble_console_plugin_create_channel_managers (GabblePlugin *plugin, GabblePluginConnection *plugin_connection) @@ -132,9 +76,6 @@ plugin_iface_init ( 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; iface->create_channel_managers = gabble_console_plugin_create_channel_managers; } diff --git a/plugins/console/sidecar.c b/plugins/console/sidecar.c index 5484a3ca0..b224b7f48 100644 --- a/plugins/console/sidecar.c +++ b/plugins/console/sidecar.c @@ -29,7 +29,6 @@ enum { PROP_0, - PROP_SESSION, PROP_SPEW }; @@ -50,23 +49,18 @@ struct _GabbleConsoleSidecarPrivate 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); +static void gabble_console_sidecar_close (TpBaseChannel *chan); G_DEFINE_TYPE_WITH_CODE (GabbleConsoleSidecar, gabble_console_sidecar, TP_TYPE_BASE_CHANNEL, - 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 @@ -79,6 +73,25 @@ gabble_console_sidecar_init (GabbleConsoleSidecar *self) self->priv->writer = wocky_xmpp_writer_new_no_stream (); } + +static void +gabble_console_sidecar_constructed (GObject *object) +{ + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); + void (*chain_up)(GObject *) = + G_OBJECT_CLASS (gabble_console_sidecar_parent_class)->constructed; + + if (chain_up != NULL) + chain_up (object); + + self->priv->session = g_object_ref ( + gabble_plugin_connection_get_session ( + GABBLE_PLUGIN_CONNECTION ( + tp_base_channel_get_connection ( + TP_BASE_CHANNEL (self))))); + g_return_if_fail (self->priv->session != NULL); +} + static void gabble_console_sidecar_get_property ( GObject *object, @@ -110,11 +123,6 @@ gabble_console_sidecar_set_property ( switch (property_id) { - 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; @@ -145,22 +153,21 @@ static void gabble_console_sidecar_class_init (GabbleConsoleSidecarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + TpBaseChannelClass *channel_class = TP_BASE_CHANNEL_CLASS (klass); static TpDBusPropertiesMixinPropImpl console_props[] = { { "SpewStanzas", "spew-stanzas", "spew-stanzas" }, { NULL }, }; + object_class->constructed = gabble_console_sidecar_constructed; 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)); + channel_class->channel_type = GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE; + channel_class->close = gabble_console_sidecar_close; - 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_type_class_add_private (klass, sizeof (GabbleConsoleSidecarPrivate)); g_object_class_install_property (object_class, PROP_SPEW, g_param_spec_boolean ("spew-stanzas", "SpewStanzas", @@ -171,25 +178,17 @@ gabble_console_sidecar_class_init (GabbleConsoleSidecarClass *klass) tp_dbus_properties_mixin_implement_interface (object_class, GABBLE_IFACE_QUARK_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); } -static void sidecar_iface_init ( - gpointer g_iface, - gpointer data) +static void +gabble_console_sidecar_close (TpBaseChannel *chan) { - GabbleSidecarInterface *iface = g_iface; + GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (chan); - iface->interface = GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE; - iface->get_immutable_properties = NULL; + gabble_console_sidecar_set_spew (self, FALSE); + tp_base_channel_destroyed (chan); } static gboolean diff --git a/tests/twisted/console.py b/tests/twisted/console.py index 8aab65f76..b2efd658c 100644 --- a/tests/twisted/console.py +++ b/tests/twisted/console.py @@ -38,9 +38,10 @@ def test(q, bus, conn, stream): allowed = [] assertContains((fixed, allowed), rccs) - path, _ = conn.Future.EnsureSidecar(CONSOLE_PLUGIN_IFACE) + path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE }) console = ProxyWrapper(bus.get_object(conn.bus_name, path), - CONSOLE_PLUGIN_IFACE) + CONSOLE_PLUGIN_IFACE, + {'Channel': cs.CHANNEL}) assert not console.Properties.Get(CONSOLE_PLUGIN_IFACE, 'SpewStanzas') es = [ @@ -99,5 +100,7 @@ def test(q, bus, conn, stream): assertEquals('body', body.name) assertEquals(ns.CLIENT, body.uri) + console.Channel.Close() + if __name__ == '__main__': exec_test(test) -- cgit v1.2.3 From 0ddbcae394fd661b539cf03bf1830d6935d18db1 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 22 Jun 2013 19:13:45 +0100 Subject: console: rename ConsoleSidecar to ConsoleChannel --- extensions/Gabble_Plugin_Console.xml | 2 +- plugins/Makefile.am | 4 +- plugins/console/channel-manager.c | 6 +- plugins/console/channel.c | 527 +++++++++++++++++++++++++++++++++++ plugins/console/channel.h | 53 ++++ plugins/console/plugin.c | 2 +- plugins/console/sidecar.c | 527 ----------------------------------- plugins/console/sidecar.h | 53 ---- 8 files changed, 587 insertions(+), 587 deletions(-) create mode 100644 plugins/console/channel.c create mode 100644 plugins/console/channel.h delete mode 100644 plugins/console/sidecar.c delete mode 100644 plugins/console/sidecar.h diff --git a/extensions/Gabble_Plugin_Console.xml b/extensions/Gabble_Plugin_Console.xml index 1e3b52385..a2d94ca0c 100644 --- a/extensions/Gabble_Plugin_Console.xml +++ b/extensions/Gabble_Plugin_Console.xml @@ -23,7 +23,7 @@ -

A sidecar interface providing an XMPP console.

+

A channel type providing an XMPP console.

diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 8cdbb423c..2b0007901 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -64,12 +64,12 @@ libgateways_la_SOURCES = \ libconsole_la_SOURCES = \ console/channel-manager.c \ console/channel-manager.h \ + console/channel.c \ + console/channel.h \ console/debug.c \ console/debug.h \ console/plugin.c \ console/plugin.h \ - console/sidecar.c \ - console/sidecar.h \ $(NULL) AM_CFLAGS = $(ERROR_CFLAGS) \ diff --git a/plugins/console/channel-manager.c b/plugins/console/channel-manager.c index b2a17902d..f2be4f776 100644 --- a/plugins/console/channel-manager.c +++ b/plugins/console/channel-manager.c @@ -21,7 +21,7 @@ #include "console/channel-manager.h" #include "extensions/extensions.h" -#include "console/sidecar.h" +#include "console/channel.h" static void channel_manager_iface_init (gpointer, gpointer); @@ -139,7 +139,7 @@ gabble_console_channel_manager_type_foreach_channel_class (GType type, static void console_channel_closed_cb ( - GabbleConsoleSidecar *channel, + GabbleConsoleChannel *channel, gpointer user_data) { GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (user_data); @@ -184,7 +184,7 @@ gabble_console_channel_manager_create_channel ( &error)) goto error; - channel = g_object_new (GABBLE_TYPE_CONSOLE_SIDECAR, + channel = g_object_new (GABBLE_TYPE_CONSOLE_CHANNEL, "connection", self->plugin_connection, NULL); tp_base_channel_register (channel); diff --git a/plugins/console/channel.c b/plugins/console/channel.c new file mode 100644 index 000000000..390920375 --- /dev/null +++ b/plugins/console/channel.c @@ -0,0 +1,527 @@ +/* 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/channel.h" + +#include +#include +#include +#include "extensions/extensions.h" + +#include "console/debug.h" + +enum { + PROP_0, + PROP_SPEW +}; + +struct _GabbleConsoleChannelPrivate +{ + WockySession *session; + 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 console_iface_init ( + gpointer g_iface, + gpointer data); +static void gabble_console_channel_set_spew ( + GabbleConsoleChannel *self, + gboolean spew); +static void gabble_console_channel_close (TpBaseChannel *chan); + +G_DEFINE_TYPE_WITH_CODE (GabbleConsoleChannel, gabble_console_channel, + TP_TYPE_BASE_CHANNEL, + G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_GABBLE_PLUGIN_CONSOLE, + console_iface_init); + ) + +static void +gabble_console_channel_init (GabbleConsoleChannel *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_CONSOLE_CHANNEL, + GabbleConsoleChannelPrivate); + 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_channel_constructed (GObject *object) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (object); + void (*chain_up)(GObject *) = + G_OBJECT_CLASS (gabble_console_channel_parent_class)->constructed; + + if (chain_up != NULL) + chain_up (object); + + self->priv->session = g_object_ref ( + gabble_plugin_connection_get_session ( + GABBLE_PLUGIN_CONNECTION ( + tp_base_channel_get_connection ( + TP_BASE_CHANNEL (self))))); + g_return_if_fail (self->priv->session != NULL); +} + +static void +gabble_console_channel_get_property ( + GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (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_channel_set_property ( + GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (object); + + switch (property_id) + { + case PROP_SPEW: + gabble_console_channel_set_spew (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +gabble_console_channel_dispose (GObject *object) +{ + void (*chain_up) (GObject *) = + G_OBJECT_CLASS (gabble_console_channel_parent_class)->dispose; + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (object); + + gabble_console_channel_set_spew (self, FALSE); + + 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_channel_class_init (GabbleConsoleChannelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + TpBaseChannelClass *channel_class = TP_BASE_CHANNEL_CLASS (klass); + static TpDBusPropertiesMixinPropImpl console_props[] = { + { "SpewStanzas", "spew-stanzas", "spew-stanzas" }, + { NULL }, + }; + + object_class->constructed = gabble_console_channel_constructed; + object_class->get_property = gabble_console_channel_get_property; + object_class->set_property = gabble_console_channel_set_property; + object_class->dispose = gabble_console_channel_dispose; + + channel_class->channel_type = GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE; + channel_class->close = gabble_console_channel_close; + + g_type_class_add_private (klass, sizeof (GabbleConsoleChannelPrivate)); + + 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)); + + tp_dbus_properties_mixin_implement_interface (object_class, + GABBLE_IFACE_QUARK_GABBLE_PLUGIN_CONSOLE, + tp_dbus_properties_mixin_getter_gobject_properties, + tp_dbus_properties_mixin_setter_gobject_properties, + console_props); +} + +static void +gabble_console_channel_close (TpBaseChannel *chan) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (chan); + + gabble_console_channel_set_spew (self, FALSE); + tp_base_channel_destroyed (chan); +} + +static gboolean +incoming_cb ( + WockyPorter *porter, + WockyStanza *stanza, + gpointer user_data) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (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) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (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_channel_set_spew ( + GabbleConsoleChannel *self, + gboolean spew) +{ + GabbleConsoleChannelPrivate *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) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (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 ( + GabbleConsoleChannel *self, + const gchar *xml, + WockyStanza **stanza_out, + GError **error) +{ + GabbleConsoleChannelPrivate *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 *channel, + const gchar *type_str, + const gchar *to, + const gchar *body, + DBusGMethodInvocation *context) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (channel); + 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 *channel, + const gchar *xml, + DBusGMethodInvocation *context) +{ + GabbleConsoleChannel *self = GABBLE_CONSOLE_CHANNEL (channel); + 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/channel.h b/plugins/console/channel.h new file mode 100644 index 000000000..7e41c5ad2 --- /dev/null +++ b/plugins/console/channel.h @@ -0,0 +1,53 @@ +/* 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 _GabbleConsoleChannel GabbleConsoleChannel; +typedef struct _GabbleConsoleChannelClass GabbleConsoleChannelClass; +typedef struct _GabbleConsoleChannelPrivate GabbleConsoleChannelPrivate; + +struct _GabbleConsoleChannel { + TpBaseChannel parent; + + GabbleConsoleChannelPrivate *priv; +}; + +struct _GabbleConsoleChannelClass { + TpBaseChannelClass parent; +}; + +GType gabble_console_channel_get_type (void); + +#define GABBLE_TYPE_CONSOLE_CHANNEL \ + (gabble_console_channel_get_type ()) +#define GABBLE_CONSOLE_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GABBLE_TYPE_CONSOLE_CHANNEL, \ + GabbleConsoleChannel)) +#define GABBLE_CONSOLE_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GABBLE_TYPE_CONSOLE_CHANNEL, \ + GabbleConsoleChannelClass)) +#define GABBLE_IS_CONSOLE_CHANNEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GABBLE_TYPE_CONSOLE_CHANNEL)) +#define GABBLE_IS_CONSOLE_CHANNEL_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GABBLE_TYPE_CONSOLE_CHANNEL)) +#define GABBLE_CONSOLE_CHANNEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONSOLE_CHANNEL, \ + GabbleConsoleChannelClass)) diff --git a/plugins/console/plugin.c b/plugins/console/plugin.c index 52386016d..c17cf71b0 100644 --- a/plugins/console/plugin.c +++ b/plugins/console/plugin.c @@ -26,8 +26,8 @@ #include "extensions/extensions.h" #include "console/channel-manager.h" +#include "console/channel.h" #include "console/debug.h" -#include "console/sidecar.h" static void plugin_iface_init ( gpointer g_iface, diff --git a/plugins/console/sidecar.c b/plugins/console/sidecar.c deleted file mode 100644 index b224b7f48..000000000 --- a/plugins/console/sidecar.c +++ /dev/null @@ -1,527 +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/sidecar.h" - -#include -#include -#include -#include "extensions/extensions.h" - -#include "console/debug.h" - -enum { - PROP_0, - PROP_SPEW -}; - -struct _GabbleConsoleSidecarPrivate -{ - WockySession *session; - 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 console_iface_init ( - gpointer g_iface, - gpointer data); -static void gabble_console_sidecar_set_spew ( - GabbleConsoleSidecar *self, - gboolean spew); -static void gabble_console_sidecar_close (TpBaseChannel *chan); - -G_DEFINE_TYPE_WITH_CODE (GabbleConsoleSidecar, gabble_console_sidecar, - TP_TYPE_BASE_CHANNEL, - 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_ns ( - WOCKY_XMPP_NS_JABBER_CLIENT); - self->priv->writer = wocky_xmpp_writer_new_no_stream (); -} - - -static void -gabble_console_sidecar_constructed (GObject *object) -{ - GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (object); - void (*chain_up)(GObject *) = - G_OBJECT_CLASS (gabble_console_sidecar_parent_class)->constructed; - - if (chain_up != NULL) - chain_up (object); - - self->priv->session = g_object_ref ( - gabble_plugin_connection_get_session ( - GABBLE_PLUGIN_CONNECTION ( - tp_base_channel_get_connection ( - TP_BASE_CHANNEL (self))))); - g_return_if_fail (self->priv->session != NULL); -} - -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_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->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); - TpBaseChannelClass *channel_class = TP_BASE_CHANNEL_CLASS (klass); - static TpDBusPropertiesMixinPropImpl console_props[] = { - { "SpewStanzas", "spew-stanzas", "spew-stanzas" }, - { NULL }, - }; - - object_class->constructed = gabble_console_sidecar_constructed; - 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; - - channel_class->channel_type = GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE; - channel_class->close = gabble_console_sidecar_close; - - g_type_class_add_private (klass, sizeof (GabbleConsoleSidecarPrivate)); - - 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)); - - tp_dbus_properties_mixin_implement_interface (object_class, - GABBLE_IFACE_QUARK_GABBLE_PLUGIN_CONSOLE, - tp_dbus_properties_mixin_getter_gobject_properties, - tp_dbus_properties_mixin_setter_gobject_properties, - console_props); -} - -static void -gabble_console_sidecar_close (TpBaseChannel *chan) -{ - GabbleConsoleSidecar *self = GABBLE_CONSOLE_SIDECAR (chan); - - gabble_console_sidecar_set_spew (self, FALSE); - tp_base_channel_destroyed (chan); -} - -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 deleted file mode 100644 index 9045b1f02..000000000 --- a/plugins/console/sidecar.h +++ /dev/null @@ -1,53 +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 - -typedef struct _GabbleConsoleSidecar GabbleConsoleSidecar; -typedef struct _GabbleConsoleSidecarClass GabbleConsoleSidecarClass; -typedef struct _GabbleConsoleSidecarPrivate GabbleConsoleSidecarPrivate; - -struct _GabbleConsoleSidecar { - TpBaseChannel parent; - - GabbleConsoleSidecarPrivate *priv; -}; - -struct _GabbleConsoleSidecarClass { - TpBaseChannelClass 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)) -- cgit v1.2.3 From e5385533d99716a27c42d41be96ea32ef7e23f71 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sat, 22 Jun 2013 19:46:56 +0100 Subject: console UI: use channel, not sidecar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I was going to complain about how this was worse than just using the raw D-Bus API via GDBus, but I noticed that the line count went down, so… --- plugins/telepathy-gabble-xmpp-console | 156 ++++++++++++++++------------------ 1 file changed, 75 insertions(+), 81 deletions(-) diff --git a/plugins/telepathy-gabble-xmpp-console b/plugins/telepathy-gabble-xmpp-console index d54d52ac7..1d131217f 100755 --- a/plugins/telepathy-gabble-xmpp-console +++ b/plugins/telepathy-gabble-xmpp-console @@ -3,10 +3,10 @@ """ The world's worst XMPP console user interface. -Pass it the bus name of a Gabble connection; type some words; get minimalistic +Pass it a Gabble account name; type some words; get minimalistic error reporting. -Copyright © 2011 Collabora Ltd. +Copyright © 2011–2013 Collabora Ltd. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -24,23 +24,13 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ import sys -import re from xml.dom import minidom -from gi.repository import Gtk -from gi.repository import GLib -from gi.repository import Gio -from gi.repository import GtkSource +from gi.repository import Gtk, GLib, Gio, GtkSource +from gi.repository import TelepathyGLib as Tp PADDING = 6 -def pathify(name): - return '/' + name.replace('.', '/') - -def nameify(path): - return (path[1:]).replace('/', '.') - -CONN_FUTURE_IFACE = "org.freedesktop.Telepathy.Connection.FUTURE" CONSOLE_IFACE = "org.freedesktop.Telepathy.Gabble.Plugin.Console" class StanzaViewer(Gtk.ScrolledWindow): @@ -302,30 +292,21 @@ class Window(Gtk.Window): STANZA_PAGE = 1 SNOOPY_PAGE = 2 - def __init__(self, bus, connection_bus_name): + def __init__(self, account): Gtk.Window.__init__(self) self.set_title('XMPP Console') self.set_default_size(600, 371) - conn_future_proxy = Gio.DBusProxy.new_sync(bus, 0, None, - connection_bus_name, pathify(connection_bus_name), - CONN_FUTURE_IFACE, None) - try: - sidecar_path, _ = conn_future_proxy.EnsureSidecar('(s)', CONSOLE_IFACE) - except Exception, e: - print """ -Couldn't connect to the XMPP console interface on '%(connection_bus_name)s': - %(e)s -Check that it's a running Jabber connection, and that you have the console -plugin installed.""" % locals() + request = Tp.AccountChannelRequest.new( + account, + { Tp.PROP_CHANNEL_CHANNEL_TYPE: CONSOLE_IFACE }, + 0) + request.create_and_handle_channel_async(None, self.__create_cb, None) - raise SystemExit(2) - - self.console_proxy = Gio.DBusProxy.new_sync(bus, 0, None, - connection_bus_name, sidecar_path, CONSOLE_IFACE, None) - self.console_proxy.connect('notify::g-name-owner', self.__console_noc_cb) + self.connect('destroy', Window.__destroy_cb) + def __build_ui(self): # Build up the UI self.grid = Gtk.Grid() self.add(self.grid) @@ -361,14 +342,42 @@ plugin installed.""" % locals() self.grid.attach_next_to(self.infobar, self.nb, Gtk.PositionType.BOTTOM, 1, 1) - self.connect('destroy', Window.__destroy_cb) + def __create_cb(self, request, result, _): + try: + channel, context = request.create_and_handle_channel_finish(result) + channel.prepare_async(None, self.__channel_prepared_cb, None) + channel.connect('invalidated', self.__channel_invalidated_cb) + + bus_name = channel.get_bus_name() + sidecar_path = channel.get_object_path() - def __console_noc_cb(self, *args): - if self.console_proxy.get_name_owner() is None: - self.infobar.show() - self.infobar_close_button.grab_focus() - self.nb.set_sensitive(False) - # TODO: reconnect if the connection comes back. + bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) + self.console_proxy = Gio.DBusProxy.new_sync(bus, 0, None, + bus_name, sidecar_path, CONSOLE_IFACE, None) + + except GLib.GError as e: + print """ +Couldn't connect to the XMPP console interface on '%(name)s': +%(e)s +Check that you have the console plugin installed.""" % { + 'name': request.get_account().get_path_suffix(), + 'e': e, + } + raise SystemExit(2) + + self.__build_ui() + self.show_all() + + def __channel_prepared_cb(self, channel, result, user_data): + # We only prepare the channel so that ::invalidated will be emitted + # when it closes. + pass + + def __channel_invalidated_cb(self, channel, domain, code, message): + self.infobar.show() + self.infobar_close_button.grab_focus() + self.nb.set_sensitive(False) + # TODO: try to reconnect? def __destroy_cb(self): try: @@ -378,66 +387,51 @@ plugin installed.""" % locals() print e Gtk.main_quit() -GABBLE_PREFIX = 'org.freedesktop.Telepathy.Connection.gabble.jabber.' - -AM_BUS_NAME = 'org.freedesktop.Telepathy.AccountManager' -AM_PATH = '/org/freedesktop/Telepathy/AccountManager' -AM_IFACE = 'org.freedesktop.Telepathy.AccountManager' -ACCOUNT_PREFIX = '/org/freedesktop/Telepathy/Account' -ACCOUNT_IFACE = 'org.freedesktop.Telepathy.Account' - -def usage(bus): - am_proxy = Gio.DBusProxy.new_sync(bus, 0, None, - AM_BUS_NAME, AM_PATH, AM_IFACE, None) - valid_accounts = am_proxy.get_cached_property('ValidAccounts').get_objv() +def usage(am): xmpp_accounts = sorted( - path[len(ACCOUNT_PREFIX + '/'):] - for path in valid_accounts - if path.startswith(ACCOUNT_PREFIX + '/gabble/') - ) + account.get_path_suffix() + for account in am.dup_valid_accounts() + if account.get_cm_name() == 'gabble') print """ Usage: %(arg0)s gabble/jabber/blahblah - %(arg0)s %(prefix)sblahblah Here are some account identifiers: %(accounts)s - -List connection bus names using `qdbus | grep gabble`. """ % { 'arg0': sys.argv[0], - 'prefix': GABBLE_PREFIX, 'accounts': '\n '.join(xmpp_accounts), } raise SystemExit(1) -if __name__ == '__main__': - bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) - - if len(sys.argv) != 2: - usage(bus) - - thing = sys.argv[1] - - if re.match('^gabble/jabber/[a-zA-Z0-9_]+$', thing): - # Looks like an account path to me. - account_proxy = Gio.DBusProxy.new_sync(bus, 0, None, - AM_BUS_NAME, '%s/%s' % (ACCOUNT_PREFIX, thing), - ACCOUNT_IFACE, None) - path = account_proxy.get_cached_property('Connection').get_string() - if path == '/': - print "%s is not online" % thing - raise SystemExit(1) - else: - thing = nameify(path) +def am_prepared_cb(am, result, account_suffix): + try: + am.prepare_finish(result) + except GLib.GError as e: + print e + raise SystemExit(2) + + if account_suffix is None: + usage(am) - if not re.match('^%s[a-zA-Z0-9_]+$' % GABBLE_PREFIX, thing): - usage(bus) + for account in am.dup_valid_accounts(): + if account.get_path_suffix() == account_suffix: + if account.get_connection() is None: + print "%s is not online." % account_suffix + raise SystemExit(2) + else: + win = Window(account) + return + + usage(am) + +if __name__ == '__main__': + account_suffix = sys.argv[1] if len(sys.argv) == 2 else None - win = Window(bus, thing) - win.show_all() + am = Tp.AccountManager.dup() + am.prepare_async([], am_prepared_cb, account_suffix) Gtk.main() -- cgit v1.2.3 From 3d4f7ce3fe75756eef5066bd79c0a123a36258ed Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sun, 23 Jun 2013 11:35:37 +0100 Subject: console: close channels on disconnect Pretty much all of the 268 lines of channel-manager.c is boilerplate :( --- plugins/console/channel-manager.c | 54 +++++++++++++++++++++++++++++++++++++-- tests/twisted/console.py | 5 ++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/plugins/console/channel-manager.c b/plugins/console/channel-manager.c index f2be4f776..48c814f2a 100644 --- a/plugins/console/channel-manager.c +++ b/plugins/console/channel-manager.c @@ -35,11 +35,33 @@ enum { PROP_CONNECTION = 1, }; +static void connection_status_changed_cb ( + TpBaseConnection *conn, + guint status, + guint reason, + GabbleConsoleChannelManager *self); +static void gabble_console_channel_manager_close_all ( + GabbleConsoleChannelManager *self); + static void gabble_console_channel_manager_init (GabbleConsoleChannelManager *self) { } + +static void +gabble_console_channel_manager_constructed (GObject *object) +{ + GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (object); + + G_OBJECT_CLASS (gabble_console_channel_manager_parent_class)->constructed (object); + + g_return_if_fail (self->plugin_connection != NULL); + g_signal_connect_object (self->plugin_connection, "status-changed", + G_CALLBACK (connection_status_changed_cb), self, 0); +} + + static void gabble_console_channel_manager_set_property ( GObject *object, @@ -87,14 +109,41 @@ gabble_console_channel_manager_dispose ( GObject *object) { GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (object); + + gabble_console_channel_manager_close_all (self); + + G_OBJECT_CLASS (gabble_console_channel_manager_parent_class)->dispose (object); +} + + +static void +connection_status_changed_cb ( + TpBaseConnection *conn, + guint status, + guint reason, + GabbleConsoleChannelManager *self) +{ + switch (status) + { + case TP_CONNECTION_STATUS_DISCONNECTED: + gabble_console_channel_manager_close_all (self); + break; + + default: + return; + } +} + +static void +gabble_console_channel_manager_close_all ( + GabbleConsoleChannelManager *self) +{ TpBaseChannel *channel; while ((channel = g_queue_peek_head (&self->console_channels)) != NULL) { tp_base_channel_close (channel); } - - G_OBJECT_CLASS (gabble_console_channel_manager_parent_class)->dispose (object); } @@ -103,6 +152,7 @@ gabble_console_channel_manager_class_init (GabbleConsoleChannelManagerClass *kla { GObjectClass *oclass = G_OBJECT_CLASS (klass); + oclass->constructed = gabble_console_channel_manager_constructed; oclass->set_property = gabble_console_channel_manager_set_property; oclass->get_property = gabble_console_channel_manager_get_property; oclass->dispose = gabble_console_channel_manager_dispose; diff --git a/tests/twisted/console.py b/tests/twisted/console.py index b2efd658c..5eddaf024 100644 --- a/tests/twisted/console.py +++ b/tests/twisted/console.py @@ -39,6 +39,11 @@ def test(q, bus, conn, stream): assertContains((fixed, allowed), rccs) path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE }) + other_path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE }) + + assertNotEquals(path, other_path) + # leave the other one open, to test we don't crash on disconnect + console = ProxyWrapper(bus.get_object(conn.bus_name, path), CONSOLE_PLUGIN_IFACE, {'Channel': cs.CHANNEL}) -- cgit v1.2.3 From 3976b375d5029e07856fe13eb5f580223e23567b Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Sun, 23 Jun 2013 11:36:15 +0100 Subject: console: give channels more meaningful object paths --- plugins/console/channel.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/console/channel.c b/plugins/console/channel.c index 390920375..6a9503eb6 100644 --- a/plugins/console/channel.c +++ b/plugins/console/channel.c @@ -55,6 +55,7 @@ static void console_iface_init ( static void gabble_console_channel_set_spew ( GabbleConsoleChannel *self, gboolean spew); +gchar *gabble_console_channel_get_path (TpBaseChannel *chan); static void gabble_console_channel_close (TpBaseChannel *chan); G_DEFINE_TYPE_WITH_CODE (GabbleConsoleChannel, gabble_console_channel, @@ -165,6 +166,7 @@ gabble_console_channel_class_init (GabbleConsoleChannelClass *klass) object_class->dispose = gabble_console_channel_dispose; channel_class->channel_type = GABBLE_IFACE_GABBLE_PLUGIN_CONSOLE; + channel_class->get_object_path_suffix = gabble_console_channel_get_path; channel_class->close = gabble_console_channel_close; g_type_class_add_private (klass, sizeof (GabbleConsoleChannelPrivate)); @@ -182,6 +184,12 @@ gabble_console_channel_class_init (GabbleConsoleChannelClass *klass) console_props); } +gchar * +gabble_console_channel_get_path (TpBaseChannel *chan) +{ + return g_strdup_printf ("console%p", chan); +} + static void gabble_console_channel_close (TpBaseChannel *chan) { -- cgit v1.2.3 From b88fa165e855a50fd479aeeb9b8406a526b73773 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Wed, 28 Aug 2013 08:27:52 +0100 Subject: console: hold a weak ref to GabblePluginConnection As Simon suggested on : > + /* Not reffing this: the connection owns all channel managers, so it > + * must outlive us. Taking a reference leads to a cycle. > + */ > + self->plugin_connection = g_value_get_object (value); > > Weak ref, or drop it on DISCONNECTED state? --- plugins/console/channel-manager.c | 27 ++++++++++++++++++--------- plugins/console/channel-manager.h | 2 +- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/plugins/console/channel-manager.c b/plugins/console/channel-manager.c index 48c814f2a..3d826db1c 100644 --- a/plugins/console/channel-manager.c +++ b/plugins/console/channel-manager.c @@ -46,6 +46,7 @@ static void gabble_console_channel_manager_close_all ( static void gabble_console_channel_manager_init (GabbleConsoleChannelManager *self) { + g_weak_ref_init (&self->plugin_connection_ref, NULL); } @@ -53,12 +54,17 @@ static void gabble_console_channel_manager_constructed (GObject *object) { GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (object); + GabblePluginConnection *plugin_connection; G_OBJECT_CLASS (gabble_console_channel_manager_parent_class)->constructed (object); - g_return_if_fail (self->plugin_connection != NULL); - g_signal_connect_object (self->plugin_connection, "status-changed", - G_CALLBACK (connection_status_changed_cb), self, 0); + plugin_connection = g_weak_ref_get (&self->plugin_connection_ref); + if (plugin_connection != NULL) + { + g_signal_connect_object (plugin_connection, "status-changed", + G_CALLBACK (connection_status_changed_cb), self, 0); + g_object_unref (plugin_connection); + } } @@ -74,10 +80,7 @@ gabble_console_channel_manager_set_property ( switch (property_id) { case PROP_CONNECTION: - /* Not reffing this: the connection owns all channel managers, so it - * must outlive us. Taking a reference leads to a cycle. - */ - self->plugin_connection = g_value_get_object (value); + g_weak_ref_set (&self->plugin_connection_ref, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -96,7 +99,7 @@ gabble_console_channel_manager_get_property ( switch (property_id) { case PROP_CONNECTION: - g_value_set_object (value, self->plugin_connection); + g_value_take_object (value, g_weak_ref_get (&self->plugin_connection_ref)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -111,6 +114,7 @@ gabble_console_channel_manager_dispose ( GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (object); gabble_console_channel_manager_close_all (self); + g_weak_ref_clear (&self->plugin_connection_ref); G_OBJECT_CLASS (gabble_console_channel_manager_parent_class)->dispose (object); } @@ -211,6 +215,7 @@ gabble_console_channel_manager_create_channel ( GHashTable *request_properties) { GabbleConsoleChannelManager *self = GABBLE_CONSOLE_CHANNEL_MANAGER (manager); + GabblePluginConnection *connection; TpBaseChannel *channel = NULL; GError *error = NULL; GSList *request_tokens; @@ -234,8 +239,11 @@ gabble_console_channel_manager_create_channel ( &error)) goto error; + connection = g_weak_ref_get (&self->plugin_connection_ref); + g_return_val_if_fail (connection != NULL, FALSE); + channel = g_object_new (GABBLE_TYPE_CONSOLE_CHANNEL, - "connection", self->plugin_connection, + "connection", connection, NULL); tp_base_channel_register (channel); g_signal_connect (channel, "closed", (GCallback) console_channel_closed_cb, @@ -247,6 +255,7 @@ gabble_console_channel_manager_create_channel ( TP_EXPORTABLE_CHANNEL (channel), request_tokens); g_slist_free (request_tokens); + g_object_unref (connection); return TRUE; error: diff --git a/plugins/console/channel-manager.h b/plugins/console/channel-manager.h index 8ec4adc0a..5e7a8b94d 100644 --- a/plugins/console/channel-manager.h +++ b/plugins/console/channel-manager.h @@ -30,7 +30,7 @@ struct _GabbleConsoleChannelManagerClass { struct _GabbleConsoleChannelManager { GObject parent; - GabblePluginConnection *plugin_connection; + GWeakRef plugin_connection_ref; GQueue console_channels; }; -- cgit v1.2.3 From 37eef7721f1d6493bb241618fc6ca73f4d658744 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Wed, 28 Aug 2013 08:30:56 +0100 Subject: console UI: don't bother preparing the channel Simon said: > + # We only prepare the channel so that ::invalidated will be emitted > + # when it closes. > > Is this really necessary? I would expect that channels would invalidate > anyway. And he's right: the channel proxy does invalidate without being prepared if the connection goes away, or if someone else calls Close() on the channel. --- plugins/telepathy-gabble-xmpp-console | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/telepathy-gabble-xmpp-console b/plugins/telepathy-gabble-xmpp-console index 1d131217f..a72c92b55 100755 --- a/plugins/telepathy-gabble-xmpp-console +++ b/plugins/telepathy-gabble-xmpp-console @@ -345,7 +345,6 @@ class Window(Gtk.Window): def __create_cb(self, request, result, _): try: channel, context = request.create_and_handle_channel_finish(result) - channel.prepare_async(None, self.__channel_prepared_cb, None) channel.connect('invalidated', self.__channel_invalidated_cb) bus_name = channel.get_bus_name() @@ -368,11 +367,6 @@ Check that you have the console plugin installed.""" % { self.__build_ui() self.show_all() - def __channel_prepared_cb(self, channel, result, user_data): - # We only prepare the channel so that ::invalidated will be emitted - # when it closes. - pass - def __channel_invalidated_cb(self, channel, domain, code, message): self.infobar.show() self.infobar_close_button.grab_focus() -- cgit v1.2.3 From 1108a8aa37c2fa4ef14ad9f5a198ac5f80f2d765 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Wed, 28 Aug 2013 09:57:17 +0100 Subject: configure: tell Automake to put object files in subdirs Previously, the object files for plugins/console/*.c were being written to plugins/debug.o, plugins/channel.o, etc. This works fine until two plugins have files with the same name. Setting subdir-objects makes the objects end up in plugins/console/ instead, with only the linked plugin ending up as plugins/libconsole.la. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bbf27718e..ed5d4e229 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ AC_INIT([Telepathy Gabble], [gabble_version], AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([1.9 -Wno-portability tar-ustar]) +AM_INIT_AUTOMAKE([1.9 -Wno-portability tar-ustar subdir-objects]) AM_CONFIG_HEADER(config.h) AC_USE_SYSTEM_EXTENSIONS -- cgit v1.2.3