[Telepathy-commits] [telepathy-gabble/master] rename gabble-connection.c to connection.c
Dafydd Harries
dafydd.harries at collabora.co.uk
Tue Aug 19 10:53:22 PDT 2008
20080714115830-c9803-c7efbff10cfc04b5d49398ae335e4b27dc2c122f.gz
---
src/Makefile.am | 2 +-
src/connection.c | 2794 +++++++++++++++++++++++++++++++++++++++++++++++
src/connection.h | 2 +-
src/gabble-connection.c | 2794 -----------------------------------------------
4 files changed, 2796 insertions(+), 2796 deletions(-)
create mode 100644 src/connection.c
delete mode 100644 src/gabble-connection.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 53b8201..e2460c2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,7 +52,7 @@ libgabble_convenience_la_our_sources = \
connection-manager.h \
gabble-connection-manager.c \
connection.h \
- gabble-connection.c \
+ connection.c \
im-channel.h \
gabble-im-channel.c \
muc-channel.h \
diff --git a/src/connection.c b/src/connection.c
new file mode 100644
index 0000000..2c1e048
--- /dev/null
+++ b/src/connection.c
@@ -0,0 +1,2794 @@
+/*
+ * gabble-connection.c - Source for GabbleConnection
+ * Copyright (C) 2005, 2006 Collabora Ltd.
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * 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 "connection.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib-object.h>
+#include <loudmouth/loudmouth.h>
+
+#include <telepathy-glib/channel-iface.h>
+#include <telepathy-glib/channel-factory-iface.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/enums.h>
+#include <telepathy-glib/errors.h>
+#include <telepathy-glib/handle-repo-dynamic.h>
+#include <telepathy-glib/handle-repo-static.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/svc-connection.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION
+
+#include "bytestream-factory.h"
+#include "capabilities.h"
+#include "caps-hash.h"
+#include "conn-aliasing.h"
+#include "conn-avatars.h"
+#include "conn-presence.h"
+#include "conn-olpc.h"
+#include "debug.h"
+#include "disco.h"
+#include "media-channel.h"
+#include "register.h"
+#include "roomlist-channel.h"
+#include "im-factory.h"
+#include "media-factory.h"
+#include "muc-factory.h"
+#include "namespaces.h"
+#include "presence-cache.h"
+#include "presence.h"
+#include "pubsub.h"
+#include "request-pipeline.h"
+#include "roster.h"
+#include "private-tubes-factory.h"
+#include "util.h"
+#include "vcard-manager.h"
+
+static guint disco_reply_timeout = 5000;
+
+#define GABBLE_TP_CAPABILITY_PAIR_TYPE (dbus_g_type_get_struct ("GValueArray", \
+ G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID))
+#define GABBLE_TP_CAPABILITIES_CHANGED_MONSTER_TYPE (dbus_g_type_get_struct \
+ ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \
+ G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
+#define GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE (dbus_g_type_get_struct \
+ ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \
+ G_TYPE_INVALID))
+
+static void conn_service_iface_init (gpointer, gpointer);
+static void capabilities_service_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE(GabbleConnection,
+ gabble_connection,
+ TP_TYPE_BASE_CONNECTION,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION,
+ conn_service_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING,
+ conn_aliasing_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS,
+ conn_avatars_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CAPABILITIES,
+ capabilities_service_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
+ conn_presence_iface_init);
+ G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_BUDDY_INFO,
+ olpc_buddy_info_iface_init);
+ G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_ACTIVITY_PROPERTIES,
+ olpc_activity_properties_iface_init);
+ )
+
+/* properties */
+enum
+{
+ PROP_CONNECT_SERVER = 1,
+ PROP_PORT,
+ PROP_OLD_SSL,
+ PROP_REQUIRE_ENCRYPTION,
+ PROP_REGISTER,
+ PROP_LOW_BANDWIDTH,
+ PROP_STREAM_SERVER,
+ PROP_USERNAME,
+ PROP_PASSWORD,
+ PROP_RESOURCE,
+ PROP_PRIORITY,
+ PROP_HTTPS_PROXY_SERVER,
+ PROP_HTTPS_PROXY_PORT,
+ PROP_FALLBACK_CONFERENCE_SERVER,
+ PROP_STUN_SERVER,
+ PROP_STUN_PORT,
+ PROP_IGNORE_SSL_ERRORS,
+ PROP_ALIAS,
+
+ LAST_PROPERTY
+};
+
+/* private structure */
+typedef struct _GabbleConnectionPrivate GabbleConnectionPrivate;
+
+struct _GabbleConnectionPrivate
+{
+ LmMessageHandler *iq_disco_cb;
+ LmMessageHandler *iq_unknown_cb;
+ LmMessageHandler *stream_error_cb;
+ LmMessageHandler *msg_cb;
+
+ /* connection properties */
+ gchar *connect_server;
+ guint port;
+ gboolean old_ssl;
+ gboolean require_encryption;
+
+ gboolean ignore_ssl_errors;
+ TpConnectionStatusReason ssl_error;
+
+ gboolean do_register;
+
+ gboolean low_bandwidth;
+
+ gchar *https_proxy_server;
+ guint16 https_proxy_port;
+
+ gchar *stun_server;
+ guint16 stun_port;
+
+ gchar *fallback_conference_server;
+
+ /* authentication properties */
+ gchar *stream_server;
+ gchar *username;
+ gchar *password;
+ gchar *resource;
+ gint8 priority;
+ gchar *alias;
+
+ /* reference to conference server name */
+ const gchar *conference_server;
+
+ /* serial number of current advertised caps */
+ guint caps_serial;
+
+ /* gobject housekeeping */
+ gboolean dispose_has_run;
+};
+
+#define GABBLE_CONNECTION_GET_PRIVATE(obj) \
+ ((GabbleConnectionPrivate *)obj->priv)
+
+static void connection_capabilities_update_cb (GabblePresenceCache *,
+ TpHandle, GabblePresenceCapabilities, GabblePresenceCapabilities,
+ gpointer);
+
+static GPtrArray *
+_gabble_connection_create_channel_factories (TpBaseConnection *conn)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (conn);
+
+ GPtrArray *channel_factories = g_ptr_array_sized_new (4);
+
+ self->roster = gabble_roster_new (self);
+ g_signal_connect (self->roster, "nickname-update", G_CALLBACK
+ (gabble_conn_aliasing_nickname_updated), self);
+
+ g_ptr_array_add (channel_factories, self->roster);
+
+ self->muc_factory = g_object_new (GABBLE_TYPE_MUC_FACTORY,
+ "connection", self,
+ NULL);
+ g_ptr_array_add (channel_factories, self->muc_factory);
+
+ g_ptr_array_add (channel_factories,
+ g_object_new (GABBLE_TYPE_MEDIA_FACTORY,
+ "connection", self,
+ NULL));
+
+ g_ptr_array_add (channel_factories,
+ g_object_new (GABBLE_TYPE_IM_FACTORY,
+ "connection", self,
+ NULL));
+
+ self->private_tubes_factory = gabble_private_tubes_factory_new (self);
+ g_ptr_array_add (channel_factories, self->private_tubes_factory);
+
+ return channel_factories;
+}
+
+static GObject *
+gabble_connection_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (
+ G_OBJECT_CLASS (gabble_connection_parent_class)->constructor (
+ type, n_construct_properties, construct_params));
+
+ DEBUG("Post-construction: (GabbleConnection *)%p", self);
+
+ self->req_pipeline = gabble_request_pipeline_new (self);
+ self->disco = gabble_disco_new (self);
+ self->vcard_manager = gabble_vcard_manager_new (self);
+ g_signal_connect (self->vcard_manager, "nickname-update", G_CALLBACK
+ (gabble_conn_aliasing_nickname_updated), self);
+
+ self->presence_cache = gabble_presence_cache_new (self);
+ g_signal_connect (self->presence_cache, "nickname-update", G_CALLBACK
+ (gabble_conn_aliasing_nickname_updated), self);
+ g_signal_connect (self->presence_cache, "capabilities-update", G_CALLBACK
+ (connection_capabilities_update_cb), self);
+
+ capabilities_fill_cache (self->presence_cache);
+
+ conn_avatars_init (self);
+ conn_presence_init (self);
+ conn_olpc_activity_properties_init (self);
+
+ self->bytestream_factory = gabble_bytestream_factory_new (self);
+
+ self->avatar_requests = g_hash_table_new (NULL, NULL);
+
+ return (GObject *)self;
+}
+
+static void
+gabble_connection_init (GabbleConnection *self)
+{
+ GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate);
+
+ DEBUG("Initializing (GabbleConnection *)%p", self);
+
+ self->priv = priv;
+ self->lmconn = lm_connection_new (NULL);
+
+ /* Set default parameters for optional parameters */
+ priv->resource = g_strdup (GABBLE_PARAMS_DEFAULT_RESOURCE);
+
+ priv->caps_serial = 1;
+}
+
+static void
+gabble_connection_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleConnection *self = (GabbleConnection *) object;
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
+
+ switch (property_id) {
+ case PROP_CONNECT_SERVER:
+ g_value_set_string (value, priv->connect_server);
+ break;
+ case PROP_STREAM_SERVER:
+ g_value_set_string (value, priv->stream_server);
+ break;
+ case PROP_PORT:
+ g_value_set_uint (value, priv->port);
+ break;
+ case PROP_OLD_SSL:
+ g_value_set_boolean (value, priv->old_ssl);
+ break;
+ case PROP_REQUIRE_ENCRYPTION:
+ g_value_set_boolean (value, priv->require_encryption);
+ break;
+ case PROP_REGISTER:
+ g_value_set_boolean (value, priv->do_register);
+ break;
+ case PROP_LOW_BANDWIDTH:
+ g_value_set_boolean (value, priv->low_bandwidth);
+ break;
+ case PROP_USERNAME:
+ g_value_set_string (value, priv->username);
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, priv->password);
+ break;
+ case PROP_RESOURCE:
+ g_value_set_string (value, priv->resource);
+ break;
+ case PROP_PRIORITY:
+ g_value_set_char (value, priv->priority);
+ break;
+ case PROP_HTTPS_PROXY_SERVER:
+ g_value_set_string (value, priv->https_proxy_server);
+ break;
+ case PROP_HTTPS_PROXY_PORT:
+ g_value_set_uint (value, priv->https_proxy_port);
+ break;
+ case PROP_FALLBACK_CONFERENCE_SERVER:
+ g_value_set_string (value, priv->fallback_conference_server);
+ break;
+ case PROP_IGNORE_SSL_ERRORS:
+ g_value_set_boolean (value, priv->ignore_ssl_errors);
+ break;
+ case PROP_ALIAS:
+ g_value_set_string (value, priv->alias);
+ break;
+ case PROP_STUN_SERVER:
+ g_value_set_string (value, priv->stun_server);
+ break;
+ case PROP_STUN_PORT:
+ g_value_set_uint (value, priv->stun_port);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gabble_connection_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GabbleConnection *self = (GabbleConnection *) object;
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
+
+ switch (property_id) {
+ case PROP_CONNECT_SERVER:
+ g_free (priv->connect_server);
+ priv->connect_server = g_value_dup_string (value);
+ break;
+ case PROP_PORT:
+ priv->port = g_value_get_uint (value);
+ break;
+ case PROP_OLD_SSL:
+ priv->old_ssl = g_value_get_boolean (value);
+ break;
+ case PROP_REQUIRE_ENCRYPTION:
+ priv->require_encryption = g_value_get_boolean (value);
+ break;
+ case PROP_REGISTER:
+ priv->do_register = g_value_get_boolean (value);
+ break;
+ case PROP_LOW_BANDWIDTH:
+ priv->low_bandwidth = g_value_get_boolean (value);
+ break;
+ case PROP_STREAM_SERVER:
+ g_free (priv->stream_server);
+ priv->stream_server = g_value_dup_string (value);
+ break;
+ case PROP_USERNAME:
+ g_free (priv->username);
+ priv->username = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD:
+ g_free (priv->password);
+ priv->password = g_value_dup_string (value);
+ break;
+ case PROP_RESOURCE:
+ g_free (priv->resource);
+ priv->resource = g_value_dup_string (value);
+ break;
+ case PROP_PRIORITY:
+ priv->priority = g_value_get_char (value);
+ break;
+ case PROP_HTTPS_PROXY_SERVER:
+ g_free (priv->https_proxy_server);
+ priv->https_proxy_server = g_value_dup_string (value);
+ break;
+ case PROP_HTTPS_PROXY_PORT:
+ priv->https_proxy_port = g_value_get_uint (value);
+ break;
+ case PROP_FALLBACK_CONFERENCE_SERVER:
+ g_free (priv->fallback_conference_server);
+ priv->fallback_conference_server = g_value_dup_string (value);
+ break;
+ case PROP_IGNORE_SSL_ERRORS:
+ priv->ignore_ssl_errors = g_value_get_boolean (value);
+ break;
+ case PROP_ALIAS:
+ g_free (priv->alias);
+ priv->alias = g_value_dup_string (value);
+ break;
+ case PROP_STUN_SERVER:
+ g_free (priv->stun_server);
+ priv->stun_server = g_value_dup_string (value);
+ break;
+ case PROP_STUN_PORT:
+ priv->stun_port = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void gabble_connection_dispose (GObject *object);
+static void gabble_connection_finalize (GObject *object);
+static void connect_callbacks (TpBaseConnection *base);
+static void disconnect_callbacks (TpBaseConnection *base);
+static void connection_shut_down (TpBaseConnection *base);
+static gboolean _gabble_connection_connect (TpBaseConnection *base,
+ GError **error);
+
+static gchar *
+gabble_connection_get_unique_name (TpBaseConnection *self)
+{
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (
+ GABBLE_CONNECTION (self));
+
+ return g_strdup_printf ("%s@%s/%s",
+ priv->username,
+ priv->stream_server,
+ priv->resource);
+}
+
+/* must be in the same order as GabbleListHandle in connection.h */
+static const char *list_handle_strings[] =
+{
+ "publish", /* GABBLE_LIST_HANDLE_PUBLISH */
+ "subscribe", /* GABBLE_LIST_HANDLE_SUBSCRIBE */
+ "known", /* GABBLE_LIST_HANDLE_KNOWN */
+ "deny", /* GABBLE_LIST_HANDLE_DENY */
+ NULL
+};
+
+/* For the benefit of the unit tests, this will allow the connection to
+ * be NULL
+ */
+void
+_gabble_connection_create_handle_repos (TpBaseConnection *conn,
+ TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES])
+{
+ repos[TP_HANDLE_TYPE_CONTACT] =
+ tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_CONTACT,
+ gabble_normalize_contact, GUINT_TO_POINTER (GABBLE_JID_ANY));
+ repos[TP_HANDLE_TYPE_ROOM] =
+ tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_ROOM, gabble_normalize_room,
+ NULL);
+ repos[TP_HANDLE_TYPE_GROUP] =
+ tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_GROUP, NULL, NULL);
+ repos[TP_HANDLE_TYPE_LIST] =
+ tp_static_handle_repo_new (TP_HANDLE_TYPE_LIST, list_handle_strings);
+}
+
+static void
+base_connected_cb (TpBaseConnection *base_conn)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (base_conn);
+
+ gabble_connection_connected_olpc (conn);
+}
+
+static void
+gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (gabble_connection_class);
+ TpBaseConnectionClass *parent_class = TP_BASE_CONNECTION_CLASS (
+ gabble_connection_class);
+ GParamSpec *param_spec;
+ static const gchar *interfaces_always_present[] = {
+ TP_IFACE_CONNECTION_INTERFACE_ALIASING,
+ TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES,
+ TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
+ TP_IFACE_CONNECTION_INTERFACE_AVATARS,
+ NULL };
+
+ DEBUG("Initializing (GabbleConnectionClass *)%p", gabble_connection_class);
+
+ object_class->get_property = gabble_connection_get_property;
+ object_class->set_property = gabble_connection_set_property;
+ object_class->constructor = gabble_connection_constructor;
+
+ parent_class->create_handle_repos = _gabble_connection_create_handle_repos;
+ parent_class->get_unique_connection_name = gabble_connection_get_unique_name;
+ parent_class->create_channel_factories =
+ _gabble_connection_create_channel_factories;
+ parent_class->connecting = connect_callbacks;
+ parent_class->connected = base_connected_cb;
+ parent_class->disconnected = disconnect_callbacks;
+ parent_class->shut_down = connection_shut_down;
+ parent_class->start_connecting = _gabble_connection_connect;
+ parent_class->interfaces_always_present = interfaces_always_present;
+
+ g_type_class_add_private (gabble_connection_class,
+ sizeof (GabbleConnectionPrivate));
+
+ object_class->dispose = gabble_connection_dispose;
+ object_class->finalize = gabble_connection_finalize;
+
+ param_spec = g_param_spec_string ("connect-server", "Hostname or IP of Jabber server",
+ "The server used when establishing a connection.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_CONNECT_SERVER, param_spec);
+
+ param_spec = g_param_spec_uint ("port", "Jabber server port",
+ "The port used when establishing a connection.",
+ 0, G_MAXUINT16, 0,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PORT, param_spec);
+
+ param_spec = g_param_spec_boolean ("old-ssl", "Old-style SSL tunneled connection",
+ "Establish the entire connection to the server "
+ "within an SSL-encrypted tunnel. Note that this "
+ "is not the same as connecting with TLS, which "
+ "is not yet supported.", FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_OLD_SSL, param_spec);
+
+ param_spec = g_param_spec_boolean ("require-encryption", "Require encryption",
+ "Require the connection to be encrypted, either "
+ "via old-style SSL, or StartTLS mechanisms.", FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_REQUIRE_ENCRYPTION, param_spec);
+
+ param_spec = g_param_spec_boolean ("register", "Register account on server",
+ "Register a new account on server.", FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_REGISTER, param_spec);
+
+ param_spec = g_param_spec_boolean ("low-bandwidth", "Low bandwidth mode",
+ "Determines whether we are in low "
+ "bandwidth mode. This influences "
+ "polling behaviour.", FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_LOW_BANDWIDTH, param_spec);
+
+ param_spec = g_param_spec_string ("stream-server", "The server name used to initialise the stream.",
+ "The server name used when initialising the stream, "
+ "which is usually the part after the @ in the user's JID.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_STREAM_SERVER, param_spec);
+
+ param_spec = g_param_spec_string ("username", "Jabber username",
+ "The username used when authenticating.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_USERNAME, param_spec);
+
+ param_spec = g_param_spec_string ("password", "Jabber password",
+ "The password used when authenticating.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PASSWORD, param_spec);
+
+ param_spec = g_param_spec_string ("resource", "Jabber resource",
+ "The Jabber resource used when authenticating.",
+ "Telepathy",
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_RESOURCE, param_spec);
+
+ param_spec = g_param_spec_char ("priority", "Jabber presence priority",
+ "The default priority used when reporting our presence.",
+ G_MININT8, G_MAXINT8, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_PRIORITY, param_spec);
+
+ param_spec = g_param_spec_string ("https-proxy-server", "The server name "
+ "used as an HTTPS proxy server",
+ "The server name used as an HTTPS proxy "
+ "server.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_HTTPS_PROXY_SERVER, param_spec);
+
+ param_spec = g_param_spec_uint ("https-proxy-port", "The HTTP proxy server "
+ "port", "The HTTP proxy server port.",
+ 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_HTTPS_PROXY_PORT, param_spec);
+
+ param_spec = g_param_spec_string ("fallback-conference-server",
+ "The conference server used as fallback",
+ "The conference server used as fallback when "
+ "everything else fails.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_FALLBACK_CONFERENCE_SERVER,
+ param_spec);
+
+ param_spec = g_param_spec_string ("stun-server",
+ "STUN server",
+ "STUN server.",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec);
+
+ param_spec = g_param_spec_uint ("stun-port",
+ "STUN port",
+ "STUN port.",
+ 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_STUN_PORT,
+ G_PARAM_CONSTRUCT |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec);
+
+ param_spec = g_param_spec_boolean ("ignore-ssl-errors", "Ignore SSL errors",
+ "Continue connecting even if the server's "
+ "SSL certificate is invalid or missing.",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_IGNORE_SSL_ERRORS, param_spec);
+
+ param_spec = g_param_spec_string ("alias",
+ "Alias/nick for local user",
+ "Alias/nick for local user",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB);
+ g_object_class_install_property (object_class, PROP_ALIAS, param_spec);
+
+ conn_presence_class_init (gabble_connection_class);
+}
+
+static gboolean
+_unref_lm_connection (gpointer data)
+{
+ LmConnection *conn = (LmConnection *) data;
+
+ lm_connection_unref (conn);
+ return FALSE;
+}
+
+static void
+gabble_connection_dispose (GObject *object)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (object);
+ TpBaseConnection *base = (TpBaseConnection *)self;
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
+
+ if (priv->dispose_has_run)
+ return;
+
+ priv->dispose_has_run = TRUE;
+
+ DEBUG ("called");
+
+ g_assert ((base->status == TP_CONNECTION_STATUS_DISCONNECTED) ||
+ (base->status == TP_INTERNAL_CONNECTION_STATUS_NEW));
+ g_assert (base->self_handle == 0);
+
+ g_object_unref (self->bytestream_factory);
+ self->bytestream_factory = NULL;
+
+ g_object_unref (self->disco);
+ self->disco = NULL;
+
+ g_object_unref (self->req_pipeline);
+ self->req_pipeline = NULL;
+
+ g_object_unref (self->vcard_manager);
+ self->vcard_manager = NULL;
+
+ /* remove borrowed references before TpBaseConnection unrefs the channel
+ * factories */
+ self->roster = NULL;
+ self->muc_factory = NULL;
+ self->private_tubes_factory = NULL;
+
+ if (self->self_presence != NULL)
+ g_object_unref (self->self_presence);
+ self->self_presence = NULL;
+
+ g_object_unref (self->presence_cache);
+ self->presence_cache = NULL;
+
+ g_hash_table_destroy (self->olpc_activities_info);
+ g_hash_table_destroy (self->olpc_pep_activities);
+ g_hash_table_destroy (self->olpc_invited_activities);
+
+ g_hash_table_destroy (self->avatar_requests);
+
+ /* if this is not already the case, we'll crash anyway */
+ g_assert (!lm_connection_is_open (self->lmconn));
+
+ g_assert (priv->iq_disco_cb == NULL);
+ g_assert (priv->iq_unknown_cb == NULL);
+ g_assert (priv->stream_error_cb == NULL);
+ g_assert (priv->msg_cb == NULL);
+
+ /*
+ * The Loudmouth connection can't be unref'd immediately because this
+ * function might (indirectly) return into Loudmouth code which expects the
+ * connection to always be there.
+ */
+ g_idle_add (_unref_lm_connection, self->lmconn);
+
+ if (G_OBJECT_CLASS (gabble_connection_parent_class)->dispose)
+ G_OBJECT_CLASS (gabble_connection_parent_class)->dispose (object);
+}
+
+static void
+gabble_connection_finalize (GObject *object)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (object);
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
+
+ DEBUG ("called with %p", object);
+
+ g_free (priv->connect_server);
+ g_free (priv->stream_server);
+ g_free (priv->username);
+ g_free (priv->password);
+ g_free (priv->resource);
+
+ g_free (priv->https_proxy_server);
+ g_free (priv->stun_server);
+ g_free (priv->fallback_conference_server);
+
+ g_free (priv->alias);
+
+ conn_presence_finalize (self);
+
+ G_OBJECT_CLASS (gabble_connection_parent_class)->finalize (object);
+}
+
+/**
+ * _gabble_connection_set_properties_from_account
+ *
+ * Parses an account string which may be one of the following forms:
+ * username at server
+ * username at server/resource
+ * and sets the properties for username, stream server and resource
+ * appropriately. Also sets the connect server to the stream server if one has
+ * not yet been specified.
+ */
+gboolean
+_gabble_connection_set_properties_from_account (GabbleConnection *conn,
+ const gchar *account,
+ GError **error)
+{
+ GabbleConnectionPrivate *priv;
+ char *username, *server, *resource;
+ gboolean result;
+
+ g_assert (GABBLE_IS_CONNECTION (conn));
+ g_assert (account != NULL);
+
+ priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+
+ username = server = resource = NULL;
+ result = TRUE;
+
+ gabble_decode_jid (account, &username, &server, &resource);
+
+ if (username == NULL || server == NULL ||
+ *username == '\0' || *server == '\0')
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "unable to get username and server from account");
+ result = FALSE;
+ goto OUT;
+ }
+
+ g_object_set (G_OBJECT (conn),
+ "username", username,
+ "stream-server", server,
+ NULL);
+
+ /* only override the default resource if we actually got one */
+ if (resource)
+ g_object_set (G_OBJECT (conn), "resource", resource, NULL);
+
+OUT:
+ g_free (username);
+ g_free (server);
+ g_free (resource);
+
+ return result;
+}
+
+
+/**
+ * _gabble_connection_send
+ *
+ * Send an LmMessage and trap network errors appropriately.
+ */
+gboolean
+_gabble_connection_send (GabbleConnection *conn, LmMessage *msg, GError **error)
+{
+ GabbleConnectionPrivate *priv;
+ GError *lmerror = NULL;
+
+ g_assert (GABBLE_IS_CONNECTION (conn));
+
+ priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+
+ if (!lm_connection_send (conn->lmconn, msg, &lmerror))
+ {
+ DEBUG ("failed: %s", lmerror->message);
+
+ g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR,
+ "message send failed: %s", lmerror->message);
+
+ g_error_free (lmerror);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+typedef struct {
+ GabbleConnectionMsgReplyFunc reply_func;
+
+ GabbleConnection *conn;
+ LmMessage *sent_msg;
+ gpointer user_data;
+
+ GObject *object;
+ gboolean object_alive;
+} GabbleMsgHandlerData;
+
+static LmHandlerResult
+message_send_reply_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *reply_msg,
+ gpointer user_data)
+{
+ GabbleMsgHandlerData *handler_data = user_data;
+ LmMessageSubType sub_type;
+
+ sub_type = lm_message_get_sub_type (reply_msg);
+
+ /* Is it a reply to this message? If we're talking to another loudmouth,
+ * they can send us messages which have the same ID as ones we send. :-O */
+ if (sub_type != LM_MESSAGE_SUB_TYPE_RESULT &&
+ sub_type != LM_MESSAGE_SUB_TYPE_ERROR)
+ {
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ if (handler_data->object_alive && handler_data->reply_func != NULL)
+ {
+ return handler_data->reply_func (handler_data->conn,
+ handler_data->sent_msg,
+ reply_msg,
+ handler_data->object,
+ handler_data->user_data);
+ }
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static void
+message_send_object_destroy_notify_cb (gpointer data,
+ GObject *where_the_object_was)
+{
+ GabbleMsgHandlerData *handler_data = data;
+
+ handler_data->object = NULL;
+ handler_data->object_alive = FALSE;
+}
+
+static void
+message_send_handler_destroy_cb (gpointer data)
+{
+ GabbleMsgHandlerData *handler_data = data;
+
+ lm_message_unref (handler_data->sent_msg);
+
+ if (handler_data->object != NULL)
+ {
+ g_object_weak_unref (handler_data->object,
+ message_send_object_destroy_notify_cb,
+ handler_data);
+ }
+
+ g_slice_free (GabbleMsgHandlerData, handler_data);
+}
+
+/**
+ * _gabble_connection_send_with_reply
+ *
+ * Send a tracked LmMessage and trap network errors appropriately.
+ *
+ * If object is non-NULL the handler will follow the lifetime of that object,
+ * which means that if the object is destroyed the callback will not be invoked.
+ *
+ * if reply_func is NULL the reply will be ignored but connection_iq_unknown_cb
+ * won't be called.
+ */
+gboolean
+_gabble_connection_send_with_reply (GabbleConnection *conn,
+ LmMessage *msg,
+ GabbleConnectionMsgReplyFunc reply_func,
+ GObject *object,
+ gpointer user_data,
+ GError **error)
+{
+ GabbleConnectionPrivate *priv;
+ LmMessageHandler *handler;
+ GabbleMsgHandlerData *handler_data;
+ gboolean ret;
+ GError *lmerror = NULL;
+
+ g_assert (GABBLE_IS_CONNECTION (conn));
+
+ priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+
+ lm_message_ref (msg);
+
+ handler_data = g_slice_new (GabbleMsgHandlerData);
+ handler_data->reply_func = reply_func;
+ handler_data->conn = conn;
+ handler_data->sent_msg = msg;
+ handler_data->user_data = user_data;
+
+ handler_data->object = object;
+ handler_data->object_alive = TRUE;
+
+ if (object != NULL)
+ {
+ g_object_weak_ref (object, message_send_object_destroy_notify_cb,
+ handler_data);
+ }
+
+ handler = lm_message_handler_new (message_send_reply_cb, handler_data,
+ message_send_handler_destroy_cb);
+
+ ret = lm_connection_send_with_reply (conn->lmconn, msg, handler, &lmerror);
+ if (!ret)
+ {
+ DEBUG ("failed: %s", lmerror->message);
+
+ if (error)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR,
+ "message send failed: %s", lmerror->message);
+ }
+
+ g_error_free (lmerror);
+ }
+
+ lm_message_handler_unref (handler);
+
+ return ret;
+}
+
+static LmHandlerResult connection_iq_disco_cb (LmMessageHandler *,
+ LmConnection *, LmMessage *, gpointer);
+static LmHandlerResult connection_iq_unknown_cb (LmMessageHandler *,
+ LmConnection *, LmMessage *, gpointer);
+static LmHandlerResult connection_stream_error_cb (LmMessageHandler *,
+ LmConnection *, LmMessage *, gpointer);
+static LmSSLResponse connection_ssl_cb (LmSSL *, LmSSLStatus, gpointer);
+static void connection_open_cb (LmConnection *, gboolean, gpointer);
+static void connection_auth_cb (LmConnection *, gboolean, gpointer);
+static void connection_disco_cb (GabbleDisco *, GabbleDiscoRequest *,
+ const gchar *, const gchar *, LmMessageNode *, GError *, gpointer);
+static void connection_disconnected_cb (LmConnection *, LmDisconnectReason,
+ gpointer);
+
+
+static gboolean
+do_connect (GabbleConnection *conn, GError **error)
+{
+ GError *lmerror = NULL;
+
+ DEBUG ("calling lm_connection_open");
+
+ if (!lm_connection_open (conn->lmconn, connection_open_cb,
+ conn, NULL, &lmerror))
+ {
+ DEBUG ("lm_connection_open failed %s", lmerror->message);
+
+ g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR,
+ "lm_connection_open failed: %s", lmerror->message);
+
+ g_error_free (lmerror);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+connect_callbacks (TpBaseConnection *base)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (base);
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+
+ g_assert (priv->iq_disco_cb == NULL);
+ g_assert (priv->iq_unknown_cb == NULL);
+ g_assert (priv->stream_error_cb == NULL);
+ g_assert (priv->msg_cb == NULL);
+
+ priv->iq_disco_cb = lm_message_handler_new (connection_iq_disco_cb,
+ conn, NULL);
+ lm_connection_register_message_handler (conn->lmconn, priv->iq_disco_cb,
+ LM_MESSAGE_TYPE_IQ,
+ LM_HANDLER_PRIORITY_NORMAL);
+
+ priv->iq_unknown_cb = lm_message_handler_new (connection_iq_unknown_cb,
+ conn, NULL);
+ lm_connection_register_message_handler (conn->lmconn, priv->iq_unknown_cb,
+ LM_MESSAGE_TYPE_IQ,
+ LM_HANDLER_PRIORITY_LAST);
+
+ priv->stream_error_cb = lm_message_handler_new (connection_stream_error_cb,
+ conn, NULL);
+ lm_connection_register_message_handler (conn->lmconn, priv->stream_error_cb,
+ LM_MESSAGE_TYPE_STREAM_ERROR,
+ LM_HANDLER_PRIORITY_LAST);
+
+ priv->msg_cb = lm_message_handler_new (pubsub_msg_event_cb,
+ conn, NULL);
+ lm_connection_register_message_handler (conn->lmconn, priv->msg_cb,
+ LM_MESSAGE_TYPE_MESSAGE,
+ LM_HANDLER_PRIORITY_FIRST);
+}
+
+static void
+disconnect_callbacks (TpBaseConnection *base)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (base);
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+
+ g_assert (priv->iq_disco_cb != NULL);
+ g_assert (priv->iq_unknown_cb != NULL);
+ g_assert (priv->stream_error_cb != NULL);
+ g_assert (priv->msg_cb != NULL);
+
+ lm_connection_unregister_message_handler (conn->lmconn, priv->iq_disco_cb,
+ LM_MESSAGE_TYPE_IQ);
+ lm_message_handler_unref (priv->iq_disco_cb);
+ priv->iq_disco_cb = NULL;
+
+ lm_connection_unregister_message_handler (conn->lmconn, priv->iq_unknown_cb,
+ LM_MESSAGE_TYPE_IQ);
+ lm_message_handler_unref (priv->iq_unknown_cb);
+ priv->iq_unknown_cb = NULL;
+
+ lm_connection_unregister_message_handler (conn->lmconn,
+ priv->stream_error_cb, LM_MESSAGE_TYPE_STREAM_ERROR);
+ lm_message_handler_unref (priv->stream_error_cb);
+ priv->stream_error_cb = NULL;
+
+ lm_connection_unregister_message_handler (conn->lmconn, priv->msg_cb,
+ LM_MESSAGE_TYPE_MESSAGE);
+ lm_message_handler_unref (priv->msg_cb);
+ priv->msg_cb = NULL;
+}
+
+/**
+ * _gabble_connection_connect
+ *
+ * Use the stored server & authentication details to commence
+ * the stages for connecting to the server and authenticating. Will
+ * re-use an existing LmConnection if it is present, or create it
+ * if necessary.
+ *
+ * Stage 1 is _gabble_connection_connect calling lm_connection_open
+ * Stage 2 is connection_open_cb calling lm_connection_authenticate
+ * Stage 3 is connection_auth_cb initiating service discovery
+ * Stage 4 is connection_disco_cb advertising initial presence, requesting
+ * the roster and setting the CONNECTED state
+ */
+static gboolean
+_gabble_connection_connect (TpBaseConnection *base,
+ GError **error)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (base);
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+ char *jid;
+
+ g_assert (priv->port <= G_MAXUINT16);
+ g_assert (priv->stream_server != NULL);
+ g_assert (priv->username != NULL);
+ g_assert (priv->password != NULL);
+ g_assert (priv->resource != NULL);
+ g_assert (lm_connection_is_open (conn->lmconn) == FALSE);
+
+ jid = g_strdup_printf ("%s@%s", priv->username, priv->stream_server);
+ lm_connection_set_jid (conn->lmconn, jid);
+ g_free (jid);
+
+ /* override server and port if either was provided */
+ if (priv->connect_server != NULL || priv->port != 0)
+ {
+ gchar *server;
+
+ if (priv->connect_server != NULL)
+ server = priv->connect_server;
+ else
+ server = priv->stream_server;
+
+ DEBUG ("disabling SRV because \"server\" or \"port\" parameter "
+ "specified, will connect to %s", server);
+
+ lm_connection_set_server (conn->lmconn, server);
+
+ if (priv->port != 0)
+ lm_connection_set_port (conn->lmconn, priv->port);
+ }
+ else
+ {
+ DEBUG ("letting SRV lookup decide server and port");
+ }
+
+ if (priv->https_proxy_server)
+ {
+ LmProxy *proxy;
+
+ proxy = lm_proxy_new_with_server (LM_PROXY_TYPE_HTTP,
+ priv->https_proxy_server, priv->https_proxy_port);
+
+ lm_connection_set_proxy (conn->lmconn, proxy);
+
+ lm_proxy_unref (proxy);
+ }
+
+ if (priv->old_ssl)
+ {
+ LmSSL *ssl = lm_ssl_new (NULL, connection_ssl_cb, conn, NULL);
+ lm_connection_set_ssl (conn->lmconn, ssl);
+ lm_ssl_unref (ssl);
+ }
+ else
+ {
+ LmSSL *ssl = lm_ssl_new (NULL, connection_ssl_cb, conn, NULL);
+ lm_connection_set_ssl (conn->lmconn, ssl);
+
+ /* Try to use StartTLS if possible, but be careful about
+ allowing SSL errors in that default case. */
+ lm_ssl_use_starttls (ssl, TRUE, priv->require_encryption);
+
+ if (!priv->require_encryption)
+ priv->ignore_ssl_errors = TRUE;
+ }
+
+ /* send whitespace to the server every 30 seconds */
+ lm_connection_set_keep_alive_rate (conn->lmconn, 30);
+
+ lm_connection_set_disconnect_function (conn->lmconn,
+ connection_disconnected_cb,
+ conn,
+ NULL);
+
+ return do_connect (conn, error);
+}
+
+
+
+static void
+connection_disconnected_cb (LmConnection *lmconn,
+ LmDisconnectReason lm_reason,
+ gpointer user_data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+ TpBaseConnection *base = (TpBaseConnection *)user_data;
+
+ g_assert (conn->lmconn == lmconn);
+
+ DEBUG ("called with reason %u", lm_reason);
+
+ /* if we were expecting this disconnection, we're done so can tell
+ * the connection manager to unref us. otherwise it's a network error
+ * or some other screw up we didn't expect, so we emit the status
+ * change */
+ if (base->status == TP_CONNECTION_STATUS_DISCONNECTED)
+ {
+ DEBUG ("expected; emitting DISCONNECTED");
+ tp_base_connection_finish_shutdown ((TpBaseConnection *)conn);
+ }
+ else
+ {
+ DEBUG ("unexpected; calling tp_base_connection_change_status");
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
+ }
+}
+
+
+static void
+connection_shut_down (TpBaseConnection *base)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (base);
+
+ g_assert (GABBLE_IS_CONNECTION (conn));
+
+ /* If we're shutting down by user request, we don't want to be
+ * unreffed until the LM connection actually closes; the event handler
+ * will tell the base class that shutdown has finished.
+ *
+ * On the other hand, if we're shutting down because the connection
+ * suffered a network error, the LM connection will already be closed,
+ * so just tell the base class to finish shutting down immediately.
+ */
+ if (lm_connection_is_open (conn->lmconn))
+ {
+ DEBUG ("still open; calling lm_connection_close");
+ lm_connection_close (conn->lmconn, NULL);
+ }
+ else
+ {
+ /* lm_connection_is_open() returns FALSE if LmConnection is in the
+ * middle of connecting, so call this just in case */
+ lm_connection_cancel_open (conn->lmconn);
+ DEBUG ("closed; emitting DISCONNECTED");
+ tp_base_connection_finish_shutdown (base);
+ }
+}
+
+
+/**
+ * _gabble_connection_signal_own_presence:
+ * @self: A #GabbleConnection
+ * @error: pointer in which to return a GError in case of failure.
+ *
+ * Signal the user's stored presence to the jabber server
+ *
+ * Retuns: FALSE if an error occurred
+ */
+gboolean
+_gabble_connection_signal_own_presence (GabbleConnection *self, GError **error)
+{
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
+ GabblePresence *presence = self->self_presence;
+ LmMessage *message = gabble_presence_as_message (presence, priv->resource);
+ LmMessageNode *node = lm_message_get_node (message);
+ gboolean ret;
+ gchar *caps_hash;
+
+ if (presence->status == GABBLE_PRESENCE_HIDDEN)
+ {
+ if ((self->features & GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE) != 0)
+ lm_message_node_set_attribute (node, "type", "invisible");
+ }
+
+ /* XEP-0115 version 1.5 uses a verification string in the 'ver' attribute */
+ caps_hash = caps_hash_compute_from_self_presence (self);
+ node = lm_message_node_add_child (node, "c", NULL);
+ lm_message_node_set_attributes (
+ node,
+ "xmlns", NS_CAPS,
+ "hash", "sha-1",
+ "node", NS_GABBLE_CAPS,
+ "ver", caps_hash,
+ NULL);
+
+ /* XEP-0115 deprecates 'ext' feature bundles. But we still need
+ * BUNDLE_VOICE_V1 it for backward-compatibility with Gabble 0.2 */
+ if (presence->caps & PRESENCE_CAP_GOOGLE_VOICE)
+ lm_message_node_set_attribute (node, "ext", BUNDLE_VOICE_V1);
+
+ ret = _gabble_connection_send (self, message, error);
+
+ g_free (caps_hash);
+ lm_message_unref (message);
+
+ return ret;
+}
+
+static LmMessage *_lm_iq_message_make_result (LmMessage *iq_message);
+
+/**
+ * _gabble_connection_send_iq_result
+ *
+ * Function used to acknowledge an IQ stanza.
+ */
+void
+_gabble_connection_acknowledge_set_iq (GabbleConnection *conn,
+ LmMessage *iq)
+{
+ LmMessage *result;
+
+ g_assert (LM_MESSAGE_TYPE_IQ == lm_message_get_type (iq));
+ g_assert (LM_MESSAGE_SUB_TYPE_SET == lm_message_get_sub_type (iq));
+
+ result = _lm_iq_message_make_result (iq);
+
+ if (NULL != result)
+ {
+ _gabble_connection_send (conn, result, NULL);
+ lm_message_unref (result);
+ }
+}
+
+
+/**
+ * _gabble_connection_send_iq_error
+ *
+ * Function used to acknowledge an IQ stanza with an error.
+ */
+void
+_gabble_connection_send_iq_error (GabbleConnection *conn,
+ LmMessage *message,
+ GabbleXmppError error,
+ const gchar *errmsg)
+{
+ const gchar *to, *id;
+ LmMessage *msg;
+ LmMessageNode *iq_node;
+
+ iq_node = lm_message_get_node (message);
+ to = lm_message_node_get_attribute (iq_node, "from");
+ id = lm_message_node_get_attribute (iq_node, "id");
+
+ if (id == NULL)
+ {
+ NODE_DEBUG (iq_node, "can't acknowledge IQ with no id");
+ return;
+ }
+
+ msg = lm_message_new_with_sub_type (to, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_ERROR);
+
+ lm_message_node_set_attribute (msg->node, "id", id);
+
+ lm_message_node_steal_children (msg->node, iq_node);
+
+ gabble_xmpp_error_to_node (error, msg->node, errmsg);
+
+ _gabble_connection_send (conn, msg, NULL);
+
+ lm_message_unref (msg);
+}
+
+static LmMessage *
+_lm_iq_message_make_result (LmMessage *iq_message)
+{
+ LmMessage *result;
+ LmMessageNode *iq, *result_iq;
+ const gchar *from_jid, *id;
+
+ g_assert (lm_message_get_type (iq_message) == LM_MESSAGE_TYPE_IQ);
+ g_assert (lm_message_get_sub_type (iq_message) == LM_MESSAGE_SUB_TYPE_GET ||
+ lm_message_get_sub_type (iq_message) == LM_MESSAGE_SUB_TYPE_SET);
+
+ iq = lm_message_get_node (iq_message);
+ id = lm_message_node_get_attribute (iq, "id");
+
+ if (id == NULL)
+ {
+ NODE_DEBUG (iq, "can't acknowledge IQ with no id");
+ return NULL;
+ }
+
+ from_jid = lm_message_node_get_attribute (iq, "from");
+
+ result = lm_message_new_with_sub_type (from_jid, LM_MESSAGE_TYPE_IQ,
+ LM_MESSAGE_SUB_TYPE_RESULT);
+ result_iq = lm_message_get_node (result);
+ lm_message_node_set_attribute (result_iq, "id", id);
+
+ return result;
+}
+
+/**
+ * connection_iq_disco_cb
+ *
+ * Called by loudmouth when we get an incoming <iq>. This handler handles
+ * disco-related IQs.
+ */
+static LmHandlerResult
+connection_iq_disco_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (user_data);
+ LmMessage *result;
+ LmMessageNode *iq, *result_iq, *query, *result_query, *identity;
+ const gchar *node, *suffix;
+ GSList *features;
+ GSList *i;
+ gchar *caps_hash;
+
+ if (lm_message_get_sub_type (message) != LM_MESSAGE_SUB_TYPE_GET)
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+
+ iq = lm_message_get_node (message);
+ query = lm_message_node_get_child_with_namespace (iq, "query",
+ NS_DISCO_INFO);
+
+ if (!query)
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+
+ node = lm_message_node_get_attribute (query, "node");
+
+ if (node && (
+ 0 != strncmp (node, NS_GABBLE_CAPS "#", strlen (NS_GABBLE_CAPS) + 1) ||
+ strlen (node) < strlen (NS_GABBLE_CAPS) + 2))
+ {
+ NODE_DEBUG (iq, "got iq disco query with unexpected node attribute");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ if (node == NULL)
+ suffix = NULL;
+ else
+ suffix = node + strlen (NS_GABBLE_CAPS) + 1;
+
+ result = _lm_iq_message_make_result (message);
+ result_iq = lm_message_get_node (result);
+ result_query = lm_message_node_add_child (result_iq, "query", NULL);
+ lm_message_node_set_attribute (result_query, "xmlns", NS_DISCO_INFO);
+
+ if (node)
+ lm_message_node_set_attribute (result_query, "node", node);
+
+ DEBUG ("got disco request for node %s, caps are %x", node,
+ self->self_presence->caps);
+
+ /* Every entity MUST have at least one identity (XEP-0030). Gabble publishs
+ * one identity. If you change the identity here, you also need to change
+ * caps_hash_compute_from_self_presence(). */
+ identity = lm_message_node_add_child
+ (result_query, "identity", NULL);
+ lm_message_node_set_attribute (identity, "category", "client");
+ lm_message_node_set_attribute (identity, "name", PACKAGE_STRING);
+ lm_message_node_set_attribute (identity, "type", "pc");
+
+ features = capabilities_get_features (self->self_presence->caps);
+
+ g_debug ("%s: caps now %u", G_STRFUNC, self->self_presence->caps);
+
+ /* If node is not NULL, it can be either a caps bundle as defined in the
+ * legacy XEP-0115 version 1.3 or an hash as defined in XEP-0115 version
+ * 1.5. */
+
+ caps_hash = caps_hash_compute_from_self_presence (self);
+
+ if (NULL == node ||
+ !tp_strdiff (suffix, BUNDLE_VOICE_V1) ||
+ !tp_strdiff (suffix, caps_hash))
+ {
+ for (i = features; NULL != i; i = i->next)
+ {
+ const Feature *feature = (const Feature *) i->data;
+ LmMessageNode *feature_node;
+
+ /* When BUNDLE_VOICE_V1 is requested, only send the bundle */
+ if (!tp_strdiff (suffix, BUNDLE_VOICE_V1) &&
+ feature->feature_type != FEATURE_BUNDLE_COMPAT)
+ continue;
+
+ /* otherwise (no node or hash), put all features */
+ feature_node = lm_message_node_add_child (result_query, "feature",
+ NULL);
+ lm_message_node_set_attribute (feature_node, "var", feature->ns);
+ }
+
+ NODE_DEBUG (result_iq, "sending disco response");
+
+ if (!lm_connection_send (self->lmconn, result, NULL))
+ {
+ DEBUG ("sending disco response failed");
+ }
+ }
+ else
+ {
+ /* Return <item-not-found>. It is possible that the remote contact
+ * requested an old version (old hash) of our capabilities. In the
+ * meantime, it will have gotten a new hash, and query the new hash
+ * anyway. */
+ _gabble_connection_send_iq_error (self, message,
+ XMPP_ERROR_ITEM_NOT_FOUND, NULL);
+ }
+ g_free (caps_hash);
+
+ lm_message_unref (result);
+ g_slist_free (features);
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+/**
+ * connection_iq_unknown_cb
+ *
+ * Called by loudmouth when we get an incoming <iq>. This handler is
+ * at a lower priority than the others, and should reply with an error
+ * about unsupported get/set attempts.
+ */
+static LmHandlerResult
+connection_iq_unknown_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+
+ g_assert (connection == conn->lmconn);
+
+ NODE_DEBUG (message->node, "got unknown iq");
+
+ switch (lm_message_get_sub_type (message))
+ {
+ case LM_MESSAGE_SUB_TYPE_GET:
+ case LM_MESSAGE_SUB_TYPE_SET:
+ _gabble_connection_send_iq_error (conn, message,
+ XMPP_ERROR_SERVICE_UNAVAILABLE, NULL);
+ break;
+ default:
+ break;
+ }
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+/**
+ * connection_stream_error_cb
+ *
+ * Called by loudmouth when we get stream error, which means that
+ * we're about to close the connection. The message contains the reason
+ * for the connection hangup.
+ */
+static LmHandlerResult
+connection_stream_error_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+ LmMessageNode *conflict_node;
+
+ g_assert (connection == conn->lmconn);
+
+ NODE_DEBUG (message->node, "got stream error");
+
+ conflict_node = lm_message_node_get_child (message->node, "conflict");
+ if (conflict_node)
+ {
+ DEBUG ("found conflict node, emiting status change");
+
+ /* Another client with the same resource just
+ * appeared, we're going down. */
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NAME_IN_USE);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+}
+
+/**
+ * connection_ssl_cb
+ *
+ * If we're doing old SSL, this function gets called if the certificate
+ * is dodgy.
+ */
+static LmSSLResponse
+connection_ssl_cb (LmSSL *lmssl,
+ LmSSLStatus status,
+ gpointer data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (data);
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+ const char *reason;
+ TpConnectionStatusReason tp_reason;
+
+ switch (status) {
+ case LM_SSL_STATUS_NO_CERT_FOUND:
+ reason = "The server doesn't provide a certificate.";
+ tp_reason = TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED;
+ break;
+ case LM_SSL_STATUS_UNTRUSTED_CERT:
+ reason = "The certificate can not be trusted.";
+ tp_reason = TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED;
+ break;
+ case LM_SSL_STATUS_CERT_EXPIRED:
+ reason = "The certificate has expired.";
+ tp_reason = TP_CONNECTION_STATUS_REASON_CERT_EXPIRED;
+ break;
+ case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
+ reason = "The certificate has not been activated.";
+ tp_reason = TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED;
+ break;
+ case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
+ reason = "The server hostname doesn't match the one in the certificate.";
+ tp_reason = TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH;
+ break;
+ case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH:
+ reason = "The fingerprint doesn't match the expected value.";
+ tp_reason = TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH;
+ break;
+ case LM_SSL_STATUS_GENERIC_ERROR:
+ reason = "An unknown SSL error occurred.";
+ tp_reason = TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR;
+ break;
+ default:
+ g_assert_not_reached ();
+ reason = "Unknown SSL error code from Loudmouth.";
+ tp_reason = TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR;
+ break;
+ }
+
+ DEBUG ("called: %s", reason);
+
+ if (priv->ignore_ssl_errors)
+ {
+ return LM_SSL_RESPONSE_CONTINUE;
+ }
+ else
+ {
+ priv->ssl_error = tp_reason;
+ return LM_SSL_RESPONSE_STOP;
+ }
+}
+
+static void
+do_auth (GabbleConnection *conn)
+{
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+ GError *error = NULL;
+
+ DEBUG ("authenticating with username: %s, password: <hidden>, resource: %s",
+ priv->username, priv->resource);
+
+ if (!lm_connection_authenticate (conn->lmconn, priv->username,
+ priv->password, priv->resource, connection_auth_cb, conn, NULL,
+ &error))
+ {
+ DEBUG ("failed: %s", error->message);
+ g_error_free (error);
+
+ /* the reason this function can fail is through network errors,
+ * authentication failures are reported to our auth_cb */
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
+ }
+}
+
+static void
+registration_finished_cb (GabbleRegister *reg,
+ gboolean success,
+ gint err_code,
+ const gchar *err_msg,
+ gpointer user_data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+ TpBaseConnection *base = (TpBaseConnection *)conn;
+
+ if (base->status != TP_CONNECTION_STATUS_CONNECTING)
+ {
+ g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED);
+ return;
+ }
+
+ DEBUG ("%s", (success) ? "succeeded" : "failed");
+
+ g_object_unref (reg);
+
+ if (success)
+ {
+ do_auth (conn);
+ }
+ else
+ {
+ DEBUG ("err_code = %d, err_msg = '%s'",
+ err_code, err_msg);
+
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ (err_code == TP_ERROR_INVALID_ARGUMENT) ?
+ TP_CONNECTION_STATUS_REASON_NAME_IN_USE :
+ TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
+ }
+}
+
+static void
+do_register (GabbleConnection *conn)
+{
+ GabbleRegister *reg;
+
+ reg = gabble_register_new (conn);
+
+ g_signal_connect (reg, "finished", (GCallback) registration_finished_cb,
+ conn);
+
+ gabble_register_start (reg);
+}
+
+/**
+ * connection_open_cb
+ *
+ * Stage 2 of connecting, this function is called by loudmouth after the
+ * result of the non-blocking lm_connection_open call is known. It makes
+ * a request to authenticate the user with the server, or optionally
+ * registers user on the server first.
+ */
+static void
+connection_open_cb (LmConnection *lmconn,
+ gboolean success,
+ gpointer data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (data);
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+ TpBaseConnection *base = (TpBaseConnection *)conn;
+
+ if ((base->status != TP_CONNECTION_STATUS_CONNECTING) &&
+ (base->status != TP_INTERNAL_CONNECTION_STATUS_NEW))
+ {
+ g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED);
+ return;
+ }
+
+ g_assert (priv);
+ g_assert (lmconn == conn->lmconn);
+
+ if (!success)
+ {
+ if (lm_connection_get_proxy (lmconn))
+ {
+ DEBUG ("failed, retrying without proxy");
+
+ lm_connection_set_proxy (lmconn, NULL);
+
+ if (do_connect (conn, NULL))
+ {
+ return;
+ }
+ }
+ else
+ {
+ DEBUG ("failed");
+ }
+
+ if (priv->ssl_error)
+ {
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ priv->ssl_error);
+ }
+ else
+ {
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
+ }
+
+ return;
+ }
+
+ if (!priv->do_register)
+ do_auth (conn);
+ else
+ do_register (conn);
+}
+
+/**
+ * connection_auth_cb
+ *
+ * Stage 3 of connecting, this function is called by loudmouth after the
+ * result of the non-blocking lm_connection_authenticate call is known.
+ * It sends a discovery request to find the server's features.
+ */
+static void
+connection_auth_cb (LmConnection *lmconn,
+ gboolean success,
+ gpointer data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (data);
+ TpBaseConnection *base = (TpBaseConnection *)conn;
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+ GError *error = NULL;
+ const gchar *jid;
+
+ if (base->status != TP_CONNECTION_STATUS_CONNECTING)
+ {
+ g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED);
+ return;
+ }
+
+ g_assert (priv);
+ g_assert (lmconn == conn->lmconn);
+
+ if (!success)
+ {
+ DEBUG ("failed");
+
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
+
+ return;
+ }
+
+
+ jid = lm_connection_get_full_jid (lmconn);
+
+ base->self_handle = tp_handle_ensure (contact_handles, jid, NULL, &error);
+
+ if (base->self_handle == 0)
+ {
+ DEBUG ("couldn't get our self handle: %s", error->message);
+
+ g_error_free (error);
+
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
+
+ return;
+ }
+
+ /* update priv->resource and priv->stream_server from the server's JID */
+ if (!_gabble_connection_set_properties_from_account (conn, jid, &error))
+ {
+ DEBUG ("couldn't parse our own JID: %s", error->message);
+
+ g_error_free (error);
+
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
+
+ return;
+ }
+
+ DEBUG ("Created self handle %d, our JID is %s", base->self_handle, jid);
+
+ /* set initial presence */
+ conn->self_presence = gabble_presence_new ();
+ gabble_presence_update (conn->self_presence, priv->resource,
+ GABBLE_PRESENCE_AVAILABLE, NULL, priv->priority);
+
+ /* set initial capabilities */
+ gabble_presence_set_capabilities (conn->self_presence, priv->resource,
+ capabilities_get_initial_caps (), priv->caps_serial++);
+
+ if (!gabble_disco_request_with_timeout (conn->disco, GABBLE_DISCO_TYPE_INFO,
+ priv->stream_server, NULL,
+ disco_reply_timeout,
+ connection_disco_cb, conn,
+ G_OBJECT (conn), &error))
+ {
+ DEBUG ("sending disco request failed: %s",
+ error->message);
+
+ g_error_free (error);
+
+ tp_base_connection_change_status ((TpBaseConnection *)conn,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
+ }
+}
+
+/**
+ * connection_disco_cb
+ *
+ * Stage 4 of connecting, this function is called by GabbleDisco after the
+ * result of the non-blocking server feature discovery call is known. It sends
+ * the user's initial presence to the server, marking them as available,
+ * and requests the roster.
+ */
+static void
+connection_disco_cb (GabbleDisco *disco,
+ GabbleDiscoRequest *request,
+ const gchar *jid,
+ const gchar *node,
+ LmMessageNode *result,
+ GError *disco_error,
+ gpointer user_data)
+{
+ GabbleConnection *conn = user_data;
+ TpBaseConnection *base = (TpBaseConnection *)conn;
+ GabbleConnectionPrivate *priv;
+ GError *error = NULL;
+
+ if (base->status != TP_CONNECTION_STATUS_CONNECTING)
+ {
+ g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED);
+ return;
+ }
+
+ g_assert (GABBLE_IS_CONNECTION (conn));
+ priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+
+ if (disco_error)
+ {
+ DEBUG ("got disco error, setting no features: %s", disco_error->message);
+ if (disco_error->code == GABBLE_DISCO_ERROR_TIMEOUT)
+ {
+ DEBUG ("didn't receive a response to our disco request: disconnect");
+ goto ERROR;
+ }
+ }
+ else
+ {
+ LmMessageNode *iter;
+
+ NODE_DEBUG (result, "got");
+
+ for (iter = result->children; iter != NULL; iter = iter->next)
+ {
+ if (0 == strcmp (iter->name, "identity"))
+ {
+ const gchar *category = lm_message_node_get_attribute (iter,
+ "category");
+ const gchar *type = lm_message_node_get_attribute (iter, "type");
+
+ if (!tp_strdiff (category, "pubsub") &&
+ !tp_strdiff (type, "pep"))
+ /* XXX: should we also check for specific PubSub <feature>s? */
+ conn->features |= GABBLE_CONNECTION_FEATURES_PEP;
+ }
+ else if (0 == strcmp (iter->name, "feature"))
+ {
+ const gchar *var = lm_message_node_get_attribute (iter, "var");
+
+ if (var == NULL)
+ continue;
+
+ if (0 == strcmp (var, NS_GOOGLE_JINGLE_INFO))
+ conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO;
+ else if (0 == strcmp (var, NS_GOOGLE_ROSTER))
+ conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER;
+ else if (0 == strcmp (var, NS_PRESENCE_INVISIBLE))
+ conn->features |= GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE;
+ else if (0 == strcmp (var, NS_PRIVACY))
+ conn->features |= GABBLE_CONNECTION_FEATURES_PRIVACY;
+ }
+ }
+
+ DEBUG ("set features flags to %d", conn->features);
+ }
+
+ if (conn->features && GABBLE_CONNECTION_FEATURES_PEP)
+ {
+ const gchar *ifaces[] = { GABBLE_IFACE_OLPC_BUDDY_INFO,
+ GABBLE_IFACE_OLPC_ACTIVITY_PROPERTIES, NULL };
+
+ tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces);
+ }
+
+ /* send presence to the server to indicate availability */
+ /* TODO: some way for the user to set this */
+ if (!_gabble_connection_signal_own_presence (conn, &error))
+ {
+ DEBUG ("sending initial presence failed: %s", error->message);
+ goto ERROR;
+ }
+
+ /* go go gadget on-line */
+ tp_base_connection_change_status (base,
+ TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED);
+
+ return;
+
+ERROR:
+ if (error != NULL)
+ g_error_free (error);
+
+ tp_base_connection_change_status (base,
+ TP_CONNECTION_STATUS_DISCONNECTED,
+ TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
+
+ return;
+}
+
+
+/****************************************************************************
+ * D-BUS EXPORTED METHODS *
+ ****************************************************************************/
+
+
+static void
+_emit_capabilities_changed (GabbleConnection *conn,
+ TpHandle handle,
+ GabblePresenceCapabilities old_caps,
+ GabblePresenceCapabilities new_caps)
+{
+ GPtrArray *caps_arr;
+ const CapabilityConversionData *ccd;
+ guint i;
+
+ if (old_caps == new_caps)
+ return;
+
+ caps_arr = g_ptr_array_new ();
+
+ for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
+ {
+ if (ccd->c2tf_fn (old_caps | new_caps))
+ {
+ GValue caps_monster_struct = {0, };
+ guint old_specific = ccd->c2tf_fn (old_caps);
+ guint old_generic = old_specific ?
+ TP_CONNECTION_CAPABILITY_FLAG_CREATE |
+ TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0;
+ guint new_specific = ccd->c2tf_fn (new_caps);
+ guint new_generic = new_specific ?
+ TP_CONNECTION_CAPABILITY_FLAG_CREATE |
+ TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0;
+
+ if (0 == (old_specific ^ new_specific))
+ continue;
+
+ g_value_init (&caps_monster_struct,
+ GABBLE_TP_CAPABILITIES_CHANGED_MONSTER_TYPE);
+ g_value_take_boxed (&caps_monster_struct,
+ dbus_g_type_specialized_construct
+ (GABBLE_TP_CAPABILITIES_CHANGED_MONSTER_TYPE));
+
+ dbus_g_type_struct_set (&caps_monster_struct,
+ 0, handle,
+ 1, ccd->iface,
+ 2, old_generic,
+ 3, new_generic,
+ 4, old_specific,
+ 5, new_specific,
+ G_MAXUINT);
+
+ g_ptr_array_add (caps_arr, g_value_get_boxed (&caps_monster_struct));
+ }
+ }
+
+ if (caps_arr->len)
+ tp_svc_connection_interface_capabilities_emit_capabilities_changed (
+ conn, caps_arr);
+
+
+ for (i = 0; i < caps_arr->len; i++)
+ {
+ g_boxed_free (GABBLE_TP_CAPABILITIES_CHANGED_MONSTER_TYPE,
+ g_ptr_array_index (caps_arr, i));
+ }
+ g_ptr_array_free (caps_arr, TRUE);
+}
+
+static void
+connection_capabilities_update_cb (GabblePresenceCache *cache,
+ TpHandle handle,
+ GabblePresenceCapabilities old_caps,
+ GabblePresenceCapabilities new_caps,
+ gpointer user_data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+
+ _emit_capabilities_changed (conn, handle, old_caps, new_caps);
+}
+
+/**
+ * gabble_connection_advertise_capabilities
+ *
+ * Implements D-Bus method AdvertiseCapabilities
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occurred, D-Bus will throw the error only if this
+ * function returns FALSE.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+static void
+gabble_connection_advertise_capabilities (TpSvcConnectionInterfaceCapabilities *iface,
+ const GPtrArray *add,
+ const gchar **del,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (iface);
+ TpBaseConnection *base = (TpBaseConnection *)self;
+ guint i;
+ GabblePresence *pres = self->self_presence;
+ GabblePresenceCapabilities add_caps = 0, remove_caps = 0, caps, save_caps;
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
+ const CapabilityConversionData *ccd;
+ GPtrArray *ret;
+ GError *error = NULL;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ DEBUG ("caps before: %x", pres->caps);
+
+ for (i = 0; i < add->len; i++)
+ {
+ GValue iface_flags_pair = {0, };
+ gchar *channel_type;
+ guint flags;
+
+ g_value_init (&iface_flags_pair, GABBLE_TP_CAPABILITY_PAIR_TYPE);
+ g_value_set_static_boxed (&iface_flags_pair, g_ptr_array_index (add, i));
+
+ dbus_g_type_struct_get (&iface_flags_pair,
+ 0, &channel_type,
+ 1, &flags,
+ G_MAXUINT);
+
+ for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
+ if (g_str_equal (channel_type, ccd->iface))
+ add_caps |= ccd->tf2c_fn (flags);
+
+ g_free (channel_type);
+ }
+
+ for (i = 0; NULL != del[i]; i++)
+ {
+ for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
+ if (g_str_equal (del[i], ccd->iface))
+ remove_caps |= ccd->tf2c_fn (~0);
+ }
+
+ save_caps = caps = pres->caps;
+
+ caps |= add_caps;
+ caps ^= (caps & remove_caps);
+
+ DEBUG ("caps to add: %x", add_caps);
+ DEBUG ("caps to remove: %x", remove_caps);
+ DEBUG ("caps after: %x", caps);
+
+ if (caps ^ save_caps)
+ {
+ DEBUG ("before != after, changing");
+ gabble_presence_set_capabilities (pres, priv->resource, caps,
+ priv->caps_serial++);
+ DEBUG ("set caps: %x", pres->caps);
+ }
+
+ ret = g_ptr_array_new ();
+
+ for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
+ {
+ if (ccd->c2tf_fn (pres->caps))
+ {
+ GValue iface_flags_pair = {0, };
+
+ g_value_init (&iface_flags_pair, GABBLE_TP_CAPABILITY_PAIR_TYPE);
+ g_value_take_boxed (&iface_flags_pair,
+ dbus_g_type_specialized_construct (GABBLE_TP_CAPABILITY_PAIR_TYPE));
+
+ dbus_g_type_struct_set (&iface_flags_pair,
+ 0, ccd->iface,
+ 1, ccd->c2tf_fn (pres->caps),
+ G_MAXUINT);
+
+ g_ptr_array_add (ret, g_value_get_boxed (&iface_flags_pair));
+ }
+ }
+
+ if (caps ^ save_caps)
+ {
+ if (!_gabble_connection_signal_own_presence (self, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ return;
+ }
+
+ _emit_capabilities_changed (self, base->self_handle,
+ save_caps, caps);
+ }
+
+ tp_svc_connection_interface_capabilities_return_from_advertise_capabilities (
+ context, ret);
+ g_ptr_array_free (ret, TRUE);
+}
+
+static const gchar *assumed_caps[] =
+{
+ TP_IFACE_CHANNEL_TYPE_TEXT,
+ NULL
+};
+
+
+/**
+ * gabble_connection_get_capabilities
+ *
+ * Implements D-Bus method GetCapabilities
+ * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
+ *
+ * @error: Used to return a pointer to a GError detailing any error
+ * that occurred, D-Bus will throw the error only if this
+ * function returns FALSE.
+ *
+ * Returns: TRUE if successful, FALSE if an error was thrown.
+ */
+static void
+gabble_connection_get_capabilities (TpSvcConnectionInterfaceCapabilities *iface,
+ const GArray *handles,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (iface);
+ TpBaseConnection *base = (TpBaseConnection *)self;
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+ guint i;
+ GPtrArray *ret;
+ GError *error = NULL;
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ if (!tp_handles_are_valid (contact_handles, handles, TRUE, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ ret = g_ptr_array_new ();
+
+ for (i = 0; i < handles->len; i++)
+ {
+ TpHandle handle = g_array_index (handles, guint, i);
+ GabblePresence *pres;
+ const CapabilityConversionData *ccd;
+ guint typeflags;
+ const gchar **assumed;
+
+ if (0 == handle)
+ {
+ /* FIXME report the magical channel types available on the
+ * connection itself */
+ continue;
+ }
+
+ if (handle == base->self_handle)
+ pres = self->self_presence;
+ else
+ pres = gabble_presence_cache_get (self->presence_cache, handle);
+
+ if (NULL != pres)
+ for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
+ {
+ typeflags = ccd->c2tf_fn (pres->caps);
+
+ if (typeflags)
+ {
+ GValue monster = {0, };
+
+ g_value_init (&monster, GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE);
+ g_value_take_boxed (&monster,
+ dbus_g_type_specialized_construct (
+ GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE));
+
+ dbus_g_type_struct_set (&monster,
+ 0, handle,
+ 1, ccd->iface,
+ 2, TP_CONNECTION_CAPABILITY_FLAG_CREATE |
+ TP_CONNECTION_CAPABILITY_FLAG_INVITE,
+ 3, typeflags,
+ G_MAXUINT);
+
+ g_ptr_array_add (ret, g_value_get_boxed (&monster));
+ }
+ }
+
+ for (assumed = assumed_caps; NULL != *assumed; assumed++)
+ {
+ GValue monster = {0, };
+
+ g_value_init (&monster, GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE);
+ g_value_take_boxed (&monster,
+ dbus_g_type_specialized_construct (GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE));
+
+ dbus_g_type_struct_set (&monster,
+ 0, handle,
+ 1, *assumed,
+ 2, TP_CONNECTION_CAPABILITY_FLAG_CREATE |
+ TP_CONNECTION_CAPABILITY_FLAG_INVITE,
+ 3, 0,
+ G_MAXUINT);
+
+ g_ptr_array_add (ret, g_value_get_boxed (&monster));
+ }
+ }
+
+ tp_svc_connection_interface_capabilities_return_from_get_capabilities (
+ context, ret);
+
+ for (i = 0; i < ret->len; i++)
+ {
+ GValue monster = {0, };
+
+ g_value_init (&monster, GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE);
+ g_value_take_boxed (&monster, g_ptr_array_index (ret, i));
+ g_value_unset (&monster);
+ }
+
+ g_ptr_array_free (ret, TRUE);
+}
+
+
+const char *
+_gabble_connection_find_conference_server (GabbleConnection *conn)
+{
+ GabbleConnectionPrivate *priv;
+
+ g_assert (GABBLE_IS_CONNECTION (conn));
+
+ priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
+
+ if (priv->conference_server == NULL)
+ {
+ /* Find first server that has NS_MUC feature */
+ const GabbleDiscoItem *item = gabble_disco_service_find (conn->disco,
+ "conference", "text", NS_MUC);
+ if (item != NULL)
+ priv->conference_server = item->jid;
+ }
+
+ if (priv->conference_server == NULL)
+ priv->conference_server = priv->fallback_conference_server;
+
+ return priv->conference_server;
+}
+
+
+static gchar *
+_gabble_connection_get_canonical_room_name (GabbleConnection *conn,
+ const gchar *name)
+{
+ const gchar *server;
+
+ g_assert (GABBLE_IS_CONNECTION (conn));
+
+ if (strchr (name, '@'))
+ return g_strdup (name);
+
+ server = _gabble_connection_find_conference_server (conn);
+
+ if (server == NULL)
+ return NULL;
+
+ return g_strdup_printf ("%s@%s", name, server);
+}
+
+
+typedef struct _RoomVerifyContext RoomVerifyContext;
+
+typedef struct {
+ GabbleConnection *conn;
+ DBusGMethodInvocation *invocation;
+ gboolean errored;
+ guint count;
+ GArray *handles;
+ RoomVerifyContext *contexts;
+} RoomVerifyBatch;
+
+struct _RoomVerifyContext {
+ gchar *jid;
+ guint index;
+ RoomVerifyBatch *batch;
+ GabbleDiscoRequest *request;
+};
+
+static void
+room_verify_batch_free (RoomVerifyBatch *batch)
+{
+ TpBaseConnection *base = (TpBaseConnection *)(batch->conn);
+ TpHandleRepoIface *room_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_ROOM);
+ guint i;
+
+ tp_handles_unref (room_handles, batch->handles);
+ g_array_free (batch->handles, TRUE);
+ for (i = 0; i < batch->count; i++)
+ {
+ g_free (batch->contexts[i].jid);
+ }
+ g_free (batch->contexts);
+ g_slice_free (RoomVerifyBatch, batch);
+}
+
+/* Frees the error and the batch. */
+static void
+room_verify_batch_raise_error (RoomVerifyBatch *batch,
+ GError *error)
+{
+ guint i;
+
+ dbus_g_method_return_error (batch->invocation, error);
+ g_error_free (error);
+ batch->errored = TRUE;
+ for (i = 0; i < batch->count; i++)
+ {
+ if (batch->contexts[i].request)
+ {
+ gabble_disco_cancel_request (batch->conn->disco,
+ batch->contexts[i].request);
+ }
+ }
+ room_verify_batch_free (batch);
+}
+
+static RoomVerifyBatch *
+room_verify_batch_new (GabbleConnection *conn,
+ DBusGMethodInvocation *invocation,
+ guint count,
+ const gchar **jids)
+{
+ TpBaseConnection *base = (TpBaseConnection *)conn;
+ TpHandleRepoIface *room_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_ROOM);
+ RoomVerifyBatch *batch = g_slice_new (RoomVerifyBatch);
+ guint i;
+
+ batch->errored = FALSE;
+ batch->conn = conn;
+ batch->invocation = invocation;
+ batch->count = count;
+ batch->handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), count);
+ batch->contexts = g_new0(RoomVerifyContext, count);
+ for (i = 0; i < count; i++)
+ {
+ const gchar *name = jids[i];
+ gchar *qualified_name;
+ TpHandle handle;
+
+ batch->contexts[i].index = i;
+ batch->contexts[i].batch = batch;
+
+ qualified_name = _gabble_connection_get_canonical_room_name (conn, name);
+
+ if (!qualified_name)
+ {
+ GError *error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "requested room handle %s does not specify a server, but we "
+ "have not discovered any local conference servers and no "
+ "fallback was provided", name);
+ DEBUG ("%s", error->message);
+ room_verify_batch_raise_error (batch, error);
+ return NULL;
+ }
+
+ batch->contexts[i].jid = qualified_name;
+
+ /* has the handle been verified before? */
+ handle = tp_handle_lookup (room_handles, qualified_name, NULL, NULL);
+ if (handle)
+ tp_handle_ref (room_handles, handle);
+ g_array_append_val (batch->handles, handle);
+ }
+
+ return batch;
+}
+
+/* If all handles in the array have been disco'd or got from cache,
+free the batch and return TRUE. Else return FALSE. */
+static gboolean
+room_verify_batch_try_return (RoomVerifyBatch *batch)
+{
+ guint i;
+ TpHandleRepoIface *room_handles = tp_base_connection_get_handles (
+ (TpBaseConnection *)batch->conn, TP_HANDLE_TYPE_ROOM);
+ gchar *sender;
+ GError *error = NULL;
+
+ for (i = 0; i < batch->count; i++)
+ {
+ if (!g_array_index (batch->handles, TpHandle, i))
+ {
+ /* we're not ready yet */
+ return FALSE;
+ }
+ }
+
+ sender = dbus_g_method_get_sender (batch->invocation);
+ if (!tp_handles_client_hold (room_handles, sender, batch->handles, &error))
+ {
+ g_assert (error != NULL);
+ }
+ g_free (sender);
+
+ if (error == NULL)
+ {
+ tp_svc_connection_return_from_request_handles (batch->invocation,
+ batch->handles);
+ }
+ else
+ {
+ dbus_g_method_return_error (batch->invocation, error);
+ g_error_free (error);
+ }
+
+ room_verify_batch_free (batch);
+ return TRUE;
+}
+
+static void
+room_jid_disco_cb (GabbleDisco *disco,
+ GabbleDiscoRequest *request,
+ const gchar *jid,
+ const gchar *node,
+ LmMessageNode *query_result,
+ GError *error,
+ gpointer user_data)
+{
+ RoomVerifyContext *rvctx = user_data;
+ RoomVerifyBatch *batch = rvctx->batch;
+ TpHandleRepoIface *room_handles = tp_base_connection_get_handles (
+ (TpBaseConnection *)batch->conn, TP_HANDLE_TYPE_ROOM);
+ LmMessageNode *lm_node;
+ gboolean found = FALSE;
+ TpHandle handle;
+
+ /* stop the request getting cancelled after it's already finished */
+ rvctx->request = NULL;
+
+ /* if an error is being handled already, quietly go away */
+ if (batch->errored)
+ {
+ return;
+ }
+
+ if (error != NULL)
+ {
+ DEBUG ("disco reply error %s", error->message);
+
+ /* disco will free the old error, _raise_error will free the new one */
+ error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "can't retrieve room info: %s", error->message);
+
+ room_verify_batch_raise_error (batch, error);
+
+ return;
+ }
+
+ for (lm_node = query_result->children; lm_node; lm_node = lm_node->next)
+ {
+ const gchar *var;
+
+ if (tp_strdiff (lm_node->name, "feature"))
+ continue;
+
+ var = lm_message_node_get_attribute (lm_node, "var");
+
+ /* for servers who consider schema compliance to be an optional bonus */
+ if (var == NULL)
+ var = lm_message_node_get_attribute (lm_node, "type");
+
+ if (!tp_strdiff (var, NS_MUC))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ DEBUG ("no MUC support for service name in jid %s", rvctx->jid);
+
+ error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "specified server doesn't support MUC");
+
+ room_verify_batch_raise_error (batch, error);
+
+ return;
+ }
+
+ /* this refs the handle, so we're putting a ref in batch->handles */
+ handle = tp_handle_ensure (room_handles, rvctx->jid, NULL, &error);
+ if (handle == 0)
+ {
+ room_verify_batch_raise_error (batch, error);
+ return;
+ }
+
+ DEBUG ("disco reported MUC support for service name in jid %s", rvctx->jid);
+ g_array_index (batch->handles, TpHandle, rvctx->index) = handle;
+
+ /* if this was the last callback to be run, send off the result */
+ room_verify_batch_try_return (batch);
+}
+
+/**
+ * room_jid_verify:
+ *
+ * Utility function that verifies that the service name of
+ * the specified jid exists and reports MUC support.
+ */
+static gboolean
+room_jid_verify (RoomVerifyBatch *batch,
+ guint i,
+ DBusGMethodInvocation *context)
+{
+ gchar *room, *service;
+ gboolean ret;
+ GError *error = NULL;
+
+ room = service = NULL;
+ gabble_decode_jid (batch->contexts[i].jid, &room, &service, NULL);
+
+ if (room == NULL || *room == '\0' || service == NULL || *service == '\0')
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "unable to get room name and service from JID %s",
+ batch->contexts[i].jid);
+
+ ret = FALSE;
+
+ goto out;
+ }
+
+ ret = (gabble_disco_request (batch->conn->disco, GABBLE_DISCO_TYPE_INFO,
+ service, NULL, room_jid_disco_cb,
+ batch->contexts + i,
+ G_OBJECT (batch->conn), &error) != NULL);
+
+out:
+ if (!ret)
+ {
+ room_verify_batch_raise_error (batch, error);
+ }
+
+ g_free (room);
+ g_free (service);
+
+ return ret;
+}
+
+
+/**
+ * gabble_connection_request_handles
+ *
+ * Implements D-Bus method RequestHandles
+ * on interface org.freedesktop.Telepathy.Connection
+ *
+ * @context: The D-Bus invocation context to use to return values
+ * or throw an error.
+ */
+static void
+gabble_connection_request_handles (TpSvcConnection *iface,
+ guint handle_type,
+ const gchar **names,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (iface);
+ TpBaseConnection *base = (TpBaseConnection *)self;
+
+ g_assert (GABBLE_IS_CONNECTION (self));
+
+ TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
+
+ if (handle_type == TP_HANDLE_TYPE_ROOM)
+ {
+ RoomVerifyBatch *batch = NULL;
+ guint count = 0, i;
+ const gchar **cur_name;
+
+ for (cur_name = names; *cur_name != NULL; cur_name++)
+ {
+ count++;
+ }
+
+ batch = room_verify_batch_new (self, context, count, names);
+ if (!batch)
+ {
+ /* an error occurred while setting up the batch, and we returned
+ error to dbus */
+ return;
+ }
+
+ /* have all the handles been verified already? If so, nothing to do */
+ if (room_verify_batch_try_return (batch))
+ {
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ if (!room_jid_verify (batch, i, context))
+ {
+ return;
+ }
+ }
+
+ /* we've set the verification process going - the callback will handle
+ returning or raising error */
+ return;
+ }
+
+ /* else it's either an invalid type, or a type we can verify immediately -
+ * in either case, let the superclass do it */
+ tp_base_connection_dbus_request_handles (iface, handle_type, names, context);
+}
+
+void
+gabble_connection_ensure_capabilities (GabbleConnection *self,
+ GabblePresenceCapabilities caps)
+{
+ GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
+ GabblePresenceCapabilities old_caps, new_caps;
+
+ old_caps = self->self_presence->caps;
+ new_caps = old_caps;
+ new_caps |= caps;
+
+ if (old_caps ^ new_caps)
+ {
+ /* We changed capabilities */
+ GError *error = NULL;
+
+ gabble_presence_set_capabilities (self->self_presence, priv->resource,
+ new_caps, priv->caps_serial++);
+
+ if (!_gabble_connection_signal_own_presence (self, &error))
+ DEBUG ("error sending presence: %s", error->message);
+ }
+}
+
+/* We reimplement RequestHandles to be able to do async validation on
+ * room handles */
+static void
+conn_service_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ TpSvcConnectionClass *klass = (TpSvcConnectionClass *)g_iface;
+
+#define IMPLEMENT(x) tp_svc_connection_implement_##x (klass, \
+ gabble_connection_##x)
+ IMPLEMENT(request_handles);
+#undef IMPLEMENT
+}
+
+static void
+capabilities_service_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ TpSvcConnectionInterfaceCapabilitiesClass *klass =
+ (TpSvcConnectionInterfaceCapabilitiesClass *)g_iface;
+
+#define IMPLEMENT(x) tp_svc_connection_interface_capabilities_implement_##x (\
+ klass, gabble_connection_##x)
+ IMPLEMENT(advertise_capabilities);
+ IMPLEMENT(get_capabilities);
+#undef IMPLEMENT
+}
+
+
+/* For unit tests only */
+void
+gabble_connection_set_disco_reply_timeout (guint timeout)
+{
+ disco_reply_timeout = timeout;
+}
diff --git a/src/connection.h b/src/connection.h
index 2cc329e..2a8bccf 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -71,7 +71,7 @@ typedef LmHandlerResult (*GabbleConnectionMsgReplyFunc) (GabbleConnection *conn,
gpointer user_data);
/* must be in the same order as the list_handle_strings in
- * gabble-connection.c */
+ * connection.c */
typedef enum
{
GABBLE_LIST_HANDLE_PUBLISH = 1,
diff --git a/src/gabble-connection.c b/src/gabble-connection.c
deleted file mode 100644
index 2c1e048..0000000
--- a/src/gabble-connection.c
+++ /dev/null
@@ -1,2794 +0,0 @@
-/*
- * gabble-connection.c - Source for GabbleConnection
- * Copyright (C) 2005, 2006 Collabora Ltd.
- * Copyright (C) 2005, 2006 Nokia Corporation
- *
- * 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 "connection.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define DBUS_API_SUBJECT_TO_CHANGE
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <glib-object.h>
-#include <loudmouth/loudmouth.h>
-
-#include <telepathy-glib/channel-iface.h>
-#include <telepathy-glib/channel-factory-iface.h>
-#include <telepathy-glib/dbus.h>
-#include <telepathy-glib/enums.h>
-#include <telepathy-glib/errors.h>
-#include <telepathy-glib/handle-repo-dynamic.h>
-#include <telepathy-glib/handle-repo-static.h>
-#include <telepathy-glib/interfaces.h>
-#include <telepathy-glib/svc-connection.h>
-
-#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION
-
-#include "bytestream-factory.h"
-#include "capabilities.h"
-#include "caps-hash.h"
-#include "conn-aliasing.h"
-#include "conn-avatars.h"
-#include "conn-presence.h"
-#include "conn-olpc.h"
-#include "debug.h"
-#include "disco.h"
-#include "media-channel.h"
-#include "register.h"
-#include "roomlist-channel.h"
-#include "im-factory.h"
-#include "media-factory.h"
-#include "muc-factory.h"
-#include "namespaces.h"
-#include "presence-cache.h"
-#include "presence.h"
-#include "pubsub.h"
-#include "request-pipeline.h"
-#include "roster.h"
-#include "private-tubes-factory.h"
-#include "util.h"
-#include "vcard-manager.h"
-
-static guint disco_reply_timeout = 5000;
-
-#define GABBLE_TP_CAPABILITY_PAIR_TYPE (dbus_g_type_get_struct ("GValueArray", \
- G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID))
-#define GABBLE_TP_CAPABILITIES_CHANGED_MONSTER_TYPE (dbus_g_type_get_struct \
- ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \
- G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
-#define GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE (dbus_g_type_get_struct \
- ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \
- G_TYPE_INVALID))
-
-static void conn_service_iface_init (gpointer, gpointer);
-static void capabilities_service_iface_init (gpointer, gpointer);
-
-G_DEFINE_TYPE_WITH_CODE(GabbleConnection,
- gabble_connection,
- TP_TYPE_BASE_CONNECTION,
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION,
- conn_service_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING,
- conn_aliasing_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS,
- conn_avatars_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CAPABILITIES,
- capabilities_service_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
- conn_presence_iface_init);
- G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_BUDDY_INFO,
- olpc_buddy_info_iface_init);
- G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_ACTIVITY_PROPERTIES,
- olpc_activity_properties_iface_init);
- )
-
-/* properties */
-enum
-{
- PROP_CONNECT_SERVER = 1,
- PROP_PORT,
- PROP_OLD_SSL,
- PROP_REQUIRE_ENCRYPTION,
- PROP_REGISTER,
- PROP_LOW_BANDWIDTH,
- PROP_STREAM_SERVER,
- PROP_USERNAME,
- PROP_PASSWORD,
- PROP_RESOURCE,
- PROP_PRIORITY,
- PROP_HTTPS_PROXY_SERVER,
- PROP_HTTPS_PROXY_PORT,
- PROP_FALLBACK_CONFERENCE_SERVER,
- PROP_STUN_SERVER,
- PROP_STUN_PORT,
- PROP_IGNORE_SSL_ERRORS,
- PROP_ALIAS,
-
- LAST_PROPERTY
-};
-
-/* private structure */
-typedef struct _GabbleConnectionPrivate GabbleConnectionPrivate;
-
-struct _GabbleConnectionPrivate
-{
- LmMessageHandler *iq_disco_cb;
- LmMessageHandler *iq_unknown_cb;
- LmMessageHandler *stream_error_cb;
- LmMessageHandler *msg_cb;
-
- /* connection properties */
- gchar *connect_server;
- guint port;
- gboolean old_ssl;
- gboolean require_encryption;
-
- gboolean ignore_ssl_errors;
- TpConnectionStatusReason ssl_error;
-
- gboolean do_register;
-
- gboolean low_bandwidth;
-
- gchar *https_proxy_server;
- guint16 https_proxy_port;
-
- gchar *stun_server;
- guint16 stun_port;
-
- gchar *fallback_conference_server;
-
- /* authentication properties */
- gchar *stream_server;
- gchar *username;
- gchar *password;
- gchar *resource;
- gint8 priority;
- gchar *alias;
-
- /* reference to conference server name */
- const gchar *conference_server;
-
- /* serial number of current advertised caps */
- guint caps_serial;
-
- /* gobject housekeeping */
- gboolean dispose_has_run;
-};
-
-#define GABBLE_CONNECTION_GET_PRIVATE(obj) \
- ((GabbleConnectionPrivate *)obj->priv)
-
-static void connection_capabilities_update_cb (GabblePresenceCache *,
- TpHandle, GabblePresenceCapabilities, GabblePresenceCapabilities,
- gpointer);
-
-static GPtrArray *
-_gabble_connection_create_channel_factories (TpBaseConnection *conn)
-{
- GabbleConnection *self = GABBLE_CONNECTION (conn);
-
- GPtrArray *channel_factories = g_ptr_array_sized_new (4);
-
- self->roster = gabble_roster_new (self);
- g_signal_connect (self->roster, "nickname-update", G_CALLBACK
- (gabble_conn_aliasing_nickname_updated), self);
-
- g_ptr_array_add (channel_factories, self->roster);
-
- self->muc_factory = g_object_new (GABBLE_TYPE_MUC_FACTORY,
- "connection", self,
- NULL);
- g_ptr_array_add (channel_factories, self->muc_factory);
-
- g_ptr_array_add (channel_factories,
- g_object_new (GABBLE_TYPE_MEDIA_FACTORY,
- "connection", self,
- NULL));
-
- g_ptr_array_add (channel_factories,
- g_object_new (GABBLE_TYPE_IM_FACTORY,
- "connection", self,
- NULL));
-
- self->private_tubes_factory = gabble_private_tubes_factory_new (self);
- g_ptr_array_add (channel_factories, self->private_tubes_factory);
-
- return channel_factories;
-}
-
-static GObject *
-gabble_connection_constructor (GType type,
- guint n_construct_properties,
- GObjectConstructParam *construct_params)
-{
- GabbleConnection *self = GABBLE_CONNECTION (
- G_OBJECT_CLASS (gabble_connection_parent_class)->constructor (
- type, n_construct_properties, construct_params));
-
- DEBUG("Post-construction: (GabbleConnection *)%p", self);
-
- self->req_pipeline = gabble_request_pipeline_new (self);
- self->disco = gabble_disco_new (self);
- self->vcard_manager = gabble_vcard_manager_new (self);
- g_signal_connect (self->vcard_manager, "nickname-update", G_CALLBACK
- (gabble_conn_aliasing_nickname_updated), self);
-
- self->presence_cache = gabble_presence_cache_new (self);
- g_signal_connect (self->presence_cache, "nickname-update", G_CALLBACK
- (gabble_conn_aliasing_nickname_updated), self);
- g_signal_connect (self->presence_cache, "capabilities-update", G_CALLBACK
- (connection_capabilities_update_cb), self);
-
- capabilities_fill_cache (self->presence_cache);
-
- conn_avatars_init (self);
- conn_presence_init (self);
- conn_olpc_activity_properties_init (self);
-
- self->bytestream_factory = gabble_bytestream_factory_new (self);
-
- self->avatar_requests = g_hash_table_new (NULL, NULL);
-
- return (GObject *)self;
-}
-
-static void
-gabble_connection_init (GabbleConnection *self)
-{
- GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate);
-
- DEBUG("Initializing (GabbleConnection *)%p", self);
-
- self->priv = priv;
- self->lmconn = lm_connection_new (NULL);
-
- /* Set default parameters for optional parameters */
- priv->resource = g_strdup (GABBLE_PARAMS_DEFAULT_RESOURCE);
-
- priv->caps_serial = 1;
-}
-
-static void
-gabble_connection_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GabbleConnection *self = (GabbleConnection *) object;
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
-
- switch (property_id) {
- case PROP_CONNECT_SERVER:
- g_value_set_string (value, priv->connect_server);
- break;
- case PROP_STREAM_SERVER:
- g_value_set_string (value, priv->stream_server);
- break;
- case PROP_PORT:
- g_value_set_uint (value, priv->port);
- break;
- case PROP_OLD_SSL:
- g_value_set_boolean (value, priv->old_ssl);
- break;
- case PROP_REQUIRE_ENCRYPTION:
- g_value_set_boolean (value, priv->require_encryption);
- break;
- case PROP_REGISTER:
- g_value_set_boolean (value, priv->do_register);
- break;
- case PROP_LOW_BANDWIDTH:
- g_value_set_boolean (value, priv->low_bandwidth);
- break;
- case PROP_USERNAME:
- g_value_set_string (value, priv->username);
- break;
- case PROP_PASSWORD:
- g_value_set_string (value, priv->password);
- break;
- case PROP_RESOURCE:
- g_value_set_string (value, priv->resource);
- break;
- case PROP_PRIORITY:
- g_value_set_char (value, priv->priority);
- break;
- case PROP_HTTPS_PROXY_SERVER:
- g_value_set_string (value, priv->https_proxy_server);
- break;
- case PROP_HTTPS_PROXY_PORT:
- g_value_set_uint (value, priv->https_proxy_port);
- break;
- case PROP_FALLBACK_CONFERENCE_SERVER:
- g_value_set_string (value, priv->fallback_conference_server);
- break;
- case PROP_IGNORE_SSL_ERRORS:
- g_value_set_boolean (value, priv->ignore_ssl_errors);
- break;
- case PROP_ALIAS:
- g_value_set_string (value, priv->alias);
- break;
- case PROP_STUN_SERVER:
- g_value_set_string (value, priv->stun_server);
- break;
- case PROP_STUN_PORT:
- g_value_set_uint (value, priv->stun_port);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void
-gabble_connection_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GabbleConnection *self = (GabbleConnection *) object;
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
-
- switch (property_id) {
- case PROP_CONNECT_SERVER:
- g_free (priv->connect_server);
- priv->connect_server = g_value_dup_string (value);
- break;
- case PROP_PORT:
- priv->port = g_value_get_uint (value);
- break;
- case PROP_OLD_SSL:
- priv->old_ssl = g_value_get_boolean (value);
- break;
- case PROP_REQUIRE_ENCRYPTION:
- priv->require_encryption = g_value_get_boolean (value);
- break;
- case PROP_REGISTER:
- priv->do_register = g_value_get_boolean (value);
- break;
- case PROP_LOW_BANDWIDTH:
- priv->low_bandwidth = g_value_get_boolean (value);
- break;
- case PROP_STREAM_SERVER:
- g_free (priv->stream_server);
- priv->stream_server = g_value_dup_string (value);
- break;
- case PROP_USERNAME:
- g_free (priv->username);
- priv->username = g_value_dup_string (value);
- break;
- case PROP_PASSWORD:
- g_free (priv->password);
- priv->password = g_value_dup_string (value);
- break;
- case PROP_RESOURCE:
- g_free (priv->resource);
- priv->resource = g_value_dup_string (value);
- break;
- case PROP_PRIORITY:
- priv->priority = g_value_get_char (value);
- break;
- case PROP_HTTPS_PROXY_SERVER:
- g_free (priv->https_proxy_server);
- priv->https_proxy_server = g_value_dup_string (value);
- break;
- case PROP_HTTPS_PROXY_PORT:
- priv->https_proxy_port = g_value_get_uint (value);
- break;
- case PROP_FALLBACK_CONFERENCE_SERVER:
- g_free (priv->fallback_conference_server);
- priv->fallback_conference_server = g_value_dup_string (value);
- break;
- case PROP_IGNORE_SSL_ERRORS:
- priv->ignore_ssl_errors = g_value_get_boolean (value);
- break;
- case PROP_ALIAS:
- g_free (priv->alias);
- priv->alias = g_value_dup_string (value);
- break;
- case PROP_STUN_SERVER:
- g_free (priv->stun_server);
- priv->stun_server = g_value_dup_string (value);
- break;
- case PROP_STUN_PORT:
- priv->stun_port = g_value_get_uint (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- break;
- }
-}
-
-static void gabble_connection_dispose (GObject *object);
-static void gabble_connection_finalize (GObject *object);
-static void connect_callbacks (TpBaseConnection *base);
-static void disconnect_callbacks (TpBaseConnection *base);
-static void connection_shut_down (TpBaseConnection *base);
-static gboolean _gabble_connection_connect (TpBaseConnection *base,
- GError **error);
-
-static gchar *
-gabble_connection_get_unique_name (TpBaseConnection *self)
-{
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (
- GABBLE_CONNECTION (self));
-
- return g_strdup_printf ("%s@%s/%s",
- priv->username,
- priv->stream_server,
- priv->resource);
-}
-
-/* must be in the same order as GabbleListHandle in connection.h */
-static const char *list_handle_strings[] =
-{
- "publish", /* GABBLE_LIST_HANDLE_PUBLISH */
- "subscribe", /* GABBLE_LIST_HANDLE_SUBSCRIBE */
- "known", /* GABBLE_LIST_HANDLE_KNOWN */
- "deny", /* GABBLE_LIST_HANDLE_DENY */
- NULL
-};
-
-/* For the benefit of the unit tests, this will allow the connection to
- * be NULL
- */
-void
-_gabble_connection_create_handle_repos (TpBaseConnection *conn,
- TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES])
-{
- repos[TP_HANDLE_TYPE_CONTACT] =
- tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_CONTACT,
- gabble_normalize_contact, GUINT_TO_POINTER (GABBLE_JID_ANY));
- repos[TP_HANDLE_TYPE_ROOM] =
- tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_ROOM, gabble_normalize_room,
- NULL);
- repos[TP_HANDLE_TYPE_GROUP] =
- tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_GROUP, NULL, NULL);
- repos[TP_HANDLE_TYPE_LIST] =
- tp_static_handle_repo_new (TP_HANDLE_TYPE_LIST, list_handle_strings);
-}
-
-static void
-base_connected_cb (TpBaseConnection *base_conn)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (base_conn);
-
- gabble_connection_connected_olpc (conn);
-}
-
-static void
-gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (gabble_connection_class);
- TpBaseConnectionClass *parent_class = TP_BASE_CONNECTION_CLASS (
- gabble_connection_class);
- GParamSpec *param_spec;
- static const gchar *interfaces_always_present[] = {
- TP_IFACE_CONNECTION_INTERFACE_ALIASING,
- TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES,
- TP_IFACE_CONNECTION_INTERFACE_PRESENCE,
- TP_IFACE_CONNECTION_INTERFACE_AVATARS,
- NULL };
-
- DEBUG("Initializing (GabbleConnectionClass *)%p", gabble_connection_class);
-
- object_class->get_property = gabble_connection_get_property;
- object_class->set_property = gabble_connection_set_property;
- object_class->constructor = gabble_connection_constructor;
-
- parent_class->create_handle_repos = _gabble_connection_create_handle_repos;
- parent_class->get_unique_connection_name = gabble_connection_get_unique_name;
- parent_class->create_channel_factories =
- _gabble_connection_create_channel_factories;
- parent_class->connecting = connect_callbacks;
- parent_class->connected = base_connected_cb;
- parent_class->disconnected = disconnect_callbacks;
- parent_class->shut_down = connection_shut_down;
- parent_class->start_connecting = _gabble_connection_connect;
- parent_class->interfaces_always_present = interfaces_always_present;
-
- g_type_class_add_private (gabble_connection_class,
- sizeof (GabbleConnectionPrivate));
-
- object_class->dispose = gabble_connection_dispose;
- object_class->finalize = gabble_connection_finalize;
-
- param_spec = g_param_spec_string ("connect-server", "Hostname or IP of Jabber server",
- "The server used when establishing a connection.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_CONNECT_SERVER, param_spec);
-
- param_spec = g_param_spec_uint ("port", "Jabber server port",
- "The port used when establishing a connection.",
- 0, G_MAXUINT16, 0,
- G_PARAM_CONSTRUCT |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_PORT, param_spec);
-
- param_spec = g_param_spec_boolean ("old-ssl", "Old-style SSL tunneled connection",
- "Establish the entire connection to the server "
- "within an SSL-encrypted tunnel. Note that this "
- "is not the same as connecting with TLS, which "
- "is not yet supported.", FALSE,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_OLD_SSL, param_spec);
-
- param_spec = g_param_spec_boolean ("require-encryption", "Require encryption",
- "Require the connection to be encrypted, either "
- "via old-style SSL, or StartTLS mechanisms.", FALSE,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_REQUIRE_ENCRYPTION, param_spec);
-
- param_spec = g_param_spec_boolean ("register", "Register account on server",
- "Register a new account on server.", FALSE,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_REGISTER, param_spec);
-
- param_spec = g_param_spec_boolean ("low-bandwidth", "Low bandwidth mode",
- "Determines whether we are in low "
- "bandwidth mode. This influences "
- "polling behaviour.", FALSE,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_LOW_BANDWIDTH, param_spec);
-
- param_spec = g_param_spec_string ("stream-server", "The server name used to initialise the stream.",
- "The server name used when initialising the stream, "
- "which is usually the part after the @ in the user's JID.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_STREAM_SERVER, param_spec);
-
- param_spec = g_param_spec_string ("username", "Jabber username",
- "The username used when authenticating.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_USERNAME, param_spec);
-
- param_spec = g_param_spec_string ("password", "Jabber password",
- "The password used when authenticating.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_PASSWORD, param_spec);
-
- param_spec = g_param_spec_string ("resource", "Jabber resource",
- "The Jabber resource used when authenticating.",
- "Telepathy",
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_RESOURCE, param_spec);
-
- param_spec = g_param_spec_char ("priority", "Jabber presence priority",
- "The default priority used when reporting our presence.",
- G_MININT8, G_MAXINT8, 0,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_PRIORITY, param_spec);
-
- param_spec = g_param_spec_string ("https-proxy-server", "The server name "
- "used as an HTTPS proxy server",
- "The server name used as an HTTPS proxy "
- "server.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_HTTPS_PROXY_SERVER, param_spec);
-
- param_spec = g_param_spec_uint ("https-proxy-port", "The HTTP proxy server "
- "port", "The HTTP proxy server port.",
- 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT,
- G_PARAM_CONSTRUCT |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_HTTPS_PROXY_PORT, param_spec);
-
- param_spec = g_param_spec_string ("fallback-conference-server",
- "The conference server used as fallback",
- "The conference server used as fallback when "
- "everything else fails.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_FALLBACK_CONFERENCE_SERVER,
- param_spec);
-
- param_spec = g_param_spec_string ("stun-server",
- "STUN server",
- "STUN server.",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec);
-
- param_spec = g_param_spec_uint ("stun-port",
- "STUN port",
- "STUN port.",
- 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_STUN_PORT,
- G_PARAM_CONSTRUCT |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec);
-
- param_spec = g_param_spec_boolean ("ignore-ssl-errors", "Ignore SSL errors",
- "Continue connecting even if the server's "
- "SSL certificate is invalid or missing.",
- FALSE,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_IGNORE_SSL_ERRORS, param_spec);
-
- param_spec = g_param_spec_string ("alias",
- "Alias/nick for local user",
- "Alias/nick for local user",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_NAME |
- G_PARAM_STATIC_BLURB);
- g_object_class_install_property (object_class, PROP_ALIAS, param_spec);
-
- conn_presence_class_init (gabble_connection_class);
-}
-
-static gboolean
-_unref_lm_connection (gpointer data)
-{
- LmConnection *conn = (LmConnection *) data;
-
- lm_connection_unref (conn);
- return FALSE;
-}
-
-static void
-gabble_connection_dispose (GObject *object)
-{
- GabbleConnection *self = GABBLE_CONNECTION (object);
- TpBaseConnection *base = (TpBaseConnection *)self;
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
-
- if (priv->dispose_has_run)
- return;
-
- priv->dispose_has_run = TRUE;
-
- DEBUG ("called");
-
- g_assert ((base->status == TP_CONNECTION_STATUS_DISCONNECTED) ||
- (base->status == TP_INTERNAL_CONNECTION_STATUS_NEW));
- g_assert (base->self_handle == 0);
-
- g_object_unref (self->bytestream_factory);
- self->bytestream_factory = NULL;
-
- g_object_unref (self->disco);
- self->disco = NULL;
-
- g_object_unref (self->req_pipeline);
- self->req_pipeline = NULL;
-
- g_object_unref (self->vcard_manager);
- self->vcard_manager = NULL;
-
- /* remove borrowed references before TpBaseConnection unrefs the channel
- * factories */
- self->roster = NULL;
- self->muc_factory = NULL;
- self->private_tubes_factory = NULL;
-
- if (self->self_presence != NULL)
- g_object_unref (self->self_presence);
- self->self_presence = NULL;
-
- g_object_unref (self->presence_cache);
- self->presence_cache = NULL;
-
- g_hash_table_destroy (self->olpc_activities_info);
- g_hash_table_destroy (self->olpc_pep_activities);
- g_hash_table_destroy (self->olpc_invited_activities);
-
- g_hash_table_destroy (self->avatar_requests);
-
- /* if this is not already the case, we'll crash anyway */
- g_assert (!lm_connection_is_open (self->lmconn));
-
- g_assert (priv->iq_disco_cb == NULL);
- g_assert (priv->iq_unknown_cb == NULL);
- g_assert (priv->stream_error_cb == NULL);
- g_assert (priv->msg_cb == NULL);
-
- /*
- * The Loudmouth connection can't be unref'd immediately because this
- * function might (indirectly) return into Loudmouth code which expects the
- * connection to always be there.
- */
- g_idle_add (_unref_lm_connection, self->lmconn);
-
- if (G_OBJECT_CLASS (gabble_connection_parent_class)->dispose)
- G_OBJECT_CLASS (gabble_connection_parent_class)->dispose (object);
-}
-
-static void
-gabble_connection_finalize (GObject *object)
-{
- GabbleConnection *self = GABBLE_CONNECTION (object);
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
-
- DEBUG ("called with %p", object);
-
- g_free (priv->connect_server);
- g_free (priv->stream_server);
- g_free (priv->username);
- g_free (priv->password);
- g_free (priv->resource);
-
- g_free (priv->https_proxy_server);
- g_free (priv->stun_server);
- g_free (priv->fallback_conference_server);
-
- g_free (priv->alias);
-
- conn_presence_finalize (self);
-
- G_OBJECT_CLASS (gabble_connection_parent_class)->finalize (object);
-}
-
-/**
- * _gabble_connection_set_properties_from_account
- *
- * Parses an account string which may be one of the following forms:
- * username at server
- * username at server/resource
- * and sets the properties for username, stream server and resource
- * appropriately. Also sets the connect server to the stream server if one has
- * not yet been specified.
- */
-gboolean
-_gabble_connection_set_properties_from_account (GabbleConnection *conn,
- const gchar *account,
- GError **error)
-{
- GabbleConnectionPrivate *priv;
- char *username, *server, *resource;
- gboolean result;
-
- g_assert (GABBLE_IS_CONNECTION (conn));
- g_assert (account != NULL);
-
- priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
-
- username = server = resource = NULL;
- result = TRUE;
-
- gabble_decode_jid (account, &username, &server, &resource);
-
- if (username == NULL || server == NULL ||
- *username == '\0' || *server == '\0')
- {
- g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "unable to get username and server from account");
- result = FALSE;
- goto OUT;
- }
-
- g_object_set (G_OBJECT (conn),
- "username", username,
- "stream-server", server,
- NULL);
-
- /* only override the default resource if we actually got one */
- if (resource)
- g_object_set (G_OBJECT (conn), "resource", resource, NULL);
-
-OUT:
- g_free (username);
- g_free (server);
- g_free (resource);
-
- return result;
-}
-
-
-/**
- * _gabble_connection_send
- *
- * Send an LmMessage and trap network errors appropriately.
- */
-gboolean
-_gabble_connection_send (GabbleConnection *conn, LmMessage *msg, GError **error)
-{
- GabbleConnectionPrivate *priv;
- GError *lmerror = NULL;
-
- g_assert (GABBLE_IS_CONNECTION (conn));
-
- priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
-
- if (!lm_connection_send (conn->lmconn, msg, &lmerror))
- {
- DEBUG ("failed: %s", lmerror->message);
-
- g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR,
- "message send failed: %s", lmerror->message);
-
- g_error_free (lmerror);
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-typedef struct {
- GabbleConnectionMsgReplyFunc reply_func;
-
- GabbleConnection *conn;
- LmMessage *sent_msg;
- gpointer user_data;
-
- GObject *object;
- gboolean object_alive;
-} GabbleMsgHandlerData;
-
-static LmHandlerResult
-message_send_reply_cb (LmMessageHandler *handler,
- LmConnection *connection,
- LmMessage *reply_msg,
- gpointer user_data)
-{
- GabbleMsgHandlerData *handler_data = user_data;
- LmMessageSubType sub_type;
-
- sub_type = lm_message_get_sub_type (reply_msg);
-
- /* Is it a reply to this message? If we're talking to another loudmouth,
- * they can send us messages which have the same ID as ones we send. :-O */
- if (sub_type != LM_MESSAGE_SUB_TYPE_RESULT &&
- sub_type != LM_MESSAGE_SUB_TYPE_ERROR)
- {
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
-
- if (handler_data->object_alive && handler_data->reply_func != NULL)
- {
- return handler_data->reply_func (handler_data->conn,
- handler_data->sent_msg,
- reply_msg,
- handler_data->object,
- handler_data->user_data);
- }
-
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-static void
-message_send_object_destroy_notify_cb (gpointer data,
- GObject *where_the_object_was)
-{
- GabbleMsgHandlerData *handler_data = data;
-
- handler_data->object = NULL;
- handler_data->object_alive = FALSE;
-}
-
-static void
-message_send_handler_destroy_cb (gpointer data)
-{
- GabbleMsgHandlerData *handler_data = data;
-
- lm_message_unref (handler_data->sent_msg);
-
- if (handler_data->object != NULL)
- {
- g_object_weak_unref (handler_data->object,
- message_send_object_destroy_notify_cb,
- handler_data);
- }
-
- g_slice_free (GabbleMsgHandlerData, handler_data);
-}
-
-/**
- * _gabble_connection_send_with_reply
- *
- * Send a tracked LmMessage and trap network errors appropriately.
- *
- * If object is non-NULL the handler will follow the lifetime of that object,
- * which means that if the object is destroyed the callback will not be invoked.
- *
- * if reply_func is NULL the reply will be ignored but connection_iq_unknown_cb
- * won't be called.
- */
-gboolean
-_gabble_connection_send_with_reply (GabbleConnection *conn,
- LmMessage *msg,
- GabbleConnectionMsgReplyFunc reply_func,
- GObject *object,
- gpointer user_data,
- GError **error)
-{
- GabbleConnectionPrivate *priv;
- LmMessageHandler *handler;
- GabbleMsgHandlerData *handler_data;
- gboolean ret;
- GError *lmerror = NULL;
-
- g_assert (GABBLE_IS_CONNECTION (conn));
-
- priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
-
- lm_message_ref (msg);
-
- handler_data = g_slice_new (GabbleMsgHandlerData);
- handler_data->reply_func = reply_func;
- handler_data->conn = conn;
- handler_data->sent_msg = msg;
- handler_data->user_data = user_data;
-
- handler_data->object = object;
- handler_data->object_alive = TRUE;
-
- if (object != NULL)
- {
- g_object_weak_ref (object, message_send_object_destroy_notify_cb,
- handler_data);
- }
-
- handler = lm_message_handler_new (message_send_reply_cb, handler_data,
- message_send_handler_destroy_cb);
-
- ret = lm_connection_send_with_reply (conn->lmconn, msg, handler, &lmerror);
- if (!ret)
- {
- DEBUG ("failed: %s", lmerror->message);
-
- if (error)
- {
- g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR,
- "message send failed: %s", lmerror->message);
- }
-
- g_error_free (lmerror);
- }
-
- lm_message_handler_unref (handler);
-
- return ret;
-}
-
-static LmHandlerResult connection_iq_disco_cb (LmMessageHandler *,
- LmConnection *, LmMessage *, gpointer);
-static LmHandlerResult connection_iq_unknown_cb (LmMessageHandler *,
- LmConnection *, LmMessage *, gpointer);
-static LmHandlerResult connection_stream_error_cb (LmMessageHandler *,
- LmConnection *, LmMessage *, gpointer);
-static LmSSLResponse connection_ssl_cb (LmSSL *, LmSSLStatus, gpointer);
-static void connection_open_cb (LmConnection *, gboolean, gpointer);
-static void connection_auth_cb (LmConnection *, gboolean, gpointer);
-static void connection_disco_cb (GabbleDisco *, GabbleDiscoRequest *,
- const gchar *, const gchar *, LmMessageNode *, GError *, gpointer);
-static void connection_disconnected_cb (LmConnection *, LmDisconnectReason,
- gpointer);
-
-
-static gboolean
-do_connect (GabbleConnection *conn, GError **error)
-{
- GError *lmerror = NULL;
-
- DEBUG ("calling lm_connection_open");
-
- if (!lm_connection_open (conn->lmconn, connection_open_cb,
- conn, NULL, &lmerror))
- {
- DEBUG ("lm_connection_open failed %s", lmerror->message);
-
- g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR,
- "lm_connection_open failed: %s", lmerror->message);
-
- g_error_free (lmerror);
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-connect_callbacks (TpBaseConnection *base)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (base);
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
-
- g_assert (priv->iq_disco_cb == NULL);
- g_assert (priv->iq_unknown_cb == NULL);
- g_assert (priv->stream_error_cb == NULL);
- g_assert (priv->msg_cb == NULL);
-
- priv->iq_disco_cb = lm_message_handler_new (connection_iq_disco_cb,
- conn, NULL);
- lm_connection_register_message_handler (conn->lmconn, priv->iq_disco_cb,
- LM_MESSAGE_TYPE_IQ,
- LM_HANDLER_PRIORITY_NORMAL);
-
- priv->iq_unknown_cb = lm_message_handler_new (connection_iq_unknown_cb,
- conn, NULL);
- lm_connection_register_message_handler (conn->lmconn, priv->iq_unknown_cb,
- LM_MESSAGE_TYPE_IQ,
- LM_HANDLER_PRIORITY_LAST);
-
- priv->stream_error_cb = lm_message_handler_new (connection_stream_error_cb,
- conn, NULL);
- lm_connection_register_message_handler (conn->lmconn, priv->stream_error_cb,
- LM_MESSAGE_TYPE_STREAM_ERROR,
- LM_HANDLER_PRIORITY_LAST);
-
- priv->msg_cb = lm_message_handler_new (pubsub_msg_event_cb,
- conn, NULL);
- lm_connection_register_message_handler (conn->lmconn, priv->msg_cb,
- LM_MESSAGE_TYPE_MESSAGE,
- LM_HANDLER_PRIORITY_FIRST);
-}
-
-static void
-disconnect_callbacks (TpBaseConnection *base)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (base);
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
-
- g_assert (priv->iq_disco_cb != NULL);
- g_assert (priv->iq_unknown_cb != NULL);
- g_assert (priv->stream_error_cb != NULL);
- g_assert (priv->msg_cb != NULL);
-
- lm_connection_unregister_message_handler (conn->lmconn, priv->iq_disco_cb,
- LM_MESSAGE_TYPE_IQ);
- lm_message_handler_unref (priv->iq_disco_cb);
- priv->iq_disco_cb = NULL;
-
- lm_connection_unregister_message_handler (conn->lmconn, priv->iq_unknown_cb,
- LM_MESSAGE_TYPE_IQ);
- lm_message_handler_unref (priv->iq_unknown_cb);
- priv->iq_unknown_cb = NULL;
-
- lm_connection_unregister_message_handler (conn->lmconn,
- priv->stream_error_cb, LM_MESSAGE_TYPE_STREAM_ERROR);
- lm_message_handler_unref (priv->stream_error_cb);
- priv->stream_error_cb = NULL;
-
- lm_connection_unregister_message_handler (conn->lmconn, priv->msg_cb,
- LM_MESSAGE_TYPE_MESSAGE);
- lm_message_handler_unref (priv->msg_cb);
- priv->msg_cb = NULL;
-}
-
-/**
- * _gabble_connection_connect
- *
- * Use the stored server & authentication details to commence
- * the stages for connecting to the server and authenticating. Will
- * re-use an existing LmConnection if it is present, or create it
- * if necessary.
- *
- * Stage 1 is _gabble_connection_connect calling lm_connection_open
- * Stage 2 is connection_open_cb calling lm_connection_authenticate
- * Stage 3 is connection_auth_cb initiating service discovery
- * Stage 4 is connection_disco_cb advertising initial presence, requesting
- * the roster and setting the CONNECTED state
- */
-static gboolean
-_gabble_connection_connect (TpBaseConnection *base,
- GError **error)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (base);
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
- char *jid;
-
- g_assert (priv->port <= G_MAXUINT16);
- g_assert (priv->stream_server != NULL);
- g_assert (priv->username != NULL);
- g_assert (priv->password != NULL);
- g_assert (priv->resource != NULL);
- g_assert (lm_connection_is_open (conn->lmconn) == FALSE);
-
- jid = g_strdup_printf ("%s@%s", priv->username, priv->stream_server);
- lm_connection_set_jid (conn->lmconn, jid);
- g_free (jid);
-
- /* override server and port if either was provided */
- if (priv->connect_server != NULL || priv->port != 0)
- {
- gchar *server;
-
- if (priv->connect_server != NULL)
- server = priv->connect_server;
- else
- server = priv->stream_server;
-
- DEBUG ("disabling SRV because \"server\" or \"port\" parameter "
- "specified, will connect to %s", server);
-
- lm_connection_set_server (conn->lmconn, server);
-
- if (priv->port != 0)
- lm_connection_set_port (conn->lmconn, priv->port);
- }
- else
- {
- DEBUG ("letting SRV lookup decide server and port");
- }
-
- if (priv->https_proxy_server)
- {
- LmProxy *proxy;
-
- proxy = lm_proxy_new_with_server (LM_PROXY_TYPE_HTTP,
- priv->https_proxy_server, priv->https_proxy_port);
-
- lm_connection_set_proxy (conn->lmconn, proxy);
-
- lm_proxy_unref (proxy);
- }
-
- if (priv->old_ssl)
- {
- LmSSL *ssl = lm_ssl_new (NULL, connection_ssl_cb, conn, NULL);
- lm_connection_set_ssl (conn->lmconn, ssl);
- lm_ssl_unref (ssl);
- }
- else
- {
- LmSSL *ssl = lm_ssl_new (NULL, connection_ssl_cb, conn, NULL);
- lm_connection_set_ssl (conn->lmconn, ssl);
-
- /* Try to use StartTLS if possible, but be careful about
- allowing SSL errors in that default case. */
- lm_ssl_use_starttls (ssl, TRUE, priv->require_encryption);
-
- if (!priv->require_encryption)
- priv->ignore_ssl_errors = TRUE;
- }
-
- /* send whitespace to the server every 30 seconds */
- lm_connection_set_keep_alive_rate (conn->lmconn, 30);
-
- lm_connection_set_disconnect_function (conn->lmconn,
- connection_disconnected_cb,
- conn,
- NULL);
-
- return do_connect (conn, error);
-}
-
-
-
-static void
-connection_disconnected_cb (LmConnection *lmconn,
- LmDisconnectReason lm_reason,
- gpointer user_data)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (user_data);
- TpBaseConnection *base = (TpBaseConnection *)user_data;
-
- g_assert (conn->lmconn == lmconn);
-
- DEBUG ("called with reason %u", lm_reason);
-
- /* if we were expecting this disconnection, we're done so can tell
- * the connection manager to unref us. otherwise it's a network error
- * or some other screw up we didn't expect, so we emit the status
- * change */
- if (base->status == TP_CONNECTION_STATUS_DISCONNECTED)
- {
- DEBUG ("expected; emitting DISCONNECTED");
- tp_base_connection_finish_shutdown ((TpBaseConnection *)conn);
- }
- else
- {
- DEBUG ("unexpected; calling tp_base_connection_change_status");
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
- }
-}
-
-
-static void
-connection_shut_down (TpBaseConnection *base)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (base);
-
- g_assert (GABBLE_IS_CONNECTION (conn));
-
- /* If we're shutting down by user request, we don't want to be
- * unreffed until the LM connection actually closes; the event handler
- * will tell the base class that shutdown has finished.
- *
- * On the other hand, if we're shutting down because the connection
- * suffered a network error, the LM connection will already be closed,
- * so just tell the base class to finish shutting down immediately.
- */
- if (lm_connection_is_open (conn->lmconn))
- {
- DEBUG ("still open; calling lm_connection_close");
- lm_connection_close (conn->lmconn, NULL);
- }
- else
- {
- /* lm_connection_is_open() returns FALSE if LmConnection is in the
- * middle of connecting, so call this just in case */
- lm_connection_cancel_open (conn->lmconn);
- DEBUG ("closed; emitting DISCONNECTED");
- tp_base_connection_finish_shutdown (base);
- }
-}
-
-
-/**
- * _gabble_connection_signal_own_presence:
- * @self: A #GabbleConnection
- * @error: pointer in which to return a GError in case of failure.
- *
- * Signal the user's stored presence to the jabber server
- *
- * Retuns: FALSE if an error occurred
- */
-gboolean
-_gabble_connection_signal_own_presence (GabbleConnection *self, GError **error)
-{
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
- GabblePresence *presence = self->self_presence;
- LmMessage *message = gabble_presence_as_message (presence, priv->resource);
- LmMessageNode *node = lm_message_get_node (message);
- gboolean ret;
- gchar *caps_hash;
-
- if (presence->status == GABBLE_PRESENCE_HIDDEN)
- {
- if ((self->features & GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE) != 0)
- lm_message_node_set_attribute (node, "type", "invisible");
- }
-
- /* XEP-0115 version 1.5 uses a verification string in the 'ver' attribute */
- caps_hash = caps_hash_compute_from_self_presence (self);
- node = lm_message_node_add_child (node, "c", NULL);
- lm_message_node_set_attributes (
- node,
- "xmlns", NS_CAPS,
- "hash", "sha-1",
- "node", NS_GABBLE_CAPS,
- "ver", caps_hash,
- NULL);
-
- /* XEP-0115 deprecates 'ext' feature bundles. But we still need
- * BUNDLE_VOICE_V1 it for backward-compatibility with Gabble 0.2 */
- if (presence->caps & PRESENCE_CAP_GOOGLE_VOICE)
- lm_message_node_set_attribute (node, "ext", BUNDLE_VOICE_V1);
-
- ret = _gabble_connection_send (self, message, error);
-
- g_free (caps_hash);
- lm_message_unref (message);
-
- return ret;
-}
-
-static LmMessage *_lm_iq_message_make_result (LmMessage *iq_message);
-
-/**
- * _gabble_connection_send_iq_result
- *
- * Function used to acknowledge an IQ stanza.
- */
-void
-_gabble_connection_acknowledge_set_iq (GabbleConnection *conn,
- LmMessage *iq)
-{
- LmMessage *result;
-
- g_assert (LM_MESSAGE_TYPE_IQ == lm_message_get_type (iq));
- g_assert (LM_MESSAGE_SUB_TYPE_SET == lm_message_get_sub_type (iq));
-
- result = _lm_iq_message_make_result (iq);
-
- if (NULL != result)
- {
- _gabble_connection_send (conn, result, NULL);
- lm_message_unref (result);
- }
-}
-
-
-/**
- * _gabble_connection_send_iq_error
- *
- * Function used to acknowledge an IQ stanza with an error.
- */
-void
-_gabble_connection_send_iq_error (GabbleConnection *conn,
- LmMessage *message,
- GabbleXmppError error,
- const gchar *errmsg)
-{
- const gchar *to, *id;
- LmMessage *msg;
- LmMessageNode *iq_node;
-
- iq_node = lm_message_get_node (message);
- to = lm_message_node_get_attribute (iq_node, "from");
- id = lm_message_node_get_attribute (iq_node, "id");
-
- if (id == NULL)
- {
- NODE_DEBUG (iq_node, "can't acknowledge IQ with no id");
- return;
- }
-
- msg = lm_message_new_with_sub_type (to, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_ERROR);
-
- lm_message_node_set_attribute (msg->node, "id", id);
-
- lm_message_node_steal_children (msg->node, iq_node);
-
- gabble_xmpp_error_to_node (error, msg->node, errmsg);
-
- _gabble_connection_send (conn, msg, NULL);
-
- lm_message_unref (msg);
-}
-
-static LmMessage *
-_lm_iq_message_make_result (LmMessage *iq_message)
-{
- LmMessage *result;
- LmMessageNode *iq, *result_iq;
- const gchar *from_jid, *id;
-
- g_assert (lm_message_get_type (iq_message) == LM_MESSAGE_TYPE_IQ);
- g_assert (lm_message_get_sub_type (iq_message) == LM_MESSAGE_SUB_TYPE_GET ||
- lm_message_get_sub_type (iq_message) == LM_MESSAGE_SUB_TYPE_SET);
-
- iq = lm_message_get_node (iq_message);
- id = lm_message_node_get_attribute (iq, "id");
-
- if (id == NULL)
- {
- NODE_DEBUG (iq, "can't acknowledge IQ with no id");
- return NULL;
- }
-
- from_jid = lm_message_node_get_attribute (iq, "from");
-
- result = lm_message_new_with_sub_type (from_jid, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_RESULT);
- result_iq = lm_message_get_node (result);
- lm_message_node_set_attribute (result_iq, "id", id);
-
- return result;
-}
-
-/**
- * connection_iq_disco_cb
- *
- * Called by loudmouth when we get an incoming <iq>. This handler handles
- * disco-related IQs.
- */
-static LmHandlerResult
-connection_iq_disco_cb (LmMessageHandler *handler,
- LmConnection *connection,
- LmMessage *message,
- gpointer user_data)
-{
- GabbleConnection *self = GABBLE_CONNECTION (user_data);
- LmMessage *result;
- LmMessageNode *iq, *result_iq, *query, *result_query, *identity;
- const gchar *node, *suffix;
- GSList *features;
- GSList *i;
- gchar *caps_hash;
-
- if (lm_message_get_sub_type (message) != LM_MESSAGE_SUB_TYPE_GET)
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-
- iq = lm_message_get_node (message);
- query = lm_message_node_get_child_with_namespace (iq, "query",
- NS_DISCO_INFO);
-
- if (!query)
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-
- node = lm_message_node_get_attribute (query, "node");
-
- if (node && (
- 0 != strncmp (node, NS_GABBLE_CAPS "#", strlen (NS_GABBLE_CAPS) + 1) ||
- strlen (node) < strlen (NS_GABBLE_CAPS) + 2))
- {
- NODE_DEBUG (iq, "got iq disco query with unexpected node attribute");
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
-
- if (node == NULL)
- suffix = NULL;
- else
- suffix = node + strlen (NS_GABBLE_CAPS) + 1;
-
- result = _lm_iq_message_make_result (message);
- result_iq = lm_message_get_node (result);
- result_query = lm_message_node_add_child (result_iq, "query", NULL);
- lm_message_node_set_attribute (result_query, "xmlns", NS_DISCO_INFO);
-
- if (node)
- lm_message_node_set_attribute (result_query, "node", node);
-
- DEBUG ("got disco request for node %s, caps are %x", node,
- self->self_presence->caps);
-
- /* Every entity MUST have at least one identity (XEP-0030). Gabble publishs
- * one identity. If you change the identity here, you also need to change
- * caps_hash_compute_from_self_presence(). */
- identity = lm_message_node_add_child
- (result_query, "identity", NULL);
- lm_message_node_set_attribute (identity, "category", "client");
- lm_message_node_set_attribute (identity, "name", PACKAGE_STRING);
- lm_message_node_set_attribute (identity, "type", "pc");
-
- features = capabilities_get_features (self->self_presence->caps);
-
- g_debug ("%s: caps now %u", G_STRFUNC, self->self_presence->caps);
-
- /* If node is not NULL, it can be either a caps bundle as defined in the
- * legacy XEP-0115 version 1.3 or an hash as defined in XEP-0115 version
- * 1.5. */
-
- caps_hash = caps_hash_compute_from_self_presence (self);
-
- if (NULL == node ||
- !tp_strdiff (suffix, BUNDLE_VOICE_V1) ||
- !tp_strdiff (suffix, caps_hash))
- {
- for (i = features; NULL != i; i = i->next)
- {
- const Feature *feature = (const Feature *) i->data;
- LmMessageNode *feature_node;
-
- /* When BUNDLE_VOICE_V1 is requested, only send the bundle */
- if (!tp_strdiff (suffix, BUNDLE_VOICE_V1) &&
- feature->feature_type != FEATURE_BUNDLE_COMPAT)
- continue;
-
- /* otherwise (no node or hash), put all features */
- feature_node = lm_message_node_add_child (result_query, "feature",
- NULL);
- lm_message_node_set_attribute (feature_node, "var", feature->ns);
- }
-
- NODE_DEBUG (result_iq, "sending disco response");
-
- if (!lm_connection_send (self->lmconn, result, NULL))
- {
- DEBUG ("sending disco response failed");
- }
- }
- else
- {
- /* Return <item-not-found>. It is possible that the remote contact
- * requested an old version (old hash) of our capabilities. In the
- * meantime, it will have gotten a new hash, and query the new hash
- * anyway. */
- _gabble_connection_send_iq_error (self, message,
- XMPP_ERROR_ITEM_NOT_FOUND, NULL);
- }
- g_free (caps_hash);
-
- lm_message_unref (result);
- g_slist_free (features);
-
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-/**
- * connection_iq_unknown_cb
- *
- * Called by loudmouth when we get an incoming <iq>. This handler is
- * at a lower priority than the others, and should reply with an error
- * about unsupported get/set attempts.
- */
-static LmHandlerResult
-connection_iq_unknown_cb (LmMessageHandler *handler,
- LmConnection *connection,
- LmMessage *message,
- gpointer user_data)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (user_data);
-
- g_assert (connection == conn->lmconn);
-
- NODE_DEBUG (message->node, "got unknown iq");
-
- switch (lm_message_get_sub_type (message))
- {
- case LM_MESSAGE_SUB_TYPE_GET:
- case LM_MESSAGE_SUB_TYPE_SET:
- _gabble_connection_send_iq_error (conn, message,
- XMPP_ERROR_SERVICE_UNAVAILABLE, NULL);
- break;
- default:
- break;
- }
-
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
-}
-
-/**
- * connection_stream_error_cb
- *
- * Called by loudmouth when we get stream error, which means that
- * we're about to close the connection. The message contains the reason
- * for the connection hangup.
- */
-static LmHandlerResult
-connection_stream_error_cb (LmMessageHandler *handler,
- LmConnection *connection,
- LmMessage *message,
- gpointer user_data)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (user_data);
- LmMessageNode *conflict_node;
-
- g_assert (connection == conn->lmconn);
-
- NODE_DEBUG (message->node, "got stream error");
-
- conflict_node = lm_message_node_get_child (message->node, "conflict");
- if (conflict_node)
- {
- DEBUG ("found conflict node, emiting status change");
-
- /* Another client with the same resource just
- * appeared, we're going down. */
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NAME_IN_USE);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-}
-
-/**
- * connection_ssl_cb
- *
- * If we're doing old SSL, this function gets called if the certificate
- * is dodgy.
- */
-static LmSSLResponse
-connection_ssl_cb (LmSSL *lmssl,
- LmSSLStatus status,
- gpointer data)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (data);
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
- const char *reason;
- TpConnectionStatusReason tp_reason;
-
- switch (status) {
- case LM_SSL_STATUS_NO_CERT_FOUND:
- reason = "The server doesn't provide a certificate.";
- tp_reason = TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED;
- break;
- case LM_SSL_STATUS_UNTRUSTED_CERT:
- reason = "The certificate can not be trusted.";
- tp_reason = TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED;
- break;
- case LM_SSL_STATUS_CERT_EXPIRED:
- reason = "The certificate has expired.";
- tp_reason = TP_CONNECTION_STATUS_REASON_CERT_EXPIRED;
- break;
- case LM_SSL_STATUS_CERT_NOT_ACTIVATED:
- reason = "The certificate has not been activated.";
- tp_reason = TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED;
- break;
- case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH:
- reason = "The server hostname doesn't match the one in the certificate.";
- tp_reason = TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH;
- break;
- case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH:
- reason = "The fingerprint doesn't match the expected value.";
- tp_reason = TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH;
- break;
- case LM_SSL_STATUS_GENERIC_ERROR:
- reason = "An unknown SSL error occurred.";
- tp_reason = TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR;
- break;
- default:
- g_assert_not_reached ();
- reason = "Unknown SSL error code from Loudmouth.";
- tp_reason = TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR;
- break;
- }
-
- DEBUG ("called: %s", reason);
-
- if (priv->ignore_ssl_errors)
- {
- return LM_SSL_RESPONSE_CONTINUE;
- }
- else
- {
- priv->ssl_error = tp_reason;
- return LM_SSL_RESPONSE_STOP;
- }
-}
-
-static void
-do_auth (GabbleConnection *conn)
-{
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
- GError *error = NULL;
-
- DEBUG ("authenticating with username: %s, password: <hidden>, resource: %s",
- priv->username, priv->resource);
-
- if (!lm_connection_authenticate (conn->lmconn, priv->username,
- priv->password, priv->resource, connection_auth_cb, conn, NULL,
- &error))
- {
- DEBUG ("failed: %s", error->message);
- g_error_free (error);
-
- /* the reason this function can fail is through network errors,
- * authentication failures are reported to our auth_cb */
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
- }
-}
-
-static void
-registration_finished_cb (GabbleRegister *reg,
- gboolean success,
- gint err_code,
- const gchar *err_msg,
- gpointer user_data)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (user_data);
- TpBaseConnection *base = (TpBaseConnection *)conn;
-
- if (base->status != TP_CONNECTION_STATUS_CONNECTING)
- {
- g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED);
- return;
- }
-
- DEBUG ("%s", (success) ? "succeeded" : "failed");
-
- g_object_unref (reg);
-
- if (success)
- {
- do_auth (conn);
- }
- else
- {
- DEBUG ("err_code = %d, err_msg = '%s'",
- err_code, err_msg);
-
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- (err_code == TP_ERROR_INVALID_ARGUMENT) ?
- TP_CONNECTION_STATUS_REASON_NAME_IN_USE :
- TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
- }
-}
-
-static void
-do_register (GabbleConnection *conn)
-{
- GabbleRegister *reg;
-
- reg = gabble_register_new (conn);
-
- g_signal_connect (reg, "finished", (GCallback) registration_finished_cb,
- conn);
-
- gabble_register_start (reg);
-}
-
-/**
- * connection_open_cb
- *
- * Stage 2 of connecting, this function is called by loudmouth after the
- * result of the non-blocking lm_connection_open call is known. It makes
- * a request to authenticate the user with the server, or optionally
- * registers user on the server first.
- */
-static void
-connection_open_cb (LmConnection *lmconn,
- gboolean success,
- gpointer data)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (data);
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
- TpBaseConnection *base = (TpBaseConnection *)conn;
-
- if ((base->status != TP_CONNECTION_STATUS_CONNECTING) &&
- (base->status != TP_INTERNAL_CONNECTION_STATUS_NEW))
- {
- g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED);
- return;
- }
-
- g_assert (priv);
- g_assert (lmconn == conn->lmconn);
-
- if (!success)
- {
- if (lm_connection_get_proxy (lmconn))
- {
- DEBUG ("failed, retrying without proxy");
-
- lm_connection_set_proxy (lmconn, NULL);
-
- if (do_connect (conn, NULL))
- {
- return;
- }
- }
- else
- {
- DEBUG ("failed");
- }
-
- if (priv->ssl_error)
- {
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- priv->ssl_error);
- }
- else
- {
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
- }
-
- return;
- }
-
- if (!priv->do_register)
- do_auth (conn);
- else
- do_register (conn);
-}
-
-/**
- * connection_auth_cb
- *
- * Stage 3 of connecting, this function is called by loudmouth after the
- * result of the non-blocking lm_connection_authenticate call is known.
- * It sends a discovery request to find the server's features.
- */
-static void
-connection_auth_cb (LmConnection *lmconn,
- gboolean success,
- gpointer data)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (data);
- TpBaseConnection *base = (TpBaseConnection *)conn;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
- TP_HANDLE_TYPE_CONTACT);
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
- GError *error = NULL;
- const gchar *jid;
-
- if (base->status != TP_CONNECTION_STATUS_CONNECTING)
- {
- g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED);
- return;
- }
-
- g_assert (priv);
- g_assert (lmconn == conn->lmconn);
-
- if (!success)
- {
- DEBUG ("failed");
-
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED);
-
- return;
- }
-
-
- jid = lm_connection_get_full_jid (lmconn);
-
- base->self_handle = tp_handle_ensure (contact_handles, jid, NULL, &error);
-
- if (base->self_handle == 0)
- {
- DEBUG ("couldn't get our self handle: %s", error->message);
-
- g_error_free (error);
-
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
-
- return;
- }
-
- /* update priv->resource and priv->stream_server from the server's JID */
- if (!_gabble_connection_set_properties_from_account (conn, jid, &error))
- {
- DEBUG ("couldn't parse our own JID: %s", error->message);
-
- g_error_free (error);
-
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
-
- return;
- }
-
- DEBUG ("Created self handle %d, our JID is %s", base->self_handle, jid);
-
- /* set initial presence */
- conn->self_presence = gabble_presence_new ();
- gabble_presence_update (conn->self_presence, priv->resource,
- GABBLE_PRESENCE_AVAILABLE, NULL, priv->priority);
-
- /* set initial capabilities */
- gabble_presence_set_capabilities (conn->self_presence, priv->resource,
- capabilities_get_initial_caps (), priv->caps_serial++);
-
- if (!gabble_disco_request_with_timeout (conn->disco, GABBLE_DISCO_TYPE_INFO,
- priv->stream_server, NULL,
- disco_reply_timeout,
- connection_disco_cb, conn,
- G_OBJECT (conn), &error))
- {
- DEBUG ("sending disco request failed: %s",
- error->message);
-
- g_error_free (error);
-
- tp_base_connection_change_status ((TpBaseConnection *)conn,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
- }
-}
-
-/**
- * connection_disco_cb
- *
- * Stage 4 of connecting, this function is called by GabbleDisco after the
- * result of the non-blocking server feature discovery call is known. It sends
- * the user's initial presence to the server, marking them as available,
- * and requests the roster.
- */
-static void
-connection_disco_cb (GabbleDisco *disco,
- GabbleDiscoRequest *request,
- const gchar *jid,
- const gchar *node,
- LmMessageNode *result,
- GError *disco_error,
- gpointer user_data)
-{
- GabbleConnection *conn = user_data;
- TpBaseConnection *base = (TpBaseConnection *)conn;
- GabbleConnectionPrivate *priv;
- GError *error = NULL;
-
- if (base->status != TP_CONNECTION_STATUS_CONNECTING)
- {
- g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED);
- return;
- }
-
- g_assert (GABBLE_IS_CONNECTION (conn));
- priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
-
- if (disco_error)
- {
- DEBUG ("got disco error, setting no features: %s", disco_error->message);
- if (disco_error->code == GABBLE_DISCO_ERROR_TIMEOUT)
- {
- DEBUG ("didn't receive a response to our disco request: disconnect");
- goto ERROR;
- }
- }
- else
- {
- LmMessageNode *iter;
-
- NODE_DEBUG (result, "got");
-
- for (iter = result->children; iter != NULL; iter = iter->next)
- {
- if (0 == strcmp (iter->name, "identity"))
- {
- const gchar *category = lm_message_node_get_attribute (iter,
- "category");
- const gchar *type = lm_message_node_get_attribute (iter, "type");
-
- if (!tp_strdiff (category, "pubsub") &&
- !tp_strdiff (type, "pep"))
- /* XXX: should we also check for specific PubSub <feature>s? */
- conn->features |= GABBLE_CONNECTION_FEATURES_PEP;
- }
- else if (0 == strcmp (iter->name, "feature"))
- {
- const gchar *var = lm_message_node_get_attribute (iter, "var");
-
- if (var == NULL)
- continue;
-
- if (0 == strcmp (var, NS_GOOGLE_JINGLE_INFO))
- conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO;
- else if (0 == strcmp (var, NS_GOOGLE_ROSTER))
- conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER;
- else if (0 == strcmp (var, NS_PRESENCE_INVISIBLE))
- conn->features |= GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE;
- else if (0 == strcmp (var, NS_PRIVACY))
- conn->features |= GABBLE_CONNECTION_FEATURES_PRIVACY;
- }
- }
-
- DEBUG ("set features flags to %d", conn->features);
- }
-
- if (conn->features && GABBLE_CONNECTION_FEATURES_PEP)
- {
- const gchar *ifaces[] = { GABBLE_IFACE_OLPC_BUDDY_INFO,
- GABBLE_IFACE_OLPC_ACTIVITY_PROPERTIES, NULL };
-
- tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces);
- }
-
- /* send presence to the server to indicate availability */
- /* TODO: some way for the user to set this */
- if (!_gabble_connection_signal_own_presence (conn, &error))
- {
- DEBUG ("sending initial presence failed: %s", error->message);
- goto ERROR;
- }
-
- /* go go gadget on-line */
- tp_base_connection_change_status (base,
- TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED);
-
- return;
-
-ERROR:
- if (error != NULL)
- g_error_free (error);
-
- tp_base_connection_change_status (base,
- TP_CONNECTION_STATUS_DISCONNECTED,
- TP_CONNECTION_STATUS_REASON_NETWORK_ERROR);
-
- return;
-}
-
-
-/****************************************************************************
- * D-BUS EXPORTED METHODS *
- ****************************************************************************/
-
-
-static void
-_emit_capabilities_changed (GabbleConnection *conn,
- TpHandle handle,
- GabblePresenceCapabilities old_caps,
- GabblePresenceCapabilities new_caps)
-{
- GPtrArray *caps_arr;
- const CapabilityConversionData *ccd;
- guint i;
-
- if (old_caps == new_caps)
- return;
-
- caps_arr = g_ptr_array_new ();
-
- for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
- {
- if (ccd->c2tf_fn (old_caps | new_caps))
- {
- GValue caps_monster_struct = {0, };
- guint old_specific = ccd->c2tf_fn (old_caps);
- guint old_generic = old_specific ?
- TP_CONNECTION_CAPABILITY_FLAG_CREATE |
- TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0;
- guint new_specific = ccd->c2tf_fn (new_caps);
- guint new_generic = new_specific ?
- TP_CONNECTION_CAPABILITY_FLAG_CREATE |
- TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0;
-
- if (0 == (old_specific ^ new_specific))
- continue;
-
- g_value_init (&caps_monster_struct,
- GABBLE_TP_CAPABILITIES_CHANGED_MONSTER_TYPE);
- g_value_take_boxed (&caps_monster_struct,
- dbus_g_type_specialized_construct
- (GABBLE_TP_CAPABILITIES_CHANGED_MONSTER_TYPE));
-
- dbus_g_type_struct_set (&caps_monster_struct,
- 0, handle,
- 1, ccd->iface,
- 2, old_generic,
- 3, new_generic,
- 4, old_specific,
- 5, new_specific,
- G_MAXUINT);
-
- g_ptr_array_add (caps_arr, g_value_get_boxed (&caps_monster_struct));
- }
- }
-
- if (caps_arr->len)
- tp_svc_connection_interface_capabilities_emit_capabilities_changed (
- conn, caps_arr);
-
-
- for (i = 0; i < caps_arr->len; i++)
- {
- g_boxed_free (GABBLE_TP_CAPABILITIES_CHANGED_MONSTER_TYPE,
- g_ptr_array_index (caps_arr, i));
- }
- g_ptr_array_free (caps_arr, TRUE);
-}
-
-static void
-connection_capabilities_update_cb (GabblePresenceCache *cache,
- TpHandle handle,
- GabblePresenceCapabilities old_caps,
- GabblePresenceCapabilities new_caps,
- gpointer user_data)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (user_data);
-
- _emit_capabilities_changed (conn, handle, old_caps, new_caps);
-}
-
-/**
- * gabble_connection_advertise_capabilities
- *
- * Implements D-Bus method AdvertiseCapabilities
- * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
- *
- * @error: Used to return a pointer to a GError detailing any error
- * that occurred, D-Bus will throw the error only if this
- * function returns FALSE.
- *
- * Returns: TRUE if successful, FALSE if an error was thrown.
- */
-static void
-gabble_connection_advertise_capabilities (TpSvcConnectionInterfaceCapabilities *iface,
- const GPtrArray *add,
- const gchar **del,
- DBusGMethodInvocation *context)
-{
- GabbleConnection *self = GABBLE_CONNECTION (iface);
- TpBaseConnection *base = (TpBaseConnection *)self;
- guint i;
- GabblePresence *pres = self->self_presence;
- GabblePresenceCapabilities add_caps = 0, remove_caps = 0, caps, save_caps;
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
- const CapabilityConversionData *ccd;
- GPtrArray *ret;
- GError *error = NULL;
-
- TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
- DEBUG ("caps before: %x", pres->caps);
-
- for (i = 0; i < add->len; i++)
- {
- GValue iface_flags_pair = {0, };
- gchar *channel_type;
- guint flags;
-
- g_value_init (&iface_flags_pair, GABBLE_TP_CAPABILITY_PAIR_TYPE);
- g_value_set_static_boxed (&iface_flags_pair, g_ptr_array_index (add, i));
-
- dbus_g_type_struct_get (&iface_flags_pair,
- 0, &channel_type,
- 1, &flags,
- G_MAXUINT);
-
- for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
- if (g_str_equal (channel_type, ccd->iface))
- add_caps |= ccd->tf2c_fn (flags);
-
- g_free (channel_type);
- }
-
- for (i = 0; NULL != del[i]; i++)
- {
- for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
- if (g_str_equal (del[i], ccd->iface))
- remove_caps |= ccd->tf2c_fn (~0);
- }
-
- save_caps = caps = pres->caps;
-
- caps |= add_caps;
- caps ^= (caps & remove_caps);
-
- DEBUG ("caps to add: %x", add_caps);
- DEBUG ("caps to remove: %x", remove_caps);
- DEBUG ("caps after: %x", caps);
-
- if (caps ^ save_caps)
- {
- DEBUG ("before != after, changing");
- gabble_presence_set_capabilities (pres, priv->resource, caps,
- priv->caps_serial++);
- DEBUG ("set caps: %x", pres->caps);
- }
-
- ret = g_ptr_array_new ();
-
- for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
- {
- if (ccd->c2tf_fn (pres->caps))
- {
- GValue iface_flags_pair = {0, };
-
- g_value_init (&iface_flags_pair, GABBLE_TP_CAPABILITY_PAIR_TYPE);
- g_value_take_boxed (&iface_flags_pair,
- dbus_g_type_specialized_construct (GABBLE_TP_CAPABILITY_PAIR_TYPE));
-
- dbus_g_type_struct_set (&iface_flags_pair,
- 0, ccd->iface,
- 1, ccd->c2tf_fn (pres->caps),
- G_MAXUINT);
-
- g_ptr_array_add (ret, g_value_get_boxed (&iface_flags_pair));
- }
- }
-
- if (caps ^ save_caps)
- {
- if (!_gabble_connection_signal_own_presence (self, &error))
- {
- dbus_g_method_return_error (context, error);
- return;
- }
-
- _emit_capabilities_changed (self, base->self_handle,
- save_caps, caps);
- }
-
- tp_svc_connection_interface_capabilities_return_from_advertise_capabilities (
- context, ret);
- g_ptr_array_free (ret, TRUE);
-}
-
-static const gchar *assumed_caps[] =
-{
- TP_IFACE_CHANNEL_TYPE_TEXT,
- NULL
-};
-
-
-/**
- * gabble_connection_get_capabilities
- *
- * Implements D-Bus method GetCapabilities
- * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities
- *
- * @error: Used to return a pointer to a GError detailing any error
- * that occurred, D-Bus will throw the error only if this
- * function returns FALSE.
- *
- * Returns: TRUE if successful, FALSE if an error was thrown.
- */
-static void
-gabble_connection_get_capabilities (TpSvcConnectionInterfaceCapabilities *iface,
- const GArray *handles,
- DBusGMethodInvocation *context)
-{
- GabbleConnection *self = GABBLE_CONNECTION (iface);
- TpBaseConnection *base = (TpBaseConnection *)self;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
- TP_HANDLE_TYPE_CONTACT);
- guint i;
- GPtrArray *ret;
- GError *error = NULL;
-
- TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
- if (!tp_handles_are_valid (contact_handles, handles, TRUE, &error))
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
- }
-
- ret = g_ptr_array_new ();
-
- for (i = 0; i < handles->len; i++)
- {
- TpHandle handle = g_array_index (handles, guint, i);
- GabblePresence *pres;
- const CapabilityConversionData *ccd;
- guint typeflags;
- const gchar **assumed;
-
- if (0 == handle)
- {
- /* FIXME report the magical channel types available on the
- * connection itself */
- continue;
- }
-
- if (handle == base->self_handle)
- pres = self->self_presence;
- else
- pres = gabble_presence_cache_get (self->presence_cache, handle);
-
- if (NULL != pres)
- for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++)
- {
- typeflags = ccd->c2tf_fn (pres->caps);
-
- if (typeflags)
- {
- GValue monster = {0, };
-
- g_value_init (&monster, GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE);
- g_value_take_boxed (&monster,
- dbus_g_type_specialized_construct (
- GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE));
-
- dbus_g_type_struct_set (&monster,
- 0, handle,
- 1, ccd->iface,
- 2, TP_CONNECTION_CAPABILITY_FLAG_CREATE |
- TP_CONNECTION_CAPABILITY_FLAG_INVITE,
- 3, typeflags,
- G_MAXUINT);
-
- g_ptr_array_add (ret, g_value_get_boxed (&monster));
- }
- }
-
- for (assumed = assumed_caps; NULL != *assumed; assumed++)
- {
- GValue monster = {0, };
-
- g_value_init (&monster, GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE);
- g_value_take_boxed (&monster,
- dbus_g_type_specialized_construct (GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE));
-
- dbus_g_type_struct_set (&monster,
- 0, handle,
- 1, *assumed,
- 2, TP_CONNECTION_CAPABILITY_FLAG_CREATE |
- TP_CONNECTION_CAPABILITY_FLAG_INVITE,
- 3, 0,
- G_MAXUINT);
-
- g_ptr_array_add (ret, g_value_get_boxed (&monster));
- }
- }
-
- tp_svc_connection_interface_capabilities_return_from_get_capabilities (
- context, ret);
-
- for (i = 0; i < ret->len; i++)
- {
- GValue monster = {0, };
-
- g_value_init (&monster, GABBLE_TP_GET_CAPABILITIES_MONSTER_TYPE);
- g_value_take_boxed (&monster, g_ptr_array_index (ret, i));
- g_value_unset (&monster);
- }
-
- g_ptr_array_free (ret, TRUE);
-}
-
-
-const char *
-_gabble_connection_find_conference_server (GabbleConnection *conn)
-{
- GabbleConnectionPrivate *priv;
-
- g_assert (GABBLE_IS_CONNECTION (conn));
-
- priv = GABBLE_CONNECTION_GET_PRIVATE (conn);
-
- if (priv->conference_server == NULL)
- {
- /* Find first server that has NS_MUC feature */
- const GabbleDiscoItem *item = gabble_disco_service_find (conn->disco,
- "conference", "text", NS_MUC);
- if (item != NULL)
- priv->conference_server = item->jid;
- }
-
- if (priv->conference_server == NULL)
- priv->conference_server = priv->fallback_conference_server;
-
- return priv->conference_server;
-}
-
-
-static gchar *
-_gabble_connection_get_canonical_room_name (GabbleConnection *conn,
- const gchar *name)
-{
- const gchar *server;
-
- g_assert (GABBLE_IS_CONNECTION (conn));
-
- if (strchr (name, '@'))
- return g_strdup (name);
-
- server = _gabble_connection_find_conference_server (conn);
-
- if (server == NULL)
- return NULL;
-
- return g_strdup_printf ("%s@%s", name, server);
-}
-
-
-typedef struct _RoomVerifyContext RoomVerifyContext;
-
-typedef struct {
- GabbleConnection *conn;
- DBusGMethodInvocation *invocation;
- gboolean errored;
- guint count;
- GArray *handles;
- RoomVerifyContext *contexts;
-} RoomVerifyBatch;
-
-struct _RoomVerifyContext {
- gchar *jid;
- guint index;
- RoomVerifyBatch *batch;
- GabbleDiscoRequest *request;
-};
-
-static void
-room_verify_batch_free (RoomVerifyBatch *batch)
-{
- TpBaseConnection *base = (TpBaseConnection *)(batch->conn);
- TpHandleRepoIface *room_handles = tp_base_connection_get_handles (base,
- TP_HANDLE_TYPE_ROOM);
- guint i;
-
- tp_handles_unref (room_handles, batch->handles);
- g_array_free (batch->handles, TRUE);
- for (i = 0; i < batch->count; i++)
- {
- g_free (batch->contexts[i].jid);
- }
- g_free (batch->contexts);
- g_slice_free (RoomVerifyBatch, batch);
-}
-
-/* Frees the error and the batch. */
-static void
-room_verify_batch_raise_error (RoomVerifyBatch *batch,
- GError *error)
-{
- guint i;
-
- dbus_g_method_return_error (batch->invocation, error);
- g_error_free (error);
- batch->errored = TRUE;
- for (i = 0; i < batch->count; i++)
- {
- if (batch->contexts[i].request)
- {
- gabble_disco_cancel_request (batch->conn->disco,
- batch->contexts[i].request);
- }
- }
- room_verify_batch_free (batch);
-}
-
-static RoomVerifyBatch *
-room_verify_batch_new (GabbleConnection *conn,
- DBusGMethodInvocation *invocation,
- guint count,
- const gchar **jids)
-{
- TpBaseConnection *base = (TpBaseConnection *)conn;
- TpHandleRepoIface *room_handles = tp_base_connection_get_handles (base,
- TP_HANDLE_TYPE_ROOM);
- RoomVerifyBatch *batch = g_slice_new (RoomVerifyBatch);
- guint i;
-
- batch->errored = FALSE;
- batch->conn = conn;
- batch->invocation = invocation;
- batch->count = count;
- batch->handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), count);
- batch->contexts = g_new0(RoomVerifyContext, count);
- for (i = 0; i < count; i++)
- {
- const gchar *name = jids[i];
- gchar *qualified_name;
- TpHandle handle;
-
- batch->contexts[i].index = i;
- batch->contexts[i].batch = batch;
-
- qualified_name = _gabble_connection_get_canonical_room_name (conn, name);
-
- if (!qualified_name)
- {
- GError *error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "requested room handle %s does not specify a server, but we "
- "have not discovered any local conference servers and no "
- "fallback was provided", name);
- DEBUG ("%s", error->message);
- room_verify_batch_raise_error (batch, error);
- return NULL;
- }
-
- batch->contexts[i].jid = qualified_name;
-
- /* has the handle been verified before? */
- handle = tp_handle_lookup (room_handles, qualified_name, NULL, NULL);
- if (handle)
- tp_handle_ref (room_handles, handle);
- g_array_append_val (batch->handles, handle);
- }
-
- return batch;
-}
-
-/* If all handles in the array have been disco'd or got from cache,
-free the batch and return TRUE. Else return FALSE. */
-static gboolean
-room_verify_batch_try_return (RoomVerifyBatch *batch)
-{
- guint i;
- TpHandleRepoIface *room_handles = tp_base_connection_get_handles (
- (TpBaseConnection *)batch->conn, TP_HANDLE_TYPE_ROOM);
- gchar *sender;
- GError *error = NULL;
-
- for (i = 0; i < batch->count; i++)
- {
- if (!g_array_index (batch->handles, TpHandle, i))
- {
- /* we're not ready yet */
- return FALSE;
- }
- }
-
- sender = dbus_g_method_get_sender (batch->invocation);
- if (!tp_handles_client_hold (room_handles, sender, batch->handles, &error))
- {
- g_assert (error != NULL);
- }
- g_free (sender);
-
- if (error == NULL)
- {
- tp_svc_connection_return_from_request_handles (batch->invocation,
- batch->handles);
- }
- else
- {
- dbus_g_method_return_error (batch->invocation, error);
- g_error_free (error);
- }
-
- room_verify_batch_free (batch);
- return TRUE;
-}
-
-static void
-room_jid_disco_cb (GabbleDisco *disco,
- GabbleDiscoRequest *request,
- const gchar *jid,
- const gchar *node,
- LmMessageNode *query_result,
- GError *error,
- gpointer user_data)
-{
- RoomVerifyContext *rvctx = user_data;
- RoomVerifyBatch *batch = rvctx->batch;
- TpHandleRepoIface *room_handles = tp_base_connection_get_handles (
- (TpBaseConnection *)batch->conn, TP_HANDLE_TYPE_ROOM);
- LmMessageNode *lm_node;
- gboolean found = FALSE;
- TpHandle handle;
-
- /* stop the request getting cancelled after it's already finished */
- rvctx->request = NULL;
-
- /* if an error is being handled already, quietly go away */
- if (batch->errored)
- {
- return;
- }
-
- if (error != NULL)
- {
- DEBUG ("disco reply error %s", error->message);
-
- /* disco will free the old error, _raise_error will free the new one */
- error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "can't retrieve room info: %s", error->message);
-
- room_verify_batch_raise_error (batch, error);
-
- return;
- }
-
- for (lm_node = query_result->children; lm_node; lm_node = lm_node->next)
- {
- const gchar *var;
-
- if (tp_strdiff (lm_node->name, "feature"))
- continue;
-
- var = lm_message_node_get_attribute (lm_node, "var");
-
- /* for servers who consider schema compliance to be an optional bonus */
- if (var == NULL)
- var = lm_message_node_get_attribute (lm_node, "type");
-
- if (!tp_strdiff (var, NS_MUC))
- {
- found = TRUE;
- break;
- }
- }
-
- if (!found)
- {
- DEBUG ("no MUC support for service name in jid %s", rvctx->jid);
-
- error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "specified server doesn't support MUC");
-
- room_verify_batch_raise_error (batch, error);
-
- return;
- }
-
- /* this refs the handle, so we're putting a ref in batch->handles */
- handle = tp_handle_ensure (room_handles, rvctx->jid, NULL, &error);
- if (handle == 0)
- {
- room_verify_batch_raise_error (batch, error);
- return;
- }
-
- DEBUG ("disco reported MUC support for service name in jid %s", rvctx->jid);
- g_array_index (batch->handles, TpHandle, rvctx->index) = handle;
-
- /* if this was the last callback to be run, send off the result */
- room_verify_batch_try_return (batch);
-}
-
-/**
- * room_jid_verify:
- *
- * Utility function that verifies that the service name of
- * the specified jid exists and reports MUC support.
- */
-static gboolean
-room_jid_verify (RoomVerifyBatch *batch,
- guint i,
- DBusGMethodInvocation *context)
-{
- gchar *room, *service;
- gboolean ret;
- GError *error = NULL;
-
- room = service = NULL;
- gabble_decode_jid (batch->contexts[i].jid, &room, &service, NULL);
-
- if (room == NULL || *room == '\0' || service == NULL || *service == '\0')
- {
- g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "unable to get room name and service from JID %s",
- batch->contexts[i].jid);
-
- ret = FALSE;
-
- goto out;
- }
-
- ret = (gabble_disco_request (batch->conn->disco, GABBLE_DISCO_TYPE_INFO,
- service, NULL, room_jid_disco_cb,
- batch->contexts + i,
- G_OBJECT (batch->conn), &error) != NULL);
-
-out:
- if (!ret)
- {
- room_verify_batch_raise_error (batch, error);
- }
-
- g_free (room);
- g_free (service);
-
- return ret;
-}
-
-
-/**
- * gabble_connection_request_handles
- *
- * Implements D-Bus method RequestHandles
- * on interface org.freedesktop.Telepathy.Connection
- *
- * @context: The D-Bus invocation context to use to return values
- * or throw an error.
- */
-static void
-gabble_connection_request_handles (TpSvcConnection *iface,
- guint handle_type,
- const gchar **names,
- DBusGMethodInvocation *context)
-{
- GabbleConnection *self = GABBLE_CONNECTION (iface);
- TpBaseConnection *base = (TpBaseConnection *)self;
-
- g_assert (GABBLE_IS_CONNECTION (self));
-
- TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
-
- if (handle_type == TP_HANDLE_TYPE_ROOM)
- {
- RoomVerifyBatch *batch = NULL;
- guint count = 0, i;
- const gchar **cur_name;
-
- for (cur_name = names; *cur_name != NULL; cur_name++)
- {
- count++;
- }
-
- batch = room_verify_batch_new (self, context, count, names);
- if (!batch)
- {
- /* an error occurred while setting up the batch, and we returned
- error to dbus */
- return;
- }
-
- /* have all the handles been verified already? If so, nothing to do */
- if (room_verify_batch_try_return (batch))
- {
- return;
- }
-
- for (i = 0; i < count; i++)
- {
- if (!room_jid_verify (batch, i, context))
- {
- return;
- }
- }
-
- /* we've set the verification process going - the callback will handle
- returning or raising error */
- return;
- }
-
- /* else it's either an invalid type, or a type we can verify immediately -
- * in either case, let the superclass do it */
- tp_base_connection_dbus_request_handles (iface, handle_type, names, context);
-}
-
-void
-gabble_connection_ensure_capabilities (GabbleConnection *self,
- GabblePresenceCapabilities caps)
-{
- GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self);
- GabblePresenceCapabilities old_caps, new_caps;
-
- old_caps = self->self_presence->caps;
- new_caps = old_caps;
- new_caps |= caps;
-
- if (old_caps ^ new_caps)
- {
- /* We changed capabilities */
- GError *error = NULL;
-
- gabble_presence_set_capabilities (self->self_presence, priv->resource,
- new_caps, priv->caps_serial++);
-
- if (!_gabble_connection_signal_own_presence (self, &error))
- DEBUG ("error sending presence: %s", error->message);
- }
-}
-
-/* We reimplement RequestHandles to be able to do async validation on
- * room handles */
-static void
-conn_service_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcConnectionClass *klass = (TpSvcConnectionClass *)g_iface;
-
-#define IMPLEMENT(x) tp_svc_connection_implement_##x (klass, \
- gabble_connection_##x)
- IMPLEMENT(request_handles);
-#undef IMPLEMENT
-}
-
-static void
-capabilities_service_iface_init (gpointer g_iface, gpointer iface_data)
-{
- TpSvcConnectionInterfaceCapabilitiesClass *klass =
- (TpSvcConnectionInterfaceCapabilitiesClass *)g_iface;
-
-#define IMPLEMENT(x) tp_svc_connection_interface_capabilities_implement_##x (\
- klass, gabble_connection_##x)
- IMPLEMENT(advertise_capabilities);
- IMPLEMENT(get_capabilities);
-#undef IMPLEMENT
-}
-
-
-/* For unit tests only */
-void
-gabble_connection_set_disco_reply_timeout (guint timeout)
-{
- disco_reply_timeout = timeout;
-}
--
1.5.6.3
More information about the Telepathy-commits
mailing list