summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonny Lamb <jonny.lamb@collabora.co.uk>2011-09-28 18:58:10 +0100
committerJonny Lamb <jonny.lamb@collabora.co.uk>2011-09-28 18:58:10 +0100
commita64cd72472d48ad8e88bfebe5e6046f02d4b5c93 (patch)
tree3a6c29cc5719c0b20f86fc15732d11b3e39556f1
parent5f1ad2c76f43842856fb01a2dc51648a42f24284 (diff)
parent9c86f446c6a00142c373aae1fa357f5c00f0f2c6 (diff)
Merge branch 'moar-caps'
-rw-r--r--gabble/capabilities.h (renamed from src/capabilities.h)2
-rw-r--r--gabble/caps-channel-manager.h (renamed from src/caps-channel-manager.h)6
-rw-r--r--gabble/connection.h23
-rw-r--r--gabble/gabble.h2
-rw-r--r--plugins/test.c76
-rw-r--r--src/Makefile.am4
-rw-r--r--src/auth-manager.c2
-rw-r--r--src/capabilities.c2
-rw-r--r--src/caps-channel-manager.c8
-rw-r--r--src/caps-hash.c7
-rw-r--r--src/connection.c329
-rw-r--r--src/connection.h4
-rw-r--r--src/ft-manager.c5
-rw-r--r--src/im-factory.c2
-rw-r--r--src/jingle-session.c2
-rw-r--r--src/legacy-caps.h2
-rw-r--r--src/media-factory.c5
-rw-r--r--src/muc-factory.c2
-rw-r--r--src/presence-cache.c94
-rw-r--r--src/presence-cache.h9
-rw-r--r--src/presence.c67
-rw-r--r--src/presence.h4
-rw-r--r--src/private-tubes-factory.c6
-rw-r--r--src/roomlist-manager.c2
-rw-r--r--src/roster.c2
-rw-r--r--src/search-manager.c2
-rw-r--r--src/server-tls-manager.c2
-rw-r--r--tests/test-presence.c6
-rw-r--r--tests/twisted/Makefile.am1
-rw-r--r--tests/twisted/caps/advertise-contact-caps.py26
-rw-r--r--tests/twisted/caps/advertise-legacy.py14
-rw-r--r--tests/twisted/caps/initial-caps.py2
-rw-r--r--tests/twisted/caps/tube-caps.py2
-rw-r--r--tests/twisted/caps_helper.py54
-rw-r--r--tests/twisted/dataforms.py32
-rw-r--r--tests/twisted/jingle-share/file_transfer_helper.py28
36 files changed, 718 insertions, 118 deletions
diff --git a/src/capabilities.h b/gabble/capabilities.h
index 8e7d1a03..2e317552 100644
--- a/src/capabilities.h
+++ b/gabble/capabilities.h
@@ -23,7 +23,7 @@
#include <glib-object.h>
-#include "gabble/capabilities-set.h"
+#include <gabble/capabilities-set.h>
/* Pseudo-capabilities for buggy or strange implementations, represented as
* strings starting with a character not allowed in XML (the ASCII beep :-) */
diff --git a/src/caps-channel-manager.h b/gabble/caps-channel-manager.h
index 05947b75..9442476a 100644
--- a/src/caps-channel-manager.h
+++ b/gabble/caps-channel-manager.h
@@ -70,7 +70,8 @@ typedef void (*GabbleCapsChannelManagerRepresentClientFunc) (
const gchar *client_name,
const GPtrArray *filters,
const gchar * const *cap_tokens,
- GabbleCapabilitySet *cap_set);
+ GabbleCapabilitySet *cap_set,
+ GPtrArray *data_forms);
void gabble_caps_channel_manager_reset_capabilities (
GabbleCapsChannelManager *caps_manager);
@@ -86,7 +87,8 @@ void gabble_caps_channel_manager_represent_client (
const gchar *client_name,
const GPtrArray *filters,
const gchar * const *cap_tokens,
- GabbleCapabilitySet *cap_set);
+ GabbleCapabilitySet *cap_set,
+ GPtrArray *data_forms);
struct _GabbleCapsChannelManagerInterface {
GTypeInterface parent;
diff --git a/gabble/connection.h b/gabble/connection.h
index 575412bb..0c946cf1 100644
--- a/gabble/connection.h
+++ b/gabble/connection.h
@@ -22,10 +22,13 @@
#define GABBLE_PLUGINS_CONNECTION_H
#include <telepathy-glib/base-connection.h>
+#include <telepathy-glib/base-contact-list.h>
#include <gabble/capabilities-set.h>
#include <gabble/types.h>
+#include <wocky/wocky-xep-0115-capabilities.h>
+
G_BEGIN_DECLS
#define GABBLE_TYPE_CONNECTION (gabble_connection_get_type ())
@@ -55,6 +58,26 @@ gchar *gabble_connection_add_sidecar_own_caps (
const GabbleCapabilitySet *cap_set,
const GPtrArray *identities) G_GNUC_WARN_UNUSED_RESULT;
+WockySession *gabble_connection_get_session (
+ GabbleConnection *connection);
+
+gchar *gabble_connection_get_full_jid (GabbleConnection *conn);
+
+const gchar * gabble_connection_get_jid_for_caps (GabbleConnection *conn,
+ WockyXep0115Capabilities *caps);
+
+const gchar * gabble_connection_pick_best_resource_for_caps (
+ GabbleConnection *connection,
+ const gchar *jid,
+ GabbleCapabilitySetPredicate predicate,
+ gconstpointer user_data);
+
+TpBaseContactList * gabble_connection_get_contact_list (
+ GabbleConnection *connection);
+
+WockyXep0115Capabilities * gabble_connection_get_caps (
+ GabbleConnection *connection, TpHandle handle);
+
G_END_DECLS
#endif
diff --git a/gabble/gabble.h b/gabble/gabble.h
index fe1a5027..8393ae8c 100644
--- a/gabble/gabble.h
+++ b/gabble/gabble.h
@@ -24,7 +24,9 @@
#include <glib-object.h>
+#include <gabble/capabilities.h>
#include <gabble/capabilities-set.h>
+#include <gabble/caps-channel-manager.h>
#include <gabble/caps-hash.h>
#include <gabble/connection.h>
#include <gabble/error.h>
diff --git a/plugins/test.c b/plugins/test.c
index add4ba5f..026091db 100644
--- a/plugins/test.c
+++ b/plugins/test.c
@@ -5,10 +5,12 @@
#include <telepathy-glib/telepathy-glib.h>
#include <wocky/wocky-disco-identity.h>
+#include <wocky/wocky-data-form.h>
#include "extensions/extensions.h"
#include <gabble/plugin.h>
+#include <gabble/caps-channel-manager.h>
#define DEBUG(msg, ...) \
g_debug ("%s: " msg, G_STRFUNC, ##__VA_ARGS__)
@@ -484,11 +486,15 @@ async_initable_iface_init (
* TestChannelManager implementation *
***********************************/
static void channel_manager_iface_init (gpointer, gpointer);
+static void caps_channel_manager_iface_init (gpointer g_iface,
+ gpointer iface_data);
G_DEFINE_TYPE_WITH_CODE (TestChannelManager, test_channel_manager,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER,
- channel_manager_iface_init));
+ channel_manager_iface_init)
+ G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER,
+ caps_channel_manager_iface_init));
static void
test_channel_manager_init (TestChannelManager *self)
@@ -593,8 +599,65 @@ test_channel_manager_type_foreach_channel_class (GType type,
}
static void
+test_channel_manager_represent_client (
+ GabbleCapsChannelManager *manager,
+ const gchar *client_name,
+ const GPtrArray *filters,
+ const gchar * const *cap_tokens,
+ GabbleCapabilitySet *cap_set,
+ GPtrArray *data_forms)
+{
+ WockyStanza *stanza;
+ WockyDataForm *form;
+
+ if (tp_strdiff (client_name, "dataformtest"))
+ return;
+
+ stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
+ WOCKY_STANZA_SUB_TYPE_NONE, NULL, "badger",
+ '(', "x",
+ ':', "jabber:x:data",
+ '@', "type", "result",
+ '(', "field",
+ '@', "var", "FORM_TYPE",
+ '@', "type", "hidden",
+ '(', "value", '$', "gabble:test:channel:manager:data:form", ')',
+ ')',
+ '(', "field",
+ '@', "var", "animal",
+ '(', "value", '$', "badger", ')',
+ '(', "value", '$', "snake", ')',
+ '(', "value", '$', "weasel", ')',
+ ')',
+ '(', "field",
+ '@', "var", "cheese",
+ '(', "value", '$', "omgnothorriblecheese", ')',
+ ')',
+ '(', "field",
+ '@', "var", "favourite_crane",
+ '(', "value", '$', "a tall one", ')',
+ '(', "value", '$', "a short one", ')',
+ ')',
+ '(', "field",
+ '@', "var", "running_out_of",
+ '(', "value", '$', "ideas", ')',
+ '(', "value", '$', "cake", ')',
+ ')',
+ ')',
+ NULL);
+
+ form = wocky_data_form_new_from_node (
+ wocky_node_get_first_child (wocky_stanza_get_top_node (stanza)),
+ NULL);
+
+ g_ptr_array_add (data_forms, form);
+
+ g_object_unref (stanza);
+}
+
+static void
channel_manager_iface_init (gpointer g_iface,
- gpointer iface_data)
+ gpointer iface_data)
{
TpChannelManagerIface *iface = g_iface;
@@ -606,3 +669,12 @@ channel_manager_iface_init (gpointer g_iface,
iface->request_channel = NULL;
iface->foreach_channel_class = NULL;
}
+
+static void
+caps_channel_manager_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ GabbleCapsChannelManagerInterface *iface = g_iface;
+
+ iface->represent_client = test_channel_manager_represent_client;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index c94cfaa0..0d5355ff 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -44,12 +44,12 @@ libgabble_convenience_la_SOURCES = \
call-stream.h \
call-stream.c \
$(top_srcdir)/gabble/capabilities-set.h \
- capabilities.h \
+ $(top_srcdir)/gabble/capabilities.h \
capabilities.c \
$(top_srcdir)/gabble/caps-hash.h \
caps-hash.h \
caps-hash.c \
- caps-channel-manager.h \
+ $(top_srcdir)/gabble/caps-channel-manager.h \
caps-channel-manager.c \
conn-aliasing.h \
conn-aliasing.c \
diff --git a/src/auth-manager.c b/src/auth-manager.c
index a9424854..d9638eed 100644
--- a/src/auth-manager.c
+++ b/src/auth-manager.c
@@ -28,7 +28,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_AUTH
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "server-sasl-channel.h"
#include "connection.h"
#include "debug.h"
diff --git a/src/capabilities.c b/src/capabilities.c
index b3ef11e8..d0b36e78 100644
--- a/src/capabilities.c
+++ b/src/capabilities.c
@@ -19,7 +19,7 @@
*/
#include "config.h"
-#include "capabilities.h"
+#include "gabble/capabilities.h"
#include <stdlib.h>
#include <string.h>
diff --git a/src/caps-channel-manager.c b/src/caps-channel-manager.c
index 73b3e222..dea13a65 100644
--- a/src/caps-channel-manager.c
+++ b/src/caps-channel-manager.c
@@ -21,7 +21,7 @@
*/
#include "config.h"
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include <telepathy-glib/dbus.h>
#include <telepathy-glib/channel-manager.h>
@@ -82,6 +82,7 @@ gabble_caps_channel_manager_get_contact_capabilities (
* GHashTable with string keys and GValue values
* @cap_tokens: the handler capability tokens supported by the client
* @cap_set: a set into which to merge additional XMPP capabilities
+ * @data_forms: a #GPtrArray of #WockyDataForm objects
*
* Convert the capabilities of a Telepathy client into XMPP capabilities to be
* advertised.
@@ -95,7 +96,8 @@ gabble_caps_channel_manager_represent_client (
const gchar *client_name,
const GPtrArray *filters,
const gchar * const *cap_tokens,
- GabbleCapabilitySet *cap_set)
+ GabbleCapabilitySet *cap_set,
+ GPtrArray *data_forms)
{
GabbleCapsChannelManagerInterface *iface =
GABBLE_CAPS_CHANNEL_MANAGER_GET_INTERFACE (caps_manager);
@@ -103,6 +105,6 @@ gabble_caps_channel_manager_represent_client (
if (method != NULL)
{
- method (caps_manager, client_name, filters, cap_tokens, cap_set);
+ method (caps_manager, client_name, filters, cap_tokens, cap_set, data_forms);
}
}
diff --git a/src/caps-hash.c b/src/caps-hash.c
index bf8a2a08..1518748e 100644
--- a/src/caps-hash.c
+++ b/src/caps-hash.c
@@ -35,7 +35,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_PRESENCE
#include "base64.h"
-#include "capabilities.h"
+#include "gabble/capabilities.h"
#include "debug.h"
#include "namespaces.h"
#include "presence-cache.h"
@@ -61,6 +61,7 @@ caps_hash_compute_from_self_presence (GabbleConnection *self)
const GabbleCapabilitySet *cap_set;
GPtrArray *features = g_ptr_array_new ();
GPtrArray *identities = wocky_disco_identity_array_new ();
+ GPtrArray *data_forms;
gchar *str;
/* XEP-0030 requires at least 1 identity. We don't need more. */
@@ -71,7 +72,9 @@ caps_hash_compute_from_self_presence (GabbleConnection *self)
cap_set = gabble_presence_peek_caps (presence);
gabble_capability_set_foreach (cap_set, ptr_array_add_str, features);
- str = wocky_caps_hash_compute_from_lists (features, identities, NULL);
+ data_forms = gabble_presence_peek_data_forms (presence);
+
+ str = wocky_caps_hash_compute_from_lists (features, identities, data_forms);
g_ptr_array_free (features, TRUE);
wocky_disco_identity_array_free (identities);
diff --git a/src/connection.c b/src/connection.c
index b06cbc10..1ea7d655 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -36,6 +36,7 @@
#include <wocky/wocky-tls-handler.h>
#include <wocky/wocky-ping.h>
#include <wocky/wocky-xmpp-error.h>
+#include <wocky/wocky-data-form.h>
#include <telepathy-glib/channel-manager.h>
#include <telepathy-glib/dbus.h>
#include <telepathy-glib/enums.h>
@@ -50,8 +51,8 @@
#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION
#include "bytestream-factory.h"
-#include "capabilities.h"
-#include "caps-channel-manager.h"
+#include "gabble/capabilities.h"
+#include "gabble/caps-channel-manager.h"
#include "caps-hash.h"
#include "auth-manager.h"
#include "conn-aliasing.h"
@@ -258,6 +259,10 @@ struct _GabbleConnectionPrivate
/* the union of the above */
GabbleCapabilitySet *all_caps;
+ /* data forms provided via UpdateCapabilities()
+ * gchar * (client name) => GPtrArray<owned WockyDataForm> */
+ GHashTable *client_data_forms;
+
/* auth manager */
GabbleAuthManager *auth_manager;
@@ -444,6 +449,9 @@ gabble_connection_constructor (GType type,
priv->client_caps = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) gabble_capability_set_free);
+ priv->client_data_forms = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_ptr_array_unref);
+
/* Historically, the optional Jingle transports were in our initial
* presence, but could be removed by AdvertiseCapabilities(). Emulate
* that here for now. */
@@ -1222,6 +1230,8 @@ gabble_connection_dispose (GObject *object)
gabble_capability_set_free (priv->sidecar_caps);
gabble_capability_set_free (priv->bonus_caps);
+ g_hash_table_destroy (priv->client_data_forms);
+
if (priv->disconnect_timer != 0)
{
g_source_remove (priv->disconnect_timer);
@@ -1362,6 +1372,14 @@ WockyPorter *gabble_connection_dup_porter (GabbleConnection *conn)
return NULL;
}
+WockySession *
+gabble_connection_get_session (GabbleConnection *connection)
+{
+ g_return_val_if_fail (GABBLE_IS_CONNECTION (connection), NULL);
+
+ return connection->session;
+}
+
/**
* _gabble_connection_send
*
@@ -2303,7 +2321,8 @@ gabble_connection_fill_in_caps (GabbleConnection *self,
/* Ensure this set of capabilities is in the cache. */
gabble_presence_cache_add_own_caps (self->presence_cache, caps_hash,
- gabble_presence_peek_caps (presence), NULL);
+ gabble_presence_peek_caps (presence), NULL,
+ gabble_presence_peek_data_forms (presence));
/* XEP-0115 deprecates 'ext' feature bundles. But we still need
* BUNDLE_VOICE_V1 it for backward-compatibility with Gabble 0.2 */
@@ -2401,10 +2420,13 @@ gabble_connection_refresh_capabilities (GabbleConnection *self,
GHashTableIter iter;
gpointer k, v;
GabbleCapabilitySet *save_set;
+ GPtrArray *data_forms;
save_set = self->priv->all_caps;
self->priv->all_caps = gabble_capability_set_new ();
+ data_forms = g_ptr_array_new ();
+
gabble_capability_set_update (self->priv->all_caps,
gabble_capabilities_get_fixed_caps ());
gabble_capability_set_update (self->priv->all_caps, self->priv->notify_caps);
@@ -2412,6 +2434,7 @@ gabble_connection_refresh_capabilities (GabbleConnection *self,
gabble_capability_set_update (self->priv->all_caps, self->priv->sidecar_caps);
gabble_capability_set_update (self->priv->all_caps, self->priv->bonus_caps);
+ /* first, normal caps */
g_hash_table_iter_init (&iter, self->priv->client_caps);
while (g_hash_table_iter_next (&iter, &k, &v))
@@ -2427,13 +2450,22 @@ gabble_connection_refresh_capabilities (GabbleConnection *self,
gabble_capability_set_update (self->priv->all_caps, v);
}
+ /* now data forms */
+ g_hash_table_iter_init (&iter, self->priv->client_data_forms);
+
+ /* just borrow the ref, data_forms doesn't have a free func */
+ while (g_hash_table_iter_next (&iter, &k, &v))
+ tp_g_ptr_array_extend (data_forms, v);
+
if (self->self_presence != NULL)
gabble_presence_set_capabilities (self->self_presence,
- self->priv->resource, self->priv->all_caps, self->priv->caps_serial++);
+ self->priv->resource, self->priv->all_caps, data_forms,
+ self->priv->caps_serial++);
if (gabble_capability_set_equals (self->priv->all_caps, save_set))
{
gabble_capability_set_free (save_set);
+ g_ptr_array_unref (data_forms);
DEBUG ("nothing to do");
return FALSE;
}
@@ -2442,6 +2474,7 @@ gabble_connection_refresh_capabilities (GabbleConnection *self,
if (base->status != TP_CONNECTION_STATUS_CONNECTED)
{
gabble_capability_set_free (save_set);
+ g_ptr_array_unref (data_forms);
DEBUG ("not emitting self-presence stanza: not connected yet");
return FALSE;
}
@@ -2449,6 +2482,7 @@ gabble_connection_refresh_capabilities (GabbleConnection *self,
if (!conn_presence_signal_own_presence (self, NULL, &error))
{
gabble_capability_set_free (save_set);
+ g_ptr_array_unref (data_forms);
DEBUG ("error sending presence: %s", error->message);
g_error_free (error);
return FALSE;
@@ -2459,6 +2493,8 @@ gabble_connection_refresh_capabilities (GabbleConnection *self,
else
*old_out = save_set;
+ g_ptr_array_unref (data_forms);
+
return TRUE;
}
@@ -2570,6 +2606,7 @@ iq_disco_cb (WockyPorter *porter,
const GabbleCapabilityInfo *info = NULL;
const GabbleCapabilitySet *features = NULL;
const GPtrArray *identities = NULL;
+ const GPtrArray *data_forms = NULL;
/* query's existence is checked by WockyPorter before this function is called */
query = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "query");
@@ -2599,19 +2636,25 @@ iq_disco_cb (WockyPorter *porter,
wocky_node_set_attribute (result_query, "node", node);
if (node == NULL)
- features = gabble_presence_peek_caps (self->self_presence);
- /* 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. Let's see if it's a verification string we've told the cache about.
- */
+ {
+ features = gabble_presence_peek_caps (self->self_presence);
+ data_forms = gabble_presence_peek_data_forms (self->self_presence);
+ }
else
- info = gabble_presence_cache_peek_own_caps (self->presence_cache,
- suffix);
+ {
+ /* 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. Let's see if it's a verification string we've told the cache about.
+ */
+ info = gabble_presence_cache_peek_own_caps (self->presence_cache,
+ suffix);
+ }
if (info)
{
features = info->cap_set;
identities = info->identities;
+ data_forms = info->data_forms;
}
if (identities && identities->len != 0)
@@ -2650,6 +2693,18 @@ iq_disco_cb (WockyPorter *porter,
features = gabble_capabilities_get_bundle_video_v1 ();
}
+ if (data_forms != NULL)
+ {
+ guint i;
+
+ for (i = 0; i < data_forms->len; i++)
+ {
+ WockyDataForm *form = g_ptr_array_index (data_forms, i);
+
+ wocky_data_form_add_to_node (form, result_query);
+ }
+ }
+
if (features == NULL && tp_strdiff (suffix, BUNDLE_PMUC_V1))
{
_gabble_connection_send_iq_error (self, stanza,
@@ -3193,6 +3248,111 @@ gabble_connection_advertise_capabilities (TpSvcConnectionInterfaceCapabilities *
g_ptr_array_free (ret, TRUE);
}
+static const gchar *
+get_form_type (WockyDataForm *form)
+{
+ WockyDataFormField *field;
+
+ field = g_hash_table_lookup (form->fields,
+ "FORM_TYPE");
+ g_assert (field != NULL);
+
+ return field->raw_value_contents[0];
+}
+
+static const gchar *
+check_form_is_unique (GabbleConnection *self,
+ const gchar *form_type)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, self->priv->client_data_forms);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const gchar *manager = key;
+ GPtrArray *data_forms = value;
+ guint i;
+
+ if (data_forms == NULL || data_forms->len == 0)
+ continue;
+
+ for (i = 0; i < data_forms->len; i++)
+ {
+ WockyDataForm *form = g_ptr_array_index (data_forms, i);
+
+ if (!tp_strdiff (get_form_type (form), form_type))
+ return manager;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+check_data_form_in_list (GPtrArray *array,
+ const gchar *form_type)
+{
+ guint i;
+
+ for (i = 0; i < array->len; i++)
+ {
+ WockyDataForm *form = g_ptr_array_index (array, i);
+
+ if (!tp_strdiff (get_form_type (form), form_type))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+check_data_form_is_valid (GabbleConnection *self,
+ WockyDataForm *form,
+ GPtrArray *existing_forms)
+{
+ WockyDataFormField *field;
+ const gchar *form_type, *other_client;
+
+ /* We want rid of forms with no FORM_TYPE quickly. */
+ field = g_hash_table_lookup (form->fields, "FORM_TYPE");
+
+ if (field == NULL || tp_str_empty (field->raw_value_contents[0]))
+ {
+ WARNING ("data form with no FORM_TYPE field; ignoring");
+ return FALSE;
+ }
+
+ form_type = field->raw_value_contents[0];
+
+ /* We'll get warnings (potentially bad) if two clients cause a
+ * channel manager to create two data forms with the same FORM_TYPE,
+ * or if multiple channel managers create two data forms with the
+ * same FORM_TYPE, for the same client. This is probably not a
+ * problem in practice given hardly anyone uses data forms in entity
+ * capabilities anyway. */
+
+ /* We don't want the same data form from another caps channel
+ * manager for this client either */
+ if (check_data_form_in_list (existing_forms, form_type))
+ {
+ WARNING ("duplicate data form '%s' from another channel "
+ "manager; ignoring", form_type);
+ return FALSE;
+ }
+
+ /* And lastly we don't want a form we're already advertising. */
+ other_client = check_form_is_unique (self, form_type);
+ if (other_client != NULL)
+ {
+ WARNING ("Data form '%s' already provided by client "
+ "%s; ignoring", form_type, other_client);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/**
* gabble_connection_update_capabilities
*
@@ -3239,10 +3399,12 @@ gabble_connection_update_capabilities (
const GPtrArray *filters = g_value_get_boxed (va->values + 1);
const gchar * const * cap_tokens = g_value_get_boxed (va->values + 2);
GabbleCapabilitySet *cap_set;
+ GPtrArray *data_forms;
g_hash_table_remove (self->priv->client_caps, client_name);
+ g_hash_table_remove (self->priv->client_data_forms, client_name);
- if ((cap_tokens == NULL || cap_tokens[0] != NULL) &&
+ if ((cap_tokens == NULL || cap_tokens[0] == NULL) &&
filters->len == 0)
{
/* no capabilities */
@@ -3251,6 +3413,8 @@ gabble_connection_update_capabilities (
}
cap_set = gabble_capability_set_new ();
+ data_forms = g_ptr_array_new_with_free_func (
+ (GDestroyNotify) g_object_unref);
tp_base_connection_channel_manager_iter_init (&iter, base);
@@ -3258,18 +3422,30 @@ gabble_connection_update_capabilities (
{
if (GABBLE_IS_CAPS_CHANNEL_MANAGER (manager))
{
+ GPtrArray *forms = g_ptr_array_new_with_free_func (
+ (GDestroyNotify) g_object_unref);
+ guint j;
+
+ /* First, represent the client... */
gabble_caps_channel_manager_represent_client (
GABBLE_CAPS_CHANNEL_MANAGER (manager), client_name, filters,
- cap_tokens, cap_set);
+ cap_tokens, cap_set, forms);
+
+ /* Now check the forms... */
+ for (j = 0; j < forms->len; j++)
+ {
+ WockyDataForm *form = g_ptr_array_index (forms, j);
+
+ if (check_data_form_is_valid (self, form, data_forms))
+ g_ptr_array_add (data_forms, g_object_ref (form));
+ }
+
+ g_ptr_array_unref (forms);
}
}
- if (gabble_capability_set_size (cap_set) == 0)
- {
- DEBUG ("client %s has no interesting capabilities", client_name);
- gabble_capability_set_free (cap_set);
- }
- else
+ /* first deal with normal caps */
+ if (gabble_capability_set_size (cap_set) > 0)
{
if (DEBUGGING)
{
@@ -3282,6 +3458,33 @@ gabble_connection_update_capabilities (
g_hash_table_insert (self->priv->client_caps, g_strdup (client_name),
cap_set);
}
+ else
+ {
+ DEBUG ("client %s has no interesting capabilities", client_name);
+ gabble_capability_set_free (cap_set);
+ }
+
+ /* now data forms */
+ if (data_forms->len > 0)
+ {
+ guint j;
+
+ /* now print out what forms we have here */
+ DEBUG ("client %s contributes %u data form%s:", client_name,
+ data_forms->len,
+ data_forms->len > 1 ? "s" : "");
+
+ for (j = 0; j < data_forms->len; j++)
+ DEBUG (" - %s", get_form_type (g_ptr_array_index (data_forms, i)));
+
+ g_hash_table_insert (self->priv->client_data_forms,
+ g_strdup (client_name), data_forms);
+ }
+ else
+ {
+ DEBUG ("client %s has no interesting data forms", client_name);
+ g_ptr_array_unref (data_forms);
+ }
}
if (gabble_connection_refresh_capabilities (self, &old_caps))
@@ -3711,9 +3914,95 @@ gabble_connection_add_sidecar_own_caps (GabbleConnection *self,
ver = gabble_caps_hash_compute (cap_set, identities_copy);
gabble_presence_cache_add_own_caps (self->presence_cache, ver,
- cap_set, identities_copy);
+ cap_set, identities_copy, NULL);
wocky_disco_identity_array_free (identities_copy);
return ver;
}
+
+const gchar *
+gabble_connection_get_jid_for_caps (GabbleConnection *conn,
+ WockyXep0115Capabilities *caps)
+{
+ TpHandle handle;
+ TpBaseConnection *base;
+ TpHandleRepoIface *contact_handles;
+
+ g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL);
+ g_return_val_if_fail (GABBLE_IS_PRESENCE (caps), NULL);
+
+ base = (TpBaseConnection *) conn;
+
+ if ((GabblePresence *) caps == conn->self_presence)
+ {
+ handle = tp_base_connection_get_self_handle (base);
+ }
+ else
+ {
+ handle = gabble_presence_cache_get_handle (conn->presence_cache,
+ (GabblePresence *) caps);
+ }
+
+ contact_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+
+ return tp_handle_inspect (contact_handles, handle);
+}
+
+const gchar *
+gabble_connection_pick_best_resource_for_caps (GabbleConnection *connection,
+ const gchar *jid,
+ GabbleCapabilitySetPredicate predicate,
+ gconstpointer user_data)
+{
+ TpBaseConnection *base;
+ TpHandleRepoIface *contact_handles;
+ TpHandle handle;
+ GabblePresence *presence;
+
+ g_return_val_if_fail (GABBLE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (!tp_str_empty (jid), NULL);
+
+ base = (TpBaseConnection *) connection;
+ contact_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+
+ handle = tp_handle_ensure (contact_handles, jid,
+ NULL, NULL);
+
+ if (handle == 0)
+ return NULL;
+
+ presence = gabble_presence_cache_get (connection->presence_cache,
+ handle);
+
+ if (presence == NULL)
+ return NULL;
+
+ return gabble_presence_pick_resource_by_caps (presence, 0,
+ predicate, user_data);
+}
+
+TpBaseContactList *
+gabble_connection_get_contact_list (GabbleConnection *connection)
+{
+ g_return_val_if_fail (GABBLE_IS_CONNECTION (connection), NULL);
+
+ return (TpBaseContactList *) connection->roster;
+}
+
+WockyXep0115Capabilities *
+gabble_connection_get_caps (GabbleConnection *connection,
+ TpHandle handle)
+{
+ GabblePresence *presence;
+
+ g_return_val_if_fail (GABBLE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (handle > 0, NULL);
+
+ presence = gabble_presence_cache_get (connection->presence_cache,
+ handle);
+
+ return (WockyXep0115Capabilities *) presence;
+}
diff --git a/src/connection.h b/src/connection.h
index 586b648c..d1dc73a5 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -34,7 +34,7 @@
#include <wocky/wocky-pep-service.h>
#include "gabble/connection.h"
-#include "capabilities.h"
+#include "gabble/capabilities.h"
#include "error.h"
#include "ft-manager.h"
#include "jingle-factory.h"
@@ -204,8 +204,6 @@ typedef enum {
GABBLE_CONNECTION_ALIAS_FROM_ROSTER
} GabbleConnectionAliasSource;
-gchar *gabble_connection_get_full_jid (GabbleConnection *conn);
-
WockyPorter *gabble_connection_dup_porter (GabbleConnection *conn);
gboolean _gabble_connection_set_properties_from_account (
diff --git a/src/ft-manager.c b/src/ft-manager.c
index 9c3128df..b79881ac 100644
--- a/src/ft-manager.c
+++ b/src/ft-manager.c
@@ -30,7 +30,7 @@
#include "jingle-session.h"
#include "jingle-share.h"
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "ft-manager.h"
#include "error.h"
@@ -824,7 +824,8 @@ gabble_ft_manager_represent_client (
const gchar *client_name,
const GPtrArray *filters,
const gchar * const *cap_tokens G_GNUC_UNUSED,
- GabbleCapabilitySet *cap_set)
+ GabbleCapabilitySet *cap_set,
+ GPtrArray *data_forms G_GNUC_UNUSED)
{
guint i;
diff --git a/src/im-factory.c b/src/im-factory.c
index 08f19f6c..be518b27 100644
--- a/src/im-factory.c
+++ b/src/im-factory.c
@@ -36,7 +36,7 @@
#include "extensions/extensions.h"
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "debug.h"
#include "disco.h"
diff --git a/src/jingle-session.c b/src/jingle-session.c
index f20c2d63..2981e3e8 100644
--- a/src/jingle-session.c
+++ b/src/jingle-session.c
@@ -30,7 +30,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
-#include "capabilities.h"
+#include "gabble/capabilities.h"
#include "connection.h"
#include "conn-presence.h"
#include "debug.h"
diff --git a/src/legacy-caps.h b/src/legacy-caps.h
index 080ac3b0..e2de46c3 100644
--- a/src/legacy-caps.h
+++ b/src/legacy-caps.h
@@ -23,7 +23,7 @@
#include <glib-object.h>
-#include "capabilities.h"
+#include "gabble/capabilities.h"
typedef void (*TypeFlagsToCapsFunc) (guint typeflags, GabbleCapabilitySet *caps);
typedef guint (*CapsToTypeFlagsFunc) (const GabbleCapabilitySet *caps);
diff --git a/src/media-factory.c b/src/media-factory.c
index 4a2c7c70..e8ec9908 100644
--- a/src/media-factory.c
+++ b/src/media-factory.c
@@ -36,7 +36,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "debug.h"
#include "jingle-factory.h"
@@ -1208,7 +1208,8 @@ gabble_media_factory_represent_client (GabbleCapsChannelManager *manager,
const gchar *client_name,
const GPtrArray *filters,
const gchar * const *cap_tokens,
- GabbleCapabilitySet *cap_set)
+ GabbleCapabilitySet *cap_set,
+ GPtrArray *data_forms)
{
static GQuark q_gtalk_p2p = 0, q_ice_udp = 0, q_h264 = 0;
static GQuark qc_gtalk_p2p = 0, qc_ice_udp = 0, qc_h264 = 0;
diff --git a/src/muc-factory.c b/src/muc-factory.c
index 1feb21a1..554b1504 100644
--- a/src/muc-factory.c
+++ b/src/muc-factory.c
@@ -36,7 +36,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_MUC
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "conn-olpc.h"
#include "debug.h"
diff --git a/src/presence-cache.c b/src/presence-cache.c
index 05556f8b..89a6534e 100644
--- a/src/presence-cache.c
+++ b/src/presence-cache.c
@@ -41,11 +41,13 @@
#include <wocky/wocky-caps-hash.h>
#include <wocky/wocky-disco-identity.h>
#include <wocky/wocky-utils.h>
+#include <wocky/wocky-namespaces.h>
+#include <wocky/wocky-data-form.h>
#define DEBUG_FLAG GABBLE_DEBUG_PRESENCE
-#include "capabilities.h"
-#include "caps-channel-manager.h"
+#include "gabble/capabilities.h"
+#include "gabble/caps-channel-manager.h"
#include "conn-presence.h"
#include "debug.h"
#include "disco.h"
@@ -251,18 +253,36 @@ capability_info_free (GabbleCapabilityInfo *info)
wocky_disco_identity_array_free (info->identities);
info->identities = NULL;
+ if (info->data_forms != NULL)
+ g_ptr_array_unref (info->data_forms);
+ info->data_forms = NULL;
+
tp_intset_destroy (info->guys);
g_slice_free (GabbleCapabilityInfo, info);
}
+static void
+replace_data_forms (GabbleCapabilityInfo *info,
+ GPtrArray *data_forms)
+{
+ if (data_forms == info->data_forms)
+ return;
+
+ tp_clear_pointer (&info->data_forms, g_ptr_array_unref);
+
+ if (data_forms != NULL)
+ info->data_forms = g_ptr_array_ref (data_forms);
+}
+
static guint
capability_info_recvd (GabblePresenceCache *cache,
const gchar *node,
TpHandle handle,
GabbleCapabilitySet *cap_set,
guint trust_inc,
- guint client_types)
+ guint client_types,
+ GPtrArray *data_forms)
{
GabbleCapabilityInfo *info = capability_info_get (cache, node);
@@ -293,6 +313,8 @@ capability_info_recvd (GabblePresenceCache *cache,
info->client_types = client_types;
+ replace_data_forms (info, data_forms);
+
return info->trust;
}
@@ -1012,7 +1034,7 @@ _parse_node (GabblePresence *presence,
gabble_capability_set_add (cap_set, QUIRK_GOOGLE_WEBMAIL_CLIENT);
gabble_capability_set_add (cap_set, QUIRK_OMITS_CONTENT_CREATORS);
- gabble_presence_set_capabilities (presence, resource, cap_set, serial);
+ gabble_presence_set_capabilities (presence, resource, cap_set, NULL, serial);
gabble_capability_set_free (cap_set);
}
}
@@ -1142,6 +1164,7 @@ set_caps_for (DiscoWaiter *waiter,
GabblePresenceCache *cache,
GabbleCapabilitySet *cap_set,
guint client_types,
+ GPtrArray *data_forms,
TpHandle responder_handle,
const gchar *responder_jid)
{
@@ -1158,7 +1181,7 @@ set_caps_for (DiscoWaiter *waiter,
waiter->handle, responder_handle, responder_jid);
gabble_presence_set_capabilities (presence, waiter->resource, cap_set,
- waiter->serial);
+ data_forms, waiter->serial);
new_cap_set = gabble_presence_peek_caps (presence);
emit_capabilities_update (cache, waiter->handle, old_cap_set, new_cap_set);
gabble_capability_set_free (old_cap_set);
@@ -1222,6 +1245,29 @@ client_types_from_message (TpHandle handle,
return client_types;
}
+static GPtrArray *
+data_forms_from_message (WockyNode *node)
+{
+ GPtrArray *out = g_ptr_array_new_with_free_func (g_object_unref);
+
+ WockyNodeIter iter;
+ WockyNode *x_node = NULL;
+
+ wocky_node_iter_init (&iter, node, "x", WOCKY_XMPP_NS_DATA);
+ while (wocky_node_iter_next (&iter, &x_node))
+ {
+ WockyDataForm *form = wocky_data_form_new_from_node (x_node, NULL);
+
+ /* we've already parsed the reply to check the hash matches, so
+ * we can already guarantee these data forms will be parsed
+ * fine */
+ if (G_LIKELY (form != NULL))
+ g_ptr_array_add (out, form);
+ }
+
+ return out;
+}
+
static void
_signal_presences_updated (GabblePresenceCache *cache,
TpHandle handle)
@@ -1257,6 +1303,7 @@ _caps_disco_cb (GabbleDisco *disco,
gboolean jid_is_valid;
gpointer key;
guint client_types = 0;
+ GPtrArray *data_forms = NULL;
cache = GABBLE_PRESENCE_CACHE (user_data);
priv = cache->priv;
@@ -1305,6 +1352,7 @@ _caps_disco_cb (GabbleDisco *disco,
cap_set = gabble_capability_set_new_from_stanza (query_result);
client_types = client_types_from_message (handle, query_result,
waiter_self->resource);
+ data_forms = data_forms_from_message (query_result);
/* Only 'sha-1' is mandatory to implement by XEP-0115. If the remote contact
* uses another hash algorithm, don't check the hash and fallback to the old
@@ -1326,7 +1374,7 @@ _caps_disco_cb (GabbleDisco *disco,
else if (g_str_equal (waiter_self->ver, computed_hash))
{
trust = capability_info_recvd (cache, node, handle, cap_set,
- CAPABILITY_BUNDLE_ENOUGH_TRUST, client_types);
+ CAPABILITY_BUNDLE_ENOUGH_TRUST, client_types, data_forms);
}
else
{
@@ -1341,7 +1389,8 @@ _caps_disco_cb (GabbleDisco *disco,
}
else
{
- trust = capability_info_recvd (cache, node, handle, cap_set, 1, client_types);
+ trust = capability_info_recvd (cache, node, handle, cap_set, 1,
+ client_types, data_forms);
}
/* Remove the node from the hash table without freeing the key or list of
@@ -1381,7 +1430,8 @@ _caps_disco_cb (GabbleDisco *disco,
{
DiscoWaiter *waiter = (DiscoWaiter *) i->data;
- set_caps_for (waiter, cache, cap_set, client_types, handle, jid);
+ set_caps_for (waiter, cache, cap_set, client_types,
+ data_forms, handle, jid);
emit_capabilities_discovered (cache, waiter->handle);
}
@@ -1409,7 +1459,8 @@ _caps_disco_cb (GabbleDisco *disco,
g_free (tmp);
}
- set_caps_for (waiter_self, cache, cap_set, client_types, handle, jid);
+ set_caps_for (waiter_self, cache, cap_set, client_types,
+ data_forms, handle, jid);
}
waiters = g_slist_remove (waiters, waiter_self);
@@ -1435,6 +1486,7 @@ _caps_disco_cb (GabbleDisco *disco,
}
gabble_capability_set_free (cap_set);
+ g_ptr_array_unref (data_forms);
OUT:
if (handle)
@@ -1501,7 +1553,7 @@ _process_caps_uri (GabblePresenceCache *cache,
guint types;
gabble_presence_set_capabilities (
- presence, resource, cap_set, serial);
+ presence, resource, cap_set, info->data_forms, serial);
/* We can only get this information from actual disco replies,
* so we depend on having this information from the caps cache. */
@@ -2100,7 +2152,8 @@ gabble_presence_cache_add_own_caps (
GabblePresenceCache *cache,
const gchar *ver,
const GabbleCapabilitySet *cap_set,
- const GPtrArray *identities)
+ const GPtrArray *identities,
+ GPtrArray *data_forms)
{
gchar *uri = g_strdup_printf ("%s#%s", NS_GABBLE_CAPS, ver);
GabbleCapabilityInfo *info = capability_info_get (cache, uri);
@@ -2135,6 +2188,8 @@ gabble_presence_cache_add_own_caps (
info->trust = CAPABILITY_BUNDLE_ENOUGH_TRUST;
tp_intset_add (info->guys, cache->priv->conn->parent.self_handle);
+ replace_data_forms (info, data_forms);
+
/* FIXME: we should satisfy any waiters for this node now. fd.o bug #24619. */
out:
@@ -2497,3 +2552,20 @@ gabble_presence_cache_disco_in_progress (GabblePresenceCache *cache,
return in_progress;
}
+
+TpHandle
+gabble_presence_cache_get_handle (GabblePresenceCache *cache,
+ GabblePresence *presence)
+{
+ GHashTableIter iter;
+ gpointer key, val;
+
+ g_hash_table_iter_init (&iter, cache->priv->presence);
+ while (g_hash_table_iter_next (&iter, &key, &val))
+ {
+ if (presence == val)
+ return GPOINTER_TO_UINT (key);
+ }
+
+ return 0;
+}
diff --git a/src/presence-cache.h b/src/presence-cache.h
index 44af23fb..0e528396 100644
--- a/src/presence-cache.h
+++ b/src/presence-cache.h
@@ -65,6 +65,9 @@ struct _GabbleCapabilityInfo
/* array of GabbleDiscoIdentity or NULL */
GPtrArray *identities;
+ /* array of WockyDataForm or NULL */
+ GPtrArray *data_forms;
+
TpIntSet *guys;
guint trust;
@@ -107,7 +110,8 @@ void gabble_presence_cache_maybe_remove (GabblePresenceCache *cache,
void gabble_presence_cache_add_own_caps (GabblePresenceCache *cache,
const gchar *ver,
const GabbleCapabilitySet *cap_set,
- const GPtrArray *identities);
+ const GPtrArray *identities,
+ GPtrArray *data_forms);
const GabbleCapabilityInfo *gabble_presence_cache_peek_own_caps (
GabblePresenceCache *cache,
const gchar *ver);
@@ -140,6 +144,9 @@ GHashTable* gabble_presence_cache_get_location (GabblePresenceCache *cache,
gboolean gabble_presence_cache_disco_in_progress (GabblePresenceCache *cache,
TpHandle handle, const gchar *resource);
+TpHandle gabble_presence_cache_get_handle (GabblePresenceCache *cache,
+ GabblePresence *presence);
+
G_END_DECLS
#endif /* __GABBLE_PRESENCE_CACHE_H__ */
diff --git a/src/presence.c b/src/presence.c
index a707822a..1ddaedf6 100644
--- a/src/presence.c
+++ b/src/presence.c
@@ -24,8 +24,9 @@
#include <string.h>
#include <telepathy-glib/channel-manager.h>
#include <wocky/wocky-utils.h>
+#include <wocky/wocky-xep-0115-capabilities.h>
-#include "capabilities.h"
+#include "gabble/capabilities.h"
#include "conn-presence.h"
#include "presence-cache.h"
#include "namespaces.h"
@@ -36,7 +37,12 @@
#include "debug.h"
-G_DEFINE_TYPE (GabblePresence, gabble_presence, G_TYPE_OBJECT);
+static void xep_0115_capabilities_iface_init (gpointer, gpointer);
+
+G_DEFINE_TYPE_WITH_CODE (GabblePresence, gabble_presence, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (WOCKY_TYPE_XEP_0115_CAPABILITIES,
+ xep_0115_capabilities_iface_init);
+)
typedef struct _Resource Resource;
@@ -44,6 +50,7 @@ struct _Resource {
gchar *name;
guint client_type;
GabbleCapabilitySet *cap_set;
+ GPtrArray *data_forms;
guint caps_serial;
GabblePresenceId status;
gchar *status_message;
@@ -58,6 +65,9 @@ struct _GabblePresencePrivate {
/* The aggregated caps of all the contacts' resources. */
GabbleCapabilitySet *cap_set;
+ /* The aggregated data forms of all the contacts' resources */
+ GPtrArray *data_forms;
+
gchar *no_resource_status_message;
GSList *resources;
guint olpc_views;
@@ -72,6 +82,8 @@ _resource_new (gchar *name)
new->name = name;
new->client_type = 0;
new->cap_set = gabble_capability_set_new ();
+ new->data_forms = g_ptr_array_new_with_free_func (
+ (GDestroyNotify) g_object_unref);
new->status = GABBLE_PRESENCE_OFFLINE;
new->status_message = NULL;
new->priority = 0;
@@ -87,6 +99,7 @@ _resource_free (Resource *resource)
g_free (resource->name);
g_free (resource->status_message);
gabble_capability_set_free (resource->cap_set);
+ g_ptr_array_unref (resource->data_forms);
g_slice_free (Resource, resource);
}
@@ -103,6 +116,7 @@ gabble_presence_finalize (GObject *object)
g_slist_free (priv->resources);
gabble_capability_set_free (priv->cap_set);
+ g_ptr_array_unref (priv->data_forms);
g_free (presence->nickname);
g_free (presence->avatar_sha1);
@@ -128,6 +142,8 @@ gabble_presence_init (GabblePresence *self)
priv = self->priv;
priv->cap_set = gabble_capability_set_new ();
+ priv->data_forms = g_ptr_array_new_with_free_func (
+ (GDestroyNotify) g_object_unref);
priv->resources = NULL;
self->status = GABBLE_PRESENCE_UNKNOWN;
@@ -198,6 +214,13 @@ gabble_presence_peek_caps (GabblePresence *presence)
return presence->priv->cap_set;
}
+GPtrArray *
+gabble_presence_peek_data_forms (GabblePresence *presence)
+{
+ g_return_val_if_fail (presence != NULL, NULL);
+ return presence->priv->data_forms;
+}
+
gboolean
gabble_presence_has_resources (GabblePresence *self)
{
@@ -266,10 +289,22 @@ gabble_presence_resource_has_caps (GabblePresence *presence,
return FALSE;
}
+static void
+extend_and_dup (GPtrArray *target,
+ GPtrArray *source)
+{
+ if (source == NULL)
+ return;
+
+ g_ptr_array_foreach (source, (GFunc) g_object_ref, NULL);
+ tp_g_ptr_array_extend (target, source);
+}
+
void
gabble_presence_set_capabilities (GabblePresence *presence,
const gchar *resource,
const GabbleCapabilitySet *cap_set,
+ const GPtrArray *data_forms,
guint serial)
{
GabblePresencePrivate *priv = presence->priv;
@@ -288,11 +323,13 @@ gabble_presence_set_capabilities (GabblePresence *presence,
}
gabble_capability_set_clear (priv->cap_set);
+ g_ptr_array_set_size (priv->data_forms, 0);
if (resource == NULL)
{
DEBUG ("Setting capabilities for bare JID");
gabble_capability_set_update (priv->cap_set, cap_set);
+ extend_and_dup (priv->data_forms, (GPtrArray *) data_forms);
return;
}
@@ -315,6 +352,7 @@ gabble_presence_set_capabilities (GabblePresence *presence,
tmp->caps_serial);
tmp->caps_serial = serial;
gabble_capability_set_clear (tmp->cap_set);
+ g_ptr_array_set_size (tmp->data_forms, 0);
}
if (serial >= tmp->caps_serial)
@@ -322,11 +360,19 @@ gabble_presence_set_capabilities (GabblePresence *presence,
DEBUG ("updating caps for resource %s", resource);
gabble_capability_set_update (tmp->cap_set, cap_set);
+
+ /* TODO: deal with duplicates */
+ extend_and_dup (tmp->data_forms, (GPtrArray *) data_forms);
}
}
gabble_capability_set_update (priv->cap_set, tmp->cap_set);
+
+ /* TODO: deal with duplicates */
+ extend_and_dup (priv->data_forms, tmp->data_forms);
}
+
+ g_signal_emit_by_name (presence, "capabilities-changed");
}
static Resource *
@@ -855,3 +901,20 @@ gabble_presence_get_client_types_array (GabblePresence *presence,
return (gchar **) g_ptr_array_free (array, FALSE);
}
+
+static const GPtrArray *
+gabble_presence_get_data_forms (WockyXep0115Capabilities *caps)
+{
+ GabblePresence *presence = GABBLE_PRESENCE (caps);
+
+ return presence->priv->data_forms;
+}
+
+static void
+xep_0115_capabilities_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ WockyXep0115CapabilitiesInterface *iface = g_iface;
+
+ iface->get_data_forms = gabble_presence_get_data_forms;
+}
diff --git a/src/presence.h b/src/presence.h
index 4f9126d0..50d6945a 100644
--- a/src/presence.h
+++ b/src/presence.h
@@ -24,7 +24,7 @@
#include <glib-object.h>
-#include "capabilities.h"
+#include "gabble/capabilities.h"
#include "connection.h"
#include "types.h"
@@ -96,11 +96,13 @@ gboolean gabble_presence_update (GabblePresence *presence,
void gabble_presence_set_capabilities (GabblePresence *presence,
const gchar *resource,
const GabbleCapabilitySet *cap_set,
+ const GPtrArray *data_forms,
guint serial);
gboolean gabble_presence_has_cap (GabblePresence *presence, const gchar *ns);
GabbleCapabilitySet *gabble_presence_dup_caps (GabblePresence *presence);
const GabbleCapabilitySet *gabble_presence_peek_caps (GabblePresence *presence);
+GPtrArray *gabble_presence_peek_data_forms (GabblePresence *presence);
gboolean gabble_presence_has_resources (GabblePresence *self);
diff --git a/src/private-tubes-factory.c b/src/private-tubes-factory.c
index 4ec36fd1..52c10a84 100644
--- a/src/private-tubes-factory.c
+++ b/src/private-tubes-factory.c
@@ -36,8 +36,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_TUBES
-#include "caps-channel-manager.h"
-#include "capabilities.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "debug.h"
#include "muc-channel.h"
@@ -612,7 +611,8 @@ gabble_private_tubes_factory_represent_client (
const gchar *client_name,
const GPtrArray *filters,
const gchar * const *cap_tokens,
- GabbleCapabilitySet *cap_set)
+ GabbleCapabilitySet *cap_set,
+ GPtrArray *data_forms)
{
guint i;
diff --git a/src/roomlist-manager.c b/src/roomlist-manager.c
index 76abee68..5f38a102 100644
--- a/src/roomlist-manager.c
+++ b/src/roomlist-manager.c
@@ -33,7 +33,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_MUC
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "debug.h"
#include "namespaces.h"
diff --git a/src/roster.c b/src/roster.c
index 798b83c6..8d8c3b7b 100644
--- a/src/roster.c
+++ b/src/roster.c
@@ -37,7 +37,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_ROSTER
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "conn-aliasing.h"
#include "conn-presence.h"
#include "conn-util.h"
diff --git a/src/search-manager.c b/src/search-manager.c
index d3c1fa57..fec4b74d 100644
--- a/src/search-manager.c
+++ b/src/search-manager.c
@@ -29,7 +29,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_SEARCH
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "debug.h"
#include "disco.h"
diff --git a/src/server-tls-manager.c b/src/server-tls-manager.c
index 62ab8865..48364512 100644
--- a/src/server-tls-manager.c
+++ b/src/server-tls-manager.c
@@ -23,7 +23,7 @@
#define DEBUG_FLAG GABBLE_DEBUG_TLS
#include "debug.h"
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "server-tls-channel.h"
#include "util.h"
diff --git a/tests/test-presence.c b/tests/test-presence.c
index 7b9e423a..8319cb2d 100644
--- a/tests/test-presence.c
+++ b/tests/test-presence.c
@@ -20,6 +20,7 @@ big_test_of_doom (void)
GabblePresence *presence;
GabbleCapabilitySet *cap_set;
time_t now = time (NULL);
+ GPtrArray *data_forms = g_ptr_array_new ();
/* When we create a new presence, we know nothing about the contact in
* question's presence.
@@ -138,7 +139,7 @@ big_test_of_doom (void)
GABBLE_PRESENCE_AVAILABLE, "dingoes", -1, NULL, now));
cap_set = gabble_capability_set_new ();
gabble_capability_set_add (cap_set, NS_GOOGLE_FEAT_VOICE);
- gabble_presence_set_capabilities (presence, "bar", cap_set, 0);
+ gabble_presence_set_capabilities (presence, "bar", cap_set, data_forms, 0);
gabble_capability_set_free (cap_set);
/* no resource with non-negative priority has the Google voice cap */
@@ -149,7 +150,7 @@ big_test_of_doom (void)
/* give voice cap to first resource */
cap_set = gabble_capability_set_new ();
gabble_capability_set_add (cap_set, NS_GOOGLE_FEAT_VOICE);
- gabble_presence_set_capabilities (presence, "foo", cap_set, 0);
+ gabble_presence_set_capabilities (presence, "foo", cap_set, data_forms, 0);
gabble_capability_set_free (cap_set);
/* resource has voice cap */
@@ -169,6 +170,7 @@ big_test_of_doom (void)
gabble_capability_set_predicate_has, NS_GOOGLE_FEAT_VOICE);
g_assert (NULL == resource);
+ g_ptr_array_unref (data_forms);
g_object_unref (presence);
}
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 68f5d2cc..b8b92cbb 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -98,6 +98,7 @@ TWISTED_TESTS = \
text/test-text.py \
tls/server-tls-channel.py \
version.py \
+ dataforms.py \
$(NULL)
TWISTED_TUBE_TESTS = \
diff --git a/tests/twisted/caps/advertise-contact-caps.py b/tests/twisted/caps/advertise-contact-caps.py
index 55630d3e..8affe93b 100644
--- a/tests/twisted/caps/advertise-contact-caps.py
+++ b/tests/twisted/caps/advertise-contact-caps.py
@@ -46,7 +46,7 @@ def run_test(q, bus, conn, stream,
args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
EventPattern('stream-presence'),
)
- (disco_response, namespaces) = disco_caps(q, stream, initial_presence)
+ (disco_response, namespaces, _) = disco_caps(q, stream, initial_presence)
check_caps(namespaces, [ns.TUBES + '/stream#x-abiword'])
conn.ContactCapabilities.UpdateCapabilities([
@@ -70,7 +70,7 @@ def run_test(q, bus, conn, stream,
media_interface + '/video/h264',
]),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, JINGLE_CAPS + [ns.TUBES + '/stream#x-abiword'])
@@ -87,7 +87,7 @@ def run_test(q, bus, conn, stream,
media_interface + '/ice-udp',
]),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces,
JINGLE_CAPS_EXCEPT_GVIDEO + [ns.TUBES + '/stream#x-abiword'])
@@ -96,7 +96,7 @@ def run_test(q, bus, conn, stream,
conn.ContactCapabilities.UpdateCapabilities([
(cs.CLIENT + '.AbiWord', [], []),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, JINGLE_CAPS_EXCEPT_GVIDEO)
@@ -104,7 +104,7 @@ def run_test(q, bus, conn, stream,
conn.ContactCapabilities.UpdateCapabilities([
(cs.CLIENT + '.KCall', [], []),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, [])
@@ -124,7 +124,7 @@ def run_test(q, bus, conn, stream,
media_interface + '/video/h264',
]),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, [ns.TUBES + '/stream#x-abiword'])
@@ -132,7 +132,7 @@ def run_test(q, bus, conn, stream,
conn.ContactCapabilities.UpdateCapabilities([
(cs.CLIENT + '.AbiWord', [], []),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, [])
@@ -148,7 +148,7 @@ def run_test(q, bus, conn, stream,
initial_audio: True},
], [media_interface + '/gtalk-p2p']),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces,
[ns.GOOGLE_P2P, ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE,
@@ -164,7 +164,7 @@ def run_test(q, bus, conn, stream,
initial_audio: True},
], [])
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces,
[ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE,
@@ -183,7 +183,7 @@ def run_test(q, bus, conn, stream,
media_interface + '/video/theora',
]),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces,
[ns.JINGLE_TRANSPORT_ICEUDP, ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE,
@@ -194,7 +194,7 @@ def run_test(q, bus, conn, stream,
conn.ContactCapabilities.UpdateCapabilities([
(cs.CLIENT + '.KCall', [], []),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, [])
@@ -205,7 +205,7 @@ def run_test(q, bus, conn, stream,
cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
}], []),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, [ns.FILE_TRANSFER])
@@ -235,7 +235,7 @@ def run_mixed_test (q, bus, conn, stream):
]),
])
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, JINGLE_CAPS)
diff --git a/tests/twisted/caps/advertise-legacy.py b/tests/twisted/caps/advertise-legacy.py
index b16c7c07..8c386321 100644
--- a/tests/twisted/caps/advertise-legacy.py
+++ b/tests/twisted/caps/advertise-legacy.py
@@ -27,7 +27,7 @@ def run_test(q, bus, conn, stream):
(cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1)]
remove = []
caps = conn.Capabilities.AdvertiseCapabilities(add, remove)
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, JINGLE_CAPS)
@@ -36,7 +36,7 @@ def run_test(q, bus, conn, stream):
remove = [cs.CHANNEL_TYPE_STREAMED_MEDIA,
cs.CHANNEL_TYPE_STREAM_TUBE]
caps = conn.Capabilities.AdvertiseCapabilities(add, remove)
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, [])
@@ -50,7 +50,7 @@ def run_test(q, bus, conn, stream):
cs.MEDIA_CAP_AUDIO | cs.MEDIA_CAP_GTALKP2P)]
remove = []
caps = conn.Capabilities.AdvertiseCapabilities(add, remove)
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces,
[ns.GOOGLE_P2P, ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE,
@@ -62,7 +62,7 @@ def run_test(q, bus, conn, stream):
remove = [cs.CHANNEL_TYPE_STREAMED_MEDIA,
cs.CHANNEL_TYPE_STREAM_TUBE]
caps = conn.Capabilities.AdvertiseCapabilities(add, remove)
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, [])
@@ -71,7 +71,7 @@ def run_test(q, bus, conn, stream):
add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.MEDIA_CAP_AUDIO)]
remove = []
caps = conn.Capabilities.AdvertiseCapabilities(add, remove)
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces,
[ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE,
@@ -83,7 +83,7 @@ def run_test(q, bus, conn, stream):
remove = [cs.CHANNEL_TYPE_STREAMED_MEDIA,
cs.CHANNEL_TYPE_STREAM_TUBE]
caps = conn.Capabilities.AdvertiseCapabilities(add, remove)
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces, [])
@@ -93,7 +93,7 @@ def run_test(q, bus, conn, stream):
cs.MEDIA_CAP_VIDEO | cs.MEDIA_CAP_ICEUDP)]
remove = []
caps = conn.Capabilities.AdvertiseCapabilities(add, remove)
- (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ (disco_response, namespaces, _, _) = receive_presence_and_ask_caps(q, stream,
False)
check_caps(namespaces,
[ns.JINGLE_TRANSPORT_ICEUDP, ns.JINGLE_TRANSPORT_RAWUDP, ns.JINGLE,
diff --git a/tests/twisted/caps/initial-caps.py b/tests/twisted/caps/initial-caps.py
index 8d50f0ee..6aef228b 100644
--- a/tests/twisted/caps/initial-caps.py
+++ b/tests/twisted/caps/initial-caps.py
@@ -15,7 +15,7 @@ import ns
def run_test(q, bus, conn, stream):
initial_presence = q.expect('stream-presence')
- _, namespaces = disco_caps(q, stream, initial_presence)
+ _, namespaces, _ = disco_caps(q, stream, initial_presence)
# For some reason, until we advertise any capabilities, these caps turn
# up in our presence
diff --git a/tests/twisted/caps/tube-caps.py b/tests/twisted/caps/tube-caps.py
index a70f286d..3d3f698d 100644
--- a/tests/twisted/caps/tube-caps.py
+++ b/tests/twisted/caps/tube-caps.py
@@ -232,7 +232,7 @@ def advertise_caps(q, conn, stream, filters, expected_features, unexpected_featu
[(cs.CLIENT + '.Foo', filters, [])])
# Expect Gabble to reply with the correct caps
- event, namespaces, signaled_caps = receive_presence_and_ask_caps(q, stream)
+ event, namespaces, _, signaled_caps = receive_presence_and_ask_caps(q, stream)
assertSameElements(expected_caps, signaled_caps)
diff --git a/tests/twisted/caps_helper.py b/tests/twisted/caps_helper.py
index 7f6f97b9..72c15421 100644
--- a/tests/twisted/caps_helper.py
+++ b/tests/twisted/caps_helper.py
@@ -205,6 +205,41 @@ def receive_presence_and_ask_caps(q, stream, expect_dbus=True):
return disco_caps(q, stream, presence) + (signaled_caps,)
+def extract_disco_parts(stanza):
+ identity_nodes = xpath.queryForNodes('/iq/query/identity', stanza)
+ assertLength(1, identity_nodes)
+ identity_node = identity_nodes[0]
+
+ assertEquals('client', identity_node['category'])
+ assertEquals(config.CLIENT_TYPE, identity_node['type'])
+ assertEquals(config.PACKAGE_STRING, identity_node['name'])
+ assertDoesNotContain('xml:lang', identity_node.attributes)
+
+ identity = 'client/%s//%s' % (config.CLIENT_TYPE, config.PACKAGE_STRING)
+
+ features = []
+ for feature in xpath.queryForNodes('/iq/query/feature', stanza):
+ features.append(feature['var'])
+
+ # a quick and ugly data form extractor
+ x_nodes = xpath.queryForNodes('/iq/query/x', stanza) or []
+ dataforms = {}
+ for form in x_nodes:
+ name = None
+ fields = {}
+ for field in xpath.queryForNodes('/x/field', form):
+ if field['var'] == 'FORM_TYPE':
+ name = str(field.firstChildElement())
+ else:
+ values = [str(x) for x in xpath.queryForNodes('/field/value', field)]
+
+ fields[field['var']] = values
+
+ if name is not None:
+ dataforms[name] = fields
+
+ return ([identity], features, dataforms)
+
def disco_caps(q, stream, presence):
c_nodes = xpath.queryForNodes('/presence/c', presence.stanza)
assert c_nodes is not None
@@ -225,25 +260,12 @@ def disco_caps(q, stream, presence):
event = q.expect('stream-iq', query_ns=ns.DISCO_INFO, iq_id=request['id'])
# Check that Gabble's announcing the identity we think it should be.
- identity_nodes = xpath.queryForNodes('/iq/query/identity', event.stanza)
- assertLength(1, identity_nodes)
- identity_node = identity_nodes[0]
-
- assertEquals('client', identity_node['category'])
- assertEquals(config.CLIENT_TYPE, identity_node['type'])
- assertEquals(config.PACKAGE_STRING, identity_node['name'])
- assertDoesNotContain('xml:lang', identity_node.attributes)
-
- identity = 'client/%s//%s' % (config.CLIENT_TYPE, config.PACKAGE_STRING)
-
- features = []
- for feature in xpath.queryForNodes('/iq/query/feature', event.stanza):
- features.append(feature['var'])
+ (identities, features, dataforms) = extract_disco_parts(event.stanza)
# Check if the hash matches the announced capabilities
- assertEquals(compute_caps_hash([identity], features, {}), ver)
+ assertEquals(compute_caps_hash(identities, features, dataforms), ver)
- return (event, features)
+ return (event, features, dataforms)
def caps_contain(event, cap):
node = xpath.queryForNodes('/iq/query/feature[@var="%s"]'
diff --git a/tests/twisted/dataforms.py b/tests/twisted/dataforms.py
new file mode 100644
index 00000000..e211bd9c
--- /dev/null
+++ b/tests/twisted/dataforms.py
@@ -0,0 +1,32 @@
+"""
+Test dataforms
+"""
+
+from servicetest import sync_dbus, assertEquals
+from gabbletest import exec_test, sync_stream
+import ns
+import constants as cs
+from caps_helper import receive_presence_and_ask_caps
+
+def test(q, bus, conn, stream):
+ q.expect('stream-presence')
+
+ # gabble won't try to represent the client if doesn't have any
+ # RCCs or HCTs, so let's add some
+ conn.ContactCapabilities.UpdateCapabilities(
+ [
+ ('dataformtest', [], ['banan', 'hi'])
+ ])
+
+ _, _, forms, _ = receive_presence_and_ask_caps(q, stream)
+
+ assertEquals({'gabble:test:channel:manager:data:form':
+ {'cheese': ['omgnothorriblecheese'],
+ 'running_out_of': ['ideas', 'cake'],
+ 'favourite_crane': ['a tall one', 'a short one'],
+ 'animal': ['badger', 'snake', 'weasel']
+ }
+ }, forms)
+
+if __name__ == '__main__':
+ exec_test(test)
diff --git a/tests/twisted/jingle-share/file_transfer_helper.py b/tests/twisted/jingle-share/file_transfer_helper.py
index 0fed8952..d555b008 100644
--- a/tests/twisted/jingle-share/file_transfer_helper.py
+++ b/tests/twisted/jingle-share/file_transfer_helper.py
@@ -11,7 +11,8 @@ import ns
from caps_helper import text_fixed_properties, text_allowed_properties, \
stream_tube_fixed_properties, stream_tube_allowed_properties, \
dbus_tube_fixed_properties, dbus_tube_allowed_properties, \
- ft_fixed_properties, ft_allowed_properties, compute_caps_hash
+ ft_fixed_properties, ft_allowed_properties, compute_caps_hash, \
+ extract_disco_parts
from twisted.words.xish import domish, xpath
@@ -59,6 +60,7 @@ generic_caps = [(text_fixed_properties, text_allowed_properties),
class FileTransferTest(object):
caps_identities = None
caps_features = None
+ caps_dataforms = None
caps_ft = None
def __init__(self, file, address_type, access_control, access_control_param):
@@ -141,12 +143,13 @@ class FileTransferTest(object):
c = nodes[0]
if 'share-v1' in c.getAttribute('ext'):
assert FileTransferTest.caps_identities is not None and \
- FileTransferTest.caps_features is not None
+ FileTransferTest.caps_features is not None and \
+ FileTransferTest.caps_dataforms is not None
new_hash = compute_caps_hash(FileTransferTest.caps_identities,
FileTransferTest.caps_features + \
[ns.GOOGLE_FEAT_SHARE],
- {})
+ FileTransferTest.caps_dataforms)
# Replace ver hash from one with file-transfer ns to one without
FileTransferTest.caps_ft = c.attributes['ver']
c.attributes['ver'] = new_hash
@@ -173,7 +176,9 @@ class FileTransferTest(object):
if iq.getAttribute('type') == 'result':
if FileTransferTest.caps_identities is None or \
- FileTransferTest.caps_features is None:
+ FileTransferTest.caps_features is None or \
+ FileTransferTest.caps_dataforms is None:
+ # create our own identity
identity_nodes = xpath.queryForNodes('/iq/query/identity', iq)
assertLength(1, identity_nodes)
identity_node = identity_nodes[0]
@@ -183,22 +188,23 @@ class FileTransferTest(object):
identity_name = identity_node['name']
identity = '%s/%s//%s' % (identity_category, identity_type,
identity_name)
- FileTransferTest.caps_identities = [identity]
- FileTransferTest.caps_features = []
- for feature in xpath.queryForNodes('/iq/query/feature', iq):
- FileTransferTest.caps_features.append(feature['var'])
+ _, features, dataforms = extract_disco_parts(iq)
+
+ FileTransferTest.caps_identities = [identity]
+ FileTransferTest.caps_features = features
+ FileTransferTest.caps_dataforms = dataforms
# Check if the hash matches the announced capabilities
assertEquals(compute_caps_hash(FileTransferTest.caps_identities,
FileTransferTest.caps_features,
- {}), ver)
+ FileTransferTest.caps_dataforms), ver)
if ver == FileTransferTest.caps_ft:
caps_share = compute_caps_hash(FileTransferTest.caps_identities,
FileTransferTest.caps_features + \
[ns.GOOGLE_FEAT_SHARE],
- {})
+ FileTransferTest.caps_dataforms)
n = query.attributes['node'].replace(ver, caps_share)
query.attributes['node'] = n
@@ -214,7 +220,7 @@ class FileTransferTest(object):
caps_share = compute_caps_hash(FileTransferTest.caps_identities,
FileTransferTest.caps_features + \
[ns.GOOGLE_FEAT_SHARE],
- {})
+ FileTransferTest.caps_dataforms)
if ver == caps_share:
n = query.attributes['node'].replace(ver,