summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Thompson <will.thompson@collabora.co.uk>2009-11-27 12:26:50 +0000
committerWill Thompson <will.thompson@collabora.co.uk>2009-11-27 12:26:50 +0000
commit0b0506542442aa801fdf21afa5f880cf6320bc05 (patch)
tree34354bb0392286b5113f3dc3212180fff2c752d4
parent2ee360dce6b0ea69d0f4d1fdea4e1148291a13d0 (diff)
parent2059502c054f55e332aafe29c2476c5963033dc7 (diff)
Merge branch 'master' into plugins
-rw-r--r--NEWS17
-rwxr-xr-xautogen.sh13
-rw-r--r--configure.ac4
m---------lib/ext/wocky0
-rw-r--r--lib/gibber/gibber-fd-transport.c1
-rw-r--r--src/bytestream-factory.c11
-rw-r--r--src/bytestream-factory.h3
-rw-r--r--src/capabilities.c2
-rw-r--r--src/capabilities.h2
-rw-r--r--src/connection-manager.c4
-rw-r--r--src/connection-manager.h2
-rw-r--r--src/connection.c407
-rw-r--r--src/connection.h5
-rw-r--r--src/ft-channel.c7
-rw-r--r--src/ft-manager.c51
-rw-r--r--src/gabble.c1
-rw-r--r--src/im-channel.c137
-rw-r--r--src/im-channel.h15
-rw-r--r--src/im-factory.c8
-rw-r--r--src/muc-factory.c3
-rw-r--r--src/muc-factory.h2
-rw-r--r--src/register.c3
-rw-r--r--src/register.h2
-rw-r--r--src/tube-dbus.c8
-rw-r--r--src/tube-stream.c4
-rw-r--r--src/util.c46
-rw-r--r--src/write-mgr-file.c5
-rw-r--r--tests/test-base64.c1
-rw-r--r--tests/twisted/Makefile.am17
-rw-r--r--tests/twisted/caps/advertise-contact-caps.py19
-rw-r--r--tests/twisted/caps/tube-caps.py2
-rw-r--r--tests/twisted/caps_helper.py3
-rw-r--r--tests/twisted/file-transfer/test-caps-file-transfer.py24
-rw-r--r--tests/twisted/gabbletest.py6
-rw-r--r--tests/twisted/muc/test-ensure.py9
-rw-r--r--tests/twisted/muc/test-muc-ownership.py19
-rw-r--r--tests/twisted/olpc/gadget-invite.py7
-rw-r--r--tests/twisted/test-fallback-socks5-proxy.py170
-rw-r--r--tests/twisted/text/test-chat-state.py217
-rw-r--r--tests/twisted/tubes/accept-muc-stream-tube.py7
-rw-r--r--tests/twisted/tubes/close-muc-with-closed-tube.py7
-rw-r--r--tests/twisted/tubes/muctubeutil.py7
-rw-r--r--tests/twisted/tubes/test-get-available-tubes.py7
43 files changed, 621 insertions, 664 deletions
diff --git a/NEWS b/NEWS
index b4cf07f88..892ca2398 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,23 @@ Fixes:
fd.o #13157; previously we assumed they would only let moderators change
the subject. (smcv)
+• fd.o #21152: allow joining chatrooms on servers that don't respond to disco
+ queries, like talk.google.com (jonny)
+
+• fd.o #22456: append fallback conference server if the room name has no @,
+ rather than just failing (if using the Requests API) (jonny)
+
+• fd.o #22768 (partial): reassure talk.google.com that we can be invited to
+ chatrooms. This enables us to be invited to private MUCs by the Google
+ client, but doesn't currently support creating them. (jonny)
+
+• fd.o #24558: correctly flag the password parameter as secret (smcv)
+
+• Use TpDebugSender for debug output, instead of reimplementing it (jonny)
+
+• Allow sending chat state to contacts whose capabilities we don't know,
+ such as invisible Google Talk users (wjt)
+
telepathy-gabble 0.9.2 (2009-10-27)
===================================
diff --git a/autogen.sh b/autogen.sh
index bdcbefec6..b6dff981b 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -3,13 +3,14 @@ set -e
if test -n "$AUTOMAKE"; then
: # don't override an explicit user request
-elif automake-1.9 --version >/dev/null 2>/dev/null && \
- aclocal-1.9 --version >/dev/null 2>/dev/null; then
- # If we have automake-1.9, use it. This helps to ensure that our build
- # system doesn't accidentally grow automake-1.10 dependencies.
- AUTOMAKE=automake-1.9
+elif automake-1.11 --version >/dev/null 2>/dev/null && \
+ aclocal-1.11 --version >/dev/null 2>/dev/null; then
+ # If we have automake-1.11, use it. This is the oldest version (=> least
+ # likely to introduce undeclared dependencies) that will give us
+ # --enable-silent-rules support.
+ AUTOMAKE=automake-1.11
export AUTOMAKE
- ACLOCAL=aclocal-1.9
+ ACLOCAL=aclocal-1.11
export ACLOCAL
fi
diff --git a/configure.ac b/configure.ac
index a35565f9c..a50c29034 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,7 @@ AC_ARG_ENABLE(coding-style-checks,
if test x$enable_debug = xyes; then
AC_DEFINE(ENABLE_DEBUG, [], [Enable debug code])
fi
+AM_CONDITIONAL([ENABLE_DEBUG], [test "x$enable_debug" = xyes])
if test x$enable_handle_leak_debug = xyes; then
AC_DEFINE(ENABLE_HANDLE_LEAK_DEBUG, [], [Enable handle leak debug code])
@@ -221,6 +222,9 @@ fi
if test x$enable_silent_rules != xno; then
confcmd="$confcmd --enable-silent-rules"
fi
+if test x$werror == xno; then
+ confcmd="$confcmd --disable-Werror"
+fi
confcmd="$confcmd --with-tls=$with_wocky_tls"
cd "$ac_top_build_prefix"lib/ext/wocky && echo "running $confcmd" &&
$confcmd ||
diff --git a/lib/ext/wocky b/lib/ext/wocky
-Subproject d02987e47e4a4a9e967365a06799449c85f43ec
+Subproject 52068812840405391668f201d81bc1103c084ce
diff --git a/lib/gibber/gibber-fd-transport.c b/lib/gibber/gibber-fd-transport.c
index 4837c11c8..710809f3a 100644
--- a/lib/gibber/gibber-fd-transport.c
+++ b/lib/gibber/gibber-fd-transport.c
@@ -288,6 +288,7 @@ _channel_io_in (GIOChannel *source, GIOCondition condition, gpointer data)
break;
case GIBBER_FD_IO_RESULT_ERROR:
gibber_transport_emit_error (GIBBER_TRANSPORT(self), error);
+ /* Deliberately falling through */
case GIBBER_FD_IO_RESULT_EOF:
DEBUG("Failed to read from the transport, closing..");
_do_disconnect (self);
diff --git a/src/bytestream-factory.c b/src/bytestream-factory.c
index 4336aa48c..728d2a414 100644
--- a/src/bytestream-factory.c
+++ b/src/bytestream-factory.c
@@ -297,8 +297,6 @@ disco_item_found_cb (GabbleDisco *disco,
send_proxy_query (self, item->jid, FALSE);
}
-static void query_socks5_proxies (GabbleBytestreamFactory *self);
-
static gboolean
socks5_proxies_timeout_cb (gpointer data)
{
@@ -316,13 +314,14 @@ socks5_proxies_timeout_cb (gpointer data)
return FALSE;
}
- query_socks5_proxies (self);
+ gabble_bytestream_factory_query_socks5_proxies (self);
return FALSE;
}
-static void
-query_socks5_proxies (GabbleBytestreamFactory *self)
+/* ask to the factory to try to find more proxies if needed */
+void
+gabble_bytestream_factory_query_socks5_proxies (GabbleBytestreamFactory *self)
{
GabbleBytestreamFactoryPrivate *priv = GABBLE_BYTESTREAM_FACTORY_GET_PRIVATE (
self);
@@ -422,8 +421,6 @@ conn_status_changed_cb (GabbleConnection *conn,
priv->socks5_potential_proxies = randomize_g_slist (
priv->socks5_potential_proxies);
- query_socks5_proxies (self);
-
g_strfreev (jids);
}
}
diff --git a/src/bytestream-factory.h b/src/bytestream-factory.h
index dcc58fc53..ff5523de6 100644
--- a/src/bytestream-factory.h
+++ b/src/bytestream-factory.h
@@ -109,6 +109,9 @@ gchar *gabble_bytestream_factory_generate_stream_id (void);
GSList *gabble_bytestream_factory_get_socks5_proxies (
GabbleBytestreamFactory *self);
+void gabble_bytestream_factory_query_socks5_proxies (
+ GabbleBytestreamFactory *self);
+
G_END_DECLS
#endif /* #ifndef __BYTESTREAM_FACTORY_H__ */
diff --git a/src/capabilities.c b/src/capabilities.c
index 4b9f44a34..0a241cf40 100644
--- a/src/capabilities.c
+++ b/src/capabilities.c
@@ -63,7 +63,7 @@ static const Feature self_advertised_features[] =
{ FEATURE_FIXED, NS_IBB },
{ FEATURE_FIXED, NS_TUBES },
{ FEATURE_FIXED, NS_BYTESTREAMS },
- { FEATURE_FIXED, NS_FILE_TRANSFER },
+ { FEATURE_OPTIONAL, NS_FILE_TRANSFER },
{ FEATURE_OPTIONAL, NS_GOOGLE_TRANSPORT_P2P },
{ FEATURE_OPTIONAL, NS_JINGLE_TRANSPORT_ICEUDP },
diff --git a/src/capabilities.h b/src/capabilities.h
index 6b4266803..9a4cbfd3e 100644
--- a/src/capabilities.h
+++ b/src/capabilities.h
@@ -112,6 +112,8 @@ const GabbleCapabilitySet *gabble_capabilities_get_olpc_notify (void);
const GabbleCapabilitySet *gabble_capabilities_get_bundle_voice_v1 (void);
const GabbleCapabilitySet *gabble_capabilities_get_bundle_video_v1 (void);
+#define BUNDLE_PMUC_V1 "pmuc-v1"
+
/*
* capabilities_fill_cache
*
diff --git a/src/connection-manager.c b/src/connection-manager.c
index 5753b2714..9e798d0cd 100644
--- a/src/connection-manager.c
+++ b/src/connection-manager.c
@@ -126,7 +126,9 @@ static TpCMParamSpec jabber_params[] = {
/* FIXME: validate the JID according to the RFC */
tp_cm_param_filter_string_nonempty, NULL },
{ "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
- TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, NULL,
+ TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER |
+ TP_CONN_MGR_PARAM_FLAG_SECRET,
+ NULL,
G_STRUCT_OFFSET(GabbleParams, password), NULL, NULL },
{ "server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL,
diff --git a/src/connection-manager.h b/src/connection-manager.h
index 4ef773511..89a5f50f4 100644
--- a/src/connection-manager.h
+++ b/src/connection-manager.h
@@ -36,8 +36,6 @@ struct _GabbleConnectionManagerClass {
struct _GabbleConnectionManager {
TpBaseConnectionManager parent;
-
- GabbleConnectionManagerPrivate *priv;
};
GType gabble_connection_manager_get_type (void);
diff --git a/src/connection.c b/src/connection.c
index d301d3dbf..1d91a4573 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -40,7 +40,6 @@
#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>
#include <telepathy-glib/svc-generic.h>
#include "extensions/extensions.h"
@@ -81,7 +80,6 @@ static guint disco_reply_timeout = 5;
#define DISCONNECT_TIMEOUT 5
-static void conn_service_iface_init (gpointer, gpointer);
static void capabilities_service_iface_init (gpointer, gpointer);
static void gabble_conn_contact_caps_iface_init (gpointer, gpointer);
static void conn_capabilities_fill_contact_attributes (GObject *obj,
@@ -92,8 +90,6 @@ static void conn_contact_capabilities_fill_contact_attributes (GObject *obj,
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,
@@ -665,7 +661,7 @@ _gabble_connection_create_handle_repos (TpBaseConnection *conn,
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);
+ conn);
repos[TP_HANDLE_TYPE_GROUP] =
tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_GROUP, NULL, NULL);
repos[TP_HANDLE_TYPE_LIST] =
@@ -1970,6 +1966,7 @@ _gabble_connection_signal_own_presence (GabbleConnection *self, GError **error)
gboolean ret;
gchar *caps_hash;
gboolean voice_v1, video_v1;
+ GString *ext = g_string_new ("");
if (presence->status == GABBLE_PRESENCE_HIDDEN)
{
@@ -1995,27 +1992,19 @@ _gabble_connection_signal_own_presence (GabbleConnection *self, GError **error)
/* XEP-0115 deprecates 'ext' feature bundles. But we still need
* BUNDLE_VOICE_V1 it for backward-compatibility with Gabble 0.2 */
+ g_string_append (ext, BUNDLE_PMUC_V1);
+
voice_v1 = gabble_presence_has_cap (presence, NS_GOOGLE_FEAT_VOICE);
video_v1 = gabble_presence_has_cap (presence, NS_GOOGLE_FEAT_VIDEO);
- if (voice_v1 || video_v1)
- {
- GString *ext = g_string_new ("");
-
- if (voice_v1)
- g_string_append (ext, BUNDLE_VOICE_V1);
+ if (voice_v1)
+ g_string_append (ext, " " BUNDLE_VOICE_V1);
- if (video_v1)
- {
- if (ext->len > 0)
- g_string_append_c (ext, ' ');
- g_string_append (ext, BUNDLE_VIDEO_V1);
- }
+ if (video_v1)
+ g_string_append (ext, " " BUNDLE_VIDEO_V1);
- lm_message_node_set_attribute (node, "ext", ext->str);
-
- g_string_free (ext, TRUE);
- }
+ lm_message_node_set_attribute (node, "ext", ext->str);
+ g_string_free (ext, TRUE);
ret = _gabble_connection_send (self, message, error);
@@ -2256,15 +2245,19 @@ connection_iq_disco_cb (LmMessageHandler *handler,
features = gabble_capabilities_get_bundle_video_v1 ();
}
- if (features == NULL)
+ if (features == NULL && tp_strdiff (suffix, BUNDLE_PMUC_V1))
{
_gabble_connection_send_iq_error (self, message,
XMPP_ERROR_ITEM_NOT_FOUND, NULL);
}
else
{
- gabble_capability_set_foreach (features, add_feature_node,
- result_query);
+ /* Send an empty reply for a pmuc-v1 disco, matching Google's behaviour. */
+ if (features != NULL)
+ {
+ gabble_capability_set_foreach (features, add_feature_node,
+ result_query);
+ }
NODE_DEBUG (result_iq, "sending disco response");
@@ -3074,8 +3067,8 @@ _gabble_connection_find_conference_server (GabbleConnection *conn)
}
-static gchar *
-_gabble_connection_get_canonical_room_name (GabbleConnection *conn,
+gchar *
+gabble_connection_get_canonical_room_name (GabbleConnection *conn,
const gchar *name)
{
const gchar *server;
@@ -3093,355 +3086,6 @@ _gabble_connection_get_canonical_room_name (GabbleConnection *conn,
return gabble_encode_jid (name, server, NULL);
}
-
-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);
- gboolean found = FALSE;
- TpHandle handle;
- NodeIter i;
-
- /* 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 (i = node_iter (query_result); i; i = node_iter_next (i))
- {
- LmMessageNode *lm_node = node_iter_data (i);
- 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;
-
- if (!gabble_decode_jid (batch->contexts[i].jid, &room, &service, NULL) ||
- room == NULL)
- {
- 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,
const GabbleCapabilitySet *ensured)
@@ -3477,19 +3121,6 @@ gabble_connection_send_presence (GabbleConnection *conn,
return result;
}
-/* 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)
{
diff --git a/src/connection.h b/src/connection.h
index e15c25d6c..383393ee1 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -55,10 +55,9 @@ G_BEGIN_DECLS
"proxy.downtempo.de",\
"proxy.im.flosoft.biz",\
"proxy.jabber.bluendo.com", "proxy.jabber.dk", "proxy.jabber.freenet.de",\
- "proxy.fsinf.at", "proxy.jabber.minus273.org",\
+ "proxy.jabber.minus273.org",\
"proxy.jabber.planetteamspeak.com", "proxy.jabber.tf-network.de",\
"proxy.jabjab.de", "proxy.jabster.pl",\
- "proxy.schokokeks.org",\
"proxy.ubuntu-jabber.de", "proxy.ubuntu-jabber.net",\
"proxy65.unstable.nl", "proxy.verdammung.org", "proxy.vke.ru",\
"proxy.vodka-pomme.net", "proxy.jabbernet.eu",\
@@ -253,6 +252,8 @@ void _gabble_connection_send_iq_error (GabbleConnection *conn,
LmMessage *message, GabbleXmppError error, const gchar *errmsg);
const char *_gabble_connection_find_conference_server (GabbleConnection *);
+gchar *gabble_connection_get_canonical_room_name (GabbleConnection *conn,
+ const gchar *jid);
gboolean _gabble_connection_signal_own_presence (GabbleConnection *,
GError **);
diff --git a/src/ft-channel.c b/src/ft-channel.c
index 516238433..2450a0b9d 100644
--- a/src/ft-channel.c
+++ b/src/ft-channel.c
@@ -494,6 +494,13 @@ gabble_file_transfer_channel_constructor (GType type,
tp_handle_inspect (contact_repo, self->priv->initiator),
self->priv->filename, self->priv->size);
+ if (self->priv->initiator == base_conn->self_handle)
+ {
+ /* Outgoing FT , we'll need SOCK5 proxies when we'll offer the file */
+ gabble_bytestream_factory_query_socks5_proxies (
+ self->priv->connection->bytestream_factory);
+ }
+
return obj;
}
diff --git a/src/ft-manager.c b/src/ft-manager.c
index 87338a35c..5ab700227 100644
--- a/src/ft-manager.c
+++ b/src/ft-manager.c
@@ -545,8 +545,6 @@ void gabble_ft_manager_handle_si_request (GabbleFtManager *self,
{
GTimeVal val;
- g_time_val_from_iso8601 (date_str, &val);
-
/* FIXME: this assume the timezone is always UTC */
if (g_time_val_from_iso8601 (date_str, &val))
date = val.tv_sec;
@@ -601,16 +599,13 @@ gabble_ft_manager_get_tmp_dir (GabbleFtManager *self)
}
static void
-add_file_transfer_channel_class (GPtrArray *arr,
- TpHandle handle)
+add_file_transfer_channel_class (GPtrArray *arr)
{
GValue monster = {0, };
GHashTable *fixed_properties;
GValue *channel_type_value;
GValue *target_handle_type_value;
- g_assert (handle != 0);
-
g_value_init (&monster, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS);
g_value_take_boxed (&monster,
dbus_g_type_specialized_construct (
@@ -641,19 +636,46 @@ add_file_transfer_channel_class (GPtrArray *arr,
}
static void
-gabble_ft_manager_get_contact_caps (GabbleCapsChannelManager *manager,
- TpHandle handle,
+gabble_ft_manager_get_contact_caps (
+ GabbleCapsChannelManager *manager G_GNUC_UNUSED,
+ TpHandle handle G_GNUC_UNUSED,
const GabbleCapabilitySet *caps,
GPtrArray *arr)
{
- GabbleFtManager *self = GABBLE_FT_MANAGER (manager);
+ if (gabble_capability_set_has (caps, NS_FILE_TRANSFER))
+ add_file_transfer_channel_class (arr);
+}
- g_assert (handle != 0);
+static void
+gabble_ft_manager_represent_client (
+ GabbleCapsChannelManager *manager G_GNUC_UNUSED,
+ const gchar *client_name,
+ const GPtrArray *filters,
+ const gchar * const *cap_tokens G_GNUC_UNUSED,
+ GabbleCapabilitySet *cap_set)
+{
+ guint i;
- /* We always support file transfer */
- if (handle == self->priv->connection->parent.self_handle ||
- gabble_capability_set_has (caps, NS_FILE_TRANSFER))
- add_file_transfer_channel_class (arr, handle);
+ for (i = 0; i < filters->len; i++)
+ {
+ GHashTable *channel_class = g_ptr_array_index (filters, i);
+
+ if (tp_strdiff (tp_asv_get_string (channel_class,
+ TP_IFACE_CHANNEL ".ChannelType"),
+ TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER))
+ continue;
+
+ if (tp_asv_get_uint32 (channel_class,
+ TP_IFACE_CHANNEL ".TargetHandleType", NULL)
+ != TP_HANDLE_TYPE_CONTACT)
+ continue;
+
+ DEBUG ("client %s supports file transfer", client_name);
+ gabble_capability_set_add (cap_set, NS_FILE_TRANSFER);
+ /* there's no point in looking at the subsequent filters if we've
+ * already added the FT capability */
+ break;
+ }
}
static void
@@ -663,4 +685,5 @@ caps_channel_manager_iface_init (gpointer g_iface,
GabbleCapsChannelManagerIface *iface = g_iface;
iface->get_contact_caps = gabble_ft_manager_get_contact_caps;
+ iface->represent_client = gabble_ft_manager_represent_client;
}
diff --git a/src/gabble.c b/src/gabble.c
index 992d12e08..18ce1ac51 100644
--- a/src/gabble.c
+++ b/src/gabble.c
@@ -144,6 +144,7 @@ gabble_main (int argc,
construct_cm, argc, argv);
#ifdef ENABLE_DEBUG
+ g_log_set_default_handler (g_log_default_handler, NULL);
g_object_unref (debug_sender);
#endif
diff --git a/src/im-channel.c b/src/im-channel.c
index 6db5e53f6..f4edc54cb 100644
--- a/src/im-channel.c
+++ b/src/im-channel.c
@@ -95,6 +95,12 @@ enum
/* private structure */
+typedef enum {
+ CHAT_STATES_UNKNOWN,
+ CHAT_STATES_SUPPORTED,
+ CHAT_STATES_NOT_SUPPORTED
+} ChatStateSupport;
+
struct _GabbleIMChannelPrivate
{
GabbleConnection *conn;
@@ -104,6 +110,7 @@ struct _GabbleIMChannelPrivate
gchar *peer_jid;
gboolean send_nick;
+ ChatStateSupport chat_states_supported;
/* FALSE unless at least one chat state notification has been sent; <gone/>
* will only be sent when the channel closes if this is TRUE. This prevents
@@ -172,6 +179,8 @@ gabble_im_channel_constructor (GType type, guint n_props,
else
priv->send_nick = TRUE;
+ priv->chat_states_supported = CHAT_STATES_UNKNOWN;
+
tp_message_mixin_init (obj, G_STRUCT_OFFSET (GabbleIMChannel, message_mixin),
conn);
@@ -394,20 +403,43 @@ gabble_im_channel_class_init (GabbleIMChannelClass *gabble_im_channel_class)
tp_message_mixin_init_dbus_properties (object_class);
}
+static gboolean
+chat_states_supported (GabbleIMChannel *self,
+ gboolean include_unknown)
+{
+ GabbleIMChannelPrivate *priv = self->priv;
+ GabblePresence *presence;
+
+ presence = gabble_presence_cache_get (priv->conn->presence_cache,
+ priv->handle);
+
+ if (presence != NULL && gabble_presence_has_cap (presence, NS_CHAT_STATES))
+ return TRUE;
+
+ switch (priv->chat_states_supported)
+ {
+ case CHAT_STATES_UNKNOWN:
+ return include_unknown;
+ case CHAT_STATES_SUPPORTED:
+ return TRUE;
+ case CHAT_STATES_NOT_SUPPORTED:
+ return FALSE;
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+ }
+}
+
static void
emit_closed_and_send_gone (GabbleIMChannel *self)
{
GabbleIMChannelPrivate *priv = self->priv;
- GabblePresence *presence;
if (priv->send_gone)
{
- presence = gabble_presence_cache_get (priv->conn->presence_cache,
- priv->handle);
-
- if (presence && gabble_presence_has_cap (presence, NS_CHAT_STATES))
+ if (chat_states_supported (self, FALSE))
gabble_message_util_send_chat_state (G_OBJECT (self), priv->conn,
- LM_MESSAGE_SUB_TYPE_NORMAL, TP_CHANNEL_CHAT_STATE_GONE,
+ LM_MESSAGE_SUB_TYPE_CHAT, TP_CHANNEL_CHAT_STATE_GONE,
priv->peer_jid, NULL);
priv->send_gone = FALSE;
@@ -487,16 +519,12 @@ _gabble_im_channel_send_message (GObject *object,
{
GabbleIMChannel *self = GABBLE_IM_CHANNEL (object);
GabbleIMChannelPrivate *priv;
- GabblePresence *presence;
gint state = -1;
g_assert (GABBLE_IS_IM_CHANNEL (self));
priv = self->priv;
- presence = gabble_presence_cache_get (priv->conn->presence_cache,
- priv->handle);
-
- if (presence && gabble_presence_has_cap (presence, NS_CHAT_STATES))
+ if (chat_states_supported (self, TRUE))
{
state = TP_CHANNEL_CHAT_STATE_ACTIVE;
priv->send_gone = TRUE;
@@ -512,7 +540,6 @@ _gabble_im_channel_send_message (GObject *object,
priv->send_nick = FALSE;
}
-
/**
* _gabble_im_channel_receive
*
@@ -526,7 +553,8 @@ _gabble_im_channel_receive (GabbleIMChannel *chan,
const gchar *id,
const char *text,
TpChannelTextSendError send_error,
- TpDeliveryStatus delivery_status)
+ TpDeliveryStatus delivery_status,
+ gint state)
{
GabbleIMChannelPrivate *priv;
TpBaseConnection *base_conn;
@@ -545,6 +573,19 @@ _gabble_im_channel_receive (GabbleIMChannel *chan,
g_free (priv->peer_jid);
priv->peer_jid = g_strdup (from);
}
+
+ if (state == -1)
+ {
+ priv->chat_states_supported = CHAT_STATES_NOT_SUPPORTED;
+ }
+ else
+ {
+ priv->chat_states_supported = CHAT_STATES_SUPPORTED;
+
+ tp_svc_channel_interface_chat_state_emit_chat_state_changed (
+ (TpSvcChannelInterfaceChatState *) chan,
+ priv->handle, (TpChannelChatState) state);
+ }
}
else
{
@@ -553,6 +594,8 @@ _gabble_im_channel_receive (GabbleIMChannel *chan,
if (slash != NULL)
*slash = '\0';
+
+ priv->chat_states_supported = CHAT_STATES_UNKNOWN;
}
msg = tp_message_new (base_conn, 2, 2);
@@ -629,7 +672,7 @@ _gabble_im_channel_receive (GabbleIMChannel *chan,
void
_gabble_im_channel_state_receive (GabbleIMChannel *chan,
- guint state)
+ TpChannelChatState state)
{
GabbleIMChannelPrivate *priv;
@@ -637,6 +680,8 @@ _gabble_im_channel_state_receive (GabbleIMChannel *chan,
g_assert (GABBLE_IS_IM_CHANNEL (chan));
priv = chan->priv;
+ priv->chat_states_supported = CHAT_STATES_SUPPORTED;
+
tp_svc_channel_interface_chat_state_emit_chat_state_changed (
(TpSvcChannelInterfaceChatState *) chan,
priv->handle, state);
@@ -790,53 +835,47 @@ gabble_im_channel_set_chat_state (TpSvcChannelInterfaceChatState *iface,
{
GabbleIMChannel *self = GABBLE_IM_CHANNEL (iface);
GabbleIMChannelPrivate *priv;
- GabblePresence *presence;
GError *error = NULL;
g_assert (GABBLE_IS_IM_CHANNEL (self));
priv = self->priv;
- presence = gabble_presence_cache_get (priv->conn->presence_cache,
- priv->handle);
-
- if (presence && gabble_presence_has_cap (presence, NS_CHAT_STATES))
+ if (state >= NUM_TP_CHANNEL_CHAT_STATES)
{
- if (state >= NUM_TP_CHANNEL_CHAT_STATES)
- {
- DEBUG ("invalid state %u", state);
-
- g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "invalid state: %u", state);
- }
-
- if (state == TP_CHANNEL_CHAT_STATE_GONE)
- {
- /* We cannot explicitly set the Gone state */
- DEBUG ("you may not explicitly set the Gone state");
-
- g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "you may not explicitly set the Gone state");
- }
- else if (gabble_message_util_send_chat_state (G_OBJECT (self), priv->conn,
- LM_MESSAGE_SUB_TYPE_NORMAL, state, priv->peer_jid, &error))
+ g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "invalid state: %u", state);
+ }
+ else if (state == TP_CHANNEL_CHAT_STATE_GONE)
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "you may not explicitly set the Gone state");
+ }
+ /* Only send anything to the peer if we actually know they support chat
+ * states.
+ */
+ else if (chat_states_supported (self, FALSE))
+ {
+ if (gabble_message_util_send_chat_state (G_OBJECT (self), priv->conn,
+ LM_MESSAGE_SUB_TYPE_CHAT, state, priv->peer_jid, &error))
{
priv->send_gone = TRUE;
- }
-
- if (error != NULL)
- {
- dbus_g_method_return_error (context, error);
- g_error_free (error);
- return;
+ /* Send the ChatStateChanged signal for the local user */
+ tp_svc_channel_interface_chat_state_emit_chat_state_changed (iface,
+ priv->conn->parent.self_handle, state);
}
-
- /* Send the ChatStateChanged signal for the local user */
- tp_svc_channel_interface_chat_state_emit_chat_state_changed (iface,
- priv->conn->parent.self_handle, state);
}
- tp_svc_channel_interface_chat_state_return_from_set_chat_state (context);
+ if (error != NULL)
+ {
+ DEBUG ("%s", error->message);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ }
+ else
+ {
+ tp_svc_channel_interface_chat_state_return_from_set_chat_state (context);
+ }
}
static void
diff --git a/src/im-channel.h b/src/im-channel.h
index 7d7d96a14..2a2a2dc1e 100644
--- a/src/im-channel.h
+++ b/src/im-channel.h
@@ -66,10 +66,17 @@ GType gabble_im_channel_get_type (void);
GabbleIMChannelClass))
void _gabble_im_channel_receive (GabbleIMChannel *chan,
- TpChannelTextMessageType type, TpHandle sender, const char *from,
- time_t timestamp, const char *id, const char *text,
- TpChannelTextSendError send_error, TpDeliveryStatus delivery_status);
-void _gabble_im_channel_state_receive (GabbleIMChannel *chan, guint state);
+ TpChannelTextMessageType type,
+ TpHandle sender,
+ const char *from,
+ time_t timestamp,
+ const char *id,
+ const char *text,
+ TpChannelTextSendError send_error,
+ TpDeliveryStatus delivery_status,
+ gint state);
+void _gabble_im_channel_state_receive (GabbleIMChannel *chan,
+ TpChannelChatState state);
G_END_DECLS
diff --git a/src/im-factory.c b/src/im-factory.c
index 8f5cccca0..d2b12a112 100644
--- a/src/im-factory.c
+++ b/src/im-factory.c
@@ -283,12 +283,12 @@ im_factory_message_cb (LmMessageHandler *handler,
from, handle, msgtype, body);
}
- if (state != -1 && send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
- _gabble_im_channel_state_receive (chan, state);
-
if (body != NULL)
_gabble_im_channel_receive (chan, msgtype, handle, from, stamp, id, body,
- send_error, delivery_status);
+ send_error, delivery_status, state);
+ else if (state != -1 && send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
+ _gabble_im_channel_state_receive (chan, (TpChannelChatState) state);
+
return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}
diff --git a/src/muc-factory.c b/src/muc-factory.c
index 2caba4787..fd3b10d35 100644
--- a/src/muc-factory.c
+++ b/src/muc-factory.c
@@ -62,7 +62,6 @@ enum
LAST_PROPERTY
};
-typedef struct _GabbleMucFactoryPrivate GabbleMucFactoryPrivate;
struct _GabbleMucFactoryPrivate
{
GabbleConnection *conn;
@@ -105,6 +104,8 @@ gabble_muc_factory_init (GabbleMucFactory *fac)
{
GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ fac->priv = priv;
+
priv->text_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_object_unref);
priv->tubes_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal,
diff --git a/src/muc-factory.h b/src/muc-factory.h
index c17935ef4..8a5485bff 100644
--- a/src/muc-factory.h
+++ b/src/muc-factory.h
@@ -29,6 +29,7 @@ G_BEGIN_DECLS
typedef struct _GabbleMucFactory GabbleMucFactory;
typedef struct _GabbleMucFactoryClass GabbleMucFactoryClass;
+typedef struct _GabbleMucFactoryPrivate GabbleMucFactoryPrivate;
struct _GabbleMucFactoryClass {
GObjectClass parent_class;
@@ -36,6 +37,7 @@ struct _GabbleMucFactoryClass {
struct _GabbleMucFactory {
GObject parent;
+ GabbleMucFactoryPrivate *priv;
};
GType gabble_muc_factory_get_type (void);
diff --git a/src/register.c b/src/register.c
index aff4cb65b..9a9e0ff1b 100644
--- a/src/register.c
+++ b/src/register.c
@@ -60,7 +60,6 @@ enum
G_DEFINE_TYPE(GabbleRegister, gabble_register, G_TYPE_OBJECT);
/* private structure */
-typedef struct _GabbleRegisterPrivate GabbleRegisterPrivate;
struct _GabbleRegisterPrivate
{
GabbleConnection *conn;
@@ -75,6 +74,8 @@ struct _GabbleRegisterPrivate
static void
gabble_register_init (GabbleRegister *obj)
{
+ obj->priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_REGISTER,
+ GabbleRegisterPrivate);
}
static void gabble_register_set_property (GObject *object, guint property_id,
diff --git a/src/register.h b/src/register.h
index e38e52044..f4ea593d4 100644
--- a/src/register.h
+++ b/src/register.h
@@ -33,6 +33,7 @@ G_BEGIN_DECLS
typedef struct _GabbleRegister GabbleRegister;
typedef struct _GabbleRegisterClass GabbleRegisterClass;
+typedef struct _GabbleRegisterPrivate GabbleRegisterPrivate;
GType gabble_register_get_type (void);
@@ -57,6 +58,7 @@ struct _GabbleRegisterClass {
struct _GabbleRegister {
GObject parent;
+ GabbleRegisterPrivate *priv;
};
GabbleRegister *gabble_register_new (GabbleConnection *conn);
diff --git a/src/tube-dbus.c b/src/tube-dbus.c
index 9db9f1968..bb4961840 100644
--- a/src/tube-dbus.c
+++ b/src/tube-dbus.c
@@ -955,6 +955,14 @@ gabble_tube_dbus_constructor (GType type,
priv->reassembly_bytes_needed = 0;
g_assert (priv->muc == NULL);
+
+ if (priv->requested)
+ {
+ /* We created this outgoing 1-1 D-Bus tube and so will need SOCKS5
+ * proxies when we'll offer it. */
+ gabble_bytestream_factory_query_socks5_proxies (
+ priv->conn->bytestream_factory);
+ }
}
/* Tube needs to be offered if we initiated AND requested it. Being
diff --git a/src/tube-stream.c b/src/tube-stream.c
index 9aedd5f98..de9bf4fb0 100644
--- a/src/tube-stream.c
+++ b/src/tube-stream.c
@@ -1516,6 +1516,10 @@ gabble_tube_stream_constructor (GType type,
else
{
priv->state = TP_TUBE_CHANNEL_STATE_LOCAL_PENDING;
+
+ /* We'll need SOCKS5 proxies if the tube is accepted */
+ gabble_bytestream_factory_query_socks5_proxies (
+ priv->conn->bytestream_factory);
}
if (priv->handle_type == TP_HANDLE_TYPE_CONTACT)
diff --git a/src/util.c b/src/util.c
index d22844d42..351bacda1 100644
--- a/src/util.c
+++ b/src/util.c
@@ -415,35 +415,47 @@ gabble_normalize_room (TpHandleRepoIface *repo,
gpointer context,
GError **error)
{
- char *at = strchr (jid, '@');
- char *slash = strchr (jid, '/');
+ GabbleConnection *conn;
+ gchar *qualified_name, *resource;
- /* there'd better be an @ somewhere after the first character */
- if (at == NULL)
+ /* Only look up the canonical room name if we got a GabbleConnection.
+ * This should only happen in the test-handles test. */
+ if (context != NULL)
{
- INVALID_HANDLE (error,
- "invalid room JID %s: does not contain '@'", jid);
- return NULL;
+ conn = GABBLE_CONNECTION (context);
+ qualified_name = gabble_connection_get_canonical_room_name (conn, jid);
+
+ if (qualified_name == NULL)
+ {
+ INVALID_HANDLE (error,
+ "requested room handle %s does not specify a server, but we "
+ "have not discovered any local conference servers and no "
+ "fallback was provided", jid);
+ return NULL;
+ }
}
- if (at == jid)
+ else
{
- INVALID_HANDLE (error,
- "invalid room JID %s: room name before '@' may not be empty", jid);
+ qualified_name = g_strdup (jid);
+ }
+
+ if (!gabble_decode_jid (qualified_name, NULL, NULL, &resource))
+ {
+ INVALID_HANDLE (error, "room JID %s is invalid", qualified_name);
return NULL;
}
- /* room names can't contain the nick part */
- if (slash != NULL)
+ if (resource != NULL)
{
INVALID_HANDLE (error,
- "invalid room JID %s: contains nickname part after '/' too", jid);
+ "invalid room JID %s: contains nickname part after '/' too",
+ qualified_name);
+ g_free (qualified_name);
+ g_free (resource);
return NULL;
}
- /* the room and service parts are both case-insensitive, so lowercase
- * them both; gabble_decode_jid is overkill here
- */
- return g_utf8_strdown (jid, -1);
+ return qualified_name;
}
gchar *
diff --git a/src/write-mgr-file.c b/src/write-mgr-file.c
index a5c9cd905..8a26a50c9 100644
--- a/src/write-mgr-file.c
+++ b/src/write-mgr-file.c
@@ -48,9 +48,10 @@ mgr_file_contents (const char *busname,
for (row = protocol->parameters; row->name; row++)
{
gchar *param_name = g_strdup_printf ("param-%s", row->name);
- gchar *param_value = g_strdup_printf ("%s%s%s", row->dtype,
+ gchar *param_value = g_strdup_printf ("%s%s%s%s", row->dtype,
(row->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED ? " required" : ""),
- (row->flags & TP_CONN_MGR_PARAM_FLAG_REGISTER ? " register" : ""));
+ (row->flags & TP_CONN_MGR_PARAM_FLAG_REGISTER ? " register" : ""),
+ (row->flags & TP_CONN_MGR_PARAM_FLAG_SECRET ? " secret" : ""));
g_key_file_set_string (f, section_name, param_name, param_value);
g_free (param_value);
g_free (param_name);
diff --git a/tests/test-base64.c b/tests/test-base64.c
index 53c62c55e..49ebbf37d 100644
--- a/tests/test-base64.c
+++ b/tests/test-base64.c
@@ -86,6 +86,7 @@ main (void)
/* test string with embedded NULL */
tmp1 = base64_decode ("Zm9vAGJhcg==");
tmp2 = g_string_new_len ("foo\0bar", 7);
+ g_assert (tmp1);
g_assert (g_string_equal (tmp1, tmp2));
g_string_free (tmp1, TRUE);
g_string_free (tmp2, TRUE);
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index e6a0f6bcb..0fcf98c5a 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -184,20 +184,23 @@ check-twisted:
exit 1;\
fi
+if ENABLE_DEBUG
+DEBUGGING_PYBOOL = True
+else
+DEBUGGING_PYBOOL = False
+endif
+
config.py: Makefile
- $(AM_V_GEN)echo "PACKAGE_STRING = \"$(PACKAGE_STRING)\"" > config.py
- @if test -n ''$(ENABLE_DEBUG); then \
- echo "DEBUGGING = True" >> config.py; \
- else \
- echo "DEBUGGING = False" >> config.py; \
- fi
+ $(AM_V_GEN) { \
+ echo "PACKAGE_STRING = \"$(PACKAGE_STRING)\""; \
+ echo "DEBUGGING = $(DEBUGGING_PYBOOL)"; \
+ } > $@
BUILT_SOURCES = config.py
EXTRA_DIST = \
$(TWISTED_TESTS) \
bytestream.py \
- config.py \
constants.py \
gabbletest.py \
httptest.py \
diff --git a/tests/twisted/caps/advertise-contact-caps.py b/tests/twisted/caps/advertise-contact-caps.py
index 822d050b2..bb91c5891 100644
--- a/tests/twisted/caps/advertise-contact-caps.py
+++ b/tests/twisted/caps/advertise-contact-caps.py
@@ -188,5 +188,24 @@ def run_test(q, bus, conn, stream):
ns.JINGLE_015, ns.JINGLE_RTP_VIDEO,
ns.JINGLE_RTP, ns.JINGLE_015_VIDEO])
+ # Remove KCall to simplify subsequent checks
+ conn.ContactCapabilities.UpdateCapabilities([
+ (cs.CLIENT + '.KCall', [], []),
+ ])
+ (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ False)
+ check_caps(namespaces, [])
+
+ # Support file transfer
+ conn.ContactCapabilities.UpdateCapabilities([
+ (cs.CLIENT + '.FileReceiver', [{
+ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ }], []),
+ ])
+ (disco_response, namespaces, _) = receive_presence_and_ask_caps(q, stream,
+ False)
+ check_caps(namespaces, [ns.FILE_TRANSFER])
+
if __name__ == '__main__':
exec_test(run_test)
diff --git a/tests/twisted/caps/tube-caps.py b/tests/twisted/caps/tube-caps.py
index 41913a1da..8ef66d1f3 100644
--- a/tests/twisted/caps/tube-caps.py
+++ b/tests/twisted/caps/tube-caps.py
@@ -244,7 +244,7 @@ def test_tube_caps_to_contact(q, bus, conn, stream):
[(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)]})
+ ]})
daap_caps = dbus.Dictionary({self_handle:
[(text_fixed_properties, text_allowed_properties),
(stream_tube_fixed_properties, stream_tube_allowed_properties),
diff --git a/tests/twisted/caps_helper.py b/tests/twisted/caps_helper.py
index 3708fea08..0626e59b2 100644
--- a/tests/twisted/caps_helper.py
+++ b/tests/twisted/caps_helper.py
@@ -43,9 +43,10 @@ JINGLE_CAPS = [
VARIABLE_CAPS = (
JINGLE_CAPS +
[
+ ns.FILE_TRANSFER,
+
# FIXME: currently we always advertise these, but in future we should
# only advertise them if >= 1 client supports them:
- # ns.FILE_TRANSFER,
# ns.TUBES,
# there is an unlimited set of these; only the ones actually relevant to
diff --git a/tests/twisted/file-transfer/test-caps-file-transfer.py b/tests/twisted/file-transfer/test-caps-file-transfer.py
index 1197ee81a..f58a23890 100644
--- a/tests/twisted/file-transfer/test-caps-file-transfer.py
+++ b/tests/twisted/file-transfer/test-caps-file-transfer.py
@@ -98,27 +98,6 @@ def test_ft_caps_from_contact(q, bus, conn, stream, contact, contact_handle, cli
assert caps_via_contacts_iface == caps[contact_handle], \
caps_via_contacts_iface
-
-def test_ft_caps_to_contact(q, bus, conn, stream):
- basic_caps = dbus.Dictionary({1:
- [(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)]})
-
- conn_caps_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACT_CAPS)
- conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS)
-
- # Check our own caps
- caps = conn_caps_iface.GetContactCapabilities([1])
- assertEquals(basic_caps, caps)
-
- # check the Contacts interface give the same caps
- caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes(
- [1], [cs.CONN_IFACE_CONTACT_CAPS], False) \
- [1][cs.CONN_IFACE_CONTACT_CAPS + '/caps']
- assertEquals(caps[1], caps_via_contacts_iface)
-
def test(q, bus, conn, stream):
conn.Connect()
q.expect('dbus-signal', signal='StatusChanged',
@@ -129,7 +108,8 @@ def test(q, bus, conn, stream):
test_ft_caps_from_contact(q, bus, conn, stream, 'bilbo1@foo.com/Foo',
2L, client)
- test_ft_caps_to_contact(q, bus, conn, stream)
+ # our own capabilities, formerly tested here, are now in
+ # tests/twisted/caps/advertise-contact-capabilities.py
if __name__ == '__main__':
exec_test(test)
diff --git a/tests/twisted/gabbletest.py b/tests/twisted/gabbletest.py
index 47fb915b8..1ec927616 100644
--- a/tests/twisted/gabbletest.py
+++ b/tests/twisted/gabbletest.py
@@ -61,12 +61,6 @@ def send_error_reply(stream, iq, error_stanza=None):
def request_muc_handle(q, conn, stream, muc_jid):
servicetest.call_async(q, conn, 'RequestHandles', 2, [muc_jid])
- host = muc_jid.split('@')[1]
- event = q.expect('stream-iq', to=host, query_ns=ns.DISCO_INFO)
- result = make_result_iq(stream, event.stanza)
- feature = result.firstChildElement().addElement('feature')
- feature['var'] = ns.MUC
- stream.send(result)
event = q.expect('dbus-return', method='RequestHandles')
return event.value[0][0]
diff --git a/tests/twisted/muc/test-ensure.py b/tests/twisted/muc/test-ensure.py
index fb3064d73..d2d907495 100644
--- a/tests/twisted/muc/test-ensure.py
+++ b/tests/twisted/muc/test-ensure.py
@@ -18,15 +18,6 @@ def test(q, bus, conn, stream):
jids = ['chat@conf.localhost', 'chien@conf.localhost']
call_async(q, conn, 'RequestHandles', 2, jids)
- # Gabble is stupid and discos the alleged conf server twice.
- for i in [0,1]:
- event = q.expect('stream-iq', to='conf.localhost',
- query_ns='http://jabber.org/protocol/disco#info')
- result = make_result_iq(stream, event.stanza)
- feature = result.firstChildElement().addElement('feature')
- feature['var'] = 'http://jabber.org/protocol/muc'
- stream.send(result)
-
event = q.expect('dbus-return', method='RequestHandles')
room_handles = event.value[0]
diff --git a/tests/twisted/muc/test-muc-ownership.py b/tests/twisted/muc/test-muc-ownership.py
index 362a16f4c..e8e0bd946 100644
--- a/tests/twisted/muc/test-muc-ownership.py
+++ b/tests/twisted/muc/test-muc-ownership.py
@@ -23,29 +23,24 @@ def test(q, bus, conn, stream):
# query
call_async(q, conn, 'RequestHandles', 2, ['chat@conf.localhost'])
- event = q.expect('stream-iq', to='conf.localhost',
- query_ns='http://jabber.org/protocol/disco#info')
- result = make_result_iq(stream, event.stanza)
- feature = result.firstChildElement().addElement('feature')
- feature['var'] = 'http://jabber.org/protocol/muc'
- stream.send(result)
-
event = q.expect('dbus-return', method='RequestHandles')
room_handle = event.value[0][0]
call_async(q, conn, 'RequestChannel', cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM,
room_handle, True)
- gfc, _, _ = q.expect_many(
- EventPattern('dbus-signal', signal='GroupFlagsChanged'),
+ gfc, _, _, _ = q.expect_many(
+ # Initial group flags
+ EventPattern('dbus-signal', signal='GroupFlagsChanged',
+ predicate=lambda e: e.args[0] != 0),
EventPattern('dbus-signal', signal='MembersChanged',
args=[u'', [], [], [], [2], 0, 0]),
+ # Removing CAN_ADD
+ EventPattern('dbus-signal', signal='GroupFlagsChanged',
+ args = [0, cs.GF_CAN_ADD], predicate=lambda e: e.args[0] == 0),
EventPattern('stream-presence', to='chat@conf.localhost/test'))
assert gfc.args[1] == 0
- event = q.expect('dbus-signal', signal='GroupFlagsChanged')
- assert event.args == [0, 1]
-
# Send presence for anonymous other member of room.
stream.send(make_muc_presence('owner', 'moderator', 'chat@conf.localhost', 'bob'))
diff --git a/tests/twisted/olpc/gadget-invite.py b/tests/twisted/olpc/gadget-invite.py
index 362490a59..47c45fbc1 100644
--- a/tests/twisted/olpc/gadget-invite.py
+++ b/tests/twisted/olpc/gadget-invite.py
@@ -17,13 +17,6 @@ import ns
def join_channel(name, q, conn, stream):
call_async(q, conn, 'RequestHandles', cs.HT_ROOM, [name])
- # announce conference service
- event = q.expect('stream-iq', to='conference.localhost', query_ns=ns.DISCO_INFO)
- reply = make_result_iq(stream, event.stanza)
- feature = reply.firstChildElement().addElement('feature')
- feature['var'] = ns.MUC
- stream.send(reply)
-
event = q.expect('dbus-return', method='RequestHandles')
handles = event.value[0]
diff --git a/tests/twisted/test-fallback-socks5-proxy.py b/tests/twisted/test-fallback-socks5-proxy.py
index 73a98f71e..c5266a4e4 100644
--- a/tests/twisted/test-fallback-socks5-proxy.py
+++ b/tests/twisted/test-fallback-socks5-proxy.py
@@ -1,6 +1,7 @@
import dbus
+import socket
from gabbletest import exec_test, elem, elem_iq, sync_stream, make_presence
-from servicetest import EventPattern
+from servicetest import EventPattern, call_async
from caps_helper import make_caps_disco_reply
from twisted.words.xish import xpath
@@ -9,32 +10,19 @@ import ns
import constants as cs
from bytestream import create_from_si_offer, BytestreamS5B
-def test(q, bus, conn, stream):
- conn.Connect()
-
- _, e1, e2 = q.expect_many(
- EventPattern('dbus-signal', signal='StatusChanged',
- args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]),
- EventPattern('stream-iq', to='fallback1-proxy.localhost', iq_type='get', query_ns=ns.BYTESTREAMS),
- EventPattern('stream-iq', to='fallback2-proxy.localhost', iq_type='get', query_ns=ns.BYTESTREAMS),
- )
+proxy_query_events = [
+ EventPattern('stream-iq', to='fallback1-proxy.localhost', iq_type='get', query_ns=ns.BYTESTREAMS),
+ EventPattern('stream-iq', to='fallback2-proxy.localhost', iq_type='get', query_ns=ns.BYTESTREAMS)]
- proxy_port = {'fallback1-proxy.localhost': '12345', 'fallback2-proxy.localhost': '6789'}
+proxy_port = {'fallback1-proxy.localhost': '12345', 'fallback2-proxy.localhost': '6789'}
- def send_socks5_reply(iq):
- jid = iq['to']
- port = proxy_port[jid]
+def connect_and_announce_alice(q, bus, conn, stream):
+ q.forbid_events(proxy_query_events)
- reply = elem_iq(stream, 'result', id=iq['id'], from_=jid)(
- elem(ns.BYTESTREAMS, 'query')(
- elem('streamhost', jid=jid, host='127.0.0.1', port=port)()))
- stream.send(reply)
-
- send_socks5_reply(e1.stanza)
- send_socks5_reply(e2.stanza)
+ conn.Connect()
- # Offer a private D-Bus tube just to check if the proxy is present in the
- # SOCKS5 offer
+ q.expect('dbus-signal', signal='StatusChanged',
+ args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
# Send Alice's presence
caps = { 'ext': '', 'ver': '0.0.0',
@@ -45,14 +33,52 @@ def test(q, bus, conn, stream):
disco_event = q.expect('stream-iq', to='alice@localhost/Test',
query_ns=ns.DISCO_INFO)
- stream.send(make_caps_disco_reply(stream, disco_event.stanza, [ns.TUBES]))
+ stream.send(make_caps_disco_reply(stream, disco_event.stanza, [ns.TUBES, ns.FILE_TRANSFER]))
sync_stream(q, stream)
- path, props = conn.Requests.CreateChannel({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE,
+ q.unforbid_events(proxy_query_events)
+
+def send_socks5_reply(stream, iq):
+ jid = iq['to']
+ port = proxy_port[jid]
+
+ reply = elem_iq(stream, 'result', id=iq['id'], from_=jid)(
+ elem(ns.BYTESTREAMS, 'query')(
+ elem('streamhost', jid=jid, host='127.0.0.1', port=port)()))
+
+ stream.send(reply)
+
+def check_socks5_stanza(stanza):
+ tmp = proxy_port.copy()
+ nodes = xpath.queryForNodes('/iq/query/streamhost', stanza)
+ for node in nodes:
+ if node['jid'] in tmp:
+ assert node['host'] == '127.0.0.1'
+ assert node['port'] == tmp.pop(node['jid'])
+ assert tmp == {}
+
+def offer_dbus_tube(q, bus, conn, stream):
+ connect_and_announce_alice(q, bus, conn, stream)
+
+ # Offer a private D-Bus tube just to check if the proxy is present in the
+ # SOCKS5 offer
+
+ call_async(q, conn.Requests, 'CreateChannel', {
+ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_DBUS_TUBE,
cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
cs.TARGET_ID: 'alice@localhost',
cs.DBUS_TUBE_SERVICE_NAME: 'com.example.TestCase'})
+ # Proxy queries are send when creating the channel
+ return_event, e1, e2 = q.expect_many(
+ EventPattern('dbus-return', method='CreateChannel'),
+ proxy_query_events[0], proxy_query_events[1])
+
+ send_socks5_reply(stream, e1.stanza)
+ send_socks5_reply(stream, e2.stanza)
+
+ path, props = return_event.value
+
tube_chan = bus.get_object(conn.bus_name, path)
dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE)
@@ -69,15 +95,91 @@ def test(q, bus, conn, stream):
stream.send(result)
e = q.expect('stream-iq', to='alice@localhost/Test')
+ check_socks5_stanza(e.stanza)
- found = False
- nodes = xpath.queryForNodes('/iq/query/streamhost', e.stanza)
- for node in nodes:
- if node['jid'] in proxy_port:
- assert node['host'] == '127.0.0.1'
- assert node['port'] == proxy_port.pop(node['jid'])
- assert proxy_port == {}
+def accept_stream_tube(q, bus, conn, stream):
+ connect_and_announce_alice(q, bus, conn, stream)
+
+ # Accept a stream tube, we'll need SOCKS5 proxies each time we'll connect
+ # on the tube socket
+
+ # Alice offers us a stream tube
+ message = elem('message', to='test@localhost/Resource', from_='alice@localhost/Test')(
+ elem(ns.TUBES, 'tube', type='stream', service='http', id='10'))
+ stream.send(message)
+
+ # we are interested in the 'NewChannels' announcing the tube channel
+ def new_chan_predicate(e):
+ path, props = e.args[0][0]
+ return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE
+
+ # Proxy queries are send when receiving an incoming stream tube
+ new_chan, e1, e2 = q.expect_many(
+ EventPattern('dbus-signal', signal='NewChannels', predicate=new_chan_predicate),
+ proxy_query_events[0], proxy_query_events[1])
+
+ send_socks5_reply(stream, e1.stanza)
+ send_socks5_reply(stream, e2.stanza)
+
+ path, props = new_chan.args[0][0]
+ assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE
+
+ tube_chan = bus.get_object(conn.bus_name, path)
+ tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE)
+
+ # connect to the socket so a SOCKS5 bytestream will be created
+ address = tube_iface.Accept(cs.SOCKET_ADDRESS_TYPE_IPV4,
+ cs.SOCKET_ACCESS_CONTROL_LOCALHOST, 0, byte_arrays=True)
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect(address)
+
+ e = q.expect('stream-iq', to='alice@localhost/Test')
+
+ bytestream, profile = create_from_si_offer(stream, q, BytestreamS5B, e.stanza,
+ 'test@localhost/Resource')
+
+ # Alice accepts the connection
+ result, si = bytestream.create_si_reply(e.stanza)
+ stream.send(result)
+
+ e = q.expect('stream-iq', to='alice@localhost/Test')
+ check_socks5_stanza(e.stanza)
+
+def send_file(q, bus, conn, stream):
+ connect_and_announce_alice(q, bus, conn, stream)
+
+ # Send a file; proxy queries are send when creating the FT channel
+
+ call_async(q, conn.Requests, 'CreateChannel', {
+ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_FILE_TRANSFER,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_ID: 'alice@localhost',
+ cs.FT_FILENAME: 'test.txt',
+ cs.FT_CONTENT_TYPE: 'text/plain',
+ cs.FT_SIZE: 10})
+
+ return_event, e1, e2 = q.expect_many(
+ EventPattern('dbus-return', method='CreateChannel'),
+ proxy_query_events[0], proxy_query_events[1])
+
+ send_socks5_reply(stream, e1.stanza)
+ send_socks5_reply(stream, e2.stanza)
+
+ e = q.expect('stream-iq', to='alice@localhost/Test')
+
+ bytestream, profile = create_from_si_offer(stream, q, BytestreamS5B, e.stanza,
+ 'test@localhost/Resource')
+
+ # Alice accepts the FT
+ result, si = bytestream.create_si_reply(e.stanza)
+ stream.send(result)
+
+ e = q.expect('stream-iq', to='alice@localhost/Test')
+ check_socks5_stanza(e.stanza)
if __name__ == '__main__':
- exec_test(test, params={'fallback-socks5-proxies':
- ['fallback1-proxy.localhost', 'fallback2-proxy.localhost']})
+ params = {'fallback-socks5-proxies': ['fallback1-proxy.localhost', 'fallback2-proxy.localhost']}
+ exec_test(offer_dbus_tube, params=params)
+ exec_test(accept_stream_tube, params=params)
+ exec_test(send_file, params=params)
diff --git a/tests/twisted/text/test-chat-state.py b/tests/twisted/text/test-chat-state.py
index d1acd6dd2..289f69226 100644
--- a/tests/twisted/text/test-chat-state.py
+++ b/tests/twisted/text/test-chat-state.py
@@ -1,4 +1,4 @@
-
+# coding=utf-8
"""
Test that chat state notifications are correctly sent and received on text
channels.
@@ -6,21 +6,34 @@ channels.
from twisted.words.xish import domish
-from servicetest import call_async, assertEquals, wrap_channel, EventPattern
+from servicetest import assertEquals, assertLength, wrap_channel, EventPattern
from gabbletest import exec_test, make_result_iq, sync_stream, make_presence
import constants as cs
import ns
-def check_state_notification(elem, name):
+def check_state_notification(elem, name, allow_body=False):
assertEquals('message', elem.name)
- assertEquals('normal', elem['type'])
+ assertEquals('chat', elem['type'])
children = list(elem.elements())
- assert len(children) == 1, elem.toXml()
- notification = children[0]
-
+ notification = [x for x in children if x.uri == ns.CHAT_STATES][0]
assert notification.name == name, notification.toXml()
- assert notification.uri == ns.CHAT_STATES, notification.toXml()
+
+ if not allow_body:
+ assert len(children) == 1, elem.toXml()
+
+def make_message(jid, body=None, state=None):
+ m = domish.Element((None, 'message'))
+ m['from'] = jid
+ m['type'] = 'chat'
+
+ if state is not None:
+ m.addElement((ns.CHAT_STATES, state))
+
+ if body is not None:
+ m.addElement('body', content=body)
+
+ return m
def test(q, bus, conn, stream):
conn.Connect()
@@ -30,6 +43,7 @@ def test(q, bus, conn, stream):
self_handle = conn.GetSelfHandle()
jid = 'foo@bar.com'
+ full_jid = 'foo@bar.com/Foo'
foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0]
path = conn.Requests.CreateChannel(
@@ -40,21 +54,21 @@ def test(q, bus, conn, stream):
chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
['ChatState', 'Destroyable'])
- presence = make_presence('foo@bar.com/Foo', status='hello',
+ presence = make_presence(full_jid, status='hello',
caps={
'node': 'http://telepathy.freedesktop.org/homeopathy',
'ver' : '0.1',
})
stream.send(presence)
- version_event = q.expect('stream-iq', to='foo@bar.com/Foo',
- query_ns='http://jabber.org/protocol/disco#info',
+ version_event = q.expect('stream-iq', to=full_jid,
+ query_ns=ns.DISCO_INFO,
query_node='http://telepathy.freedesktop.org/homeopathy#0.1')
result = make_result_iq(stream, version_event.stanza)
query = result.firstChildElement()
feature = query.addElement('feature')
- feature['var'] = 'http://jabber.org/protocol/chatstates'
+ feature['var'] = ns.CHAT_STATES
stream.send(result)
sync_stream(q, stream)
@@ -62,11 +76,7 @@ def test(q, bus, conn, stream):
# Receiving chat states:
# Composing...
- m = domish.Element((None, 'message'))
- m['from'] = 'foo@bar.com/Foo'
- m['type'] = 'chat'
- m.addElement((ns.CHAT_STATES, 'composing'))
- stream.send(m)
+ stream.send(make_message(full_jid, state='composing'))
changed = q.expect('dbus-signal', signal='ChatStateChanged')
handle, state = changed.args
@@ -74,13 +84,7 @@ def test(q, bus, conn, stream):
assertEquals(cs.CHAT_STATE_COMPOSING, state)
# Message!
-
- m = domish.Element((None, 'message'))
- m['from'] = 'foo@bar.com/Foo'
- m['type'] = 'chat'
- m.addElement((ns.CHAT_STATES, 'active'))
- m.addElement('body', content='hello')
- stream.send(m)
+ stream.send(make_message(full_jid, body='hello', state='active'))
changed = q.expect('dbus-signal', signal='ChatStateChanged')
handle, state = changed.args
@@ -90,19 +94,20 @@ def test(q, bus, conn, stream):
# Sending chat states:
# Composing...
- call_async(q, chan.ChatState, 'SetChatState', cs.CHAT_STATE_COMPOSING)
+ chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)
stream_message = q.expect('stream-message')
check_state_notification(stream_message.stanza, 'composing')
# XEP 0085:
# every content message SHOULD contain an <active/> notification.
- call_async(q, chan.Text, 'Send', 0, 'hi.')
+ chan.Text.Send(0, 'hi.')
stream_message = q.expect('stream-message')
elem = stream_message.stanza
- assert elem.name == 'message'
- assert elem['type'] == 'chat', elem['type']
+ assertEquals('chat', elem['type'])
+
+ check_state_notification(elem, 'active', allow_body=True)
def is_body(e):
if e.name == 'body':
@@ -110,16 +115,7 @@ def test(q, bus, conn, stream):
return True
return False
- def is_active(e):
- if e.uri == ns.CHAT_STATES:
- assert e.name == 'active', e.toXml()
- return True
- return False
-
- children = list(elem.elements())
-
- assert len(filter(is_body, children)) == 1, elem.toXml()
- assert len(filter(is_active, children)) == 1, elem.toXml()
+ assert len([x for x in elem.elements() if is_body(x)]) == 1, elem.toXml()
# Close the channel without acking the received message. The peer should
# get a <gone/> notification, and the channel should respawn.
@@ -157,6 +153,151 @@ def test(q, bus, conn, stream):
# notification, since we haven't sent any notifications on that channel.
chan.Close()
sync_stream(q, stream)
+ q.unforbid_events(es)
+
+ # XEP-0085 §5.1 defines how to negotiate support for chat states with a
+ # contact in the absence of capabilities. This is useful when talking to
+ # invisible contacts, for example.
+
+ # First, if we receive a message from a contact, containing an <active/>
+ # notification, they support chat states, so we should send them.
+
+ jid = 'i@example.com'
+ full_jid = jid + '/GTalk'
+
+ path = conn.Requests.CreateChannel(
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_ID: jid,
+ })[0]
+ chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
+ ['ChatState'])
+
+ stream.send(make_message(full_jid, body='i am invisible', state='active'))
+
+ changed = q.expect('dbus-signal', signal='ChatStateChanged')
+ assertEquals(cs.CHAT_STATE_ACTIVE, changed.args[1])
+
+ # We've seen them send a chat state notification, so we should send them
+ # notifications when the UI tells us to.
+ chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)
+ stream_message = q.expect('stream-message', to=full_jid)
+ check_state_notification(stream_message.stanza, 'composing')
+
+ chan.Text.Send(0, 'very convincing')
+ stream_message = q.expect('stream-message', to=full_jid)
+ check_state_notification(stream_message.stanza, 'active', allow_body=True)
+
+ # Now, test the case where we start the negotiation, and the contact
+ # turns out to support chat state notifications.
+
+ jid = 'c@example.com'
+ full_jid = jid + '/GTalk'
+ path = conn.Requests.CreateChannel(
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_ID: jid,
+ })[0]
+ chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
+ ['ChatState'])
+
+ # We shouldn't send any notifications until we actually send a message.
+ e = EventPattern('stream-message', to=jid)
+ q.forbid_events([e])
+ for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
+ cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]:
+ chan.ChatState.SetChatState(i)
+ sync_stream(q, stream)
+ q.unforbid_events([e])
+
+ # When we send a message, say we're active.
+ chan.Text.Send(0, 'is anyone there?')
+ stream_message = q.expect('stream-message', to=jid)
+ check_state_notification(stream_message.stanza, 'active', allow_body=True)
+
+ # We get a notification back from our contact.
+ stream.send(make_message(full_jid, state='composing'))
+
+ changed = q.expect('dbus-signal', signal='ChatStateChanged')
+ _, state = changed.args
+ assertEquals(cs.CHAT_STATE_COMPOSING, state)
+
+ # So now we know they support notification, so should send notifications.
+ chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING)
+
+ # This doesn't check whether we're sending to the bare jid, or the
+ # jid+resource. In fact, the notification is sent to the bare jid, because
+ # we only update which jid we send to when we actually receive a message,
+ # not when we receive a notification. wjt thinks this is less surprising
+ # than the alternative:
+ #
+ # • I'm talking to you on my N900, and signed in on my laptop;
+ # • I enter one character in a tab to you on my laptop, and then delete
+ # it;
+ # • Now your messages to me appear on my laptop (until I send you another
+ # one from my N900)!
+ stream_message = q.expect('stream-message')
+ check_state_notification(stream_message.stanza, 'composing')
+
+ # But! Now they start messaging us from a different client, which *doesn't*
+ # support notifications.
+ other_jid = jid + '/Library'
+ stream.send(make_message(other_jid, body='grr, library computers'))
+ q.expect('dbus-signal', signal='Received')
+
+ # Okay, we should stop sending typing notifications.
+ e = EventPattern('stream-message', to=other_jid)
+ q.forbid_events([e])
+ for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
+ cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]:
+ chan.ChatState.SetChatState(i)
+ sync_stream(q, stream)
+ q.unforbid_events([e])
+
+ # Now, test the case where we start the negotiation, and the contact
+ # does not support chat state notifications
+
+ jid = 'twitterbot@example.com'
+ full_jid = jid + '/Nonsense'
+ path = conn.Requests.CreateChannel(
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_ID: jid,
+ })[0]
+ chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text',
+ ['ChatState'])
+
+ # We shouldn't send any notifications until we actually send a message.
+ e = EventPattern('stream-message', to=jid)
+ q.forbid_events([e])
+ for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
+ cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]:
+ chan.ChatState.SetChatState(i)
+ sync_stream(q, stream)
+ q.unforbid_events([e])
+
+ # When we send a message, say we're active.
+ chan.Text.Send(0, '#n900 #maemo #zomg #woo #yay http://bit.ly/n900')
+ stream_message = q.expect('stream-message', to=jid)
+ check_state_notification(stream_message.stanza, 'active', allow_body=True)
+
+ # They reply without a chat state.
+ stream.send(make_message(full_jid, body="posted."))
+ q.expect('dbus-signal', signal='Received')
+
+ # Okay, we shouldn't send any more.
+ e = EventPattern('stream-message', to=other_jid)
+ q.forbid_events([e])
+ for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE,
+ cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]:
+ chan.ChatState.SetChatState(i)
+ sync_stream(q, stream)
+ q.unforbid_events([e])
+
+ chan.Text.Send(0, '@stephenfry simmer down')
+ message = q.expect('stream-message')
+ states = [x for x in message.stanza.elements() if x.uri == ns.CHAT_STATES]
+ assertLength(0, states)
if __name__ == '__main__':
exec_test(test)
diff --git a/tests/twisted/tubes/accept-muc-stream-tube.py b/tests/twisted/tubes/accept-muc-stream-tube.py
index 3a1990705..5f726c476 100644
--- a/tests/twisted/tubes/accept-muc-stream-tube.py
+++ b/tests/twisted/tubes/accept-muc-stream-tube.py
@@ -44,13 +44,6 @@ def test(q, bus, conn, stream, bytestream_cls,
call_async(q, conn, 'RequestHandles', 2,
['chat@conf.localhost'])
- event = q.expect('stream-iq', to='conf.localhost',
- query_ns='http://jabber.org/protocol/disco#info')
- result = make_result_iq(stream, event.stanza)
- feature = result.firstChildElement().addElement('feature')
- feature['var'] = 'http://jabber.org/protocol/muc'
- stream.send(result)
-
event = q.expect('dbus-return', method='RequestHandles')
handles = event.value[0]
room_handle = handles[0]
diff --git a/tests/twisted/tubes/close-muc-with-closed-tube.py b/tests/twisted/tubes/close-muc-with-closed-tube.py
index d0a8b11d5..042cb79fc 100644
--- a/tests/twisted/tubes/close-muc-with-closed-tube.py
+++ b/tests/twisted/tubes/close-muc-with-closed-tube.py
@@ -28,13 +28,6 @@ def test(q, bus, conn, stream):
call_async(q, conn, 'RequestHandles', cs.HT_ROOM, ['chat@conf.localhost'])
- event = q.expect('stream-iq', to='conf.localhost',
- query_ns='http://jabber.org/protocol/disco#info')
- result = make_result_iq(stream, event.stanza)
- feature = result.firstChildElement().addElement('feature')
- feature['var'] = 'http://jabber.org/protocol/muc'
- stream.send(result)
-
event = q.expect('dbus-return', method='RequestHandles')
handles = event.value[0]
room_handle = handles[0]
diff --git a/tests/twisted/tubes/muctubeutil.py b/tests/twisted/tubes/muctubeutil.py
index a298eb7ff..61fa41f88 100644
--- a/tests/twisted/tubes/muctubeutil.py
+++ b/tests/twisted/tubes/muctubeutil.py
@@ -18,13 +18,6 @@ def get_muc_tubes_channel(q, bus, conn, stream, muc_jid, anonymous=True):
call_async(q, conn, 'RequestHandles', cs.HT_ROOM, [muc_jid])
- event = q.expect('stream-iq', to=muc_server,
- query_ns='http://jabber.org/protocol/disco#info')
- result = make_result_iq(stream, event.stanza)
- feature = result.firstChildElement().addElement('feature')
- feature['var'] = 'http://jabber.org/protocol/muc'
- stream.send(result)
-
event = q.expect('dbus-return', method='RequestHandles')
handles = event.value[0]
room_handle = handles[0]
diff --git a/tests/twisted/tubes/test-get-available-tubes.py b/tests/twisted/tubes/test-get-available-tubes.py
index e22bb98ab..af13da4d7 100644
--- a/tests/twisted/tubes/test-get-available-tubes.py
+++ b/tests/twisted/tubes/test-get-available-tubes.py
@@ -30,13 +30,6 @@ def test(q, bus, conn, stream):
call_async(q, conn, 'RequestHandles', cs.HT_ROOM, ['chat@conf.localhost'])
- event = q.expect('stream-iq', to='conf.localhost',
- query_ns='http://jabber.org/protocol/disco#info')
- result = make_result_iq(stream, event.stanza)
- feature = result.firstChildElement().addElement('feature')
- feature['var'] = 'http://jabber.org/protocol/muc'
- stream.send(result)
-
event = q.expect('dbus-return', method='RequestHandles')
handles = event.value[0]