summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWill Thompson <will.thompson@collabora.co.uk>2012-01-26 13:09:47 +0000
committerWill Thompson <will.thompson@collabora.co.uk>2012-01-26 13:09:47 +0000
commite142bebe3e112d715af2b4784b24730eb9c310a0 (patch)
tree594393ed618bf82b4600f61c624de58ad3340ca1 /src
parenta2a603bdfb12bc9aa80726bf1f822cc22c76917e (diff)
parent035a6b0a6e8ce1787abf2583bb918436282026fe (diff)
Merge branch 'master' into BYE-BYE-LOUDMOUTH
Conflicts: src/connection.c src/connection.h src/ft-manager.c src/jingle-content.c src/jingle-factory.c src/jingle-session.c src/message-util.c src/muc-channel.c src/muc-channel.h
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am58
-rw-r--r--src/addressing-util.c521
-rw-r--r--src/addressing-util.h76
-rw-r--r--src/auth-manager.c7
-rw-r--r--src/bytestream-factory.c15
-rw-r--r--src/bytestream-ibb.c2
-rw-r--r--src/bytestream-muc.c6
-rw-r--r--src/bytestream-socks5.c4
-rw-r--r--src/call-muc-channel.c10
-rw-r--r--src/call-stream.c16
-rw-r--r--src/capabilities.c5
-rw-r--r--src/capabilities.h73
-rw-r--r--src/caps-channel-manager.c10
-rw-r--r--src/caps-channel-manager.h105
-rw-r--r--src/caps-hash.c30
-rw-r--r--src/conn-addressing.c169
-rw-r--r--src/conn-addressing.h34
-rw-r--r--src/conn-aliasing.c370
-rw-r--r--src/conn-aliasing.h3
-rw-r--r--src/conn-avatars.c8
-rw-r--r--src/conn-contact-info.c18
-rw-r--r--src/conn-mail-notif.c68
-rw-r--r--src/conn-olpc.c30
-rw-r--r--src/conn-olpc.h2
-rw-r--r--src/conn-power-saving.c13
-rw-r--r--src/conn-presence.c229
-rw-r--r--src/connection.c557
-rw-r--r--src/connection.h18
-rw-r--r--src/debug.c2
-rw-r--r--src/debug.h28
-rw-r--r--src/disco.c10
-rw-r--r--src/ft-channel.c186
-rw-r--r--src/ft-channel.h2
-rw-r--r--src/ft-manager.c282
-rw-r--r--src/gabble.c2
-rw-r--r--src/google-relay.c341
-rw-r--r--src/google-relay.h45
-rw-r--r--src/gtalk-file-collection.c6
-rw-r--r--src/im-channel.c257
-rw-r--r--src/im-channel.h12
-rw-r--r--src/im-factory.c294
-rw-r--r--src/jingle-content.c16
-rw-r--r--src/jingle-factory.c528
-rw-r--r--src/jingle-media-rtp.c6
-rw-r--r--src/jingle-session.c37
-rw-r--r--src/jingle-session.h3
-rw-r--r--src/jingle-transport-google.c2
-rw-r--r--src/jingle-transport-iface.c2
-rw-r--r--src/legacy-caps.h2
-rw-r--r--src/main.c7
-rw-r--r--src/media-channel-hold.c2
-rw-r--r--src/media-channel.c28
-rw-r--r--src/media-factory.c11
-rw-r--r--src/media-stream.c14
-rw-r--r--src/message-util.c86
-rw-r--r--src/message-util.h12
-rw-r--r--src/muc-channel.c1916
-rw-r--r--src/muc-channel.h30
-rw-r--r--src/muc-factory.c338
-rw-r--r--src/namespaces.h13
-rw-r--r--src/olpc-activity.c4
-rw-r--r--src/plugin-loader.c53
-rw-r--r--src/presence-cache.c173
-rw-r--r--src/presence-cache.h9
-rw-r--r--src/presence.c80
-rw-r--r--src/presence.h4
-rw-r--r--src/private-tubes-factory.c89
-rw-r--r--src/protocol.c84
-rw-r--r--src/room-config.c101
-rw-r--r--src/room-config.h61
-rw-r--r--src/roomlist-channel.c4
-rw-r--r--src/roomlist-manager.c6
-rw-r--r--src/roster.c127
-rw-r--r--src/search-channel.c14
-rw-r--r--src/search-manager.c6
-rw-r--r--src/server-tls-channel.c53
-rw-r--r--src/server-tls-manager.c111
-rw-r--r--src/tls-certificate.c2
-rw-r--r--src/tube-dbus.c24
-rw-r--r--src/tube-stream.c14
-rw-r--r--src/tubes-channel.c36
-rw-r--r--src/util.c33
-rw-r--r--src/util.h3
-rw-r--r--src/vcard-manager.c4
-rw-r--r--src/write-mgr-file.c41
85 files changed, 5396 insertions, 2717 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 66a873c11..b3d140e5e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,6 +13,8 @@ libexec_PROGRAMS=telepathy-gabble
noinst_PROGRAMS = write-mgr-file
libgabble_convenience_la_SOURCES = \
+ addressing-util.h \
+ addressing-util.c \
auth-manager.h \
auth-manager.c \
base64.h \
@@ -44,13 +46,15 @@ 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-addressing.h \
+ conn-addressing.c \
conn-aliasing.h \
conn-aliasing.c \
conn-avatars.h \
@@ -84,14 +88,10 @@ libgabble_convenience_la_SOURCES = \
disco.c \
error.c \
error.h \
- ft-channel.c \
- ft-channel.h \
- ft-manager.c \
- ft-manager.h \
gabble.c \
gabble.h \
- gtalk-file-collection.h \
- gtalk-file-collection.c \
+ google-relay.c \
+ google-relay.h \
im-channel.h \
im-channel.c \
im-factory.h \
@@ -150,6 +150,8 @@ libgabble_convenience_la_SOURCES = \
request-pipeline.c \
roster.h \
roster.c \
+ room-config.h \
+ room-config.c \
roomlist-channel.h \
roomlist-channel.c \
roomlist-manager.h \
@@ -183,8 +185,28 @@ libgabble_convenience_la_SOURCES = \
vcard-manager.h \
vcard-manager.c
+if ENABLE_FILE_TRANSFER
+libgabble_convenience_la_SOURCES += \
+ ft-channel.c \
+ ft-channel.h \
+ ft-manager.c \
+ ft-manager.h \
+ gtalk-file-collection.h \
+ gtalk-file-collection.c
+else
+EXTRA_DIST += \
+ ft-channel.c \
+ ft-channel.h \
+ ft-manager.c \
+ ft-manager.h \
+ gtalk-file-collection.h \
+ gtalk-file-collection.c
+endif
+
enumtype_sources = \
+ $(top_srcdir)/src/connection.h \
$(top_srcdir)/src/jingle-factory.h \
+ $(top_srcdir)/src/room-config.h \
$(top_srcdir)/src/presence.h
libgabble_convenience_la_LIBADD = \
@@ -218,14 +240,19 @@ telepathy_gabble_LDFLAGS = -export-dynamic
noinst_LTLIBRARIES = libgabble-convenience.la
AM_CFLAGS = $(ERROR_CFLAGS) -I$(top_srcdir) -I$(top_builddir) \
+ @TP_YELL_CFLAGS@ \
@DBUS_CFLAGS@ @GLIB_CFLAGS@ @WOCKY_CFLAGS@ \
- @HANDLE_LEAK_DEBUG_CFLAGS@ @TP_GLIB_CFLAGS@ \
+ @TP_GLIB_CFLAGS@ \
@SOUP_CFLAGS@ @NICE_CFLAGS@ @GMODULE_CFLAGS@ \
- @TP_YELL_CFLAGS@ \
-I $(top_srcdir)/lib -I $(top_builddir)/lib \
-DG_LOG_DOMAIN=\"gabble\" \
-DPLUGIN_DIR=\"$(libdir)/telepathy/gabble-0\"
+# following flag is requied to make getnameinfo work
+if WINDOWS
+ AM_CFLAGS += -D_WIN32_WINNT=0x0501
+endif
+
ALL_LIBS = @DBUS_LIBS@ @GLIB_LIBS@ @WOCKY_LIBS@ @TP_GLIB_LIBS@ \
@SOUP_LIBS@ @NICE_LIBS@ @GMODULE_LIBS@
@@ -271,3 +298,14 @@ gabble-enumtypes.c: $(enumtype_sources) Makefile.in
--vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
--vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \
$(enumtype_sources) > $@
+
+Android.mk: Makefile.am $(BUILT_SOURCES)
+ androgenizer -:PROJECT telepathy-gabble -:SHARED telepathy-gabble -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgabble_convenience_la_SOURCES) \
+ $(nodist_libgabble_convenience_la_SOURCES) main.c \
+ -:CFLAGS $(DEFS) $(CFLAGS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CFLAGS) -DBUILD_AS_ANDROID_SERVICE \
+ -:CPPFLAGS $(CPPFLAGS) $(AM_CPPFLAGS) $(telepathy_gabble_LDFLAGS) \
+ -:LDFLAGS $(telepathy_gabble_LDADD) $(libgabble_convenience_la_LIBADD) \
+ > $@
diff --git a/src/addressing-util.c b/src/addressing-util.c
new file mode 100644
index 000000000..1ea089854
--- /dev/null
+++ b/src/addressing-util.c
@@ -0,0 +1,521 @@
+/*
+ * addressing-util.c - Source for Gabble addressing utility functions
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "addressing-util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <wocky/wocky-utils.h>
+
+#include "connection.h"
+#include "util.h"
+
+static const gchar *addressable_vcard_fields[] = {"x-jabber", "x-facebook-id", NULL};
+static const gchar *addressable_uri_schemes[] = {"xmpp", NULL};
+
+
+const gchar * const *
+gabble_get_addressable_uri_schemes ()
+{
+ return addressable_uri_schemes;
+}
+
+const gchar * const *
+gabble_get_addressable_vcard_fields ()
+{
+ return addressable_vcard_fields;
+}
+
+gchar *
+gabble_normalize_contact_uri (const gchar *uri,
+ GError **error)
+{
+ gchar *scheme = NULL;
+ gchar *normalized_jid = NULL;
+ gchar *normalized_uri = NULL;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ normalized_jid = gabble_uri_to_jid (uri, error);
+ if (normalized_jid == NULL)
+ {
+ goto OUT;
+ }
+
+ scheme = g_uri_parse_scheme (uri);
+
+ normalized_uri = gabble_jid_to_uri (scheme, normalized_jid, error);
+
+OUT:
+ g_free (scheme);
+ g_free (normalized_jid);
+
+ return normalized_uri;
+}
+
+gchar *
+gabble_uri_to_jid (const gchar *uri,
+ GError **error)
+{
+ gchar *scheme;
+ gchar *normalized_jid = NULL;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ scheme = g_uri_parse_scheme (uri);
+
+ if (scheme == NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid URI", uri);
+ goto OUT;
+ }
+ else if (g_ascii_strcasecmp (scheme, "xmpp") == 0)
+ {
+ gchar *node = NULL;
+ gchar *domain = NULL;
+ gchar *resource = NULL;
+
+ if (!gabble_parse_xmpp_uri (uri, &node, &domain, &resource, error))
+ goto OUT;
+
+ normalized_jid = gabble_encode_jid (node, domain, resource);
+
+ g_free (node);
+ g_free (domain);
+ g_free (resource);
+ }
+ else
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
+ "'%s' URI scheme is not supported by this protocol",
+ scheme);
+ goto OUT;
+ }
+
+OUT:
+ g_free (scheme);
+
+ return normalized_jid;
+}
+
+gchar *
+gabble_jid_to_uri (const gchar *scheme,
+ const gchar *jid,
+ GError **error)
+{
+ gchar *normalized_uri = NULL;
+ gchar *node = NULL;
+ gchar *domain = NULL;
+ gchar *resource = NULL;
+ gchar *escaped_node = NULL;
+ gchar *escaped_domain = NULL;
+ gchar *escaped_resource = NULL;
+ gchar *escaped_jid = NULL;
+ gchar *normalized_scheme = NULL;
+
+ g_return_val_if_fail (scheme != NULL, NULL);
+
+ if (!wocky_decode_jid (jid, &node, &domain, &resource))
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid JID", jid);
+ return NULL;
+ }
+
+ /* convert from "foo?" to "foo%3F" */
+ if (node)
+ escaped_node = g_uri_escape_string (node, NULL, TRUE);
+
+ g_assert (domain != NULL);
+ escaped_domain = g_uri_escape_string (domain, NULL, TRUE);
+
+ if (resource)
+ escaped_resource = g_uri_escape_string (resource, NULL, TRUE);
+
+ escaped_jid = gabble_encode_jid (escaped_node, escaped_domain, escaped_resource);
+
+ normalized_scheme = g_ascii_strdown (scheme, -1);
+
+ normalized_uri = g_strdup_printf ("%s:%s", normalized_scheme, escaped_jid);
+
+ g_free (node);
+ g_free (domain);
+ g_free (resource);
+ g_free (escaped_node);
+ g_free (escaped_domain);
+ g_free (escaped_resource);
+ g_free (escaped_jid);
+ g_free (normalized_scheme);
+
+ return normalized_uri;
+}
+
+TpHandle
+gabble_ensure_handle_from_uri (TpHandleRepoIface *repo,
+ const gchar *uri,
+ GError **error)
+{
+ TpHandle handle;
+
+ gchar *jid = gabble_uri_to_jid (uri, error);
+
+ if (jid == NULL)
+ return 0;
+
+ handle = tp_handle_ensure (repo, jid, NULL, error);
+
+ g_free (jid);
+
+ return handle;
+}
+
+gchar *
+gabble_normalize_vcard_address (const gchar *vcard_field,
+ const gchar *vcard_address,
+ GError **error)
+{
+ gchar *normalized_jid = NULL;
+ gchar *normalized_address = NULL;
+
+ g_return_val_if_fail (vcard_field != NULL, NULL);
+ g_return_val_if_fail (vcard_address != NULL, NULL);
+
+ normalized_jid = gabble_vcard_address_to_jid (vcard_field, vcard_address, error);
+ if (normalized_jid == NULL)
+ {
+ goto OUT;
+ }
+
+ normalized_address = gabble_jid_to_vcard_address (vcard_field, normalized_jid, error);
+
+OUT:
+ g_free (normalized_jid);
+
+ return normalized_address;
+}
+
+gchar *
+gabble_vcard_address_to_jid (const gchar *vcard_field,
+ const gchar *vcard_address,
+ GError **error)
+{
+ gchar *normalized_jid = NULL;
+
+ g_return_val_if_fail (vcard_field != NULL, NULL);
+ g_return_val_if_fail (vcard_address != NULL, NULL);
+
+ if (g_ascii_strcasecmp (vcard_field, "x-jabber") == 0)
+ {
+ GError *gabble_error = NULL;
+
+ normalized_jid = gabble_normalize_contact (NULL,
+ vcard_address, GUINT_TO_POINTER (GABBLE_JID_GLOBAL),
+ &gabble_error);
+
+ if (gabble_error != NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is an invalid address: %s", vcard_address,
+ gabble_error->message);
+ g_error_free (gabble_error);
+ }
+ }
+ else if (g_ascii_strcasecmp (vcard_field, "x-facebook-id") == 0)
+ {
+ const gchar *s;
+
+ s = vcard_address;
+ while (*s && (g_ascii_isdigit (*s)))
+ s++;
+ if (G_UNLIKELY (*s != '\0'))
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is an invalid facebook chat address", vcard_address);
+ goto OUT;
+ }
+
+ normalized_jid = g_strdup_printf ("-%s@chat.facebook.com", vcard_address);
+ }
+ else
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
+ "'%s' vCard field is not supported by this protocol", vcard_field);
+ }
+
+OUT:
+ return normalized_jid;
+}
+
+gchar *
+gabble_jid_to_vcard_address (const gchar *vcard_field,
+ const gchar *jid,
+ GError **error)
+{
+ gchar *normalized_address = NULL;
+
+ g_return_val_if_fail (vcard_field != NULL, NULL);
+ g_return_val_if_fail (jid != NULL, NULL);
+
+ if (g_ascii_strcasecmp (vcard_field, "x-jabber") == 0)
+ {
+ GError *gabble_error = NULL;
+
+ normalized_address = gabble_normalize_contact (NULL,
+ jid, GUINT_TO_POINTER (GABBLE_JID_GLOBAL),
+ &gabble_error);
+
+ if (gabble_error != NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is an invalid address: %s", jid,
+ gabble_error->message);
+ g_error_free (gabble_error);
+ }
+ }
+ else if (g_ascii_strcasecmp (vcard_field, "x-facebook-id") == 0)
+ {
+ gchar *address = g_ascii_strdown (jid, -1);
+
+ if (address[0] == '-' &&
+ g_str_has_suffix (address, "@chat.facebook.com"))
+ {
+ const gchar *at = strchr (address, '@');
+ const gchar *start_of_number = address + 1;
+ const gchar *s;
+
+ g_assert (at != NULL);
+
+ normalized_address = g_strndup (start_of_number, (int) (at - start_of_number));
+
+ s = normalized_address;
+ while (*s && (g_ascii_isdigit (*s)))
+ s++;
+ if (G_UNLIKELY (*s != '\0'))
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is an invalid facebook chat address", jid);
+ }
+ }
+ else
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is an invalid facebook chat address", jid);
+ }
+
+ g_free (address);
+ }
+ else
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
+ "'%s' vCard field is not supported by this protocol", vcard_field);
+ }
+
+ return normalized_address;
+}
+
+TpHandle
+gabble_ensure_handle_from_vcard_address (TpHandleRepoIface *repo,
+ const gchar *vcard_field,
+ const gchar *vcard_address,
+ GError **error)
+{
+ gchar *normalized_jid;
+ TpHandle handle;
+
+ normalized_jid = gabble_vcard_address_to_jid (vcard_field, vcard_address, error);
+ if (normalized_jid == NULL)
+ return 0;
+
+ handle = tp_handle_ensure (repo, normalized_jid, NULL, error);
+
+ g_free (normalized_jid);
+
+ return handle;
+}
+
+gchar **
+gabble_uris_for_handle (TpHandleRepoIface *contact_repo,
+ TpHandle contact)
+{
+ GPtrArray *uris = g_ptr_array_new ();
+
+ for (const gchar * const *scheme = addressable_uri_schemes; *scheme != NULL; scheme++)
+ {
+ gchar *uri = gabble_uri_for_handle (contact_repo, *scheme, contact);
+
+ if (uri != NULL)
+ {
+ g_ptr_array_add (uris, uri);
+ }
+ }
+
+ g_ptr_array_add (uris, NULL);
+ return (gchar **) g_ptr_array_free (uris, FALSE);
+}
+
+GHashTable *
+gabble_vcard_addresses_for_handle (TpHandleRepoIface *contact_repo,
+ TpHandle contact)
+{
+ GHashTable *addresses = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) g_free);
+
+ for (const gchar * const *field = addressable_vcard_fields; *field != NULL; field++)
+ {
+ gchar *vcard_address = gabble_vcard_address_for_handle (contact_repo, *field, contact);
+
+ if (vcard_address != NULL)
+ {
+ g_hash_table_insert (addresses, (gpointer) *field, vcard_address);
+ }
+ }
+
+ return addresses;
+}
+
+gchar *
+gabble_vcard_address_for_handle (TpHandleRepoIface *contact_repo,
+ const gchar *vcard_field,
+ TpHandle contact)
+{
+ const gchar *identifier = tp_handle_inspect (contact_repo, contact);
+ return gabble_jid_to_vcard_address (vcard_field, identifier, NULL);
+}
+
+gchar *
+gabble_uri_for_handle (TpHandleRepoIface *contact_repo,
+ const gchar *scheme,
+ TpHandle contact)
+{
+ const gchar *identifier = tp_handle_inspect (contact_repo, contact);
+ return gabble_jid_to_uri (scheme, identifier, NULL);
+}
+
+gboolean
+gabble_parse_xmpp_uri (const gchar *uri,
+ gchar **node,
+ gchar **domain,
+ gchar **resource,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gchar *scheme;
+ const gchar *jid;
+ gchar *tmp_node = NULL;
+ gchar *tmp_domain = NULL;
+ gchar *tmp_resource = NULL;
+ gchar *unescaped_node = NULL;
+ gchar *unescaped_domain = NULL;
+ gchar *unescaped_resource = NULL;
+ gchar *unescaped_jid = NULL;
+ gchar *normalized_jid = NULL;
+ GError *gabble_error = NULL;
+
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (domain != NULL, FALSE);
+
+ scheme = g_uri_parse_scheme (uri);
+
+ if (scheme == NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid URI", uri);
+ goto OUT;
+ }
+
+ jid = uri + strlen (scheme) + 1; /* Strip the scheme */
+
+ if (!wocky_decode_jid (jid, &tmp_node, &tmp_domain, &tmp_resource))
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid XMPP URI", uri);
+ goto OUT;
+ }
+
+ /* convert from "foo%3F" to "foo?" */
+ if (tmp_node)
+ {
+ unescaped_node = g_uri_unescape_string (tmp_node, NULL);
+ if (unescaped_node == NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid XMPP URI", uri);
+ goto OUT;
+ }
+ }
+
+ g_assert (tmp_domain);
+ unescaped_domain = g_uri_unescape_string (tmp_domain, NULL);
+ if (unescaped_domain == NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid XMPP URI", uri);
+ goto OUT;
+ }
+
+ if (tmp_resource)
+ {
+ unescaped_resource = g_uri_unescape_string (tmp_resource, NULL);
+ if (unescaped_resource == NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid XMPP URI", uri);
+ goto OUT;
+ }
+ }
+
+ unescaped_jid = gabble_encode_jid (unescaped_node, unescaped_domain, unescaped_resource);
+
+ normalized_jid = gabble_normalize_contact (NULL, unescaped_jid,
+ GUINT_TO_POINTER (GABBLE_JID_GLOBAL), &gabble_error);
+
+ if (gabble_error != NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid XMPP URI: %s", uri,
+ gabble_error->message);
+ g_error_free (gabble_error);
+ goto OUT;
+ }
+
+ if (!wocky_decode_jid (normalized_jid, node, domain, resource))
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "'%s' is not a valid XMPP URI", uri);
+ goto OUT;
+ }
+
+ ret = TRUE;
+
+OUT:
+ g_free (scheme);
+ g_free (tmp_node);
+ g_free (tmp_domain);
+ g_free (tmp_resource);
+ g_free (unescaped_node);
+ g_free (unescaped_domain);
+ g_free (unescaped_resource);
+ g_free (unescaped_jid);
+ g_free (normalized_jid);
+ return ret;
+}
diff --git a/src/addressing-util.h b/src/addressing-util.h
new file mode 100644
index 000000000..9835e0d70
--- /dev/null
+++ b/src/addressing-util.h
@@ -0,0 +1,76 @@
+/*
+ * addressing-util.c - Headers for Gabble addressing utility functions
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GABBLE_UTIL_ADDRESSING_H__
+#define __GABBLE_UTIL_ADDRESSING_H__
+
+#include <telepathy-glib/handle-repo-dynamic.h>
+
+const gchar * const * gabble_get_addressable_uri_schemes (void);
+
+const gchar * const * gabble_get_addressable_vcard_fields (void);
+
+gchar * gabble_normalize_contact_uri (const gchar *uri,
+ GError **error);
+gchar * gabble_uri_to_jid (const gchar *uri,
+ GError **error);
+gchar * gabble_jid_to_uri (const gchar *scheme,
+ const gchar *jid,
+ GError **error);
+
+TpHandle gabble_ensure_handle_from_uri (TpHandleRepoIface *repo,
+ const gchar *uri,
+ GError **error);
+
+gchar * gabble_normalize_vcard_address (const gchar *vcard_field,
+ const gchar *vcard_address,
+ GError **error);
+gchar * gabble_vcard_address_to_jid (const gchar *vcard_field,
+ const gchar *vcard_address,
+ GError **error);
+gchar * gabble_jid_to_vcard_address (const gchar *vcard_field,
+ const gchar *jid,
+ GError **error);
+
+TpHandle gabble_ensure_handle_from_vcard_address (TpHandleRepoIface *repo,
+ const gchar *vcard_field,
+ const gchar *vcard_address,
+ GError **error);
+
+gchar **gabble_uris_for_handle (TpHandleRepoIface *contact_repo,
+ TpHandle contact);
+
+GHashTable *gabble_vcard_addresses_for_handle (TpHandleRepoIface *contact_repo,
+ TpHandle contact);
+
+gchar *gabble_uri_for_handle (TpHandleRepoIface *contact_repo,
+ const gchar *uri_scheme,
+ TpHandle contact);
+
+gchar *gabble_vcard_address_for_handle (TpHandleRepoIface *contact_repo,
+ const gchar *vcard_field,
+ TpHandle contact);
+
+gboolean gabble_parse_xmpp_uri (const gchar *uri,
+ gchar **node,
+ gchar **domain,
+ gchar **resource,
+ GError **error);
+
+#endif /* __GABBLE_UTIL_ADDRESSING_H__ */
diff --git a/src/auth-manager.c b/src/auth-manager.c
index a94248542..5089e9aed 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"
@@ -325,7 +325,10 @@ gabble_auth_manager_start_auth_async (WockyAuthRegistry *registry,
g_ptr_array_add (mech_array, iter->data);
}
- g_ptr_array_add (mech_array, X_TELEPATHY_PASSWORD);
+ if (wocky_auth_registry_supports_one_of (registry, mechanisms,
+ allow_plain))
+ g_ptr_array_add (mech_array, X_TELEPATHY_PASSWORD);
+
g_ptr_array_add (mech_array, NULL);
/* we'll use these if we fall back to the base class to use
diff --git a/src/bytestream-factory.c b/src/bytestream-factory.c
index 64696f79b..57d8509a0 100644
--- a/src/bytestream-factory.c
+++ b/src/bytestream-factory.c
@@ -26,6 +26,7 @@
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <loudmouth/loudmouth.h>
+#include <wocky/wocky-utils.h>
#include <telepathy-glib/interfaces.h>
#define DEBUG_FLAG GABBLE_DEBUG_BYTESTREAM
@@ -679,16 +680,16 @@ gabble_bytestream_factory_dispose (GObject *object)
priv->iq_socks5_cb, LM_MESSAGE_TYPE_IQ);
lm_message_handler_unref (priv->iq_socks5_cb);
- g_hash_table_destroy (priv->ibb_bytestreams);
+ g_hash_table_unref (priv->ibb_bytestreams);
priv->ibb_bytestreams = NULL;
- g_hash_table_destroy (priv->muc_bytestreams);
+ g_hash_table_unref (priv->muc_bytestreams);
priv->muc_bytestreams = NULL;
- g_hash_table_destroy (priv->socks5_bytestreams);
+ g_hash_table_unref (priv->socks5_bytestreams);
priv->socks5_bytestreams = NULL;
- g_hash_table_destroy (priv->multiple_bytestreams);
+ g_hash_table_unref (priv->multiple_bytestreams);
priv->multiple_bytestreams = NULL;
proxies = g_slist_concat (priv->socks5_proxies,
@@ -1150,7 +1151,7 @@ bytestream_factory_iq_si_cb (LmMessageHandler *handler,
{
/* jid is not a muc jid so we need contact's resource */
- if (!gabble_decode_jid (from, NULL, NULL, &peer_resource))
+ if (!wocky_decode_jid (from, NULL, NULL, &peer_resource))
{
DEBUG ("Got an SI IQ response from a bad JID. Ignoring.");
goto out;
@@ -1272,11 +1273,13 @@ bytestream_factory_iq_si_cb (LmMessageHandler *handler,
si_tube_received (self, msg, si, bytestream, peer_handle, room_handle,
stream_id);
}
+#ifdef ENABLE_FILE_TRANSFER
else if (!tp_strdiff (profile, NS_FILE_TRANSFER))
{
gabble_ft_manager_handle_si_request (priv->conn->ft_manager, bytestream,
peer_handle, stream_id, msg);
}
+#endif
else
{
GError e = { WOCKY_SI_ERROR, WOCKY_SI_ERROR_BAD_PROFILE, "" };
@@ -2081,7 +2084,7 @@ streaminit_reply_cb (GabbleConnection *conn,
{
/* jid is not a muc jid so we need contact's resource */
- if (!gabble_decode_jid (from, NULL, NULL, &peer_resource))
+ if (!wocky_decode_jid (from, NULL, NULL, &peer_resource))
{
DEBUG ("Got an SI request with a bad JID");
goto END;
diff --git a/src/bytestream-ibb.c b/src/bytestream-ibb.c
index 52868f03c..c4ac85afd 100644
--- a/src/bytestream-ibb.c
+++ b/src/bytestream-ibb.c
@@ -166,7 +166,7 @@ gabble_bytestream_ibb_finalize (GObject *object)
if (priv->write_buffer != NULL)
g_string_free (priv->write_buffer, TRUE);
- g_hash_table_destroy (priv->sent_stanzas_not_acked);
+ g_hash_table_unref (priv->sent_stanzas_not_acked);
G_OBJECT_CLASS (gabble_bytestream_ibb_parent_class)->finalize (object);
}
diff --git a/src/bytestream-muc.c b/src/bytestream-muc.c
index 4cab448a3..359158bb9 100644
--- a/src/bytestream-muc.c
+++ b/src/bytestream-muc.c
@@ -129,7 +129,7 @@ gabble_bytestream_muc_finalize (GObject *object)
if (priv->buffers != NULL)
{
- g_hash_table_destroy (priv->buffers);
+ g_hash_table_unref (priv->buffers);
priv->buffers = NULL;
}
@@ -516,7 +516,7 @@ gabble_bytestream_muc_receive (GabbleBytestreamMuc *self,
}
else
{
- DEBUG ("Append data to buffer of %s (%zu bytes)", from, str->len);
+ DEBUG ("Append data to buffer of %s (%" G_GSIZE_FORMAT " bytes)", from, str->len);
g_string_append_len (buffer, str->str, str->len);
}
@@ -542,7 +542,7 @@ gabble_bytestream_muc_receive (GabbleBytestreamMuc *self,
if (fully_received)
{
- DEBUG ("fully received %zu bytes of data", str->len);
+ DEBUG ("fully received %" G_GSIZE_FORMAT " bytes of data", str->len);
g_signal_emit_by_name (G_OBJECT (self), "data-received", sender, str);
g_string_free (str, TRUE);
}
diff --git a/src/bytestream-socks5.c b/src/bytestream-socks5.c
index d9d45db94..f306ecf70 100644
--- a/src/bytestream-socks5.c
+++ b/src/bytestream-socks5.c
@@ -1639,7 +1639,7 @@ get_local_interfaces_ips (void)
GSList *ips = NULL;
/* FIXME: add IPv6 addresses */
- if ((sockfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP)) == INVALID_SOCKET)
+ if ((sockfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP)) == (int) INVALID_SOCKET)
{
DEBUG ("Cannot open socket to retrieve interface list");
return NULL;
@@ -1659,7 +1659,7 @@ get_local_interfaces_ips (void)
}
ret = WSAIoctl (sockfd, SIO_GET_INTERFACE_LIST, NULL, 0, iflist,
- size, &bytes, NULL, NULL);
+ size, (LPDWORD) &bytes, NULL, NULL);
error = WSAGetLastError ();
if (ret == SOCKET_ERROR && error != WSAEFAULT)
diff --git a/src/call-muc-channel.c b/src/call-muc-channel.c
index 33392e301..f07088f21 100644
--- a/src/call-muc-channel.c
+++ b/src/call-muc-channel.c
@@ -296,9 +296,7 @@ static void
call_muc_do_update (GabbleCallMucChannel *self)
{
GabbleCallMucChannelPrivate *priv = self->priv;
-#ifdef ENABLE_DEBUG
MucCallState old = priv->state;
-#endif
switch (priv->state)
{
@@ -757,7 +755,7 @@ call_muc_channel_got_participant_presence (GabbleCallMucChannel *self,
static void
call_muc_channel_presence_cb (WockyMuc *wmuc,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
WockyMucMember *who,
gpointer user_data)
{
@@ -769,7 +767,7 @@ call_muc_channel_presence_cb (WockyMuc *wmuc,
static void
call_muc_channel_left_cb (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
WockyMucMember *member,
const gchar *actor_jid,
const gchar *why,
@@ -820,7 +818,7 @@ call_muc_channel_update_all_members (GabbleCallMucChannel *self)
static void
call_muc_channel_joined_cb (WockyMuc *muc,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
gpointer user_data)
{
GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data);
@@ -845,7 +843,7 @@ call_muc_channel_pre_presence_cb (WockyMuc *wmuc,
static void
call_muc_channel_own_presence_cb (WockyMuc *wmuc,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
gpointer user_data)
{
GabbleCallMucChannel *self = GABBLE_CALL_MUC_CHANNEL (user_data);
diff --git a/src/call-stream.c b/src/call-stream.c
index 1e0bc0053..4c51ed8ea 100644
--- a/src/call-stream.c
+++ b/src/call-stream.c
@@ -176,9 +176,17 @@ static void
google_relay_session_cb (GPtrArray *relays,
gpointer user_data)
{
- TpyBaseMediaCallStream *stream = TPY_BASE_MEDIA_CALL_STREAM (user_data);
+ TpWeakRef *weak_ref = user_data;
+ TpyBaseMediaCallStream *stream = TPY_BASE_MEDIA_CALL_STREAM (
+ tp_weak_ref_dup_object (weak_ref));
- tpy_base_media_call_stream_set_relay_info (stream, relays);
+ if (stream != NULL)
+ {
+ tpy_base_media_call_stream_set_relay_info (stream, relays);
+ g_object_unref (stream);
+ }
+
+ tp_weak_ref_destroy (weak_ref);
}
static void
@@ -343,13 +351,13 @@ gabble_call_stream_constructed (GObject *obj)
* We ask for enough relays for 2 components (RTP and RTCP) since we
* don't yet know whether there will be RTCP. */
gabble_jingle_factory_create_google_relay_session (conn->jingle_factory,
- 2, google_relay_session_cb, obj);
+ 2, google_relay_session_cb, tp_weak_ref_new (self, NULL, NULL));
}
else
{
GPtrArray *relays = g_ptr_array_new ();
tpy_base_media_call_stream_set_relay_info (media_base, relays);
- g_ptr_array_free (relays, TRUE);
+ g_ptr_array_unref (relays);
}
stun_servers = get_stun_servers (self);
diff --git a/src/capabilities.c b/src/capabilities.c
index b3ef11e87..a29b78468 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>
@@ -61,7 +61,10 @@ static const Feature self_advertised_features[] =
{ FEATURE_FIXED, NS_BYTESTREAMS },
{ FEATURE_FIXED, NS_VERSION },
+#ifdef ENABLE_FILE_TRANSFER
{ FEATURE_OPTIONAL, NS_FILE_TRANSFER },
+ { FEATURE_OPTIONAL, NS_TP_FT_METADATA },
+#endif
{ FEATURE_OPTIONAL, NS_GOOGLE_TRANSPORT_P2P },
{ FEATURE_OPTIONAL, NS_JINGLE_TRANSPORT_ICEUDP },
diff --git a/src/capabilities.h b/src/capabilities.h
deleted file mode 100644
index 811f11375..000000000
--- a/src/capabilities.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * capabilities.h - Connection.Interface.Capabilities constants and utilities
- * Copyright (C) 2005 Collabora Ltd.
- * Copyright (C) 2005 Nokia Corporation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __GABBLE_CAPABILITIES__H__
-#define __GABBLE_CAPABILITIES__H__
-
-#include <glib-object.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 :-) */
-#define QUIRK_PREFIX_CHAR '\x07'
-#define QUIRK_PREFIX "\x07"
-/* Gabble 0.7.x with 16 <= x < 29 omits @creator on <content/> */
-#define QUIRK_OMITS_CONTENT_CREATORS "\x07omits-content-creators"
-/* The Google Webmail client doesn't support some features */
-#define QUIRK_GOOGLE_WEBMAIL_CLIENT "\x07google-webmail-client"
-
-/* Some useful capability sets for Jingle etc. */
-const GabbleCapabilitySet *gabble_capabilities_get_legacy (void);
-const GabbleCapabilitySet *gabble_capabilities_get_any_audio (void);
-const GabbleCapabilitySet *gabble_capabilities_get_any_video (void);
-const GabbleCapabilitySet *gabble_capabilities_get_any_audio_video (void);
-const GabbleCapabilitySet *gabble_capabilities_get_any_google_av (void);
-const GabbleCapabilitySet *gabble_capabilities_get_any_jingle_av (void);
-const GabbleCapabilitySet *gabble_capabilities_get_any_transport (void);
-const GabbleCapabilitySet *gabble_capabilities_get_geoloc_notify (void);
-const GabbleCapabilitySet *gabble_capabilities_get_olpc_notify (void);
-
-/* XEP-0115 version 1.3:
- *
- * "The names of the feature bundles MUST NOT be used for semantic purposes:
- * they are merely opaque identifiers"
- *
- * However, some old Jabber clients (e.g. Gabble 0.2) and various Google
- * clients require the bundle names "voice-v1" and "video-v1". We keep these
- * names for compatibility.
- */
-#define BUNDLE_SHARE_V1 "share-v1"
-#define BUNDLE_VOICE_V1 "voice-v1"
-#define BUNDLE_VIDEO_V1 "video-v1"
-#define BUNDLE_PMUC_V1 "pmuc-v1"
-
-const GabbleCapabilitySet *gabble_capabilities_get_bundle_share_v1 (void);
-const GabbleCapabilitySet *gabble_capabilities_get_bundle_voice_v1 (void);
-const GabbleCapabilitySet *gabble_capabilities_get_bundle_video_v1 (void);
-
-/* Return the capabilities we always have */
-const GabbleCapabilitySet *gabble_capabilities_get_fixed_caps (void);
-
-void gabble_capabilities_init (gpointer conn);
-void gabble_capabilities_finalize (gpointer conn);
-
-#endif /* __GABBLE_CAPABILITIES__H__ */
-
diff --git a/src/caps-channel-manager.c b/src/caps-channel-manager.c
index bc3d2ea98..dea13a652 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>
@@ -71,7 +71,7 @@ gabble_caps_channel_manager_get_contact_capabilities (
{
method (caps_manager, handle, caps, arr);
}
- /* ... else assume there is not caps for this kind of channels */
+ /* ... else assume there are no caps for this kind of channel */
}
/**
@@ -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-channel-manager.h b/src/caps-channel-manager.h
deleted file mode 100644
index 05947b757..000000000
--- a/src/caps-channel-manager.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * caps-channel-manager.h - interface holding capabilities functions for
- * channel managers
- *
- * Copyright (C) 2008 Collabora Ltd.
- * Copyright (C) 2008 Nokia Corporation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef GABBLE_CAPS_CHANNEL_MANAGER_H
-#define GABBLE_CAPS_CHANNEL_MANAGER_H
-
-#include <glib-object.h>
-#include <telepathy-glib/exportable-channel.h>
-#include <telepathy-glib/handle.h>
-
-#include "capabilities.h"
-
-G_BEGIN_DECLS
-
-#define GABBLE_TYPE_CAPS_CHANNEL_MANAGER \
- (gabble_caps_channel_manager_get_type ())
-
-#define GABBLE_CAPS_CHANNEL_MANAGER(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
- GABBLE_TYPE_CAPS_CHANNEL_MANAGER, GabbleCapsChannelManager))
-
-#define GABBLE_IS_CAPS_CHANNEL_MANAGER(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
- GABBLE_TYPE_CAPS_CHANNEL_MANAGER))
-
-#define GABBLE_CAPS_CHANNEL_MANAGER_GET_INTERFACE(obj) \
- (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \
- GABBLE_TYPE_CAPS_CHANNEL_MANAGER, GabbleCapsChannelManagerInterface))
-
-typedef struct _GabbleCapsChannelManager GabbleCapsChannelManager;
-typedef struct _GabbleCapsChannelManagerInterface GabbleCapsChannelManagerInterface;
-
-/* virtual methods */
-
-typedef void (*GabbleCapsChannelManagerGetContactCapsFunc) (
- GabbleCapsChannelManager *manager,
- TpHandle handle,
- const GabbleCapabilitySet *caps,
- GPtrArray *arr);
-
-typedef void (*GabbleCapsChannelManagerResetCapsFunc) (
- GabbleCapsChannelManager *manager);
-
-typedef void (*GabbleCapsChannelManagerAddCapFunc) (
- GabbleCapsChannelManager *manager,
- GHashTable *cap,
- GabbleCapabilitySet *cap_set);
-
-typedef void (*GabbleCapsChannelManagerRepresentClientFunc) (
- GabbleCapsChannelManager *manager,
- const gchar *client_name,
- const GPtrArray *filters,
- const gchar * const *cap_tokens,
- GabbleCapabilitySet *cap_set);
-
-void gabble_caps_channel_manager_reset_capabilities (
- GabbleCapsChannelManager *caps_manager);
-
-void gabble_caps_channel_manager_get_contact_capabilities (
- GabbleCapsChannelManager *caps_manager,
- TpHandle handle,
- const GabbleCapabilitySet *caps,
- GPtrArray *arr);
-
-void gabble_caps_channel_manager_represent_client (
- GabbleCapsChannelManager *caps_manager,
- const gchar *client_name,
- const GPtrArray *filters,
- const gchar * const *cap_tokens,
- GabbleCapabilitySet *cap_set);
-
-struct _GabbleCapsChannelManagerInterface {
- GTypeInterface parent;
-
- GabbleCapsChannelManagerResetCapsFunc reset_caps;
- GabbleCapsChannelManagerGetContactCapsFunc get_contact_caps;
- GabbleCapsChannelManagerRepresentClientFunc represent_client;
-
- gpointer priv;
-};
-
-GType gabble_caps_channel_manager_get_type (void);
-
-G_END_DECLS
-
-#endif
diff --git a/src/caps-hash.c b/src/caps-hash.c
index bf8a2a085..bd143eecd 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,9 +72,11 @@ 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);
- g_ptr_array_free (features, TRUE);
+ str = wocky_caps_hash_compute_from_lists (features, identities, data_forms);
+
+ g_ptr_array_unref (features);
wocky_disco_identity_array_free (identities);
return str;
@@ -85,8 +88,8 @@ caps_hash_compute_from_self_presence (GabbleConnection *self)
* Returns: the hash. The called must free the returned hash with g_free().
*/
gchar *
-gabble_caps_hash_compute (const GabbleCapabilitySet *cap_set,
- const GPtrArray *identities)
+gabble_caps_hash_compute_full (const GabbleCapabilitySet *cap_set,
+ const GPtrArray *identities, GPtrArray *data_forms)
{
GPtrArray *features = g_ptr_array_new ();
GPtrArray *identities_copy = ((identities == NULL) ?
@@ -96,10 +99,23 @@ gabble_caps_hash_compute (const GabbleCapabilitySet *cap_set,
gabble_capability_set_foreach (cap_set, ptr_array_add_str, features);
- str = wocky_caps_hash_compute_from_lists (features, identities_copy, NULL);
+ str = wocky_caps_hash_compute_from_lists (features, identities_copy,
+ data_forms);
- g_ptr_array_free (features, TRUE);
+ g_ptr_array_unref (features);
wocky_disco_identity_array_free (identities_copy);
return str;
}
+
+/**
+ * Compute the hash as defined by the XEP-0115 from a received GabbleCapabilitySet
+ *
+ * Returns: the hash. The called must free the returned hash with g_free().
+ */
+gchar *
+gabble_caps_hash_compute (const GabbleCapabilitySet *cap_set,
+ const GPtrArray *identities)
+{
+ return gabble_caps_hash_compute_full (cap_set, identities, NULL);
+}
diff --git a/src/conn-addressing.c b/src/conn-addressing.c
new file mode 100644
index 000000000..6842ae008
--- /dev/null
+++ b/src/conn-addressing.c
@@ -0,0 +1,169 @@
+/*
+ * conn-addressing.h - Header for Gabble connection code handling addressing.
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "conn-addressing.h"
+
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <telepathy-glib/gtypes.h>
+#include <telepathy-glib/interfaces.h>
+
+#include "extensions/extensions.h"
+
+#include "addressing-util.h"
+#include "namespaces.h"
+#include "util.h"
+
+static const char *assumed_interfaces[] = {
+ TP_IFACE_CONNECTION,
+ GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING,
+ NULL
+ };
+
+
+static void
+_fill_contact_attributes (TpHandleRepoIface *contact_repo,
+ TpHandle contact,
+ GHashTable *attributes_hash)
+{
+ gchar **uris = gabble_uris_for_handle (contact_repo, contact);
+ GHashTable *addresses = gabble_vcard_addresses_for_handle (contact_repo, contact);
+
+ tp_contacts_mixin_set_contact_attribute (attributes_hash,
+ contact, GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING"/uris",
+ tp_g_value_slice_new_take_boxed (G_TYPE_STRV, uris));
+
+ tp_contacts_mixin_set_contact_attribute (attributes_hash,
+ contact, GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING"/addresses",
+ tp_g_value_slice_new_take_boxed (TP_HASH_TYPE_STRING_STRING_MAP, addresses));
+}
+
+static void
+conn_addressing_fill_contact_attributes (GObject *obj,
+ const GArray *contacts,
+ GHashTable *attributes_hash)
+{
+ guint i;
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *) obj, TP_HANDLE_TYPE_CONTACT);
+
+ for (i = 0; i < contacts->len; i++)
+ {
+ TpHandle contact = g_array_index (contacts, TpHandle, i);
+ _fill_contact_attributes (contact_repo, contact, attributes_hash);
+ }
+}
+
+static void
+conn_addressing_get_contacts_by_uri (GabbleSvcConnectionInterfaceAddressing *iface,
+ const gchar **uris,
+ const gchar **interfaces,
+ DBusGMethodInvocation *context)
+{
+ const gchar **uri;
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *) iface, TP_HANDLE_TYPE_CONTACT);
+ GHashTable *attributes;
+ GHashTable *requested = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ GArray *handles = g_array_sized_new (TRUE, TRUE, sizeof (TpHandle),
+ g_strv_length ((gchar **) uris));
+ gchar *sender = dbus_g_method_get_sender (context);
+
+ for (uri = uris; *uri != NULL; uri++)
+ {
+ TpHandle h = gabble_ensure_handle_from_uri (contact_repo, *uri, NULL);
+
+ if (h == 0)
+ continue;
+
+ g_hash_table_insert (requested, g_strdup (*uri), GUINT_TO_POINTER (h));
+ g_array_append_val (handles, h);
+ }
+
+ attributes = tp_contacts_mixin_get_contact_attributes (G_OBJECT (iface), handles,
+ interfaces, assumed_interfaces, sender);
+
+ gabble_svc_connection_interface_addressing_return_from_get_contacts_by_uri (
+ context, requested, attributes);
+
+ tp_handles_unref (contact_repo, handles);
+ g_hash_table_unref (requested);
+ g_hash_table_unref (attributes);
+ g_free (sender);
+}
+
+static void
+conn_addressing_get_contacts_by_vcard_field (GabbleSvcConnectionInterfaceAddressing *iface,
+ const gchar *field,
+ const gchar **addresses,
+ const gchar **interfaces,
+ DBusGMethodInvocation *context)
+{
+ const gchar **address;
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *) iface, TP_HANDLE_TYPE_CONTACT);
+ GHashTable *attributes;
+ GHashTable *requested = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ GArray *handles = g_array_sized_new (TRUE, TRUE, sizeof (TpHandle),
+ g_strv_length ((gchar **) addresses));
+ gchar *sender = dbus_g_method_get_sender (context);
+
+ for (address = addresses; *address != NULL; address++)
+ {
+ TpHandle h = gabble_ensure_handle_from_vcard_address (contact_repo, field,
+ *address, NULL);
+
+ if (h == 0)
+ continue;
+
+ g_hash_table_insert (requested, g_strdup (*address), GUINT_TO_POINTER (h));
+ g_array_append_val (handles, h);
+ }
+
+ attributes = tp_contacts_mixin_get_contact_attributes (G_OBJECT (iface), handles,
+ interfaces, assumed_interfaces, sender);
+
+ gabble_svc_connection_interface_addressing_return_from_get_contacts_by_vcard_field (
+ context, requested, attributes);
+
+ tp_handles_unref (contact_repo, handles);
+ g_hash_table_unref (requested);
+ g_hash_table_unref (attributes);
+ g_free (sender);
+}
+
+void
+conn_addressing_init (GabbleConnection *self) {
+ tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (self),
+ GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING,
+ conn_addressing_fill_contact_attributes);
+}
+
+void
+conn_addressing_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+#define IMPLEMENT(x) \
+ gabble_svc_connection_interface_addressing_implement_##x (\
+ g_iface, conn_addressing_##x)
+
+ IMPLEMENT (get_contacts_by_uri);
+ IMPLEMENT (get_contacts_by_vcard_field);
+#undef IMPLEMENT
+}
diff --git a/src/conn-addressing.h b/src/conn-addressing.h
new file mode 100644
index 000000000..2dbe08756
--- /dev/null
+++ b/src/conn-addressing.h
@@ -0,0 +1,34 @@
+/*
+ * conn-addressing.h - Header for Gabble connection code handling addressing.
+ * Copyright (C) 2010 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef GABBLE_CONN_ADDRESSING_H
+#define GABBLE_CONN_ADDRESSING_H
+
+#include <glib.h>
+
+#include "connection.h"
+
+G_BEGIN_DECLS
+
+void conn_addressing_iface_init (gpointer g_iface, gpointer iface_data);
+void conn_addressing_init (GabbleConnection *self);
+
+G_END_DECLS
+
+#endif /* GABBLE_CONN_ADDRESSING_H */
diff --git a/src/conn-aliasing.c b/src/conn-aliasing.c
index 08ca7cd76..af39c51bc 100644
--- a/src/conn-aliasing.c
+++ b/src/conn-aliasing.c
@@ -21,6 +21,7 @@
#include "config.h"
#include "conn-aliasing.h"
+#include <wocky/wocky-utils.h>
#include <telepathy-glib/contacts-mixin.h>
#include <telepathy-glib/gtypes.h>
#include <telepathy-glib/interfaces.h>
@@ -42,6 +43,11 @@ static void gabble_conn_aliasing_pep_nick_reply_handler (
GabbleConnection *conn, LmMessage *msg, TpHandle handle);
static GQuark gabble_conn_aliasing_pep_alias_quark (void);
+static GabbleConnectionAliasSource _gabble_connection_get_cached_remote_alias (
+ GabbleConnection *, TpHandle, gchar **);
+static void maybe_request_vcard (GabbleConnection *self, TpHandle handle,
+ GabbleConnectionAliasSource source);
+
/* distinct from any strdup()d pointer - used for negative caching */
static const gchar *NO_ALIAS = "";
@@ -137,7 +143,7 @@ aliases_request_free (AliasesRequest *request)
(TpBaseConnection *) request->conn, TP_HANDLE_TYPE_CONTACT);
tp_handles_unref (contact_handles, request->contacts);
- g_array_free (request->contacts, TRUE);
+ g_array_unref (request->contacts);
g_free (request->vcard_requests);
g_free (request->pep_requests);
g_strfreev (request->aliases);
@@ -473,14 +479,6 @@ gabble_connection_request_aliases (TpSvcConnectionInterfaceAliasing *iface,
aliases_request_free (request);
}
-
-struct _i_hate_g_hash_table_foreach
-{
- GabbleConnection *conn;
- GError **error;
- gboolean retval;
-};
-
static LmHandlerResult
nick_publish_msg_reply_cb (GabbleConnection *conn,
LmMessage *sent_msg,
@@ -503,85 +501,104 @@ nick_publish_msg_reply_cb (GabbleConnection *conn,
return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}
-static void
-setaliases_foreach (gpointer key, gpointer value, gpointer user_data)
+static gboolean
+set_one_alias (
+ GabbleConnection *conn,
+ TpHandle handle,
+ gchar *alias,
+ GError **error)
{
- struct _i_hate_g_hash_table_foreach *data =
- (struct _i_hate_g_hash_table_foreach *) user_data;
- TpHandle handle = GPOINTER_TO_UINT (key);
- gchar *alias = (gchar *) value;
- GError *error = NULL;
- TpBaseConnection *base = (TpBaseConnection *) data->conn;
+ TpBaseConnection *base = (TpBaseConnection *) conn;
TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
TP_HANDLE_TYPE_CONTACT);
+ gboolean ret = TRUE;
g_assert (base->status == TP_CONNECTION_STATUS_CONNECTED);
- if (!tp_handle_is_valid (contact_handles, handle, &error))
+ if (tp_str_empty (alias))
+ alias = NULL;
+
+ if (!tp_handle_is_valid (contact_handles, handle, error))
{
- data->retval = FALSE;
+ ret = FALSE;
}
else if (base->self_handle == handle)
{
/* only alter the roster if we're already there, e.g. because someone
* added us with another client
*/
- if (gabble_roster_handle_has_entry (data->conn->roster, handle)
- && !gabble_roster_handle_set_name (data->conn->roster, handle,
- alias, data->error))
+ if (gabble_roster_handle_has_entry (conn->roster, handle)
+ && !gabble_roster_handle_set_name (conn->roster, handle,
+ alias, error))
{
- data->retval = FALSE;
+ ret = FALSE;
}
}
- else if (!gabble_roster_handle_set_name (data->conn->roster, handle, alias,
- data->error))
+ else
{
- data->retval = FALSE;
+ gchar *remote_alias = NULL;
+ GabbleConnectionAliasSource source = GABBLE_CONNECTION_ALIAS_FROM_ROSTER;
+
+ if (alias == NULL)
+ {
+ source = _gabble_connection_get_cached_remote_alias (conn, handle,
+ &remote_alias);
+ alias = remote_alias;
+ }
+
+ ret = gabble_roster_handle_set_name (conn->roster, handle, alias, error);
+ g_free (remote_alias);
+
+ /* If we don't have a cached remote alias for this contact, try to ask
+ * for one. (Maybe we haven't seen a PEP update or fetched their vCard in
+ * this session?)
+ */
+ maybe_request_vcard (conn, handle, source);
}
if (base->self_handle == handle)
{
- GList *edits = NULL;
+ GabbleVCardManagerEditInfo *edit;
+ GQueue edits = G_QUEUE_INIT;
/* User has called SetAliases on themselves - patch their vCard.
* FIXME: because SetAliases is currently synchronous, we ignore errors
* here, and just let the request happen in the background.
*/
- if (data->conn->features & GABBLE_CONNECTION_FEATURES_PEP)
+ if (conn->features & GABBLE_CONNECTION_FEATURES_PEP)
{
/* Publish nick using PEP */
LmMessage *msg;
WockyNode *item;
- msg = wocky_pep_service_make_publish_stanza (data->conn->pep_nick,
- &item);
- wocky_node_add_child_with_content_ns (item, "nick",
- alias, NS_NICK);
+ msg = wocky_pep_service_make_publish_stanza (conn->pep_nick, &item);
+ /* Does the right thing if alias == NULL. */
+ wocky_node_add_child_with_content_ns (item, "nick", alias, NS_NICK);
- _gabble_connection_send_with_reply (data->conn, msg,
+ _gabble_connection_send_with_reply (conn, msg,
nick_publish_msg_reply_cb, NULL, NULL, NULL);
lm_message_unref (msg);
}
- edits = g_list_append (edits, gabble_vcard_manager_edit_info_new (
- NULL, alias, GABBLE_VCARD_EDIT_SET_ALIAS, NULL));
- gabble_vcard_manager_edit (data->conn->vcard_manager, 0, NULL,
- NULL, G_OBJECT (data->conn), edits);
- }
-
- if (NULL != error)
- {
- if (NULL == *(data->error))
- {
- *(data->error) = error;
- }
+ if (alias == NULL)
+ /* Deliberately not doing the fall-back-to-FN-on-GTalk dance because
+ * clearing your FN is more serious.
+ */
+ edit = gabble_vcard_manager_edit_info_new ("NICKNAME", NULL,
+ GABBLE_VCARD_EDIT_DELETE, NULL);
else
- {
- g_error_free (error);
- }
+ edit = gabble_vcard_manager_edit_info_new (NULL, alias,
+ GABBLE_VCARD_EDIT_SET_ALIAS, NULL);
+
+ g_queue_push_head (&edits, edit);
+ /* Yes, gabble_vcard_manager_edit steals the list you pass it. */
+ gabble_vcard_manager_edit (conn->vcard_manager, 0, NULL,
+ NULL, G_OBJECT (conn), edits.head);
}
+
+ return ret;
}
/**
@@ -597,27 +614,32 @@ gabble_connection_set_aliases (TpSvcConnectionInterfaceAliasing *iface,
{
GabbleConnection *self = GABBLE_CONNECTION (iface);
TpBaseConnection *base = (TpBaseConnection *) self;
- GError *error = NULL;
- struct _i_hate_g_hash_table_foreach data = { NULL, NULL, TRUE };
+ GHashTableIter iter;
+ gpointer key, value;
+ gboolean retval = TRUE;
+ GError *first_error = NULL;
g_assert (GABBLE_IS_CONNECTION (self));
TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context);
- data.conn = self;
- data.error = &error;
-
- g_hash_table_foreach (aliases, setaliases_foreach, &data);
+ g_hash_table_iter_init (&iter, aliases);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ if (!set_one_alias (self, GPOINTER_TO_UINT (key), value,
+ (first_error == NULL ? &first_error : NULL)))
+ retval = FALSE;
+ }
- if (data.retval)
+ if (retval)
{
tp_svc_connection_interface_aliasing_return_from_set_aliases (
context);
}
else
{
- dbus_g_method_return_error (context, error);
- g_error_free (error);
+ dbus_g_method_return_error (context, first_error);
+ g_error_free (first_error);
}
}
@@ -766,17 +788,32 @@ gabble_conn_aliasing_pep_nick_reply_handler (GabbleConnection *conn,
}
}
-
void
gabble_conn_aliasing_nickname_updated (GObject *object,
TpHandle handle,
gpointer user_data)
{
+ GArray *handles;
+
+ handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1);
+ g_array_append_val (handles, handle);
+
+ gabble_conn_aliasing_nicknames_updated (object, handles, user_data);
+
+ g_array_unref (handles);
+}
+
+void
+gabble_conn_aliasing_nicknames_updated (GObject *object,
+ GArray *handles,
+ gpointer user_data)
+{
GabbleConnection *conn = GABBLE_CONNECTION (user_data);
- GabbleConnectionAliasSource signal_source, current_source;
- gchar *alias = NULL;
+ GabbleConnectionAliasSource signal_source;
GPtrArray *aliases;
- GValue entry = { 0, };
+ guint i;
+
+ g_return_if_fail (handles->len > 0);
if (object == user_data)
{
@@ -801,47 +838,58 @@ gabble_conn_aliasing_nickname_updated (GObject *object,
return;
}
- current_source = _gabble_connection_get_cached_alias (conn, handle, &alias);
-
- g_assert (current_source != GABBLE_CONNECTION_ALIAS_NONE);
+ aliases = g_ptr_array_sized_new (handles->len);
- /* if the active alias for this handle is already known and from
- * a higher priority, this signal is not interesting so we do
- * nothing */
- if (signal_source < current_source)
+ for (i = 0; i < handles->len; i++)
{
- DEBUG ("ignoring boring alias change for handle %u, signal from %u "
- "but source %u has alias \"%s\"", handle, signal_source,
- current_source, alias);
- goto OUT;
- }
+ TpHandle handle = g_array_index (handles, TpHandle, i);
+ GabbleConnectionAliasSource current_source;
+ gchar *alias = NULL;
+ GValue entry = { 0, };
+
+ current_source = _gabble_connection_get_cached_alias (conn, handle,
+ &alias);
+ g_assert (current_source != GABBLE_CONNECTION_ALIAS_NONE);
+
+ /* if the active alias for this handle is already known and from
+ * a higher priority, this signal is not interesting so we do
+ * nothing */
+ if (signal_source < current_source)
+ {
+ DEBUG ("ignoring boring alias change for handle %u, signal from %u "
+ "but source %u has alias \"%s\"", handle, signal_source,
+ current_source, alias);
+ g_free (alias);
+ continue;
+ }
- g_value_init (&entry, TP_STRUCT_TYPE_ALIAS_PAIR);
- g_value_take_boxed (&entry, dbus_g_type_specialized_construct
- (TP_STRUCT_TYPE_ALIAS_PAIR));
+ g_value_init (&entry, TP_STRUCT_TYPE_ALIAS_PAIR);
+ g_value_take_boxed (&entry,
+ dbus_g_type_specialized_construct (TP_STRUCT_TYPE_ALIAS_PAIR));
- dbus_g_type_struct_set (&entry,
- 0, handle,
- 1, alias,
- G_MAXUINT);
+ dbus_g_type_struct_set (&entry,
+ 0, handle,
+ 1, alias,
+ G_MAXUINT);
- aliases = g_ptr_array_sized_new (1);
- g_ptr_array_add (aliases, g_value_get_boxed (&entry));
+ g_ptr_array_add (aliases, g_value_get_boxed (&entry));
+ /* Check whether the roster has an entry for the handle and if so, set
+ * the roster alias so the vCard isn't fetched on every connect. */
+ if (signal_source < GABBLE_CONNECTION_ALIAS_FROM_ROSTER &&
+ gabble_roster_handle_has_entry (conn->roster, handle))
+ gabble_roster_handle_set_name (conn->roster, handle, alias, NULL);
- tp_svc_connection_interface_aliasing_emit_aliases_changed (conn, aliases);
+ g_free (alias);
+ }
- g_value_unset (&entry);
- g_ptr_array_free (aliases, TRUE);
+ if (aliases->len > 0)
+ tp_svc_connection_interface_aliasing_emit_aliases_changed (conn, aliases);
- /* Check whether the roster has an entry for the handle and if so, set the
- * roster alias so the vCard isn't fetched on every connect. */
- if (signal_source < GABBLE_CONNECTION_ALIAS_FROM_ROSTER &&
- gabble_roster_handle_has_entry (conn->roster, handle))
- gabble_roster_handle_set_name (conn->roster, handle, alias, NULL);
+ for (i = 0; i < aliases->len; i++)
+ g_boxed_free (TP_STRUCT_TYPE_ALIAS_PAIR, g_ptr_array_index (aliases, i));
-OUT:
- g_free (alias);
+ g_ptr_array_unref (aliases);
}
static void
@@ -862,29 +910,18 @@ maybe_set (gchar **target,
*target = g_strdup (source);
}
-GabbleConnectionAliasSource
-_gabble_connection_get_cached_alias (GabbleConnection *conn,
- TpHandle handle,
- gchar **alias)
+static GabbleConnectionAliasSource
+get_cached_remote_alias (
+ GabbleConnection *conn,
+ TpHandleRepoIface *contact_handles,
+ TpHandle handle,
+ const gchar *jid,
+ gchar **alias)
{
TpBaseConnection *base = (TpBaseConnection *) conn;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
- TP_HANDLE_TYPE_CONTACT);
GabblePresence *pres;
- const gchar *tmp, *jid;
- gchar *resource = NULL;
-
- g_return_val_if_fail (NULL != conn, GABBLE_CONNECTION_ALIAS_NONE);
- g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), GABBLE_CONNECTION_ALIAS_NONE);
- g_return_val_if_fail (tp_handle_is_valid (contact_handles, handle, NULL),
- GABBLE_CONNECTION_ALIAS_NONE);
-
- tmp = gabble_roster_handle_get_name (conn->roster, handle);
- if (NULL != tmp)
- {
- maybe_set (alias, tmp);
- return GABBLE_CONNECTION_ALIAS_FROM_ROSTER;
- }
+ const gchar *tmp;
+ gchar *resource;
tmp = tp_handle_get_qdata (contact_handles, handle,
gabble_conn_aliasing_pep_alias_quark ());
@@ -918,12 +955,8 @@ _gabble_connection_get_cached_alias (GabbleConnection *conn,
}
}
- jid = tp_handle_inspect (contact_handles, handle);
- g_assert (NULL != jid);
-
-
/* MUC handles have the nickname in the resource */
- if (gabble_decode_jid (jid, NULL, NULL, &resource) &&
+ if (wocky_decode_jid (jid, NULL, NULL, &resource) &&
NULL != resource)
{
set_or_clear (alias, resource);
@@ -942,9 +975,106 @@ _gabble_connection_get_cached_alias (GabbleConnection *conn,
}
}
- /* otherwise just take their jid */
+ maybe_set (alias, NULL);
+ return GABBLE_CONNECTION_ALIAS_NONE;
+}
+
+/*
+ * _gabble_connection_get_cached_alias:
+ * @conn: a connection
+ * @handle: a handle
+ * @alias: (allow-none): location at which to store @handle's alias. If
+ * provided, it will always be set to a non-NULL, non-empty string,
+ * which the caller must free.
+ *
+ * Gets the best possible alias for @handle, falling back to their JID if
+ * necessary.
+ *
+ * Returns: the source of the alias.
+ */
+GabbleConnectionAliasSource
+_gabble_connection_get_cached_alias (GabbleConnection *conn,
+ TpHandle handle,
+ gchar **alias)
+{
+ TpBaseConnection *base = (TpBaseConnection *) conn;
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+ const gchar *tmp, *jid;
+ gboolean roster_alias_was_jid = FALSE;
+ GabbleConnectionAliasSource source;
+
+ g_return_val_if_fail (NULL != conn, GABBLE_CONNECTION_ALIAS_NONE);
+ g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), GABBLE_CONNECTION_ALIAS_NONE);
+ g_return_val_if_fail (tp_handle_is_valid (contact_handles, handle, NULL),
+ GABBLE_CONNECTION_ALIAS_NONE);
+
+ jid = tp_handle_inspect (contact_handles, handle);
+ g_assert (NULL != jid);
+
+ tmp = gabble_roster_handle_get_name (conn->roster, handle);
+ if (!tp_strdiff (tmp, jid))
+ {
+ /* Normally, we prefer whatever we've cached on the roster, to avoid
+ * wasting bandwidth checking for aliases by repeatedly fetching the
+ * vCard, and (more importantly) to prefer anything the local user set
+ * over what the contact says their name is.
+ *
+ * However, if the alias stored on the roster is just the contact's JID,
+ * we check for better aliases that we happen to have received from other
+ * sources (maybe a PEP nick update, or a vCard we've fetched for the
+ * avatar, or whatever). If we can't find anything better, we'll use the
+ * JID, and still say that it came from the roster: this means we don't
+ * defeat negative caching for contacts who genuinely don't have an
+ * alias.
+ */
+ roster_alias_was_jid = TRUE;
+ }
+ else if (!tp_str_empty (tmp))
+ {
+ maybe_set (alias, tmp);
+ return GABBLE_CONNECTION_ALIAS_FROM_ROSTER;
+ }
+
+ source = get_cached_remote_alias (conn, contact_handles, handle, jid, alias);
+ if (source != GABBLE_CONNECTION_ALIAS_NONE)
+ return source;
+
+ /* otherwise just take their jid, which may have been specified on the roster
+ * as the contact's alias. */
maybe_set (alias, jid);
- return GABBLE_CONNECTION_ALIAS_FROM_JID;
+ return roster_alias_was_jid ? GABBLE_CONNECTION_ALIAS_FROM_ROSTER
+ : GABBLE_CONNECTION_ALIAS_FROM_JID;
+}
+
+/*
+ * _gabble_connection_get_cached_remote_alias:
+ * @conn: a connection
+ * @handle: a handle
+ * @alias: (allow-none): location at which to store @handle's alias. If
+ * provided, it may be set to %NULL (if @handle has no cached remote
+ * alias) or a non-empty string which the caller must free.
+ *
+ * Gets the best cached alias for @handle as provided by them (such as via PEP
+ * Nicknames, in their vCard, etc), not considering anything the local user has
+ * specified on their roster.
+ *
+ * Returns: the source of the alias, or GABBLE_CONNECTION_ALIAS_NONE if we have
+ * no cached remote alias for @handle
+ */
+static GabbleConnectionAliasSource
+_gabble_connection_get_cached_remote_alias (
+ GabbleConnection *conn,
+ TpHandle handle,
+ gchar **alias)
+{
+ TpBaseConnection *base = (TpBaseConnection *) conn;
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+ const gchar *jid = tp_handle_inspect (contact_handles, handle);
+
+ g_assert (NULL != jid);
+ return get_cached_remote_alias (conn, contact_handles, handle, jid, alias);
}
static void
@@ -1054,7 +1184,7 @@ gabble_connection_get_aliases (TpSvcConnectionInterfaceAliasing *iface,
tp_svc_connection_interface_aliasing_return_from_get_aliases (context,
result);
- g_hash_table_destroy (result);
+ g_hash_table_unref (result);
}
diff --git a/src/conn-aliasing.h b/src/conn-aliasing.h
index 8716bb78b..353e3238e 100644
--- a/src/conn-aliasing.h
+++ b/src/conn-aliasing.h
@@ -33,6 +33,9 @@ void conn_aliasing_iface_init (gpointer g_iface, gpointer iface_data);
void gabble_conn_aliasing_nickname_updated (GObject *object,
TpHandle handle, gpointer user_data);
+void gabble_conn_aliasing_nicknames_updated (GObject *object,
+ GArray *handles, gpointer user_data);
+
GabbleConnectionAliasSource _gabble_connection_get_cached_alias (
GabbleConnection *, TpHandle, gchar **);
diff --git a/src/conn-avatars.c b/src/conn-avatars.c
index aabe8ed43..9ec2e941f 100644
--- a/src/conn-avatars.c
+++ b/src/conn-avatars.c
@@ -303,7 +303,7 @@ _got_self_avatar_for_get_known_avatar_tokens (GObject *obj,
tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens (
context->invocation, context->ret);
- g_hash_table_destroy (context->ret);
+ g_hash_table_unref (context->ret);
g_slice_free (GetKnownAvatarTokensContext, context);
}
@@ -403,7 +403,7 @@ gabble_connection_get_known_avatar_tokens (TpSvcConnectionInterfaceAvatars *ifac
tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens (
invocation, ret);
- g_hash_table_destroy (ret);
+ g_hash_table_unref (ret);
}
@@ -569,7 +569,7 @@ _request_avatar_cb (GabbleVCardManager *self,
g_array_append_vals (arr, avatar->str, avatar->len);
tp_svc_connection_interface_avatars_return_from_request_avatar (
context, arr, mime_type);
- g_array_free (arr, TRUE);
+ g_array_unref (arr);
out:
if (avatar != NULL)
@@ -640,7 +640,7 @@ emit_avatar_retrieved (TpSvcConnectionInterfaceAvatars *iface,
g_array_append_vals (arr, avatar_str->str, avatar_str->len);
tp_svc_connection_interface_avatars_emit_avatar_retrieved (iface, contact,
sha1, arr, mime_type);
- g_array_free (arr, TRUE);
+ g_array_unref (arr);
g_free (sha1);
g_string_free (avatar_str, TRUE);
}
diff --git a/src/conn-contact-info.c b/src/conn-contact-info.c
index d67aa0ee4..aaaafc7e2 100644
--- a/src/conn-contact-info.c
+++ b/src/conn-contact-info.c
@@ -57,7 +57,10 @@ typedef enum {
/* in Telepathy, one multi-line value; in XMPP, a sequence of <LINE>s */
FIELD_LABEL,
/* same as FIELD_STRUCTURED except the last element may repeat n times */
- FIELD_ORG
+ FIELD_ORG,
+
+ /* a field we intentionally ignore */
+ FIELD_IGNORED
} FieldBehaviour;
typedef struct {
@@ -128,7 +131,8 @@ static VCardField known_fields[] = {
/* Things we don't handle: */
- /* PHOTO: we treat it as the avatar instead */
+ /* PHOTO is handled by the Avatar code */
+ { "PHOTO", NULL, FIELD_IGNORED },
/* KEY: is Base64 (perhaps? hard to tell from the XEP) */
/* LOGO: can be base64 or a URL */
@@ -238,7 +242,7 @@ _create_contact_field_extended (GPtrArray *contact_info,
/* The strings in both arrays are borrowed, so we just need to free the
* arrays themselves. */
- g_ptr_array_free (field_params, TRUE);
+ g_ptr_array_unref (field_params);
g_free (field_values);
}
@@ -331,7 +335,7 @@ _parse_vcard (WockyNode *vcard_node,
_insert_contact_field (contact_info, "org", NULL,
(const gchar * const *) field_values->pdata);
- g_ptr_array_free (field_values, TRUE);
+ g_ptr_array_unref (field_values);
}
break;
@@ -371,6 +375,9 @@ _parse_vcard (WockyNode *vcard_node,
}
break;
+ case FIELD_IGNORED:
+ break;
+
default:
g_assert_not_reached ();
}
@@ -971,6 +978,9 @@ conn_contact_info_build_supported_fields (GabbleConnection *conn,
guint i;
TpContactInfoFieldFlags tp_flags = field->tp_flags;
+ if (field->behaviour == FIELD_IGNORED)
+ continue;
+
/* Shorthand to avoid having to put it in the struct initialization:
* on XMPP, there is no field that supports arbitrary type-parameters.
* Setting Parameters_Mandatory eliminates the special case that an
diff --git a/src/conn-mail-notif.c b/src/conn-mail-notif.c
index cd7aa1548..9bd5373c1 100644
--- a/src/conn-mail-notif.c
+++ b/src/conn-mail-notif.c
@@ -73,6 +73,7 @@ struct _GabbleConnectionMailNotificationPrivate
guint poll_timeout_id;
guint poll_count;
GList *inbox_url_requests; /* list of DBusGMethodInvocation */
+ gboolean should_set_google_settings;
};
@@ -125,7 +126,7 @@ return_from_request_inbox_url (GabbleConnection *conn)
if (error == NULL)
{
g_value_array_free (result);
- g_ptr_array_free (empty_array, TRUE);
+ g_ptr_array_unref (empty_array);
}
else
{
@@ -214,7 +215,7 @@ gabble_mail_notification_request_mail_url (
context, result);
g_value_array_free (result);
- g_ptr_array_free (empty_array, TRUE);
+ g_ptr_array_unref (empty_array);
g_free (url);
}
else
@@ -460,10 +461,28 @@ store_unread_mails (GabbleConnection *conn,
conn, priv->unread_count, collector.mails_added,
(const char **)mails_removed->pdata);
- g_ptr_array_free (collector.mails_added, TRUE);
- g_ptr_array_free (mails_removed, TRUE);
+ g_ptr_array_unref (collector.mails_added);
+ g_ptr_array_unref (mails_removed);
}
+static void
+set_settings_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ WockyPorter *porter = WOCKY_PORTER (source_object);
+ WockyStanza *reply = wocky_porter_send_iq_finish (porter, res, &error);
+
+ if (reply == NULL ||
+ wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL))
+ {
+ DEBUG ("Failed to set google user settings: %s", error->message);
+ g_error_free (error);
+ }
+
+ tp_clear_object (&reply);
+}
static void
query_unread_mails_cb (GObject *source_object,
@@ -580,6 +599,40 @@ new_mail_handler (WockyPorter *porter,
return TRUE;
}
+/* Make sure google knows we want mail notifications. According to
+ * Google clients should set 'mailnotifications' to true when needed
+ * but never to false, for compatibility reasons:
+ * https://code.google.com/apis/talk/jep_extensions/usersettings.html#3 */
+static void
+ensure_google_settings (GabbleConnection *self)
+{
+ TpBaseConnection *base_conn = TP_BASE_CONNECTION (self);
+ WockyStanza *query;
+ WockyPorter *porter;
+
+ if (!self->mail_priv->should_set_google_settings)
+ return;
+
+ if (base_conn->status != TP_CONNECTION_STATUS_CONNECTED)
+ return;
+
+ porter = wocky_session_get_porter (self->session);
+ query = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
+ WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL,
+ '@', "id", "user-setting-3",
+ '(', "usersetting",
+ ':', NS_GOOGLE_SETTING,
+ '(', "mailnotifications",
+ '@', "value", "true",
+ ')',
+ ')',
+ NULL);
+ wocky_porter_send_iq_async (porter, query, NULL,
+ set_settings_cb, self);
+ self->mail_priv->should_set_google_settings = FALSE;
+
+ g_object_unref (query);
+}
static void
connection_status_changed (GabbleConnection *conn,
@@ -603,10 +656,14 @@ connection_status_changed (GabbleConnection *conn,
')',
NULL);
+ if (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_SETTING)
+ conn->mail_priv->should_set_google_settings = TRUE;
+
if (conn->mail_priv->interested)
{
DEBUG ("Someone is already interested in MailNotification");
update_unread_mails (conn);
+ ensure_google_settings (conn);
}
}
}
@@ -621,6 +678,7 @@ mail_clients_interested_cb (GabbleConnection *self,
self->mail_priv->interested = TRUE;
update_unread_mails (self);
+ ensure_google_settings (self);
}
/* called on transition from 1 to 0 interested clients */
@@ -769,7 +827,7 @@ conn_mail_notif_properties_getter (GObject *object,
{
GPtrArray *mails = get_unread_mails (conn);
g_value_set_boxed (value, mails);
- g_ptr_array_free (mails, TRUE);
+ g_ptr_array_unref (mails);
}
else if (name == prop_quarks[PROP_MAIL_ADDRESS])
{
diff --git a/src/conn-olpc.c b/src/conn-olpc.c
index 9ab0ddfbb..c47fc7221 100644
--- a/src/conn-olpc.c
+++ b/src/conn-olpc.c
@@ -263,7 +263,7 @@ get_properties_reply_cb (GObject *source,
gabble_svc_olpc_buddy_info_return_from_get_properties (ctx->context,
properties);
- g_hash_table_destroy (properties);
+ g_hash_table_unref (properties);
out:
pubsub_query_ctx_free (ctx);
@@ -382,7 +382,7 @@ invitees_quark (void)
return q;
}
-void
+static void
gabble_connection_connected_olpc (GabbleConnection *conn)
{
GHashTable *preload = g_object_steal_qdata ((GObject *) conn,
@@ -391,7 +391,7 @@ gabble_connection_connected_olpc (GabbleConnection *conn)
if (preload != NULL)
{
transmit_properties (conn, preload, NULL);
- g_hash_table_destroy (preload);
+ g_hash_table_unref (preload);
}
}
@@ -427,7 +427,7 @@ olpc_buddy_info_set_properties (GabbleSvcOLPCBuddyInfo *iface,
preload = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) tp_g_value_slice_free);
g_object_set_qdata_full ((GObject *) conn, preload_quark, preload,
- (GDestroyNotify) g_hash_table_destroy);
+ (GDestroyNotify) g_hash_table_unref);
}
tp_g_hash_table_update (preload, properties,
@@ -469,7 +469,7 @@ olpc_buddy_props_pep_node_changed (WockyPepService *pep,
properties = lm_message_node_extract_properties (node, "property");
gabble_svc_olpc_buddy_info_emit_properties_changed (conn, handle,
properties);
- g_hash_table_destroy (properties);
+ g_hash_table_unref (properties);
out:
tp_handle_unref (contact_repo, handle);
}
@@ -722,7 +722,7 @@ free_activities (GPtrArray *activities)
for (i = 0; i < activities->len; i++)
g_boxed_free (GABBLE_STRUCT_TYPE_ACTIVITY, activities->pdata[i]);
- g_ptr_array_free (activities, TRUE);
+ g_ptr_array_unref (activities);
}
static void
@@ -1941,7 +1941,7 @@ olpc_activity_properties_get_properties (GabbleSvcOLPCActivityProperties *iface,
properties);
if (not_prop)
- g_hash_table_destroy (properties);
+ g_hash_table_unref (properties);
}
struct _i_hate_g_hash_table_foreach
@@ -2072,7 +2072,7 @@ update_activity_properties (GabbleConnection *conn,
if (g_hash_table_size (new_properties) == 0)
{
- g_hash_table_destroy (new_properties);
+ g_hash_table_unref (new_properties);
return;
}
@@ -2176,6 +2176,8 @@ connection_status_changed_cb (GabbleConnection *conn,
DEBUG ("Failed to send PEP activity props reset in response to "
"initial connection");
}
+
+ gabble_connection_connected_olpc (conn);
}
}
@@ -2868,7 +2870,7 @@ connection_presence_do_update (GabblePresenceCache *cache,
gabble_svc_olpc_buddy_info_emit_activities_changed (conn, handle,
empty);
- g_ptr_array_free (empty, TRUE);
+ g_ptr_array_unref (empty);
}
}
@@ -2968,20 +2970,20 @@ unref_activities_in_each_set (TpHandle handle,
void
conn_olpc_activity_properties_dispose (GabbleConnection *self)
{
- g_hash_table_destroy (self->olpc_current_act);
+ g_hash_table_unref (self->olpc_current_act);
self->olpc_current_act = NULL;
g_hash_table_foreach (self->olpc_pep_activities,
(GHFunc) unref_activities_in_each_set, self);
- g_hash_table_destroy (self->olpc_pep_activities);
+ g_hash_table_unref (self->olpc_pep_activities);
self->olpc_pep_activities = NULL;
g_hash_table_foreach (self->olpc_invited_activities,
(GHFunc) unref_activities_in_each_set, self);
- g_hash_table_destroy (self->olpc_invited_activities);
+ g_hash_table_unref (self->olpc_invited_activities);
self->olpc_invited_activities = NULL;
- g_hash_table_destroy (self->olpc_activities_info);
+ g_hash_table_unref (self->olpc_activities_info);
self->olpc_activities_info = NULL;
}
@@ -2996,7 +2998,7 @@ find_activity_by_id (GabbleConnection *self,
while (g_hash_table_iter_next (&iter, &key, &value))
{
GabbleOlpcActivity *activity = GABBLE_OLPC_ACTIVITY (value);
- if (strcmp (activity->id, activity_id) == 0)
+ if (!tp_strdiff (activity->id, activity_id))
return activity;
}
diff --git a/src/conn-olpc.h b/src/conn-olpc.h
index 43be890bc..11958c40e 100644
--- a/src/conn-olpc.h
+++ b/src/conn-olpc.h
@@ -27,8 +27,6 @@
void
olpc_buddy_info_iface_init (gpointer g_iface, gpointer iface_data);
-void gabble_connection_connected_olpc (GabbleConnection *conn);
-
void
olpc_activity_properties_iface_init (gpointer g_iface, gpointer iface_data);
diff --git a/src/conn-power-saving.c b/src/conn-power-saving.c
index 7c47396c0..e1f25ad95 100644
--- a/src/conn-power-saving.c
+++ b/src/conn-power-saving.c
@@ -139,13 +139,12 @@ conn_power_saving_set_power_saving (
DEBUG ("%sabling presence queueing", enable ? "en" : "dis");
- /* Of course, the Google Talk server doesn't advertise support for
- * google:queue. So we use google:roster. We still support the hypothetically
- * advertised google:queue just in case google starts using it, or another
- * server implementation adopts it. google:queue is described here:
- * http://mail.jabber.org/pipermail/summit/2010-February/000528.html */
- if (self->features & (GABBLE_CONNECTION_FEATURES_GOOGLE_QUEUE |
- GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER))
+ /* google:queue is loosely described here:
+ * <http://mail.jabber.org/pipermail/summit/2010-February/000528.html>. Since
+ * April 2011, it is advertised as a stream feature by the Google Talk
+ * server; the development version of M-Link, and possibly other servers,
+ * also implement the protocol and advertise this stream feature. */
+ if (self->features & GABBLE_CONNECTION_FEATURES_GOOGLE_QUEUE)
{
ToggleQueueingContext *queueing_context;
queueing_context = g_slice_new0 (ToggleQueueingContext);
diff --git a/src/conn-presence.c b/src/conn-presence.c
index 0072b09cf..22283b4dc 100644
--- a/src/conn-presence.c
+++ b/src/conn-presence.c
@@ -83,6 +83,9 @@ struct _GabbleConnectionPresencePrivate {
/* The shared status IQ handler */
guint iq_shared_status_cb;
+
+ /* The previous presence when using shared status */
+ GabblePresenceId previous_shared_status;
};
static const TpPresenceStatusOptionalArgumentSpec gabble_status_arguments[] = {
@@ -223,7 +226,7 @@ construct_contact_statuses_cb (GObject *obj,
}
contact_status = tp_presence_status_new (status, parameters);
- g_hash_table_destroy (parameters);
+ g_hash_table_unref (parameters);
g_hash_table_insert (contact_statuses, GUINT_TO_POINTER (handle),
contact_status);
@@ -251,7 +254,7 @@ conn_presence_emit_presence_update (
contact_statuses = construct_contact_statuses_cb ((GObject *) self,
contact_handles, NULL);
tp_presence_mixin_emit_presence_update ((GObject *) self, contact_statuses);
- g_hash_table_destroy (contact_statuses);
+ g_hash_table_unref (contact_statuses);
}
@@ -270,7 +273,7 @@ emit_presences_changed_for_self (GabbleConnection *self)
g_array_insert_val (handles, 0, base->self_handle);
conn_presence_emit_presence_update (self, handles);
- g_array_free (handles, TRUE);
+ g_array_unref (handles);
}
static WockyStanza *
@@ -317,6 +320,12 @@ build_shared_status_stanza (GabbleConnection *self)
return iq;
}
+static gboolean
+is_presence_away (GabblePresenceId status)
+{
+ return status == GABBLE_PRESENCE_AWAY || status == GABBLE_PRESENCE_XA;
+}
+
static void
set_shared_status_cb (GObject *source_object,
GAsyncResult *res,
@@ -324,18 +333,41 @@ set_shared_status_cb (GObject *source_object,
{
GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
GabbleConnection *self = GABBLE_CONNECTION (source_object);
+ GabbleConnectionPresencePrivate *priv = self->presence_priv;
+ GabblePresence *presence = self->self_presence;
GError *error = NULL;
- if (!conn_util_send_iq_finish (self, res, NULL, &error) ||
- !conn_presence_signal_own_presence (self, NULL, &error))
+ if (!conn_util_send_iq_finish (self, res, NULL, &error))
{
g_simple_async_result_set_error (result,
CONN_PRESENCE_ERROR, CONN_PRESENCE_ERROR_SET_SHARED_STATUS,
"error setting Google shared status: %s", error->message);
}
+ else
+ {
+ gabble_muc_factory_broadcast_presence (self->muc_factory);
- g_simple_async_result_complete (result);
- g_object_unref (result);
+ if (is_presence_away (priv->previous_shared_status))
+ {
+ /* To use away and xa we need to send a <presence/> to the server,
+ * but then GTalk also expects us to leave the status using
+ * <presence/> too. */
+ conn_presence_signal_own_presence (self, NULL, &error);
+ }
+ else if (priv->previous_shared_status == GABBLE_PRESENCE_HIDDEN &&
+ is_presence_away (presence->status))
+ {
+ /* We sent the shared status change to leave the invisibility, so
+ * now we can actually go to away / xa. */
+ conn_presence_signal_own_presence (self, NULL, &error);
+ emit_presences_changed_for_self (self);
+ }
+
+ priv->previous_shared_status = presence->status;
+ }
+
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
if (error != NULL)
g_error_free (error);
@@ -349,7 +381,7 @@ insert_presence_to_shared_statuses (GabbleConnection *self)
const gchar *show = presence->status == GABBLE_PRESENCE_DND ? "dnd" : "default";
gchar **statuses = g_hash_table_lookup (priv->shared_statuses, show);
- if (presence->status_message == NULL)
+ if (presence->status_message == NULL || is_presence_away (presence->status))
return;
if (statuses == NULL)
@@ -380,23 +412,52 @@ set_shared_status (GabbleConnection *self,
{
GabbleConnectionPresencePrivate *priv = self->presence_priv;
GabblePresence *presence = self->self_presence;
- WockyStanza *iq;
g_object_ref (result);
- DEBUG ("shared status invisibility is %savailable",
- priv->shared_status_compat ? "" : "un");
+ /* Away is treated like idleness in GTalk; it's per connection and not
+ * global. To set the presence as away we use the traditional <presence/>,
+ * but, if we were invisible, we need to first leave invisibility. */
+ if (!is_presence_away (presence->status) ||
+ priv->previous_shared_status == GABBLE_PRESENCE_HIDDEN)
+ {
+ WockyStanza *iq;
- if (presence->status == GABBLE_PRESENCE_HIDDEN && !priv->shared_status_compat)
- presence->status = GABBLE_PRESENCE_DND;
+ DEBUG ("shared status invisibility is %savailable",
+ priv->shared_status_compat ? "" : "un");
- insert_presence_to_shared_statuses (self);
+ if (presence->status == GABBLE_PRESENCE_HIDDEN && !priv->shared_status_compat)
+ presence->status = GABBLE_PRESENCE_DND;
- iq = build_shared_status_stanza (self);
+ insert_presence_to_shared_statuses (self);
- conn_util_send_iq_async (self, iq, NULL, set_shared_status_cb, result);
+ iq = build_shared_status_stanza (self);
- g_object_unref (iq);
+ conn_util_send_iq_async (self, iq, NULL, set_shared_status_cb, result);
+
+ g_object_unref (iq);
+ }
+ else
+ {
+ gboolean retval;
+ GError *error = NULL;
+
+ DEBUG ("not updating shared status as it's not supported for away");
+
+ retval = conn_presence_signal_own_presence (self, NULL, &error);
+ if (!retval)
+ {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ }
+
+ emit_presences_changed_for_self (self);
+
+ priv->previous_shared_status = presence->status;
+
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ }
}
static void
@@ -758,7 +819,7 @@ store_shared_statuses (GabbleConnection *self,
NULL);
if (priv->shared_statuses != NULL)
- g_hash_table_destroy (priv->shared_statuses);
+ g_hash_table_unref (priv->shared_statuses);
priv->shared_statuses = g_hash_table_new_full (
g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev);
@@ -801,8 +862,17 @@ store_shared_statuses (GabbleConnection *self,
}
}
+ /* - status-min-ver == 0 means that at least one resource doesn't support
+ * Google shared status, so we fallback to "dnd".
+ * - status-min-ver == 1 means that all the resources support shared
+ * status, but at least one doesn't support invisibility; we have to fall
+ * fall back to "dnd".
+ * - status-miv-ver == 2 means that all the resources support shared status
+ * with invisibility.
+ * - any other value means that the other resources will have to fall back
+ * to version 2 for us. */
priv->shared_status_compat =
- g_strcmp0 (min_version, GOOGLE_SHARED_STATUS_VERSION) == 0;
+ (g_strcmp0 (min_version, "0") != 0 && g_strcmp0 (min_version, "1") != 0);
if (invisible)
{
@@ -821,12 +891,23 @@ store_shared_statuses (GabbleConnection *self,
presence_id = GABBLE_PRESENCE_AVAILABLE;
}
- /* If we are connected, use the new shared status. If not, override with local */
- if (base->status == TP_CONNECTION_STATUS_CONNECTED)
- rv = gabble_presence_update (self->self_presence, resource, presence_id,
- status_message, prio, NULL, time (NULL));
+ if (base->status != TP_CONNECTION_STATUS_CONNECTED)
+ {
+ /* Not connected, override with the local status. */
+ rv = TRUE;
+ }
+ else if (is_presence_away (self->self_presence->status))
+ {
+ /* Away presence is not overridden with remote presence because it's
+ * per connection. */
+ rv = FALSE;
+ }
else
- rv = TRUE;
+ {
+ /* Update with the remote presence */
+ rv = gabble_presence_update (self->self_presence, resource, presence_id,
+ status_message, prio, NULL, time (NULL));
+ }
g_free (resource);
@@ -998,6 +1079,10 @@ get_shared_status_async (GabbleConnection *self,
conn_util_send_iq_async (self, iq, NULL, get_shared_status_cb, result);
+ /* We cannot use the chat status with GTalk's shared status. */
+ if (self->self_presence->status == GABBLE_PRESENCE_CHAT)
+ self->self_presence->status = GABBLE_PRESENCE_AVAILABLE;
+
g_object_unref (iq);
}
@@ -1354,6 +1439,38 @@ privacy_lists_loaded_cb (GObject *source_object,
}
static void
+shared_status_toggle_initial_presence_visibility_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (source_object);
+ GSimpleAsyncResult *external_result = G_SIMPLE_ASYNC_RESULT (user_data);
+ GError *error = NULL;
+
+ if (!toggle_presence_visibility_finish (self, result, &error))
+ {
+ g_simple_async_result_set_from_error (external_result, error);
+ g_clear_error (&error);
+ }
+ else if (self->self_presence->status != GABBLE_PRESENCE_AWAY &&
+ self->self_presence->status != GABBLE_PRESENCE_XA)
+ {
+ /* With shared status we send the normal <presence/> only with away and
+ * extended away, but for initial status we need to send <presence/> as
+ * it also contains the caps. */
+ if (!conn_presence_signal_own_presence (self, NULL, &error))
+ {
+ g_simple_async_result_set_from_error (external_result, error);
+ g_error_free (error);
+ }
+ }
+
+ g_simple_async_result_complete_in_idle (external_result);
+
+ g_object_unref (external_result);
+}
+
+static void
shared_status_setup_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
@@ -1383,7 +1500,8 @@ shared_status_setup_cb (GObject *source_object,
g_error_free (error);
}
- get_existing_privacy_lists_async (self, privacy_lists_loaded_cb, user_data);
+ toggle_presence_visibility_async (self,
+ shared_status_toggle_initial_presence_visibility_cb, user_data);
}
void
@@ -1780,6 +1898,12 @@ status_available_cb (GObject *obj, guint status)
TpConnectionPresenceType presence_type =
gabble_statuses[status].presence_type;
+ if (base->status != TP_CONNECTION_STATUS_CONNECTED)
+ {
+ /* we just don't know yet */
+ return TRUE;
+ }
+
/* This relies on the fact the first entries in the statuses table
* are from gabble_base_statuses. If index to the statuses table is outside
* the gabble_base_statuses table, the status is provided by a plugin. */
@@ -1789,36 +1913,35 @@ status_available_cb (GObject *obj, guint status)
* lists, so any extra status should be backed by one. If it's not
* (or if privacy lists are not supported by the server at all)
* by the time we're connected, it's not available. */
-
- if (base->status == TP_CONNECTION_STATUS_CONNECTED)
+ if (priv->privacy_statuses != NULL &&
+ g_hash_table_lookup (priv->privacy_statuses,
+ gabble_statuses[status].name))
{
- if (priv->privacy_statuses != NULL &&
- g_hash_table_lookup (priv->privacy_statuses,
- gabble_statuses[status].name))
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
+ return TRUE;
}
else
{
- /* we just don't know yet */
- return TRUE;
+ return FALSE;
}
}
- /* If we've gone online and found that the server doesn't support invisible,
- * reject it.
- */
- if (base->status == TP_CONNECTION_STATUS_CONNECTED &&
- presence_type == TP_CONNECTION_PRESENCE_TYPE_HIDDEN &&
+ if (presence_type == TP_CONNECTION_PRESENCE_TYPE_HIDDEN &&
priv->invisibility_method == INVISIBILITY_METHOD_NONE)
- return FALSE;
+ {
+ /* If we've gone online and found that the server doesn't support
+ * invisible, reject it. */
+ return FALSE;
+ }
+ else if (status == GABBLE_PRESENCE_CHAT &&
+ priv->shared_statuses != NULL)
+ {
+ /* We cannot use the chat status with GTalk's shared status. */
+ return FALSE;
+ }
else
- return TRUE;
+ {
+ return TRUE;
+ }
}
GabblePresenceId
@@ -1853,6 +1976,7 @@ void
conn_presence_init (GabbleConnection *conn)
{
conn->presence_priv = g_slice_new0 (GabbleConnectionPresencePrivate);
+ conn->presence_priv->previous_shared_status = GABBLE_PRESENCE_UNKNOWN;
g_signal_connect (conn->presence_cache, "presences-updated",
G_CALLBACK (connection_presences_updated_cb), conn);
@@ -1897,22 +2021,17 @@ conn_presence_finalize (GabbleConnection *conn)
g_free (priv->invisible_list_name);
if (priv->privacy_statuses != NULL)
- g_hash_table_destroy (priv->privacy_statuses);
+ g_hash_table_unref (priv->privacy_statuses);
if (priv->shared_statuses != NULL)
- g_hash_table_destroy (priv->shared_statuses);
+ g_hash_table_unref (priv->shared_statuses);
if (priv->iq_list_push_cb != NULL)
lm_message_handler_unref (priv->iq_list_push_cb);
- tp_presence_mixin_finalize ((GObject *) conn);
-}
-
+ g_slice_free (GabbleConnectionPresencePrivate, priv);
-void
-conn_presence_iface_init (gpointer g_iface, gpointer iface_data)
-{
- tp_presence_mixin_iface_init (g_iface, iface_data);
+ tp_presence_mixin_finalize ((GObject *) conn);
}
static void
diff --git a/src/connection.c b/src/connection.c
index c676b828b..312d33952 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -18,14 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "config.h"
#include "connection.h"
#include "gabble.h"
+#include <stdio.h>
#include <string.h>
-#define DBUS_API_SUBJECT_TO_CHANGE
-
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <glib-object.h>
@@ -34,7 +32,9 @@
#include <wocky/wocky-disco-identity.h>
#include <wocky/wocky-tls-handler.h>
#include <wocky/wocky-ping.h>
+#include <wocky/wocky-utils.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 +50,8 @@
#include <gabble/error.h>
#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"
@@ -86,6 +86,7 @@
#include "util.h"
#include "vcard-manager.h"
#include "conn-util.h"
+#include "conn-addressing.h"
static guint disco_reply_timeout = 5;
@@ -117,10 +118,10 @@ G_DEFINE_TYPE_WITH_CODE(GabbleConnection,
tp_base_contact_list_mixin_list_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_GROUPS,
tp_base_contact_list_mixin_groups_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_BLOCKING,
+ tp_base_contact_list_mixin_blocking_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
tp_presence_mixin_simple_presence_iface_init);
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
- conn_presence_iface_init);
G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CONNECTION_INTERFACE_GABBLE_DECLOAK,
conn_decloak_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION,
@@ -140,6 +141,8 @@ G_DEFINE_TYPE_WITH_CODE(GabbleConnection,
conn_client_types_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_POWER_SAVING,
conn_power_saving_iface_init);
+ G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CONNECTION_INTERFACE_ADDRESSING,
+ conn_addressing_iface_init);
)
/* properties */
@@ -237,6 +240,10 @@ struct _GabbleConnectionPrivate
/* serial number of current advertised caps */
guint caps_serial;
+ /* Last activity time for XEP-0012 purposes, where "activity" is defined to
+ * mean "sending a message".
+ */
+ time_t last_activity_time;
/* capabilities from various sources: */
/* subscriptions on behalf of the Connection, like PEP "+notify"
@@ -256,6 +263,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;
@@ -309,8 +320,8 @@ _gabble_connection_create_channel_managers (TpBaseConnection *conn)
GPtrArray *tmp;
self->roster = gabble_roster_new (self);
- g_signal_connect (self->roster, "nickname-update", G_CALLBACK
- (gabble_conn_aliasing_nickname_updated), self);
+ g_signal_connect (self->roster, "nicknames-update", G_CALLBACK
+ (gabble_conn_aliasing_nicknames_updated), self);
g_ptr_array_add (channel_managers, self->roster);
self->priv->im_factory = g_object_new (GABBLE_TYPE_IM_FACTORY,
@@ -354,8 +365,10 @@ _gabble_connection_create_channel_managers (TpBaseConnection *conn)
"connection", self,
NULL));
+#ifdef ENABLE_FILE_TRANSFER
self->ft_manager = gabble_ft_manager_new (self);
g_ptr_array_add (channel_managers, self->ft_manager);
+#endif
/* plugin channel managers */
loader = gabble_plugin_loader_dup ();
@@ -363,7 +376,7 @@ _gabble_connection_create_channel_managers (TpBaseConnection *conn)
g_object_unref (loader);
g_ptr_array_foreach (tmp, add_to_array, channel_managers);
- g_ptr_array_free (tmp, TRUE);
+ g_ptr_array_unref (tmp);
return channel_managers;
}
@@ -412,6 +425,7 @@ gabble_connection_constructor (GType type,
conn_sidecars_init (self);
conn_mail_notif_init (self);
conn_client_types_init (self);
+ conn_addressing_init (self);
tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (self),
TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES,
@@ -442,6 +456,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. */
@@ -519,6 +536,7 @@ gabble_connection_init (GabbleConnection *self)
self->lmconn = lm_connection_new ();
priv->caps_serial = 1;
+ priv->last_activity_time = time (NULL);
priv->port = 5222;
gabble_capabilities_init (self);
@@ -568,7 +586,11 @@ gabble_connection_get_property (GObject *object,
g_value_set_string (value, priv->resource);
break;
case PROP_PRIORITY:
+#if GLIB_CHECK_VERSION (2, 31, 0)
+ g_value_set_schar (value, priv->priority);
+#else
g_value_set_char (value, priv->priority);
+#endif
break;
case PROP_HTTPS_PROXY_SERVER:
g_value_set_string (value, priv->https_proxy_server);
@@ -690,7 +712,11 @@ gabble_connection_set_property (GObject *object,
}
break;
case PROP_PRIORITY:
+#if GLIB_CHECK_VERSION (2, 31, 0)
+ priv->priority = g_value_get_schar (value);
+#else
priv->priority = g_value_get_char (value);
+#endif
break;
case PROP_HTTPS_PROXY_SERVER:
g_free (priv->https_proxy_server);
@@ -801,14 +827,6 @@ _gabble_connection_create_handle_repos (TpBaseConnection *conn,
conn);
}
-static void
-base_connected_cb (TpBaseConnection *base_conn)
-{
- GabbleConnection *conn = GABBLE_CONNECTION (base_conn);
-
- gabble_connection_connected_olpc (conn);
-}
-
#define TWICE(x) (x), (x)
static const gchar *implemented_interfaces[] = {
@@ -834,6 +852,7 @@ static const gchar *implemented_interfaces[] = {
GABBLE_IFACE_CONNECTION_INTERFACE_GABBLE_DECLOAK,
GABBLE_IFACE_CONNECTION_FUTURE,
TP_IFACE_CONNECTION_INTERFACE_CLIENT_TYPES,
+ GABBLE_IFACE_CONNECTION_INTERFACE_ADDRESSING,
NULL
};
static const gchar **interfaces_always_present = implemented_interfaces + 3;
@@ -926,7 +945,6 @@ gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class)
parent_class->create_channel_factories = NULL;
parent_class->create_channel_managers =
_gabble_connection_create_channel_managers;
- parent_class->connected = base_connected_cb;
parent_class->shut_down = connection_shut_down;
parent_class->start_connecting = _gabble_connection_connect;
parent_class->interfaces_always_present = interfaces_always_present;
@@ -1140,7 +1158,7 @@ gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class)
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (
- object_class, PROP_DECLOAK_AUTOMATICALLY,
+ object_class, PROP_POWER_SAVING,
g_param_spec_boolean (
"power-saving", "Power saving active?",
"Queue remote presence updates server-side for less network chatter",
@@ -1207,8 +1225,8 @@ gabble_connection_dispose (GObject *object)
conn_olpc_activity_properties_dispose (self);
- g_hash_table_destroy (self->avatar_requests);
- g_hash_table_destroy (self->vcard_requests);
+ g_hash_table_unref (self->avatar_requests);
+ g_hash_table_unref (self->vcard_requests);
conn_presence_dispose (self);
@@ -1221,13 +1239,15 @@ gabble_connection_dispose (GObject *object)
priv->porter = NULL;
tp_clear_pointer (&self->lmconn, lm_connection_unref);
- g_hash_table_destroy (priv->client_caps);
+ g_hash_table_unref (priv->client_caps);
gabble_capability_set_free (priv->all_caps);
gabble_capability_set_free (priv->notify_caps);
gabble_capability_set_free (priv->legacy_caps);
gabble_capability_set_free (priv->sidecar_caps);
gabble_capability_set_free (priv->bonus_caps);
+ g_hash_table_unref (priv->client_data_forms);
+
if (priv->disconnect_timer != 0)
{
g_source_remove (priv->disconnect_timer);
@@ -1309,7 +1329,7 @@ _gabble_connection_set_properties_from_account (GabbleConnection *conn,
username = server = resource = NULL;
result = TRUE;
- if (!gabble_decode_jid (account, &username, &server, &resource))
+ if (!wocky_decode_jid (account, &username, &server, &resource))
{
g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
"unable to extract JID from account name");
@@ -1368,6 +1388,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
*
@@ -1389,6 +1417,18 @@ _gabble_connection_send (GabbleConnection *conn, LmMessage *msg, GError **error)
return TRUE;
}
+void
+gabble_connection_update_last_use (GabbleConnection *conn)
+{
+ conn->priv->last_activity_time = time (NULL);
+}
+
+static gdouble
+gabble_connection_get_last_use (GabbleConnection *conn)
+{
+ return difftime (time (NULL), conn->priv->last_activity_time);
+}
+
typedef struct {
GabbleConnectionMsgReplyFunc reply_func;
@@ -1837,7 +1877,10 @@ connector_connected (GabbleConnection *self,
self->session = wocky_session_new_with_connection (conn, jid);
priv->porter = wocky_session_get_porter (self->session);
- priv->pinger = wocky_ping_new (priv->porter, priv->keepalive_interval);
+
+ g_assert (WOCKY_IS_C2S_PORTER (priv->porter));
+ priv->pinger = wocky_ping_new (WOCKY_C2S_PORTER (priv->porter),
+ priv->keepalive_interval);
g_signal_connect (priv->porter, "remote-closed",
G_CALLBACK (remote_closed_cb), self);
@@ -1980,6 +2023,41 @@ connector_register_cb (GObject *source,
g_free (jid);
}
+static gboolean
+connection_iq_last_cb (
+ WockyPorter *porter,
+ WockyStanza *stanza,
+ gpointer user_data)
+{
+ GabbleConnection *self = GABBLE_CONNECTION (user_data);
+ const gchar *from = wocky_stanza_get_from (stanza);
+ /* Aside from 21 being an appropriate number, 2 ^ 64 is 20 digits long. */
+ char seconds[21];
+
+ /* Check if the peer, if any, is authorized to receive our presence. */
+ if (from != NULL)
+ {
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *) self, TP_HANDLE_TYPE_CONTACT);
+ TpHandle handle = tp_handle_lookup (contact_repo, from, NULL, NULL);
+
+ /* If there's no handle for them, they're certainly not on the roster. */
+ if (handle == 0 ||
+ !gabble_roster_handle_gets_presence_from_us (self->roster, handle))
+ {
+ wocky_porter_send_iq_error (porter, stanza,
+ WOCKY_XMPP_ERROR_FORBIDDEN, NULL);
+ return TRUE;
+ }
+ }
+
+ sprintf (seconds, "%.0f", gabble_connection_get_last_use (self));
+ wocky_porter_acknowledge_iq (porter, stanza,
+ '(', "query", ':', NS_LAST, '@', "seconds", seconds, ')',
+ NULL);
+ return TRUE;
+}
+
static void
connect_iq_callbacks (GabbleConnection *conn)
{
@@ -1997,6 +2075,12 @@ connect_iq_callbacks (GabbleConnection *conn)
iq_version_cb, conn,
'(', "query", ':', NS_VERSION, ')', NULL);
+ wocky_porter_register_handler_from_anyone (priv->porter,
+ WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET,
+ WOCKY_PORTER_HANDLER_PRIORITY_NORMAL,
+ connection_iq_last_cb, conn,
+ '(', "query", ':', NS_LAST, ')', NULL);
+
/* FIXME: the porter should do this for us. */
wocky_porter_register_handler_from_anyone (priv->porter,
WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_NONE,
@@ -2253,7 +2337,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 */
@@ -2270,8 +2355,10 @@ gabble_connection_fill_in_caps (GabbleConnection *self,
if (voice_v1)
g_string_append (ext, " " BUNDLE_VOICE_V1);
- if (video_v1)
+ if (video_v1) {
g_string_append (ext, " " BUNDLE_VIDEO_V1);
+ g_string_append (ext, " " BUNDLE_CAMERA_V1);
+ }
wocky_node_set_attribute (node, "ext", ext->str);
g_string_free (ext, TRUE);
@@ -2349,10 +2436,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);
@@ -2360,6 +2450,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))
@@ -2375,13 +2466,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;
}
@@ -2390,6 +2490,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;
}
@@ -2397,6 +2498,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;
@@ -2407,6 +2509,8 @@ gabble_connection_refresh_capabilities (GabbleConnection *self,
else
*old_out = save_set;
+ g_ptr_array_unref (data_forms);
+
return TRUE;
}
@@ -2468,6 +2572,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");
@@ -2497,19 +2602,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)
@@ -2548,6 +2659,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))
{
wocky_porter_send_iq_error (porter, stanza,
@@ -2633,6 +2756,13 @@ set_status_to_connected (GabbleConnection *conn)
{
TpBaseConnection *base = (TpBaseConnection *) conn;
+ if (base->status == TP_CONNECTION_STATUS_DISCONNECTED)
+ {
+ /* We already failed to connect, but at the time an async thing was
+ * still pending, and now it has finished. Do nothing special. */
+ return;
+ }
+
if (conn->features & GABBLE_CONNECTION_FEATURES_PEP)
{
const gchar *ifaces[] = { GABBLE_IFACE_OLPC_BUDDY_INFO,
@@ -2650,6 +2780,14 @@ set_status_to_connected (GabbleConnection *conn)
tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces);
}
+ if (tp_base_contact_list_can_block (gabble_connection_get_contact_list (conn)))
+ {
+ const gchar *ifaces[] =
+ { TP_IFACE_CONNECTION_INTERFACE_CONTACT_BLOCKING, NULL };
+
+ tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces);
+ }
+
/* go go gadget on-line */
tp_base_connection_change_status (base,
TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED);
@@ -2737,6 +2875,8 @@ connection_disco_cb (GabbleDisco *disco,
conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_SHARED_STATUS;
else if (0 == strcmp (var, NS_GOOGLE_QUEUE))
conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_QUEUE;
+ else if (0 == strcmp (var, NS_GOOGLE_SETTING))
+ conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_SETTING;
}
}
@@ -2897,7 +3037,7 @@ _emit_capabilities_changed (GabbleConnection *conn,
g_boxed_free (TP_STRUCT_TYPE_CAPABILITY_CHANGE,
g_ptr_array_index (caps_arr, i));
}
- g_ptr_array_free (caps_arr, TRUE);
+ g_ptr_array_unref (caps_arr);
/* o.f.T.C.ContactCapabilities */
caps_arr = gabble_connection_build_contact_caps (conn, handle, new_set);
@@ -2909,14 +3049,24 @@ _emit_capabilities_changed (GabbleConnection *conn,
tp_svc_connection_interface_contact_capabilities_emit_contact_capabilities_changed (
conn, hash);
- g_hash_table_destroy (hash);
+ g_hash_table_unref (hash);
}
-/**
+static const GabbleCapabilitySet *
+empty_caps_set (void)
+{
+ static GabbleCapabilitySet *empty = NULL;
+
+ if (G_UNLIKELY (empty == NULL))
+ empty = gabble_capability_set_new ();
+
+ return empty;
+}
+
+/*
* gabble_connection_get_handle_contact_capabilities:
*
- * Returns: a set of channel classes representing @handle's capabilities, or
- * %NULL if unknown.
+ * Returns: an array of channel classes representing @handle's capabilities
*/
static GPtrArray *
gabble_connection_get_handle_contact_capabilities (
@@ -2926,7 +3076,6 @@ gabble_connection_get_handle_contact_capabilities (
TpBaseConnection *base_conn = TP_BASE_CONNECTION (self);
GabblePresence *p;
const GabbleCapabilitySet *caps;
- GPtrArray *arr;
if (handle == base_conn->self_handle)
p = self->self_presence;
@@ -2934,20 +3083,11 @@ gabble_connection_get_handle_contact_capabilities (
p = gabble_presence_cache_get (self->presence_cache, handle);
if (p == NULL)
- {
- DEBUG ("don't know %u's presence; assuming text chat caps.", handle);
-
- arr = g_ptr_array_new ();
- gabble_caps_channel_manager_get_contact_capabilities (
- GABBLE_CAPS_CHANNEL_MANAGER (self->priv->im_factory),
- handle, NULL, arr);
-
- return arr;
- }
+ caps = empty_caps_set ();
+ else
+ caps = gabble_presence_peek_caps (p);
- caps = gabble_presence_peek_caps (p);
- arr = gabble_connection_build_contact_caps (self, handle, caps);
- return arr;
+ return gabble_connection_build_contact_caps (self, handle, caps);
}
static void
@@ -3078,7 +3218,112 @@ gabble_connection_advertise_capabilities (TpSvcConnectionInterfaceCapabilities *
context, ret);
g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL);
- g_ptr_array_free (ret, TRUE);
+ g_ptr_array_unref (ret);
+}
+
+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;
}
/**
@@ -3096,13 +3341,11 @@ gabble_connection_update_capabilities (
{
GabbleConnection *self = GABBLE_CONNECTION (iface);
TpBaseConnection *base = (TpBaseConnection *) self;
- GabbleCapabilitySet *old_caps;
+ GabbleCapabilitySet *old_caps = NULL;
TpChannelManagerIter iter;
TpChannelManager *manager;
guint i;
- old_caps = gabble_capability_set_copy (self->priv->all_caps);
-
/* Now that someone has told us our *actual* capabilities, we can stop
* advertising spurious caps in initial presence */
gabble_capability_set_clear (self->priv->bonus_caps);
@@ -3127,10 +3370,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 */
@@ -3139,6 +3384,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);
@@ -3146,18 +3393,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)
{
@@ -3170,6 +3429,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, j)));
+
+ 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))
@@ -3300,7 +3586,7 @@ conn_capabilities_fill_contact_attributes (GObject *obj,
}
if (array != NULL)
- g_ptr_array_free (array, TRUE);
+ g_ptr_array_unref (array);
}
static void
@@ -3313,21 +3599,14 @@ conn_contact_capabilities_fill_contact_attributes (GObject *obj,
for (i = 0; i < contacts->len; i++)
{
TpHandle handle = g_array_index (contacts, TpHandle, i);
- GPtrArray *array;
-
- array = gabble_connection_get_handle_contact_capabilities (self, handle);
-
- if (array != NULL)
- {
- GValue *val = tp_g_value_slice_new (
- TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST);
+ GValue *val = tp_g_value_slice_new_take_boxed (
+ TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST,
+ gabble_connection_get_handle_contact_capabilities (self, handle));
- g_value_take_boxed (val, array);
- tp_contacts_mixin_set_contact_attribute (attributes_hash,
- handle,
- TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES"/capabilities",
- val);
- }
+ tp_contacts_mixin_set_contact_attribute (attributes_hash,
+ handle,
+ TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES"/capabilities",
+ val);
}
}
@@ -3376,7 +3655,7 @@ gabble_connection_get_capabilities (TpSvcConnectionInterfaceCapabilities *iface,
g_value_array_free (g_ptr_array_index (ret, i));
}
- g_ptr_array_free (ret, TRUE);
+ g_ptr_array_unref (ret);
}
/**
@@ -3414,19 +3693,17 @@ gabble_connection_get_contact_capabilities (
for (i = 0; i < handles->len; i++)
{
- GPtrArray *arr;
TpHandle handle = g_array_index (handles, TpHandle, i);
+ GPtrArray *arr;
arr = gabble_connection_get_handle_contact_capabilities (self, handle);
-
- if (arr != NULL)
- g_hash_table_insert (ret, GUINT_TO_POINTER (handle), arr);
+ g_hash_table_insert (ret, GUINT_TO_POINTER (handle), arr);
}
tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities
(context, ret);
- g_hash_table_destroy (ret);
+ g_hash_table_unref (ret);
}
@@ -3500,7 +3777,7 @@ gabble_connection_send_presence (GabbleConnection *conn,
lm_message_node_add_own_nick (
wocky_stanza_get_top_node (message), conn);
- if (!CHECK_STR_EMPTY(status))
+ if (!tp_str_empty (status))
wocky_node_add_child_with_content (
wocky_stanza_get_top_node (message), "status", status);
@@ -3590,9 +3867,10 @@ gabble_connection_update_sidecar_capabilities (GabbleConnection *self,
/* identities is actually a WockyDiscoIdentityArray */
gchar *
-gabble_connection_add_sidecar_own_caps (GabbleConnection *self,
+gabble_connection_add_sidecar_own_caps_full (GabbleConnection *self,
const GabbleCapabilitySet *cap_set,
- const GPtrArray *identities)
+ const GPtrArray *identities,
+ GPtrArray *data_forms)
{
GPtrArray *identities_copy = ((identities == NULL) ?
wocky_disco_identity_array_new () :
@@ -3605,12 +3883,107 @@ gabble_connection_add_sidecar_own_caps (GabbleConnection *self,
wocky_disco_identity_new ("client", CLIENT_TYPE,
NULL, PACKAGE_STRING));
- ver = gabble_caps_hash_compute (cap_set, identities_copy);
+ ver = gabble_caps_hash_compute_full (cap_set, identities_copy, data_forms);
gabble_presence_cache_add_own_caps (self->presence_cache, ver,
- cap_set, identities_copy);
+ cap_set, identities_copy, data_forms);
wocky_disco_identity_array_free (identities_copy);
return ver;
}
+
+gchar *
+gabble_connection_add_sidecar_own_caps (GabbleConnection *self,
+ const GabbleCapabilitySet *cap_set,
+ const GPtrArray *identities)
+{
+ return gabble_connection_add_sidecar_own_caps_full (self, cap_set,
+ identities, NULL);
+}
+
+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 ba34a257f..77fb1e24a 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -21,6 +21,8 @@
#ifndef __GABBLE_CONNECTION_H__
#define __GABBLE_CONNECTION_H__
+#include "config.h"
+
#include <dbus/dbus-glib.h>
#include <glib-object.h>
#include <loudmouth/loudmouth.h>
@@ -34,8 +36,10 @@
#include <wocky/wocky-pep-service.h>
#include "gabble/connection.h"
-#include "capabilities.h"
+#include "gabble/capabilities.h"
+#ifdef ENABLE_FILE_TRANSFER
#include "ft-manager.h"
+#endif
#include "jingle-factory.h"
#include "muc-factory.h"
#include "types.h"
@@ -51,21 +55,23 @@ G_BEGIN_DECLS
/* order must match array of statuses in conn-presence.c */
/* in increasing order of presence */
+/*< prefix=GABBLE_PRESENCE >*/
typedef enum
{
GABBLE_PRESENCE_OFFLINE = 0,
GABBLE_PRESENCE_UNKNOWN,
GABBLE_PRESENCE_ERROR,
- GABBLE_PRESENCE_LAST_UNAVAILABLE = GABBLE_PRESENCE_ERROR,
+ GABBLE_PRESENCE_LAST_UNAVAILABLE = GABBLE_PRESENCE_ERROR, /*< skip >*/
GABBLE_PRESENCE_HIDDEN,
GABBLE_PRESENCE_XA,
GABBLE_PRESENCE_AWAY,
GABBLE_PRESENCE_DND,
GABBLE_PRESENCE_AVAILABLE,
GABBLE_PRESENCE_CHAT,
- NUM_GABBLE_PRESENCES
+ NUM_GABBLE_PRESENCES /*< skip >*/
} GabblePresenceId;
+/*< flags >*/
typedef enum
{
GABBLE_CONNECTION_FEATURES_NONE = 0,
@@ -78,6 +84,7 @@ typedef enum
GABBLE_CONNECTION_FEATURES_INVISIBLE = 1 << 6,
GABBLE_CONNECTION_FEATURES_GOOGLE_SHARED_STATUS = 1 << 7,
GABBLE_CONNECTION_FEATURES_GOOGLE_QUEUE = 1 << 8,
+ GABBLE_CONNECTION_FEATURES_GOOGLE_SETTING = 1 << 9,
} GabbleConnectionFeatures;
typedef struct _GabbleConnectionPrivate GabbleConnectionPrivate;
@@ -163,8 +170,10 @@ struct _GabbleConnection {
/* jingle factory */
GabbleJingleFactory *jingle_factory;
+#ifdef ENABLE_FILE_TRANSFER
/* file transfer manager */
GabbleFtManager *ft_manager;
+#endif
/* PEP */
WockyPepService *pep_nick;
@@ -200,8 +209,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 (
@@ -213,6 +220,7 @@ gboolean _gabble_connection_send_with_reply (GabbleConnection *conn,
gpointer user_data, GError **error);
void _gabble_connection_acknowledge_set_iq (GabbleConnection *conn,
LmMessage *iq);
+void gabble_connection_update_last_use (GabbleConnection *conn);
const char *_gabble_connection_find_conference_server (GabbleConnection *);
gchar *gabble_connection_get_canonical_room_name (GabbleConnection *conn,
diff --git a/src/debug.c b/src/debug.c
index bb19422b6..24d2b1808 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -114,7 +114,7 @@ gabble_debug_free (void)
if (flag_to_domains == NULL)
return;
- g_hash_table_destroy (flag_to_domains);
+ g_hash_table_unref (flag_to_domains);
flag_to_domains = NULL;
}
diff --git a/src/debug.h b/src/debug.h
index f51ad8a55..191a51e1c 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -4,6 +4,8 @@
#include "config.h"
#include <glib.h>
+#include <wocky/wocky-stanza.h>
+#include <loudmouth/loudmouth.h>
G_BEGIN_DECLS
@@ -89,10 +91,30 @@ G_END_DECLS
} G_STMT_END
#else /* !defined (ENABLE_DEBUG) */
-# define DEBUG(format, ...) G_STMT_START { } G_STMT_END
+static inline void
+DEBUG (
+ const gchar *format,
+ ...)
+{
+}
+
# define DEBUGGING 0
-# define STANZA_DEBUG(st, s) G_STMT_START { } G_STMT_END
-# define NODE_DEBUG(n, s) G_STMT_START { } G_STMT_END
+
+static inline void
+STANZA_DEBUG (
+ WockyStanza *stanza,
+ const gchar *format,
+ ...)
+{
+}
+
+static inline void
+NODE_DEBUG (
+ LmMessageNode *node,
+ const gchar *format,
+ ...)
+{
+}
#endif /* !defined (ENABLE_DEBUG) */
#endif /* DEBUG_FLAG */
diff --git a/src/disco.c b/src/disco.c
index fce4509a3..bee1c3e1b 100644
--- a/src/disco.c
+++ b/src/disco.c
@@ -26,8 +26,6 @@
#include <string.h>
-#define DBUS_API_SUBJECT_TO_CHANGE
-
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <telepathy-glib/dbus.h>
@@ -239,7 +237,7 @@ gabble_disco_dispose (GObject *object)
g_free ((char *) item->name);
g_free ((char *) item->category);
g_free ((char *) item->type);
- g_hash_table_destroy (item->features);
+ g_hash_table_unref (item->features);
g_free (item);
}
@@ -657,7 +655,7 @@ item_info_cb (GabbleDisco *disco,
item.features = keys;
pipeline->callback (pipeline, &item, pipeline->user_data);
- g_hash_table_destroy (keys);
+ g_hash_table_unref (keys);
done:
gabble_disco_fill_pipeline (disco, pipeline);
@@ -842,8 +840,8 @@ gabble_disco_pipeline_destroy (gpointer self)
gabble_disco_cancel_request (pipeline->disco, request);
}
- g_hash_table_destroy (pipeline->remaining_items);
- g_ptr_array_free (pipeline->disco_pipeline, TRUE);
+ g_hash_table_unref (pipeline->remaining_items);
+ g_ptr_array_unref (pipeline->disco_pipeline);
g_free (pipeline);
}
diff --git a/src/ft-channel.c b/src/ft-channel.c
index fb55fad73..878dc8fba 100644
--- a/src/ft-channel.c
+++ b/src/ft-channel.c
@@ -80,6 +80,8 @@ G_DEFINE_TYPE_WITH_CODE (GabbleFileTransferChannel, gabble_file_transfer_channel
file_transfer_iface_init);
G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CHANNEL_TYPE_FILETRANSFER_FUTURE,
NULL);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
+ NULL);
);
#define GABBLE_UNDEFINED_FILE_SIZE G_MAXUINT64
@@ -122,7 +124,14 @@ enum
PROP_CONNECTION,
PROP_BYTESTREAM,
+
+ /* Chan.Type.FileTransfer.FUTURE */
PROP_GTALK_FILE_COLLECTION,
+
+ /* Chan.Iface.FileTransfer.Metadata */
+ PROP_SERVICE_NAME,
+ PROP_METADATA,
+
LAST_PROPERTY
};
@@ -160,6 +169,8 @@ struct _GabbleFileTransferChannelPrivate {
guint64 date;
gchar *file_collection;
gchar *uri;
+ gchar *service_name;
+ GHashTable *metadata;
gboolean channel_opened;
};
@@ -317,6 +328,8 @@ gabble_file_transfer_channel_get_property (GObject *object,
TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "TransferredBytes",
TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, "InitialOffset",
GABBLE_IFACE_CHANNEL_TYPE_FILETRANSFER_FUTURE, "FileCollection",
+ TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "ServiceName",
+ TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA, "Metadata",
NULL);
/* URI is immutable only for outgoing transfers */
@@ -335,6 +348,24 @@ gabble_file_transfer_channel_get_property (GObject *object,
case PROP_GTALK_FILE_COLLECTION:
g_value_set_object (value, self->priv->gtalk_file_collection);
break;
+ case PROP_SERVICE_NAME:
+ g_value_set_string (value, self->priv->service_name);
+ break;
+ case PROP_METADATA:
+ {
+ /* We're fine with priv->metadata being NULL but dbus-glib
+ * doesn't like iterating NULL as if it was a hash table. */
+ if (self->priv->metadata == NULL)
+ {
+ g_value_take_boxed (value,
+ g_hash_table_new (g_str_hash, g_str_equal));
+ }
+ else
+ {
+ g_value_set_boxed (value, self->priv->metadata);
+ }
+ }
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -361,13 +392,10 @@ gabble_file_transfer_channel_set_property (GObject *object,
case PROP_CONNECTION:
self->priv->connection = g_value_get_object (value);
break;
+ /* these properties are writable in the interface, but not actually
+ * meaningfully changeable on this channel, so we do nothing */
case PROP_HANDLE_TYPE:
- g_assert (g_value_get_uint (value) == 0
- || g_value_get_uint (value) == TP_HANDLE_TYPE_CONTACT);
- break;
case PROP_CHANNEL_TYPE:
- /* these properties are writable in the interface, but not actually
- * meaningfully changeable on this channel, so we do nothing */
break;
case PROP_STATE:
gabble_file_transfer_channel_set_state (
@@ -426,6 +454,12 @@ gabble_file_transfer_channel_set_property (GObject *object,
set_gtalk_file_collection (self,
GTALK_FILE_COLLECTION (g_value_get_object (value)));
break;
+ case PROP_SERVICE_NAME:
+ self->priv->service_name = g_value_dup_string (value);
+ break;
+ case PROP_METADATA:
+ self->priv->metadata = g_value_dup_boxed (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -435,7 +469,7 @@ gabble_file_transfer_channel_set_property (GObject *object,
static void
free_array (GArray *array)
{
- g_array_free (array, TRUE);
+ g_array_unref (array);
}
static void
@@ -650,6 +684,12 @@ gabble_file_transfer_channel_class_init (
{ NULL }
};
+ static TpDBusPropertiesMixinPropImpl file_metadata_props[] = {
+ { "ServiceName", "service-name", NULL },
+ { "Metadata", "metadata", NULL },
+ { NULL }
+ };
+
static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
{ TP_IFACE_CHANNEL,
tp_dbus_properties_mixin_getter_gobject_properties,
@@ -666,6 +706,11 @@ gabble_file_transfer_channel_class_init (
NULL,
file_future_props
},
+ { TP_IFACE_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ file_metadata_props
+ },
{ NULL }
};
@@ -894,6 +939,22 @@ gabble_file_transfer_channel_class_init (
g_object_class_install_property (object_class, PROP_URI,
param_spec);
+ param_spec = g_param_spec_string ("service-name",
+ "ServiceName",
+ "The Metadata.ServiceName property of this channel",
+ "",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_SERVICE_NAME,
+ param_spec);
+
+ param_spec = g_param_spec_boxed ("metadata",
+ "Metadata",
+ "The Metadata.Metadata property of this channel",
+ TP_HASH_TYPE_METADATA,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_METADATA,
+ param_spec);
+
gabble_file_transfer_channel_class->dbus_props_class.interfaces =
prop_interfaces;
tp_dbus_properties_mixin_class_init (object_class,
@@ -967,9 +1028,12 @@ gabble_file_transfer_channel_finalize (GObject *object)
g_free (self->priv->content_type);
g_free (self->priv->content_hash);
g_free (self->priv->description);
- g_hash_table_destroy (self->priv->available_socket_types);
+ g_hash_table_unref (self->priv->available_socket_types);
g_free (self->priv->file_collection);
g_free (self->priv->uri);
+ g_free (self->priv->service_name);
+ if (self->priv->metadata != NULL)
+ g_hash_table_unref (self->priv->metadata);
G_OBJECT_CLASS (gabble_file_transfer_channel_parent_class)->finalize (object);
}
@@ -1298,6 +1362,83 @@ bytestream_negotiate_cb (GabbleBytestreamIface *bytestream,
}
+static void
+add_metadata_forms (GabbleFileTransferChannel *self,
+ WockyNode *file)
+{
+ if (!tp_str_empty (self->priv->service_name))
+ {
+ WockyStanza *tmp = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
+ WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL,
+ '(', "x",
+ ':', NS_X_DATA,
+ '@', "type", "result",
+ '(', "field",
+ '@', "var", "FORM_TYPE",
+ '@', "type", "hidden",
+ '(', "value",
+ '$', NS_TP_FT_METADATA_SERVICE,
+ ')',
+ ')',
+ '(', "field",
+ '@', "var", "ServiceName",
+ '(', "value",
+ '$', self->priv->service_name,
+ ')',
+ ')',
+ ')',
+ NULL);
+ WockyNode *x = wocky_node_get_first_child (wocky_stanza_get_top_node (tmp));
+ WockyNodeTree *tree = wocky_node_tree_new_from_node (x);
+
+ wocky_node_add_node_tree (file, tree);
+ g_object_unref (tree);
+ g_object_unref (tmp);
+ }
+
+ if (self->priv->metadata != NULL
+ && g_hash_table_size (self->priv->metadata) > 0)
+ {
+ WockyStanza *tmp = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
+ WOCKY_STANZA_SUB_TYPE_RESULT, NULL, NULL,
+ '(', "x",
+ ':', NS_X_DATA,
+ '@', "type", "result",
+ '(', "field",
+ '@', "var", "FORM_TYPE",
+ '@', "type", "hidden",
+ '(', "value",
+ '$', NS_TP_FT_METADATA,
+ ')',
+ ')',
+ ')',
+ NULL);
+ WockyNode *x = wocky_node_get_first_child (wocky_stanza_get_top_node (tmp));
+ WockyNodeTree *tree;
+ GHashTableIter iter;
+ gpointer key, val;
+
+ g_hash_table_iter_init (&iter, self->priv->metadata);
+ while (g_hash_table_iter_next (&iter, &key, &val))
+ {
+ const gchar * const *values = val;
+
+ WockyNode *field = wocky_node_add_child (x, "field");
+ wocky_node_set_attribute (field, "var", key);
+
+ for (; values != NULL && *values != NULL; values++)
+ {
+ wocky_node_add_child_with_content (field, "value", *values);
+ }
+ }
+
+ tree = wocky_node_tree_new_from_node (x);
+ wocky_node_add_node_tree (file, tree);
+ g_object_unref (tree);
+ g_object_unref (tmp);
+ }
+}
+
static gboolean
offer_bytestream (GabbleFileTransferChannel *self, const gchar *jid,
const gchar *resource, GError **error)
@@ -1338,6 +1479,8 @@ offer_bytestream (GabbleFileTransferChannel *self, const gchar *jid,
"mime-type", self->priv->content_type,
NULL);
+ add_metadata_forms (self, file_node);
+
if (self->priv->content_hash != NULL)
wocky_node_set_attribute (file_node, "hash", self->priv->content_hash);
@@ -1350,7 +1493,11 @@ offer_bytestream (GabbleFileTransferChannel *self, const gchar *jid,
t = (time_t) self->priv->date;
tm = gmtime (&t);
+#ifdef G_OS_WIN32
+ strftime (date_str, sizeof (date_str), "%Y-%m-%dT%H:%M:%SZ", tm);
+#else
strftime (date_str, sizeof (date_str), "%FT%H:%M:%SZ", tm);
+#endif
wocky_node_set_attribute (file_node, "date", date_str);
}
@@ -1474,7 +1621,7 @@ gabble_file_transfer_channel_offer_file (GabbleFileTransferChannel *self,
gboolean jingle_share = FALSE;
const gchar *si_resource = NULL;
const gchar *share_resource = NULL;
- g_assert (!CHECK_STR_EMPTY (self->priv->filename));
+ g_assert (!tp_str_empty (self->priv->filename));
g_assert (self->priv->size != GABBLE_UNDEFINED_FILE_SIZE);
g_return_val_if_fail (self->priv->bytestream == NULL, FALSE);
g_return_val_if_fail (self->priv->gtalk_file_collection == NULL, FALSE);
@@ -1491,6 +1638,21 @@ gabble_file_transfer_channel_offer_file (GabbleFileTransferChannel *self,
return FALSE;
}
+ if (self->priv->service_name != NULL || self->priv->metadata != NULL)
+ {
+ if (!gabble_presence_has_cap (presence, NS_TP_FT_METADATA))
+ {
+ DEBUG ("trying to use Metadata properties on a contact "
+ "who doesn't support it");
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_CAPABLE,
+ "The specified contact does not support the "
+ "Metadata extension; you should ensure both ServiceName and "
+ "Metadata properties are not present in the channel "
+ "request");
+ return FALSE;
+ }
+ }
+
contact_repo = tp_base_connection_get_handles (
(TpBaseConnection *) self->priv->connection, TP_HANDLE_TYPE_CONTACT);
room_repo = tp_base_connection_get_handles (
@@ -2166,7 +2328,7 @@ setup_local_socket (GabbleFileTransferChannel *self,
DEBUG ("local socket %s", path);
g_free (path);
- g_array_free (array, TRUE);
+ g_array_unref (array);
}
else
#endif
@@ -2252,7 +2414,9 @@ gabble_file_transfer_channel_new (GabbleConnection *conn,
GabbleBytestreamIface *bytestream,
GTalkFileCollection *gtalk_file_collection,
const gchar *file_collection,
- const gchar *uri)
+ const gchar *uri,
+ const gchar *service_name,
+ const GHashTable *metadata)
{
return g_object_new (GABBLE_TYPE_FILE_TRANSFER_CHANNEL,
@@ -2273,5 +2437,7 @@ gabble_file_transfer_channel_new (GabbleConnection *conn,
"bytestream", bytestream,
"gtalk-file-collection", gtalk_file_collection,
"uri", uri,
+ "service-name", service_name,
+ "metadata", metadata,
NULL);
}
diff --git a/src/ft-channel.h b/src/ft-channel.h
index 3465b79a6..e015e17f6 100644
--- a/src/ft-channel.h
+++ b/src/ft-channel.h
@@ -72,7 +72,7 @@ gabble_file_transfer_channel_new (GabbleConnection *conn,
const gchar *description, guint64 date, guint64 initial_offset,
gboolean resume_supported, GabbleBytestreamIface *bytestream,
GTalkFileCollection *gtalk_fc, const gchar *file_collection,
- const gchar *uri);
+ const gchar *uri, const gchar *service_name, const GHashTable *metadata);
gboolean gabble_file_transfer_channel_offer_file (
GabbleFileTransferChannel *self, GError **error);
diff --git a/src/ft-manager.c b/src/ft-manager.c
index 0f1ab3e09..6e8aefc87 100644
--- a/src/ft-manager.c
+++ b/src/ft-manager.c
@@ -30,15 +30,16 @@
#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 "ft-channel.h"
#include "gabble-signals-marshal.h"
#include "namespaces.h"
#include "presence-cache.h"
#include "util.h"
-#include "ft-channel.h"
+#include <wocky/wocky-data-form.h>
#include <telepathy-glib/base-connection.h>
#include <telepathy-glib/channel-factory-iface.h>
@@ -307,7 +308,7 @@ gabble_ft_manager_channels_created (GabbleFtManager *self, GList *channels)
tp_channel_manager_emit_new_channels (self, new_channels);
- g_hash_table_destroy (new_channels);
+ g_hash_table_unref (new_channels);
}
static void
@@ -381,7 +382,7 @@ new_jingle_session_cb (GabbleJingleFactory *jf,
channel = gabble_file_transfer_channel_new (self->priv->connection,
sess->peer, sess->peer, TP_FILE_TRANSFER_STATE_PENDING,
NULL, filename, entry->size, TP_FILE_HASH_TYPE_NONE, NULL,
- NULL, 0, 0, FALSE, NULL, gtalk_fc, token, NULL);
+ NULL, 0, 0, FALSE, NULL, gtalk_fc, token, NULL, NULL, NULL);
g_free (filename);
gtalk_file_collection_add_channel (gtalk_fc, channel);
@@ -442,9 +443,10 @@ gabble_ft_manager_handle_request (TpChannelManager *manager,
tp_base_connection_get_handles (base_connection, TP_HANDLE_TYPE_CONTACT);
TpHandle handle;
const gchar *content_type, *filename, *content_hash, *description;
- const gchar *file_uri;
+ const gchar *file_uri, *service_name;
guint64 size, date, initial_offset;
TpFileHashType content_hash_type;
+ const GHashTable *metadata;
GError *error = NULL;
gboolean valid;
@@ -549,13 +551,28 @@ gabble_ft_manager_handle_request (TpChannelManager *manager,
file_uri = tp_asv_get_string (request_properties,
TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI);
+ service_name = tp_asv_get_string (request_properties,
+ TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME);
+
+ metadata = tp_asv_get_boxed (request_properties,
+ TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_METADATA,
+ TP_HASH_TYPE_METADATA);
+
+ if (metadata != NULL && g_hash_table_lookup ((GHashTable *) metadata, "FORM_TYPE"))
+ {
+ g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "Metadata cannot contain an item with key 'FORM_TYPE'");
+ goto error;
+ }
+
DEBUG ("Requested outgoing channel with contact: %s",
tp_handle_inspect (contact_repo, handle));
chan = gabble_file_transfer_channel_new (self->priv->connection,
handle, base_connection->self_handle, TP_FILE_TRANSFER_STATE_PENDING,
content_type, filename, size, content_hash_type, content_hash,
- description, date, initial_offset, TRUE, NULL, NULL, NULL, file_uri);
+ description, date, initial_offset, TRUE, NULL, NULL, NULL, file_uri,
+ service_name, metadata);
if (!gabble_file_transfer_channel_offer_file (chan, &error))
{
@@ -582,20 +599,38 @@ static const gchar * const file_transfer_channel_fixed_properties[] = {
NULL
};
+ /* ContentHashType has to be first so we can easily skip it when needed */
+#define STANDARD_PROPERTIES \
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE, \
+ TP_PROP_CHANNEL_TARGET_HANDLE, \
+ TP_PROP_CHANNEL_TARGET_ID, \
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE, \
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME, \
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE, \
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH, \
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DESCRIPTION, \
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE, \
+ TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI
+
static const gchar * const file_transfer_channel_allowed_properties[] =
{
- /* ContentHashType has to be first so we can easily skip it when needed */
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH_TYPE,
- TP_PROP_CHANNEL_TARGET_HANDLE,
- TP_PROP_CHANNEL_TARGET_ID,
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_TYPE,
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_FILENAME,
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_SIZE,
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_CONTENT_HASH,
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DESCRIPTION,
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE,
- TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI,
- NULL
+ STANDARD_PROPERTIES,
+ NULL
+};
+
+static const gchar * const file_transfer_channel_allowed_properties_with_metadata_prop[] =
+{
+ STANDARD_PROPERTIES,
+ TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_METADATA,
+ NULL
+};
+
+static const gchar * const file_transfer_channel_allowed_properties_with_both_metadata_props[] =
+{
+ STANDARD_PROPERTIES,
+ TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME,
+ TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_METADATA,
+ NULL
};
static void
@@ -615,7 +650,7 @@ gabble_ft_manager_type_foreach_channel_class (GType type,
g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
tp_g_value_slice_new_uint (TP_HANDLE_TYPE_CONTACT));
- func (type, table, file_transfer_channel_allowed_properties,
+ func (type, table, file_transfer_channel_allowed_properties_with_both_metadata_props,
user_data);
/* MD5 HashType class */
@@ -624,10 +659,10 @@ gabble_ft_manager_type_foreach_channel_class (GType type,
tp_g_value_slice_new_uint (TP_FILE_HASH_TYPE_MD5));
/* skip ContentHashType in allowed properties */
- func (type, table, file_transfer_channel_allowed_properties + 1,
+ func (type, table, file_transfer_channel_allowed_properties_with_both_metadata_props + 1,
user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
}
static WockyNode *
@@ -658,6 +693,111 @@ hyvaa_vappua (
#undef die_if_null
}
+static WockyDataForm *
+find_data_form (WockyNode *file,
+ const gchar *form_type)
+{
+ WockyNodeIter iter;
+ WockyNode *x;
+
+ wocky_node_iter_init (&iter, file, "x", NS_X_DATA);
+ while (wocky_node_iter_next (&iter, &x))
+ {
+ GError *error = NULL;
+ WockyDataForm *form = wocky_data_form_new_from_node (x, &error);
+ WockyDataFormField *field;
+
+ if (form == NULL)
+ {
+ DEBUG ("Failed to parse data form: %s", error->message);
+ g_clear_error (&error);
+ continue;
+ }
+
+ field = g_hash_table_lookup (form->fields, "FORM_TYPE");
+
+ if (field == NULL)
+ {
+ DEBUG ("Data form doesn't have FORM_TYPE field!");
+ g_object_unref (form);
+ continue;
+ }
+
+ /* found it! */
+ if (!tp_strdiff (field->raw_value_contents[0], form_type))
+ return form;
+
+ g_object_unref (form);
+ }
+
+ return NULL;
+}
+
+static gchar *
+extract_service_name (WockyNode *file)
+{
+ WockyDataForm *form = find_data_form (file, NS_TP_FT_METADATA_SERVICE);
+ WockyDataFormField *field;
+ gchar *service_name = NULL;
+
+ if (form == NULL)
+ return NULL;
+
+ field = g_hash_table_lookup (form->fields, "ServiceName");
+
+ if (field == NULL)
+ {
+ DEBUG ("ServiceName propery not present in data form; odd...");
+ goto out;
+ }
+
+ if (field->raw_value_contents == NULL
+ || field->raw_value_contents[0] == NULL)
+ {
+ DEBUG ("ServiceName property doesn't have a real value; odd...");
+ }
+ else
+ {
+ service_name = g_strdup (field->raw_value_contents[0]);
+ }
+
+out:
+ g_object_unref (form);
+ return service_name;
+}
+
+static GHashTable *
+extract_metadata (WockyNode *file)
+{
+ WockyDataForm *form = find_data_form (file, NS_TP_FT_METADATA);
+ GHashTable *metadata;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if (form == NULL)
+ return NULL;
+
+ metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_strfreev);
+
+ g_hash_table_iter_init (&iter, form->fields);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const gchar *var = key;
+ WockyDataFormField *field = value;
+
+ if (!tp_strdiff (var, "FORM_TYPE"))
+ continue;
+
+ g_hash_table_insert (metadata,
+ g_strdup (var),
+ g_strdupv (field->raw_value_contents));
+ }
+
+ g_object_unref (form);
+ return metadata;
+}
+
void gabble_ft_manager_handle_si_request (GabbleFtManager *self,
GabbleBytestreamIface *bytestream,
TpHandle handle,
@@ -667,6 +807,8 @@ void gabble_ft_manager_handle_si_request (GabbleFtManager *self,
WockyNode *si_node, *file_node, *desc_node;
const gchar *filename, *size_str, *content_type, *content_hash, *description;
const gchar *date_str;
+ gchar *service_name;
+ GHashTable *metadata;
guint64 size;
guint64 date = 0;
TpFileHashType content_hash_type;
@@ -719,12 +861,21 @@ void gabble_ft_manager_handle_si_request (GabbleFtManager *self,
resume_supported = (wocky_node_get_child (file_node, "range") != NULL);
+ /* metadata */
+ service_name = extract_service_name (file_node);
+ metadata = extract_metadata (file_node);
+
chan = gabble_file_transfer_channel_new (self->priv->connection,
handle, handle, TP_FILE_TRANSFER_STATE_PENDING,
content_type, filename, size, content_hash_type, content_hash,
- description, date, 0, resume_supported, bytestream, NULL, NULL, NULL);
+ description, date, 0, resume_supported, bytestream, NULL, NULL, NULL,
+ service_name, metadata);
gabble_ft_manager_channel_created (self, chan, NULL);
+
+ g_free (service_name);
+ if (metadata != NULL)
+ g_hash_table_unref (metadata);
}
static void
@@ -770,12 +921,16 @@ gabble_ft_manager_get_tmp_dir (GabbleFtManager *self)
#endif
static void
-add_file_transfer_channel_class (GPtrArray *arr)
+add_file_transfer_channel_class (GPtrArray *arr,
+ gboolean include_metadata_properties,
+ const gchar *service_name_str)
{
GValue monster = {0, };
GHashTable *fixed_properties;
GValue *channel_type_value;
GValue *target_handle_type_value;
+ GValue *service_name_value;
+ const gchar * const *allowed_properties;
g_value_init (&monster, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS);
g_value_take_boxed (&monster,
@@ -785,28 +940,60 @@ add_file_transfer_channel_class (GPtrArray *arr)
fixed_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
(GDestroyNotify) tp_g_value_slice_free);
- channel_type_value = tp_g_value_slice_new (G_TYPE_STRING);
- g_value_set_static_string (channel_type_value,
+ channel_type_value = tp_g_value_slice_new_static_string (
TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER);
g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".ChannelType",
channel_type_value);
- target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
- g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT);
+ target_handle_type_value = tp_g_value_slice_new_uint (TP_HANDLE_TYPE_CONTACT);
g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".TargetHandleType",
target_handle_type_value);
+ if (service_name_str != NULL)
+ {
+ service_name_value = tp_g_value_slice_new_string (service_name_str);
+ g_hash_table_insert (fixed_properties,
+ TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME,
+ service_name_value);
+ }
+
+ if (include_metadata_properties)
+ {
+ if (service_name_str == NULL)
+ allowed_properties = file_transfer_channel_allowed_properties_with_both_metadata_props;
+ else
+ allowed_properties = file_transfer_channel_allowed_properties_with_metadata_prop;
+ }
+ else
+ {
+ allowed_properties = file_transfer_channel_allowed_properties;
+ }
+
dbus_g_type_struct_set (&monster,
0, fixed_properties,
- 1, file_transfer_channel_allowed_properties,
+ 1, allowed_properties,
G_MAXUINT);
- g_hash_table_destroy (fixed_properties);
+ g_hash_table_unref (fixed_properties);
g_ptr_array_add (arr, g_value_get_boxed (&monster));
}
static void
+get_contact_caps_foreach (gpointer data,
+ gpointer user_data)
+{
+ const gchar *ns = data;
+ GPtrArray *arr = user_data;
+
+ if (!g_str_has_prefix (ns, NS_TP_FT_METADATA "#"))
+ return;
+
+ add_file_transfer_channel_class (arr, TRUE,
+ ns + strlen (NS_TP_FT_METADATA "#"));
+}
+
+static void
gabble_ft_manager_get_contact_caps (
GabbleCapsChannelManager *manager G_GNUC_UNUSED,
TpHandle handle G_GNUC_UNUSED,
@@ -815,7 +1002,12 @@ gabble_ft_manager_get_contact_caps (
{
if (gabble_capability_set_has (caps, NS_FILE_TRANSFER) ||
gabble_capability_set_has (caps, NS_GOOGLE_FEAT_SHARE))
- add_file_transfer_channel_class (arr);
+ {
+ add_file_transfer_channel_class (arr,
+ gabble_capability_set_has (caps, NS_TP_FT_METADATA), NULL);
+ }
+
+ gabble_capability_set_foreach (caps, get_contact_caps_foreach, arr);
}
static void
@@ -824,13 +1016,16 @@ 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;
for (i = 0; i < filters->len; i++)
{
GHashTable *channel_class = g_ptr_array_index (filters, i);
+ const gchar *service_name;
+ gchar *ns;
if (tp_strdiff (tp_asv_get_string (channel_class,
TP_IFACE_CHANNEL ".ChannelType"),
@@ -845,9 +1040,28 @@ gabble_ft_manager_represent_client (
DEBUG ("client %s supports file transfer", client_name);
gabble_capability_set_add (cap_set, NS_FILE_TRANSFER);
gabble_capability_set_add (cap_set, NS_GOOGLE_FEAT_SHARE);
- /* there's no point in looking at the subsequent filters if we've
- * already added the FT capability */
- break;
+ gabble_capability_set_add (cap_set, NS_TP_FT_METADATA);
+
+ /* now look at service names */
+
+ /* capabilities mean being able to RECEIVE said kinds of
+ * FTs. hence, skip Requested=true (locally initiated) channel
+ * classes */
+ if (tp_asv_get_boolean (channel_class,
+ TP_PROP_CHANNEL_REQUESTED, FALSE))
+ continue;
+
+ service_name = tp_asv_get_string (channel_class,
+ TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME);
+
+ if (service_name == NULL)
+ continue;
+
+ ns = g_strconcat (NS_TP_FT_METADATA "#", service_name, NULL);
+
+ DEBUG ("%s: adding capability %s", client_name, ns);
+ gabble_capability_set_add (cap_set, ns);
+ g_free (ns);
}
}
diff --git a/src/gabble.c b/src/gabble.c
index 0165e789c..fef4454e7 100644
--- a/src/gabble.c
+++ b/src/gabble.c
@@ -109,10 +109,12 @@ log_handler (const gchar *log_domain,
void
gabble_init (void)
{
+#if !GLIB_CHECK_VERSION (2, 31, 0)
/* libsoup uses glib in multiple threads and don't have this, so we have to
* enable it manually here */
if (!g_thread_supported ())
g_thread_init (NULL);
+#endif
g_type_init ();
wocky_init ();
diff --git a/src/google-relay.c b/src/google-relay.c
new file mode 100644
index 000000000..d78f37e00
--- /dev/null
+++ b/src/google-relay.c
@@ -0,0 +1,341 @@
+/*
+ * google-relay.c - Support for Google relays for Jingle
+ *
+ * Copyright (C) 2006-2008 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "google-relay.h"
+
+#include <string.h>
+
+#ifdef ENABLE_GOOGLE_RELAY
+#include <libsoup/soup.h>
+#endif
+
+#include <telepathy-glib/util.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
+
+#ifdef G_OS_WIN32
+#undef ERROR
+#endif
+
+#include "debug.h"
+
+#define RELAY_HTTP_TIMEOUT 5
+
+struct _GabbleGoogleRelayResolver {
+#ifdef ENABLE_GOOGLE_RELAY
+ SoupSession *soup;
+#else
+ GObject *soup;
+#endif
+};
+
+typedef struct
+{
+ GPtrArray *relays;
+ guint component;
+ guint requests_to_do;
+ GabbleJingleFactoryRelaySessionCb callback;
+ gpointer user_data;
+} RelaySessionData;
+
+static RelaySessionData *
+relay_session_data_new (guint requests_to_do,
+ GabbleJingleFactoryRelaySessionCb callback,
+ gpointer user_data)
+{
+ RelaySessionData *rsd = g_slice_new0 (RelaySessionData);
+
+ rsd->relays = g_ptr_array_sized_new (requests_to_do);
+ rsd->component = 1;
+ rsd->requests_to_do = requests_to_do;
+ rsd->callback = callback;
+ rsd->user_data = user_data;
+
+ return rsd;
+}
+
+/* This is a GSourceFunc */
+static gboolean
+relay_session_data_call (gpointer p)
+{
+ RelaySessionData *rsd = p;
+
+ g_assert (rsd->callback != NULL);
+
+ rsd->callback (rsd->relays, rsd->user_data);
+
+ return FALSE;
+}
+
+/* This is a GDestroyNotify */
+static void
+relay_session_data_destroy (gpointer p)
+{
+ RelaySessionData *rsd = p;
+
+ g_ptr_array_foreach (rsd->relays, (GFunc) g_hash_table_unref, NULL);
+ g_ptr_array_unref (rsd->relays);
+
+ g_slice_free (RelaySessionData, rsd);
+}
+
+#ifdef ENABLE_GOOGLE_RELAY
+
+static void
+translate_relay_info (GPtrArray *relays,
+ const gchar *relay_ip,
+ const gchar *username,
+ const gchar *password,
+ const gchar *static_type,
+ const gchar *port_string,
+ guint component)
+{
+ GHashTable *asv;
+ guint64 portll;
+ guint port;
+
+ if (port_string == NULL)
+ {
+ DEBUG ("no relay port for %s found", static_type);
+ return;
+ }
+
+ portll = g_ascii_strtoull (port_string, NULL, 10);
+
+ if (portll == 0 || portll > G_MAXUINT16)
+ {
+ DEBUG ("failed to parse relay port '%s' for %s", port_string,
+ static_type);
+ return;
+ }
+ port = (guint) portll;
+
+ DEBUG ("type=%s ip=%s port=%u username=%s password=%s component=%u",
+ static_type, relay_ip, port, username, password, component);
+ /* keys are static, values are slice-allocated */
+ asv = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) tp_g_value_slice_free);
+ g_hash_table_insert (asv, "ip",
+ tp_g_value_slice_new_string (relay_ip));
+ g_hash_table_insert (asv, "type",
+ tp_g_value_slice_new_static_string (static_type));
+ g_hash_table_insert (asv, "port",
+ tp_g_value_slice_new_uint (port));
+ g_hash_table_insert (asv, "username",
+ tp_g_value_slice_new_string (username));
+ g_hash_table_insert (asv, "password",
+ tp_g_value_slice_new_string (password));
+ g_hash_table_insert (asv, "component",
+ tp_g_value_slice_new_uint (component));
+
+ g_ptr_array_add (relays, asv);
+}
+
+static void
+on_http_response (SoupSession *soup,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ RelaySessionData *rsd = user_data;
+
+ if (msg->status_code != 200)
+ {
+ DEBUG ("Google session creation failed, relaying not used: %d %s",
+ msg->status_code, msg->reason_phrase);
+ }
+ else
+ {
+ /* parse a=b lines into GHashTable
+ * (key, value both borrowed from items of the strv 'lines') */
+ GHashTable *map = g_hash_table_new (g_str_hash, g_str_equal);
+ gchar **lines;
+ guint i;
+ const gchar *relay_ip;
+ const gchar *relay_udp_port;
+ const gchar *relay_tcp_port;
+ const gchar *relay_ssltcp_port;
+ const gchar *username;
+ const gchar *password;
+ gchar *escaped_str;
+
+ escaped_str = g_strescape (msg->response_body->data, "\r\n");
+ DEBUG ("Response from Google:\n====\n%s\n====", escaped_str);
+ g_free (escaped_str);
+
+ lines = g_strsplit (msg->response_body->data, "\n", 0);
+
+ if (lines != NULL)
+ {
+ for (i = 0; lines[i] != NULL; i++)
+ {
+ gchar *delim = strchr (lines[i], '=');
+ size_t len;
+
+ if (delim == NULL || delim == lines[i])
+ {
+ /* ignore empty keys or lines without '=' */
+ continue;
+ }
+
+ len = strlen (lines[i]);
+
+ if (lines[i][len - 1] == '\r')
+ {
+ lines[i][len - 1] = '\0';
+ }
+
+ *delim = '\0';
+ g_hash_table_insert (map, lines[i], delim + 1);
+ }
+ }
+
+ relay_ip = g_hash_table_lookup (map, "relay.ip");
+ relay_udp_port = g_hash_table_lookup (map, "relay.udp_port");
+ relay_tcp_port = g_hash_table_lookup (map, "relay.tcp_port");
+ relay_ssltcp_port = g_hash_table_lookup (map, "relay.ssltcp_port");
+ username = g_hash_table_lookup (map, "username");
+ password = g_hash_table_lookup (map, "password");
+
+ if (relay_ip == NULL)
+ {
+ DEBUG ("No relay.ip found");
+ }
+ else if (username == NULL)
+ {
+ DEBUG ("No username found");
+ }
+ else if (password == NULL)
+ {
+ DEBUG ("No password found");
+ }
+ else
+ {
+ translate_relay_info (rsd->relays, relay_ip, username, password,
+ "udp", relay_udp_port, rsd->component);
+ translate_relay_info (rsd->relays, relay_ip, username, password,
+ "tcp", relay_tcp_port, rsd->component);
+ translate_relay_info (rsd->relays, relay_ip, username, password,
+ "tls", relay_ssltcp_port, rsd->component);
+ }
+
+ g_strfreev (lines);
+ g_hash_table_unref (map);
+ }
+
+ rsd->component++;
+
+ if ((--rsd->requests_to_do) == 0)
+ {
+ relay_session_data_call (rsd);
+ relay_session_data_destroy (rsd);
+ }
+}
+
+#endif /* ENABLE_GOOGLE_RELAY */
+
+GabbleGoogleRelayResolver *
+gabble_google_relay_resolver_new (void)
+{
+ GabbleGoogleRelayResolver *resolver =
+ g_slice_new0 (GabbleGoogleRelayResolver);
+
+#ifdef ENABLE_GOOGLE_RELAY
+
+ resolver->soup = soup_session_async_new ();
+
+ /* If we don't get answer in a few seconds, relay won't do
+ * us much help anyways. */
+ g_object_set (resolver->soup, "timeout", RELAY_HTTP_TIMEOUT, NULL);
+
+#endif
+
+ return resolver;
+}
+
+void
+gabble_google_relay_resolver_destroy (GabbleGoogleRelayResolver *self)
+{
+ tp_clear_object (&self->soup);
+
+ g_slice_free (GabbleGoogleRelayResolver, self);
+}
+
+void
+gabble_google_relay_resolver_resolve (GabbleGoogleRelayResolver *self,
+ guint components,
+ const gchar *server,
+ guint16 port,
+ const gchar *token,
+ GabbleJingleFactoryRelaySessionCb callback,
+ gpointer user_data)
+{
+ RelaySessionData *rsd =
+ relay_session_data_new (components, callback, user_data);
+
+#ifdef ENABLE_GOOGLE_RELAY
+
+ gchar *url;
+ guint i;
+
+ if (server == NULL)
+ {
+ DEBUG ("No relay server provided, not creating google relay session");
+ g_idle_add_full (G_PRIORITY_DEFAULT, relay_session_data_call, rsd,
+ relay_session_data_destroy);
+ return;
+ }
+
+ if (token == NULL)
+ {
+ DEBUG ("No relay token provided, not creating google relay session");
+ g_idle_add_full (G_PRIORITY_DEFAULT, relay_session_data_call, rsd,
+ relay_session_data_destroy);
+ return;
+ }
+
+ url = g_strdup_printf ("http://%s:%u/create_session", server, (guint) port);
+
+ for (i = 0; i < components; i++)
+ {
+ SoupMessage *msg = soup_message_new ("GET", url);
+
+ DEBUG ("Trying to create a new relay session on %s", url);
+
+ /* libjingle sets both headers, so shall we */
+ soup_message_headers_append (msg->request_headers,
+ "X-Talk-Google-Relay-Auth", token);
+ soup_message_headers_append (msg->request_headers,
+ "X-Google-Relay-Auth", token);
+
+ soup_session_queue_message (self->soup, msg, on_http_response, rsd);
+ }
+
+ g_free (url);
+
+#else /* !ENABLE_GOOGLE_RELAY */
+
+ DEBUG ("Google relay service is not supported");
+
+ g_idle_add_full (G_PRIORITY_DEFAULT, relay_session_data_call, rsd,
+ relay_session_data_destroy);
+
+#endif
+}
diff --git a/src/google-relay.h b/src/google-relay.h
new file mode 100644
index 000000000..6eb180e35
--- /dev/null
+++ b/src/google-relay.h
@@ -0,0 +1,45 @@
+/*
+ * google-relay.h - Header for GabbleGoogleRelaySession
+ *
+ * Copyright (C) 2006-2008 Collabora Ltd.
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __GABBLE_GOOGLE_RELAY_H__
+#define __GABBLE_GOOGLE_RELAY_H__
+
+#include <glib.h>
+
+#include "jingle-factory.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GabbleGoogleRelayResolver GabbleGoogleRelayResolver;
+
+GabbleGoogleRelayResolver * gabble_google_relay_resolver_new (void);
+void gabble_google_relay_resolver_destroy (GabbleGoogleRelayResolver *self);
+void gabble_google_relay_resolver_resolve (GabbleGoogleRelayResolver *self,
+ guint requests_to_do,
+ const gchar *server,
+ guint16 port,
+ const gchar *token,
+ GabbleJingleFactoryRelaySessionCb callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __GABBLE_GOOGLE_RELAY_H__ */
diff --git a/src/gtalk-file-collection.c b/src/gtalk-file-collection.c
index 2bdaf5fa5..ffc40d580 100644
--- a/src/gtalk-file-collection.c
+++ b/src/gtalk-file-collection.c
@@ -256,9 +256,9 @@ gtalk_file_collection_dispose (GObject *object)
set_current_channel (self, NULL);
- tp_clear_pointer (&self->priv->channels_reading, g_hash_table_destroy);
- tp_clear_pointer (&self->priv->channels_usable, g_hash_table_destroy);
- tp_clear_pointer (&self->priv->share_channels, g_hash_table_destroy);
+ tp_clear_pointer (&self->priv->channels_reading, g_hash_table_unref);
+ tp_clear_pointer (&self->priv->channels_usable, g_hash_table_unref);
+ tp_clear_pointer (&self->priv->share_channels, g_hash_table_unref);
for (i = self->priv->channels; i; i = i->next)
{
diff --git a/src/im-channel.c b/src/im-channel.c
index c1b52a5da..dba3aedba 100644
--- a/src/im-channel.c
+++ b/src/im-channel.c
@@ -95,6 +95,12 @@ struct _GabbleIMChannelPrivate
gboolean dispose_has_run;
};
+typedef struct {
+ GabbleIMChannel *channel;
+ TpMessage *message;
+ gchar *token;
+} _GabbleIMSendMessageCtx;
+
static void
gabble_im_channel_init (GabbleIMChannel *self)
{
@@ -170,6 +176,13 @@ gabble_im_channel_fill_immutable_properties (TpBaseChannel *chan,
NULL);
}
+static gchar *
+gabble_im_channel_get_object_path_suffix (TpBaseChannel *chan)
+{
+ return g_strdup_printf ("ImChannel%u",
+ tp_base_channel_get_target_handle (chan));
+}
+
static void
gabble_im_channel_class_init (GabbleIMChannelClass *gabble_im_channel_class)
{
@@ -190,6 +203,7 @@ gabble_im_channel_class_init (GabbleIMChannelClass *gabble_im_channel_class)
base_class->close = gabble_im_channel_close;
base_class->fill_immutable_properties =
gabble_im_channel_fill_immutable_properties;
+ base_class->get_object_path_suffix = gabble_im_channel_get_object_path_suffix;
tp_message_mixin_init_dbus_properties (object_class);
}
@@ -292,6 +306,33 @@ gabble_im_channel_finalize (GObject *object)
G_OBJECT_CLASS (gabble_im_channel_parent_class)->finalize (object);
}
+static void
+_gabble_im_channel_message_sent_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ WockyPorter *porter = WOCKY_PORTER (source);
+ GError *error = NULL;
+ _GabbleIMSendMessageCtx *context = user_data;
+ GabbleIMChannel *chan = context->channel;
+ TpMessage *message = context->message;
+
+ if (wocky_porter_send_finish (porter, res, &error))
+ {
+ tp_message_mixin_sent ((GObject *) chan, message, 0,
+ context->token, NULL);
+ }
+ else
+ {
+ tp_message_mixin_sent ((GObject *) chan, context->message,
+ 0, NULL, error);
+ }
+
+ g_object_unref (context->channel);
+ g_object_unref (context->message);
+ g_free (context->token);
+ g_slice_free (_GabbleIMSendMessageCtx, context);
+}
static void
_gabble_im_channel_send_message (GObject *object,
@@ -300,8 +341,14 @@ _gabble_im_channel_send_message (GObject *object,
{
GabbleIMChannel *self = GABBLE_IM_CHANNEL (object);
TpBaseChannel *base = (TpBaseChannel *) self;
+ GabbleConnection *gabble_conn;
GabbleIMChannelPrivate *priv;
gint state = -1;
+ WockyStanza *stanza = NULL;
+ gchar *id = NULL;
+ GError *error = NULL;
+ WockyPorter *porter;
+ _GabbleIMSendMessageCtx *context;
g_assert (GABBLE_IS_IM_CHANNEL (self));
priv = self->priv;
@@ -314,127 +361,185 @@ _gabble_im_channel_send_message (GObject *object,
/* We don't support providing successful delivery reports. */
flags = 0;
+ gabble_conn =
+ GABBLE_CONNECTION (tp_base_channel_get_connection (base));
+
+ stanza = gabble_message_util_build_stanza (message,
+ gabble_conn, 0, state, priv->peer_jid,
+ priv->send_nick, &id, &error);
+
+
+ if (stanza != NULL)
+ {
+ porter = gabble_connection_dup_porter (gabble_conn);
+ context = g_slice_new0 (_GabbleIMSendMessageCtx);
+ context->channel = g_object_ref (base);
+ context->message = g_object_ref (message);
+ context->token = id;
+ wocky_porter_send_async (porter, stanza, NULL,
+ _gabble_im_channel_message_sent_cb, context);
+ g_object_unref (porter);
+ g_object_unref (stanza);
+ }
+ else
+ {
+ tp_message_mixin_sent (object, message, flags, NULL, error);
+ g_error_free (error);
+ }
- gabble_message_util_send_message (object,
- GABBLE_CONNECTION (tp_base_channel_get_connection (base)),
- message, flags, 0, state, priv->peer_jid, priv->send_nick);
if (priv->send_nick)
priv->send_nick = FALSE;
}
-/**
- * _gabble_im_channel_receive
+static TpMessage *
+build_message (
+ GabbleIMChannel *self,
+ TpChannelTextMessageType type,
+ time_t timestamp,
+ const char *text)
+{
+ TpBaseChannel *base_chan = (TpBaseChannel *) self;
+ TpBaseConnection *base_conn = tp_base_channel_get_connection (base_chan);
+ TpMessage *msg = tp_cm_message_new (base_conn, 2);
+
+ if (type != TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL)
+ tp_message_set_uint32 (msg, 0, "message-type", type);
+
+ if (timestamp != 0)
+ tp_message_set_int64 (msg, 0, "message-sent", timestamp);
+
+ /* Body */
+ tp_message_set_string (msg, 1, "content-type", "text/plain");
+ tp_message_set_string (msg, 1, "content", text);
+
+ return msg;
+}
+
+/*
+ * _gabble_im_channel_receive:
+ * @chan: a channel
+ * @type: the message type
+ * @from: the full JID we received the message from
+ * @timestamp: the time at which the message was sent (not the time it was
+ * received)
+ * @id: the id='' attribute from the <message/> stanza, if any
+ * @text: the plaintext body of the message
+ * @send_error: the reason why sending @text to @sender failed, or
+ * GABBLE_TEXT_CHANNEL_SEND_NO_ERROR if this call is not to report
+ * a failure to send.
+ * @delivery_status: if @send_error is GABBLE_TEXT_CHANNEL_SEND_NO_ERROR,
+ * ignored; else the delivery status to attach to the report.
+ * @state: a #TpChannelChatState, or -1 if there was no chat state in the
+ * message.
*
+ * Shoves an incoming message into @chan, possibly updating the chat state at
+ * the same time; or maybe this is a delivery report? Who knows! It's a magical
+ * adventure.
*/
void
_gabble_im_channel_receive (GabbleIMChannel *chan,
TpChannelTextMessageType type,
- TpHandle sender,
const char *from,
time_t timestamp,
const gchar *id,
const char *text,
- TpChannelTextSendError send_error,
- TpDeliveryStatus delivery_status,
gint state)
{
GabbleIMChannelPrivate *priv;
TpBaseChannel *base_chan;
- TpBaseConnection *base_conn;
+ TpHandle peer;
TpMessage *msg;
- gchar *tmp;
g_assert (GABBLE_IS_IM_CHANNEL (chan));
priv = chan->priv;
base_chan = (TpBaseChannel *) chan;
- base_conn = tp_base_channel_get_connection (base_chan);
+ peer = tp_base_channel_get_target_handle (base_chan);
- if (send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
+ /* update peer's full JID if it's changed */
+ if (tp_strdiff (from, priv->peer_jid))
{
- /* update peer's full JID if it's changed */
- if (tp_strdiff (from, priv->peer_jid))
- {
- g_free (priv->peer_jid);
- priv->peer_jid = g_strdup (from);
- }
-
- if (state == -1)
- {
- priv->chat_states_supported = CHAT_STATES_NOT_SUPPORTED;
- }
- else
- {
- _gabble_im_channel_state_receive (chan, state);
- }
+ g_free (priv->peer_jid);
+ priv->peer_jid = g_strdup (from);
}
- else
- {
- /* strip off the resource (if any), since we just failed to send to it */
- char *slash = strchr (priv->peer_jid, '/');
- if (slash != NULL)
- *slash = '\0';
+ if (state == -1)
+ priv->chat_states_supported = CHAT_STATES_NOT_SUPPORTED;
+ else
+ _gabble_im_channel_state_receive (chan, state);
- priv->chat_states_supported = CHAT_STATES_UNKNOWN;
- }
+ msg = build_message (chan, type, timestamp, text);
+ tp_cm_message_set_sender (msg, peer);
+ tp_message_set_int64 (msg, 0, "message-received", time (NULL));
- msg = tp_cm_message_new (base_conn, 2);
+ if (id != NULL)
+ tp_message_set_string (msg, 0, "message-token", id);
- /* Header */
- if (type != TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL)
- tp_message_set_uint32 (msg, 0, "message-type", type);
+ tp_message_mixin_take_received (G_OBJECT (chan), msg);
+}
- if (timestamp != 0)
- tp_message_set_int64 (msg, 0, "message-sent", timestamp);
+void
+_gabble_im_channel_report_delivery (
+ GabbleIMChannel *self,
+ TpChannelTextMessageType type,
+ time_t timestamp,
+ const gchar *id,
+ const char *text,
+ TpChannelTextSendError send_error,
+ TpDeliveryStatus delivery_status)
+{
+ GabbleIMChannelPrivate *priv;
+ TpBaseChannel *base_chan = (TpBaseChannel *) self;
+ TpBaseConnection *base_conn;
+ TpHandle peer;
+ TpMessage *msg, *delivery_report;
+ gchar *tmp;
- /* Body */
- tp_message_set_string (msg, 1, "content-type", "text/plain");
- tp_message_set_string (msg, 1, "content", text);
+ g_return_if_fail (GABBLE_IS_IM_CHANNEL (self));
+ priv = self->priv;
+ peer = tp_base_channel_get_target_handle (base_chan);
+ base_conn = tp_base_channel_get_connection (base_chan);
- if (send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
+ if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
{
- tp_cm_message_set_sender (msg, sender);
- tp_message_set_int64 (msg, 0, "message-received", time (NULL));
+ /* strip off the resource (if any), since we just failed to send to it */
+ char *slash = strchr (priv->peer_jid, '/');
- if (id != NULL)
- tp_message_set_string (msg, 0, "message-token", id);
+ if (slash != NULL)
+ *slash = '\0';
- tp_message_mixin_take_received (G_OBJECT (chan), msg);
+ priv->chat_states_supported = CHAT_STATES_UNKNOWN;
}
- else
- {
- TpMessage *delivery_report = tp_cm_message_new (base_conn, 1);
- tp_message_set_uint32 (delivery_report, 0, "message-type",
- TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT);
- tp_cm_message_set_sender (delivery_report, sender);
- tp_message_set_int64 (delivery_report, 0, "message-received",
- time (NULL));
+ msg = build_message (self, type, timestamp, text);
+ delivery_report = tp_cm_message_new (base_conn, 1);
+ tp_message_set_uint32 (delivery_report, 0, "message-type",
+ TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT);
+ tp_cm_message_set_sender (delivery_report, peer);
+ tp_message_set_int64 (delivery_report, 0, "message-received",
+ time (NULL));
- tmp = gabble_generate_id ();
- tp_message_set_string (delivery_report, 0, "message-token", tmp);
- g_free (tmp);
+ tmp = gabble_generate_id ();
+ tp_message_set_string (delivery_report, 0, "message-token", tmp);
+ g_free (tmp);
- tp_message_set_uint32 (delivery_report, 0, "delivery-status",
- delivery_status);
- tp_message_set_uint32 (delivery_report, 0, "delivery-error", send_error);
+ tp_message_set_uint32 (delivery_report, 0, "delivery-status",
+ delivery_status);
+ tp_message_set_uint32 (delivery_report, 0, "delivery-error", send_error);
- if (id != NULL)
- tp_message_set_string (delivery_report, 0, "delivery-token", id);
+ if (id != NULL)
+ tp_message_set_string (delivery_report, 0, "delivery-token", id);
- /* We're getting a send error, so the original sender of the echoed
- * message must be us! */
- tp_cm_message_set_sender (msg, base_conn->self_handle);
+ /* This is a delivery report, so the original sender of the echoed message
+ * must be us! */
+ tp_cm_message_set_sender (msg, base_conn->self_handle);
- /* Since this is a send error, we can trust the id on the message. */
- if (id != NULL)
- tp_message_set_string (msg, 0, "message-token", id);
+ /* Since this is a delivery report, we can trust the id on the message. */
+ if (id != NULL)
+ tp_message_set_string (msg, 0, "message-token", id);
- tp_cm_message_take_message (delivery_report, 0, "delivery-echo", msg);
-
- tp_message_mixin_take_received (G_OBJECT (chan), delivery_report);
- }
+ tp_cm_message_take_message (delivery_report, 0, "delivery-echo", msg);
+ tp_message_mixin_take_received (G_OBJECT (self), delivery_report);
}
/**
diff --git a/src/im-channel.h b/src/im-channel.h
index 17d6271b0..fdbae19bf 100644
--- a/src/im-channel.h
+++ b/src/im-channel.h
@@ -66,17 +66,23 @@ GType gabble_im_channel_get_type (void);
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,
gint state);
void _gabble_im_channel_state_receive (GabbleIMChannel *chan,
TpChannelChatState state);
+void _gabble_im_channel_report_delivery (
+ GabbleIMChannel *self,
+ TpChannelTextMessageType type,
+ time_t timestamp,
+ const gchar *id,
+ const char *text,
+ TpChannelTextSendError send_error,
+ TpDeliveryStatus delivery_status);
+
G_END_DECLS
#endif /* #ifndef __GABBLE_IM_CHANNEL_H__*/
diff --git a/src/im-factory.c b/src/im-factory.c
index 08f19f6c4..6932d2201 100644
--- a/src/im-factory.c
+++ b/src/im-factory.c
@@ -20,8 +20,6 @@
#include "config.h"
#include "im-factory.h"
-#define DBUS_API_SUBJECT_TO_CHANGE
-
#include <string.h>
#include <dbus/dbus-glib.h>
@@ -31,17 +29,19 @@
#include <telepathy-glib/dbus.h>
#include <telepathy-glib/gtypes.h>
#include <telepathy-glib/interfaces.h>
+#include <wocky/wocky-c2s-porter.h>
#define DEBUG_FLAG GABBLE_DEBUG_IM
#include "extensions/extensions.h"
-#include "caps-channel-manager.h"
+#include "gabble/caps-channel-manager.h"
#include "connection.h"
#include "debug.h"
#include "disco.h"
#include "im-channel.h"
#include "message-util.h"
+#include "namespaces.h"
static void channel_manager_iface_init (gpointer, gpointer);
static void caps_channel_manager_iface_init (gpointer, gpointer);
@@ -70,11 +70,6 @@ struct _GabbleImFactoryPrivate
gboolean dispose_has_run;
};
-#define GABBLE_IM_FACTORY_GET_PRIVATE(o) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_IM_FACTORY,\
- GabbleImFactoryPrivate))
-
-
static void
gabble_im_factory_init (GabbleImFactory *self)
{
@@ -93,20 +88,24 @@ gabble_im_factory_init (GabbleImFactory *self)
static void connection_status_changed_cb (GabbleConnection *conn,
guint status, guint reason, GabbleImFactory *self);
+static void porter_available_cb (
+ GabbleConnection *conn,
+ WockyPorter *porter,
+ gpointer user_data);
-
-static GObject *
-gabble_im_factory_constructor (GType type, guint n_props,
- GObjectConstructParam *props)
+static void
+gabble_im_factory_constructed (GObject *obj)
{
- GObject *obj = G_OBJECT_CLASS (gabble_im_factory_parent_class)->
- constructor (type, n_props, props);
GabbleImFactory *self = GABBLE_IM_FACTORY (obj);
+ GObjectClass *parent_class = gabble_im_factory_parent_class;
+
+ if (parent_class->constructed != NULL)
+ parent_class->constructed (obj);
self->priv->status_changed_id = g_signal_connect (self->priv->conn,
"status-changed", (GCallback) connection_status_changed_cb, obj);
-
- return obj;
+ tp_g_signal_connect_object (self->priv->conn,
+ "porter-available", (GCallback) porter_available_cb, obj, 0);
}
@@ -117,7 +116,7 @@ static void
gabble_im_factory_dispose (GObject *object)
{
GabbleImFactory *fac = GABBLE_IM_FACTORY (object);
- GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac);
+ GabbleImFactoryPrivate *priv = fac->priv;
if (priv->dispose_has_run)
return;
@@ -139,7 +138,7 @@ gabble_im_factory_get_property (GObject *object,
GParamSpec *pspec)
{
GabbleImFactory *fac = GABBLE_IM_FACTORY (object);
- GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac);
+ GabbleImFactoryPrivate *priv = fac->priv;
switch (property_id) {
case PROP_CONNECTION:
@@ -158,7 +157,7 @@ gabble_im_factory_set_property (GObject *object,
GParamSpec *pspec)
{
GabbleImFactory *fac = GABBLE_IM_FACTORY (object);
- GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac);
+ GabbleImFactoryPrivate *priv = fac->priv;
switch (property_id) {
case PROP_CONNECTION:
@@ -179,7 +178,7 @@ gabble_im_factory_class_init (GabbleImFactoryClass *gabble_im_factory_class)
g_type_class_add_private (gabble_im_factory_class,
sizeof (GabbleImFactoryPrivate));
- object_class->constructor = gabble_im_factory_constructor;
+ object_class->constructed = gabble_im_factory_constructed;
object_class->dispose = gabble_im_factory_dispose;
object_class->get_property = gabble_im_factory_get_property;
@@ -193,11 +192,10 @@ gabble_im_factory_class_init (GabbleImFactoryClass *gabble_im_factory_class)
}
-static GabbleIMChannel *new_im_channel (GabbleImFactory *fac,
- TpHandle handle, TpHandle initiator, gpointer request_token);
-
-static void im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data);
-
+static GabbleIMChannel *get_channel_for_incoming_message (
+ GabbleImFactory *self,
+ const gchar *jid,
+ gboolean create_if_missing);
/**
* im_factory_message_cb:
@@ -211,18 +209,14 @@ im_factory_message_cb (LmMessageHandler *handler,
gpointer user_data)
{
GabbleImFactory *fac = GABBLE_IM_FACTORY (user_data);
- GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac);
- TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
- TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
- TP_HANDLE_TYPE_CONTACT);
const gchar *from, *body, *id;
time_t stamp;
TpChannelTextMessageType msgtype;
- TpHandle handle;
GabbleIMChannel *chan;
gint state;
TpChannelTextSendError send_error;
TpDeliveryStatus delivery_status;
+ gboolean create_if_missing;
if (!gabble_message_util_parse_incoming_message (message, &from, &stamp,
&msgtype, &id, &body, &state, &send_error, &delivery_status))
@@ -233,62 +227,46 @@ im_factory_message_cb (LmMessageHandler *handler,
return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
}
- handle = tp_handle_ensure (contact_repo, from, NULL, NULL);
- if (handle == 0)
+ /* We don't want to open up a channel for the sole purpose of reporting a
+ * send error, nor if this is just a chat state notification.
+ */
+ create_if_missing =
+ (send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) &&
+ (body != NULL);
+ chan = get_channel_for_incoming_message (fac, from, create_if_missing);
+ if (chan == NULL)
{
- STANZA_DEBUG (message, "ignoring message node from malformed jid");
+ if (create_if_missing)
+ STANZA_DEBUG (message, "ignoring message from non-contact JID");
+ else
+ DEBUG ("ignoring message error or chat state notification from '%s': "
+ "no existing channel", from);
+
return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}
- chan = g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (handle));
-
- if (chan == NULL)
+ if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
{
- if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
- {
- DEBUG ("ignoring message error; no sending channel");
- tp_handle_unref (contact_repo, handle);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
if (body == NULL)
{
- /* don't create a new channel if all we have is a chat state */
- DEBUG ("ignoring message without body; no existing channel");
- tp_handle_unref (contact_repo, handle);
+ DEBUG ("ignoring error sending chat state to %s", from);
return LM_HANDLER_RESULT_REMOVE_MESSAGE;
}
- DEBUG ("found no IM channel, creating one");
+ DEBUG ("got error sending to %s, msgtype %u, body:\n%s",
+ from, msgtype, body);
- chan = new_im_channel (fac, handle, handle, NULL);
+ _gabble_im_channel_report_delivery (chan, msgtype, stamp, id, body,
+ send_error, delivery_status);
}
-
- g_assert (chan != NULL);
-
- /* now the channel is referencing the handle, so if we unref it, that's
- * not a problem */
- tp_handle_unref (contact_repo, handle);
-
- if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR)
+ else if (body != NULL)
{
- if (body == NULL)
- {
- DEBUG ("ignoring error sending chat state to %s (handle %u)", from,
- handle);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
- DEBUG ("got error sending to %s (handle %u), msgtype %u, body:\n%s",
- from, handle, msgtype, body);
+ _gabble_im_channel_receive (chan, msgtype, from, stamp, id, body, state);
+ }
+ else if (state != -1)
+ {
+ _gabble_im_channel_state_receive (chan, (TpChannelChatState) state);
}
-
- if (body != NULL)
- _gabble_im_channel_receive (chan, msgtype, handle, from, stamp, id, body,
- 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;
}
@@ -305,7 +283,7 @@ static void
im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data)
{
GabbleImFactory *self = GABBLE_IM_FACTORY (user_data);
- GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (self);
+ GabbleImFactoryPrivate *priv = self->priv;
TpHandle contact_handle;
gboolean really_destroyed;
@@ -338,40 +316,43 @@ im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data)
}
}
-/**
- * new_im_channel
+/*
+ * new_im_channel:
+ * @fac: the factory
+ * @handle: a contact handle, for whom a channel must not yet exist
+ * @request_token: if the channel is being created in response to a channel
+ * request, the associated request token; otherwise, NULL.
+ *
+ * Creates a new 1-1 text channel to a contact. Must only be called when no 1-1
+ * text channel is already open to that contact.
+ *
+ * Returns: (transfer none): a freshly-constructed channel
*/
static GabbleIMChannel *
new_im_channel (GabbleImFactory *fac,
TpHandle handle,
- TpHandle initiator,
gpointer request_token)
{
- GabbleImFactoryPrivate *priv;
- TpBaseConnection *conn;
+ GabbleImFactoryPrivate *priv = fac->priv;
+ TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
GabbleIMChannel *chan;
- char *object_path;
GSList *request_tokens;
+ TpHandle initiator;
- g_return_val_if_fail (GABBLE_IS_IM_FACTORY (fac), NULL);
g_return_val_if_fail (handle != 0, NULL);
- g_return_val_if_fail (initiator != 0, NULL);
- priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac);
- conn = (TpBaseConnection *) priv->conn;
+ if (request_token != NULL)
+ initiator = conn->self_handle;
+ else
+ initiator = handle;
- object_path = g_strdup_printf ("%s/ImChannel%u",
- conn->object_path, handle);
chan = g_object_new (GABBLE_TYPE_IM_CHANNEL,
"connection", priv->conn,
- "object-path", object_path,
"handle", handle,
"initiator-handle", initiator,
"requested", (handle != initiator),
NULL);
- DEBUG ("object path %s", object_path);
tp_base_channel_register ((TpBaseChannel *) chan);
- g_free (object_path);
g_signal_connect (chan, "closed", (GCallback) im_channel_closed_cb, fac);
@@ -390,13 +371,55 @@ new_im_channel (GabbleImFactory *fac,
return chan;
}
+/*
+ * get_channel_for_incoming_message:
+ * @self: a factory
+ * @jid: a contact's JID
+ * @create_if_missing: if %TRUE, a new channel will be created if there is no
+ * existing channel to @jid
+ *
+ * Retrieves a 1-1 text channel to a particular contact. If no channel is open
+ * to @jid, it will be created only if @create_if_missing is %TRUE. If @jid is
+ * not of the form 'user@domain' (optionally with a resource), no channel will
+ * be opened.
+ *
+ * Returns: an IM channel to @jid, or %NULL
+ */
+static GabbleIMChannel *
+get_channel_for_incoming_message (
+ GabbleImFactory *self,
+ const gchar *jid,
+ gboolean create_if_missing)
+{
+ GabbleImFactoryPrivate *priv = self->priv;
+ TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
+ TP_HANDLE_TYPE_CONTACT);
+ TpHandle handle;
+ GabbleIMChannel *chan;
+
+ g_return_val_if_fail (jid != NULL, NULL);
+
+ handle = tp_handle_ensure (contact_repo, jid, NULL, NULL);
+ if (handle == 0)
+ return NULL;
+
+ chan = g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (handle));
+ if (chan != NULL)
+ return chan;
+ else if (create_if_missing)
+ return new_im_channel (self, handle, NULL);
+ else
+ return NULL;
+}
+
static void
gabble_im_factory_close_all (GabbleImFactory *self)
{
/* Use a temporary variable (the macro does this) because we don't want
* im_channel_closed_cb to remove the channel from the hash table a
* second time */
- tp_clear_pointer (&self->priv->channels, g_hash_table_destroy);
+ tp_clear_pointer (&self->priv->channels, g_hash_table_unref);
if (self->priv->status_changed_id != 0)
{
@@ -441,6 +464,92 @@ connection_status_changed_cb (GabbleConnection *conn,
}
}
+static gboolean
+im_factory_own_message_cb (
+ WockyPorter *porter,
+ WockyStanza *stanza,
+ gpointer user_data)
+{
+ GabbleImFactory *self = GABBLE_IM_FACTORY (user_data);
+ WockyNode *own_message, *body;
+ const gchar *to;
+ gboolean sent_locally;
+ GabbleIMChannel *chan;
+
+ /* Our stanza filter should guarantee that these are present. */
+ own_message = wocky_node_get_child (wocky_stanza_get_top_node (stanza),
+ "own-message");
+ g_return_val_if_fail (own_message != NULL, FALSE);
+ body = wocky_node_get_child (own_message, "body");
+ g_return_val_if_fail (body != NULL, FALSE);
+
+ to = wocky_node_get_attribute (own_message, "to");
+ if (to == NULL)
+ {
+ DEBUG ("own-message missing to='' attribute; ignoring");
+ return FALSE;
+ }
+
+ /* If self='true', the message was sent by the local user on this machine,
+ * rather than by the local user on some other machine. We don't really have
+ * a good way to show this in Messages. Also we don't get told the id='' of
+ * the original message, which is annoying.
+ */
+ sent_locally = !tp_strdiff ("true",
+ wocky_node_get_attribute (own_message, "self"));
+ DEBUG ("this report is for a message to '%s', sent %s",
+ to, sent_locally ? "locally" : "remotely");
+
+ /* Don't create a channel for the sole purpose of reporting an own-message.
+ * This is consistent with not creating a channel to report send errors
+ * (given that both are delivery reports).
+ */
+ chan = get_channel_for_incoming_message (self, to, FALSE);
+ if (chan != NULL)
+ _gabble_im_channel_report_delivery (chan,
+ TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, 0, NULL, body->content,
+ GABBLE_TEXT_CHANNEL_SEND_NO_ERROR, TP_DELIVERY_STATUS_ACCEPTED);
+ else
+ DEBUG ("no channel for '%s'; not spawning one just for the delivery report",
+ to);
+
+ wocky_porter_acknowledge_iq (porter, stanza, NULL);
+ return TRUE;
+}
+
+static void
+porter_available_cb (
+ GabbleConnection *conn,
+ WockyPorter *porter,
+ gpointer user_data)
+{
+ GabbleImFactory *self = GABBLE_IM_FACTORY (user_data);
+ gchar *stream_server;
+
+ g_object_get (conn, "stream-server", &stream_server, NULL);
+
+ if (!tp_strdiff (stream_server, "chat.facebook.com"))
+ {
+ wocky_porter_register_handler_from (
+ porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET,
+ /* We could use _from_server() if that accepted messages from our
+ * JID's domain, not just from bare JID, full JID, or no sender
+ * specified—which would allow the extension to work on other servers
+ * too—but it doesn't.
+ */
+ stream_server,
+ WOCKY_PORTER_HANDLER_PRIORITY_NORMAL,
+ im_factory_own_message_cb, self,
+ '(',
+ "own-message", ':', NS_FACEBOOK_MESSAGES,
+ '(', "body", ')',
+ ')', NULL);
+ }
+
+ g_free (stream_server);
+}
+
+
static void
gabble_im_factory_get_contact_caps (GabbleCapsChannelManager *manager,
TpHandle handle,
@@ -491,7 +600,7 @@ gabble_im_factory_get_contact_caps (GabbleCapsChannelManager *manager,
1, text_allowed_properties,
G_MAXUINT);
- g_hash_table_destroy (fixed_properties);
+ g_hash_table_unref (fixed_properties);
g_ptr_array_add (arr, g_value_get_boxed (&monster));
}
@@ -560,7 +669,7 @@ gabble_im_factory_type_foreach_channel_class (GType type,
func (type, table, im_channel_allowed_properties, user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
}
@@ -570,7 +679,6 @@ gabble_im_factory_requestotron (GabbleImFactory *self,
GHashTable *request_properties,
gboolean require_new)
{
- TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->conn;
TpHandle handle;
GError *error = NULL;
TpExportableChannel *channel;
@@ -598,7 +706,7 @@ gabble_im_factory_requestotron (GabbleImFactory *self,
if (channel == NULL)
{
- new_im_channel (self, handle, base_conn->self_handle, request_token);
+ new_im_channel (self, handle, request_token);
return TRUE;
}
diff --git a/src/jingle-content.c b/src/jingle-content.c
index 9e8194122..cfa339deb 100644
--- a/src/jingle-content.c
+++ b/src/jingle-content.c
@@ -508,6 +508,8 @@ gabble_jingle_content_parse_add (GabbleJingleContent *c,
GabbleJingleTransportIface *trans = NULL;
JingleDialect dialect = gabble_jingle_session_get_dialect (c->session);
+ priv->created_by_us = FALSE;
+
desc_node = wocky_node_get_child (content_node, "description");
trans_node = wocky_node_get_child (content_node, "transport");
creator = wocky_node_get_attribute (content_node, "creator");
@@ -550,6 +552,19 @@ gabble_jingle_content_parse_add (GabbleJingleContent *c,
}
else
{
+ if (creator == NULL &&
+ gabble_jingle_session_peer_has_quirk (c->session,
+ QUIRK_GOOGLE_WEBMAIL_CLIENT))
+ {
+ if (gabble_jingle_content_creator_is_initiator (c))
+ creator = "initiator";
+ else
+ creator = "responder";
+
+ DEBUG ("Working around GMail omitting creator=''; assuming '%s'",
+ creator);
+ }
+
if ((trans_node == NULL) || (creator == NULL) || (name == NULL))
{
SET_BAD_REQ ("missing required content attributes or elements");
@@ -578,7 +593,6 @@ gabble_jingle_content_parse_add (GabbleJingleContent *c,
priv->transport_ns = g_strdup (ns);
}
- priv->created_by_us = FALSE;
if (senders == NULL)
priv->senders = get_default_senders (c);
else
diff --git a/src/jingle-factory.c b/src/jingle-factory.c
index a24de0740..02b251df2 100644
--- a/src/jingle-factory.c
+++ b/src/jingle-factory.c
@@ -18,20 +18,20 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "config.h"
#include "jingle-factory.h"
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <glib.h>
-#include <loudmouth/loudmouth.h>
-#include <libsoup/soup.h>
+#include <wocky/wocky-c2s-porter.h>
#include <wocky/wocky-utils.h>
#define DEBUG_FLAG GABBLE_DEBUG_MEDIA
#include "connection.h"
+#include "conn-util.h"
#include "debug.h"
#include "gabble-signals-marshal.h"
#include "jingle-share.h"
@@ -43,6 +43,8 @@
#include "namespaces.h"
#include "util.h"
+#include "google-relay.h"
+
G_DEFINE_TYPE(GabbleJingleFactory, gabble_jingle_factory, G_TYPE_OBJECT);
/* signal enum */
@@ -65,14 +67,15 @@ enum
struct _GabbleJingleFactoryPrivate
{
GabbleConnection *conn;
- LmMessageHandler *jingle_cb;
- LmMessageHandler *jingle_info_cb;
+ guint jingle_handler_id;
+ guint jingle_info_handler_id;
GHashTable *content_types;
GHashTable *transports;
/* instances of SESSION_MAP_KEY_FORMAT => GabbleJingleSession. */
GHashTable *sessions;
- SoupSession *soup;
+
+ GabbleGoogleRelayResolver *google_resolver;
gchar *stun_server;
guint16 stun_port;
@@ -89,8 +92,10 @@ struct _GabbleJingleFactoryPrivate
gboolean dispose_has_run;
};
-static LmHandlerResult jingle_cb (LmMessageHandler *handler,
- LmConnection *lmconn, LmMessage *message, gpointer user_data);
+static gboolean jingle_cb (
+ WockyPorter *porter,
+ WockyStanza *msg,
+ gpointer user_data);
static GabbleJingleSession *create_session (GabbleJingleFactory *fac,
const gchar *sid,
TpHandle peer,
@@ -105,8 +110,10 @@ static void session_terminated_cb (GabbleJingleSession *sess,
static void connection_status_changed_cb (GabbleConnection *conn,
guint status, guint reason, GabbleJingleFactory *self);
-
-#define RELAY_HTTP_TIMEOUT 5
+static void connection_porter_available_cb (
+ GabbleConnection *conn,
+ WockyPorter *porter,
+ gpointer user_data);
static gboolean test_mode = FALSE;
@@ -133,8 +140,6 @@ gabble_jingle_factory_init (GabbleJingleFactory *obj)
priv->content_types = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, NULL);
- priv->jingle_cb = NULL;
-
priv->conn = NULL;
priv->dispose_has_run = FALSE;
priv->relay_http_port = 80;
@@ -253,54 +258,18 @@ take_stun_server (GabbleJingleFactory *self,
}
-static LmHandlerResult
-got_jingle_info_stanza (GabbleJingleFactory *fac,
- LmMessage *message)
+static void
+got_jingle_info_stanza (
+ GabbleJingleFactory *fac,
+ WockyStanza *stanza)
{
- GabbleJingleFactoryPrivate *priv = fac->priv;
- LmMessageSubType sub_type;
- WockyNode *query_node, *node;
- const gchar *from = wocky_stanza_get_from (message);
- GError *error = NULL;
-
- if (from != NULL)
- {
- TpBaseConnection *base_conn = TP_BASE_CONNECTION (priv->conn);
- TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
- base_conn, TP_HANDLE_TYPE_CONTACT);
- TpHandle sender = tp_handle_lookup (contact_repo, from, NULL, NULL);
+ WockyNode *node, *query_node;
- if (sender != base_conn->self_handle)
- {
- DEBUG ("ignoring jingleinfo from '%s', not ourself nor the server",
- from);
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
- }
-
- query_node = lm_message_node_get_child_with_namespace (
- wocky_stanza_get_top_node (message), "query", NS_GOOGLE_JINGLE_INFO);
+ query_node = wocky_node_get_child_ns (
+ wocky_stanza_get_top_node (stanza), "query", NS_GOOGLE_JINGLE_INFO);
if (query_node == NULL)
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-
- sub_type = lm_message_get_sub_type (message);
-
- if (wocky_stanza_extract_errors (message, NULL, &error, NULL, NULL))
- {
- DEBUG ("jingle info error: %s",
- wocky_xmpp_stanza_error_to_string (error));
- g_error_free (error);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
- if (sub_type != LM_MESSAGE_SUB_TYPE_RESULT &&
- sub_type != LM_MESSAGE_SUB_TYPE_SET)
- {
- DEBUG ("jingle info: unexpected IQ type, ignoring");
-
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
- }
+ return;
if (fac->priv->get_stun_from_jingle)
node = wocky_node_get_child (query_node, "stun");
@@ -332,19 +301,17 @@ got_jingle_info_stanza (GabbleJingleFactory *fac,
}
}
+#ifdef ENABLE_GOOGLE_RELAY
node = wocky_node_get_child (query_node, "relay");
if (node != NULL)
{
- WockyNode *subnode;
-
- subnode = wocky_node_get_child (node, "token");
+ WockyNode *subnode = wocky_node_get_child (node, "token");
if (subnode != NULL)
{
- const gchar *token;
+ const gchar *token = subnode->content;
- token = subnode->content;
if (token != NULL)
{
DEBUG ("jingle info: got Google relay token %s", token);
@@ -414,75 +381,61 @@ got_jingle_info_stanza (GabbleJingleFactory *fac,
}
}
-
- if (sub_type == LM_MESSAGE_SUB_TYPE_SET)
- {
- _gabble_connection_acknowledge_set_iq (priv->conn, message);
- }
-
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+#endif /* ENABLE_GOOGLE_RELAY */
}
-/*
- * jingle_info_cb
- *
- * Called by loudmouth when we get an incoming <iq>. This handler
- * is concerned only with Jingle info queries.
- */
-static LmHandlerResult
-jingle_info_cb (LmMessageHandler *handler,
- LmConnection *lmconn,
- LmMessage *message,
- gpointer user_data)
+static gboolean
+jingle_info_cb (
+ WockyPorter *porter,
+ WockyStanza *stanza,
+ gpointer user_data)
{
GabbleJingleFactory *fac = GABBLE_JINGLE_FACTORY (user_data);
- return got_jingle_info_stanza (fac, message);
+ got_jingle_info_stanza (fac, stanza);
+ wocky_porter_acknowledge_iq (porter, stanza, NULL);
+
+ return TRUE;
}
-static LmHandlerResult
-jingle_info_reply_cb (GabbleConnection *conn,
- LmMessage *sent_msg,
- LmMessage *reply_msg,
- GObject *factory_obj,
+static void
+jingle_info_reply_cb (
+ GObject *source,
+ GAsyncResult *result,
gpointer user_data)
{
- GabbleJingleFactory *fac = GABBLE_JINGLE_FACTORY (factory_obj);
+ GabbleJingleFactory *fac = GABBLE_JINGLE_FACTORY (user_data);
+ WockyStanza *reply = NULL;
+ GError *error = NULL;
- return got_jingle_info_stanza (fac, reply_msg);
+ if (conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, &reply,
+ &error))
+ {
+ got_jingle_info_stanza (fac, reply);
+ }
+ else
+ {
+ DEBUG ("jingle info request failed: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ tp_clear_object (&reply);
}
static void
jingle_info_send_request (GabbleJingleFactory *fac)
{
GabbleJingleFactoryPrivate *priv = fac->priv;
- TpBaseConnection *base = (TpBaseConnection *) priv->conn;
- LmMessage *msg;
- WockyNode *node;
- const gchar *jid;
- GError *error = NULL;
- TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
- TP_HANDLE_TYPE_CONTACT);
+ const gchar *jid = conn_util_get_bare_self_jid (priv->conn);
+ WockyStanza *stanza = wocky_stanza_build (
+ WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, jid,
+ '(', "query", ':', NS_GOOGLE_JINGLE_INFO, ')', NULL);
- jid = tp_handle_inspect (contact_handles, base->self_handle);
- msg = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_GET);
+ conn_util_send_iq_async (priv->conn, stanza, NULL, jingle_info_reply_cb, fac);
- node = wocky_node_add_child_with_content (
- wocky_stanza_get_top_node (msg), "query", NULL);
- node->ns = g_quark_from_string (NS_GOOGLE_JINGLE_INFO);
-
- if (!_gabble_connection_send_with_reply (priv->conn, msg,
- jingle_info_reply_cb, G_OBJECT (fac), fac, &error))
- {
- DEBUG ("jingle info send failed: %s\n", error->message);
- g_error_free (error);
- }
-
- lm_message_unref (msg);
+ g_object_unref (stanza);
}
-
static void
gabble_jingle_factory_dispose (GObject *object)
{
@@ -495,10 +448,10 @@ gabble_jingle_factory_dispose (GObject *object)
DEBUG ("dispose called");
priv->dispose_has_run = TRUE;
- tp_clear_object (&priv->soup);
- tp_clear_pointer (&priv->sessions, g_hash_table_destroy);
- tp_clear_pointer (&priv->content_types, g_hash_table_destroy);
- tp_clear_pointer (&priv->transports, g_hash_table_destroy);
+ tp_clear_pointer (&priv->google_resolver, gabble_google_relay_resolver_destroy);
+ tp_clear_pointer (&priv->sessions, g_hash_table_unref);
+ tp_clear_pointer (&priv->content_types, g_hash_table_unref);
+ tp_clear_pointer (&priv->transports, g_hash_table_unref);
tp_clear_pointer (&priv->stun_server, g_free);
tp_clear_pointer (&priv->fallback_stun_server, g_free);
tp_clear_pointer (&priv->relay_token, g_free);
@@ -546,32 +499,26 @@ gabble_jingle_factory_set_property (GObject *object,
}
}
-static GObject *
-gabble_jingle_factory_constructor (GType type,
- guint n_props,
- GObjectConstructParam *props)
+static void
+gabble_jingle_factory_constructed (GObject *obj)
{
- GObject *obj;
- GabbleJingleFactory *self;
- GabbleJingleFactoryPrivate *priv;
-
- obj = G_OBJECT_CLASS (gabble_jingle_factory_parent_class)->
- constructor (type, n_props, props);
+ GabbleJingleFactory *self = GABBLE_JINGLE_FACTORY (obj);
+ GabbleJingleFactoryPrivate *priv = self->priv;
+ GObjectClass *parent = G_OBJECT_CLASS (gabble_jingle_factory_parent_class);
- self = GABBLE_JINGLE_FACTORY (obj);
- priv = self->priv;
+ if (parent->constructed != NULL)
+ parent->constructed (obj);
- /* FIXME: why was this in _constructed in media factory? */
gabble_signal_connect_weak (priv->conn, "status-changed",
(GCallback) connection_status_changed_cb, G_OBJECT (self));
+ gabble_signal_connect_weak (priv->conn, "porter-available",
+ (GCallback) connection_porter_available_cb, G_OBJECT (self));
jingle_share_register (self);
jingle_media_rtp_register (self);
jingle_transport_google_register (self);
jingle_transport_rawudp_register (self);
jingle_transport_iceudp_register (self);
-
- return obj;
}
static void
@@ -582,7 +529,7 @@ gabble_jingle_factory_class_init (GabbleJingleFactoryClass *cls)
g_type_class_add_private (cls, sizeof (GabbleJingleFactoryPrivate));
- object_class->constructor = gabble_jingle_factory_constructor;
+ object_class->constructed = gabble_jingle_factory_constructed;
object_class->get_property = gabble_jingle_factory_get_property;
object_class->set_property = gabble_jingle_factory_set_property;
object_class->dispose = gabble_jingle_factory_dispose;
@@ -619,22 +566,6 @@ connection_status_changed_cb (GabbleConnection *conn,
{
case TP_CONNECTION_STATUS_CONNECTING:
g_assert (priv->conn != NULL);
-
- g_assert (priv->jingle_cb == NULL);
- g_assert (priv->jingle_info_cb == NULL);
-
- priv->jingle_cb = lm_message_handler_new (jingle_cb,
- self, NULL);
- lm_connection_register_message_handler (priv->conn->lmconn,
- priv->jingle_cb, LM_MESSAGE_TYPE_IQ,
- LM_HANDLER_PRIORITY_NORMAL);
-
- priv->jingle_info_cb = lm_message_handler_new (
- jingle_info_cb, self, NULL);
- lm_connection_register_message_handler (priv->conn->lmconn,
- priv->jingle_info_cb, LM_MESSAGE_TYPE_IQ,
- LM_HANDLER_PRIORITY_NORMAL);
-
break;
case TP_CONNECTION_STATUS_CONNECTED:
@@ -675,20 +606,44 @@ connection_status_changed_cb (GabbleConnection *conn,
break;
case TP_CONNECTION_STATUS_DISCONNECTED:
- if (priv->jingle_cb != NULL)
+ if (priv->jingle_handler_id != 0)
{
- lm_connection_unregister_message_handler (priv->conn->lmconn,
- priv->jingle_cb, LM_MESSAGE_TYPE_IQ);
- lm_connection_unregister_message_handler (priv->conn->lmconn,
- priv->jingle_info_cb, LM_MESSAGE_TYPE_IQ);
+ WockyPorter *p = wocky_session_get_porter (priv->conn->session);
+
+ wocky_porter_unregister_handler (p, priv->jingle_handler_id);
+ wocky_porter_unregister_handler (p, priv->jingle_info_handler_id);
+ priv->jingle_handler_id = 0;
+ priv->jingle_info_handler_id = 0;
}
- tp_clear_pointer (&priv->jingle_cb, lm_message_handler_unref);
- tp_clear_pointer (&priv->jingle_info_cb, lm_message_handler_unref);
break;
}
}
+static void
+connection_porter_available_cb (
+ GabbleConnection *conn,
+ WockyPorter *porter,
+ gpointer user_data)
+{
+ GabbleJingleFactory *self = GABBLE_JINGLE_FACTORY (user_data);
+ GabbleJingleFactoryPrivate *priv = self->priv;
+
+ g_assert (priv->jingle_handler_id == 0);
+
+ /* TODO: we could match different dialects here maybe? */
+ priv->jingle_handler_id = wocky_porter_register_handler_from_anyone (porter,
+ WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET,
+ WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, jingle_cb, self,
+ NULL);
+
+ priv->jingle_info_handler_id = wocky_c2s_porter_register_handler_from_server (
+ WOCKY_C2S_PORTER (porter),
+ WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET,
+ WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, jingle_info_cb, self,
+ '(', "query", ':', NS_GOOGLE_JINGLE_INFO, ')', NULL);
+}
+
/* The 'session' map is keyed by:
* "<peer's handle>\n<peer's jid>\n<session id>"
*/
@@ -781,11 +736,11 @@ ensure_session (GabbleJingleFactory *self,
return sess;
}
-static LmHandlerResult
-jingle_cb (LmMessageHandler *handler,
- LmConnection *lmconn,
- LmMessage *msg,
- gpointer user_data)
+static gboolean
+jingle_cb (
+ WockyPorter *porter,
+ WockyStanza *msg,
+ gpointer user_data)
{
GabbleJingleFactory *self = GABBLE_JINGLE_FACTORY (user_data);
GabbleJingleFactoryPrivate *priv = self->priv;
@@ -798,10 +753,10 @@ jingle_cb (LmMessageHandler *handler,
/* see if it's a jingle message and detect dialect */
sid = gabble_jingle_session_detect (msg, &action, &dialect);
- from = wocky_node_get_attribute (lm_message_get_node (msg), "from");
+ from = wocky_stanza_get_from (msg);
if (sid == NULL || from == NULL)
- return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ return FALSE;
sess = ensure_session (self, sid, from, action, dialect, &new_session,
&error);
@@ -819,7 +774,7 @@ jingle_cb (LmMessageHandler *handler,
/* all went well, we can acknowledge the IQ */
_gabble_connection_acknowledge_set_iq (priv->conn, msg);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ return TRUE;
REQUEST_ERROR:
g_assert (error != NULL);
@@ -831,7 +786,7 @@ REQUEST_ERROR:
if (sess != NULL && new_session)
gabble_jingle_session_terminate (sess, JINGLE_REASON_UNKNOWN, NULL, NULL);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ return TRUE;
}
/*
@@ -987,204 +942,6 @@ gabble_jingle_factory_get_stun_server (GabbleJingleFactory *self,
return TRUE;
}
-typedef struct
-{
- GPtrArray *relays;
- guint component;
- guint requests_to_do;
- GabbleJingleFactoryRelaySessionCb callback;
- gpointer user_data;
-} RelaySessionData;
-
-static RelaySessionData *
-relay_session_data_new (guint requests_to_do,
- GabbleJingleFactoryRelaySessionCb callback,
- gpointer user_data)
-{
- RelaySessionData *rsd = g_slice_new0 (RelaySessionData);
-
- rsd->relays = g_ptr_array_sized_new (requests_to_do);
- rsd->component = 1;
- rsd->requests_to_do = requests_to_do;
- rsd->callback = callback;
- rsd->user_data = user_data;
-
- return rsd;
-}
-
-/* This is a GSourceFunc */
-static gboolean
-relay_session_data_call (gpointer p)
-{
- RelaySessionData *rsd = p;
-
- g_assert (rsd->callback != NULL);
-
- rsd->callback (rsd->relays, rsd->user_data);
-
- return FALSE;
-}
-
-/* This is a GDestroyNotify */
-static void
-relay_session_data_destroy (gpointer p)
-{
- RelaySessionData *rsd = p;
-
- g_ptr_array_foreach (rsd->relays, (GFunc) g_hash_table_destroy, NULL);
- g_ptr_array_free (rsd->relays, TRUE);
-
- g_slice_free (RelaySessionData, rsd);
-}
-
-static void
-translate_relay_info (GPtrArray *relays,
- const gchar *relay_ip,
- const gchar *username,
- const gchar *password,
- const gchar *static_type,
- const gchar *port_string,
- guint component)
-{
- GHashTable *asv;
- guint port = 0;
-
- if (port_string == NULL)
- {
- DEBUG ("no relay port for %s found", static_type);
- return;
- }
-
- port = atoi (port_string);
-
- if (port == 0 || port > G_MAXUINT16)
- {
- DEBUG ("failed to parse relay port '%s' for %s", port_string,
- static_type);
- return;
- }
-
- DEBUG ("type=%s ip=%s port=%u username=%s password=%s component=%u",
- static_type, relay_ip, port, username, password, component);
- /* keys are static, values are slice-allocated */
- asv = g_hash_table_new_full (g_str_hash, g_str_equal,
- NULL, (GDestroyNotify) tp_g_value_slice_free);
- g_hash_table_insert (asv, "ip",
- tp_g_value_slice_new_string (relay_ip));
- g_hash_table_insert (asv, "type",
- tp_g_value_slice_new_static_string (static_type));
- g_hash_table_insert (asv, "port",
- tp_g_value_slice_new_uint (port));
- g_hash_table_insert (asv, "username",
- tp_g_value_slice_new_string (username));
- g_hash_table_insert (asv, "password",
- tp_g_value_slice_new_string (password));
- g_hash_table_insert (asv, "component",
- tp_g_value_slice_new_uint (component));
-
- g_ptr_array_add (relays, asv);
-}
-
-static void
-on_http_response (SoupSession *soup,
- SoupMessage *msg,
- gpointer user_data)
-{
- RelaySessionData *rsd = user_data;
-
- if (msg->status_code != 200)
- {
- DEBUG ("Google session creation failed, relaying not used: %d %s",
- msg->status_code, msg->reason_phrase);
- }
- else
- {
- /* parse a=b lines into GHashTable
- * (key, value both borrowed from items of the strv 'lines') */
- GHashTable *map = g_hash_table_new (g_str_hash, g_str_equal);
- gchar **lines;
- guint i;
- const gchar *relay_ip;
- const gchar *relay_udp_port;
- const gchar *relay_tcp_port;
- const gchar *relay_ssltcp_port;
- const gchar *username;
- const gchar *password;
- gchar *escaped_str;
-
- escaped_str = g_strescape (msg->response_body->data, "\r\n");
- DEBUG ("Response from Google:\n====\n%s\n====", escaped_str);
- g_free (escaped_str);
-
- lines = g_strsplit (msg->response_body->data, "\n", 0);
-
- if (lines != NULL)
- {
- for (i = 0; lines[i] != NULL; i++)
- {
- gchar *delim = strchr (lines[i], '=');
- size_t len;
-
- if (delim == NULL || delim == lines[i])
- {
- /* ignore empty keys or lines without '=' */
- continue;
- }
-
- len = strlen (lines[i]);
-
- if (lines[i][len - 1] == '\r')
- {
- lines[i][len - 1] = '\0';
- }
-
- *delim = '\0';
- g_hash_table_insert (map, lines[i], delim + 1);
- }
- }
-
- relay_ip = g_hash_table_lookup (map, "relay.ip");
- relay_udp_port = g_hash_table_lookup (map, "relay.udp_port");
- relay_tcp_port = g_hash_table_lookup (map, "relay.tcp_port");
- relay_ssltcp_port = g_hash_table_lookup (map, "relay.ssltcp_port");
- username = g_hash_table_lookup (map, "username");
- password = g_hash_table_lookup (map, "password");
-
- if (relay_ip == NULL)
- {
- DEBUG ("No relay.ip found");
- }
- else if (username == NULL)
- {
- DEBUG ("No username found");
- }
- else if (password == NULL)
- {
- DEBUG ("No password found");
- }
- else
- {
- translate_relay_info (rsd->relays, relay_ip, username, password,
- "udp", relay_udp_port, rsd->component);
- translate_relay_info (rsd->relays, relay_ip, username, password,
- "tcp", relay_tcp_port, rsd->component);
- translate_relay_info (rsd->relays, relay_ip, username, password,
- "tls", relay_ssltcp_port, rsd->component);
- }
-
- g_strfreev (lines);
- g_hash_table_destroy (map);
- }
-
- rsd->component++;
-
- if ((--rsd->requests_to_do) == 0)
- {
- relay_session_data_call (rsd);
- relay_session_data_destroy (rsd);
- }
-}
-
void
gabble_jingle_factory_create_google_relay_session (
GabbleJingleFactory *fac,
@@ -1193,56 +950,15 @@ gabble_jingle_factory_create_google_relay_session (
gpointer user_data)
{
GabbleJingleFactoryPrivate *priv = fac->priv;
- gchar *url;
- guint i;
- RelaySessionData *rsd;
g_return_if_fail (callback != NULL);
- rsd = relay_session_data_new (components, callback, user_data);
-
- if (fac->priv->relay_server == NULL)
+ if (priv->google_resolver == NULL)
{
- DEBUG ("No relay server provided, not creating google relay session");
- g_idle_add_full (G_PRIORITY_DEFAULT, relay_session_data_call, rsd,
- relay_session_data_destroy);
- return;
- }
-
- if (fac->priv->relay_token == NULL)
- {
- DEBUG ("No relay token provided, not creating google relay session");
- g_idle_add_full (G_PRIORITY_DEFAULT, relay_session_data_call, rsd,
- relay_session_data_destroy);
- return;
- }
-
- if (priv->soup == NULL)
- {
- priv->soup = soup_session_async_new ();
-
- /* If we don't get answer in a few seconds, relay won't do
- * us much help anyways. */
- g_object_set (priv->soup, "timeout", RELAY_HTTP_TIMEOUT, NULL);
- }
-
- url = g_strdup_printf ("http://%s:%d/create_session",
- fac->priv->relay_server, fac->priv->relay_http_port);
-
- for (i = 0; i < components; i++)
- {
- SoupMessage *msg = soup_message_new ("GET", url);
-
- DEBUG ("Trying to create a new relay session on %s", url);
-
- /* libjingle sets both headers, so shall we */
- soup_message_headers_append (msg->request_headers,
- "X-Talk-Google-Relay-Auth", fac->priv->relay_token);
- soup_message_headers_append (msg->request_headers,
- "X-Google-Relay-Auth", fac->priv->relay_token);
-
- soup_session_queue_message (priv->soup, msg, on_http_response, rsd);
+ priv->google_resolver = gabble_google_relay_resolver_new ();
}
- g_free (url);
+ gabble_google_relay_resolver_resolve (priv->google_resolver,
+ components, priv->relay_server, priv->relay_http_port, priv->relay_token,
+ callback, user_data);
}
diff --git a/src/jingle-media-rtp.c b/src/jingle-media-rtp.c
index 182c52e17..2114386d0 100644
--- a/src/jingle-media-rtp.c
+++ b/src/jingle-media-rtp.c
@@ -337,7 +337,9 @@ static void transport_created (GabbleJingleContent *content,
dialect = gabble_jingle_session_get_dialect (content->session);
if (priv->media_type == JINGLE_MEDIA_TYPE_VIDEO &&
- JINGLE_IS_GOOGLE_DIALECT (dialect))
+ (JINGLE_IS_GOOGLE_DIALECT (dialect) ||
+ gabble_jingle_session_peer_has_quirk (content->session,
+ QUIRK_GOOGLE_WEBMAIL_CLIENT)))
{
jingle_transport_google_set_component_name (gtrans, "video_rtp", 1);
jingle_transport_google_set_component_name (gtrans, "video_rtcp", 2);
@@ -613,7 +615,7 @@ codec_update_coherent (const JingleCodec *old_c,
return FALSE;
}
- if (tp_strdiff (new_c->name, old_c->name))
+ if (g_ascii_strcasecmp (new_c->name, old_c->name))
{
g_set_error (e, domain, code,
"tried to change codec %u's name from %s to %s",
diff --git a/src/jingle-session.c b/src/jingle-session.c
index 8d060ec00..039881b0e 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"
@@ -218,10 +218,10 @@ gabble_jingle_session_dispose (GObject *object)
g_assert ((priv->state == JINGLE_STATE_PENDING_CREATED) ||
(priv->state == JINGLE_STATE_ENDED));
- g_hash_table_destroy (priv->initiator_contents);
+ g_hash_table_unref (priv->initiator_contents);
priv->initiator_contents = NULL;
- g_hash_table_destroy (priv->responder_contents);
+ g_hash_table_unref (priv->responder_contents);
priv->responder_contents = NULL;
tp_handle_unref (contact_repo, sess->peer);
@@ -368,7 +368,7 @@ gabble_jingle_session_constructed (GObject *object)
{
/* The peer jid isn't exactly what is in the contact repo so it will have
* a resource */
- if (gabble_decode_jid (priv->peer_jid, NULL, NULL,
+ if (wocky_decode_jid (priv->peer_jid, NULL, NULL,
&priv->peer_resource))
{
/* fake for gcc */;
@@ -631,6 +631,21 @@ static void set_state (GabbleJingleSession *sess,
JingleState state, JingleReason termination_reason, const gchar *text);
static GabbleJingleContent *_get_any_content (GabbleJingleSession *session);
+gboolean
+gabble_jingle_session_peer_has_quirk (
+ GabbleJingleSession *self,
+ const gchar *quirk)
+{
+ GabbleJingleSessionPrivate *priv = self->priv;
+ GabblePresence *presence = gabble_presence_cache_get (
+ priv->conn->presence_cache, self->peer);
+
+ return (presence != NULL &&
+ priv->peer_resource != NULL &&
+ gabble_presence_resource_has_caps (presence, priv->peer_resource,
+ gabble_capability_set_predicate_has, quirk));
+}
+
static gboolean
lookup_content (GabbleJingleSession *sess,
const gchar *name,
@@ -668,13 +683,8 @@ lookup_content (GabbleJingleSession *sess,
* of the moon, and get kind of confused in the process), and we try to
* pick globally-unique content names.
*/
- GabblePresence *presence = gabble_presence_cache_get (
- priv->conn->presence_cache, sess->peer);
-
- if (creator == NULL && presence != NULL &&
- priv->peer_resource != NULL &&
- gabble_presence_resource_has_caps (presence, priv->peer_resource,
- gabble_capability_set_predicate_has,
+ if (creator == NULL &&
+ gabble_jingle_session_peer_has_quirk (sess,
QUIRK_OMITS_CONTENT_CREATORS))
{
DEBUG ("working around missing 'creator' attribute");
@@ -2078,7 +2088,7 @@ gabble_jingle_session_terminate (GabbleJingleSession *sess,
wocky_node_add_child_with_content (r, reason_elt, NULL);
- if (!CHECK_STR_EMPTY(text))
+ if (!tp_str_empty (text))
wocky_node_add_child_with_content (r, "text", text);
}
@@ -2350,7 +2360,8 @@ gabble_jingle_session_get_remote_ringing (GabbleJingleSession *sess)
gboolean
gabble_jingle_session_can_modify_contents (GabbleJingleSession *sess)
{
- return !JINGLE_IS_GOOGLE_DIALECT (sess->priv->dialect);
+ return !JINGLE_IS_GOOGLE_DIALECT (sess->priv->dialect) &&
+ !gabble_jingle_session_peer_has_quirk (sess, QUIRK_GOOGLE_WEBMAIL_CLIENT);
}
JingleDialect
diff --git a/src/jingle-session.h b/src/jingle-session.h
index fda18e3b2..2f02df405 100644
--- a/src/jingle-session.h
+++ b/src/jingle-session.h
@@ -107,6 +107,9 @@ const gchar *gabble_jingle_session_get_sid (GabbleJingleSession *sess);
JingleDialect gabble_jingle_session_get_dialect (GabbleJingleSession *sess);
gboolean gabble_jingle_session_can_modify_contents (GabbleJingleSession *sess);
+gboolean gabble_jingle_session_peer_has_quirk (
+ GabbleJingleSession *self,
+ const gchar *quirk);
typedef void (*JingleReplyHandler) (GObject *, gboolean success,
LmMessage *reply);
diff --git a/src/jingle-transport-google.c b/src/jingle-transport-google.c
index c63bb33c5..67e0e3e4f 100644
--- a/src/jingle-transport-google.c
+++ b/src/jingle-transport-google.c
@@ -110,7 +110,7 @@ gabble_jingle_transport_google_dispose (GObject *object)
DEBUG ("dispose called");
priv->dispose_has_run = TRUE;
- g_hash_table_destroy (priv->component_names);
+ g_hash_table_unref (priv->component_names);
priv->component_names = NULL;
jingle_transport_free_candidates (priv->remote_candidates);
diff --git a/src/jingle-transport-iface.c b/src/jingle-transport-iface.c
index 671eac20d..fe4223623 100644
--- a/src/jingle-transport-iface.c
+++ b/src/jingle-transport-iface.c
@@ -107,7 +107,7 @@ gabble_jingle_transport_iface_can_accept (GabbleJingleTransportIface *self)
if (state != JINGLE_TRANSPORT_STATE_CONNECTED)
return FALSE;
- /* Only Raw UDP *needs* contents in order to accept. */
+ /* Only Raw UDP *needs* candidates in order to accept. */
if (m != NULL)
return m (self);
else
diff --git a/src/legacy-caps.h b/src/legacy-caps.h
index 080ac3b0e..e2de46c37 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/main.c b/src/main.c
index e6fc2288f..462183c7d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -21,7 +21,12 @@
#include "gabble.h"
int
-main (int argc,
+#ifdef BUILD_AS_ANDROID_SERVICE
+telepathy_gabble_main
+#else
+main
+#endif
+ (int argc,
char **argv)
{
gabble_init ();
diff --git a/src/media-channel-hold.c b/src/media-channel-hold.c
index 93496aa0f..ce657e646 100644
--- a/src/media-channel-hold.c
+++ b/src/media-channel-hold.c
@@ -366,7 +366,7 @@ gabble_media_channel_get_call_states (TpSvcChannelInterfaceCallState *iface,
tp_svc_channel_interface_call_state_return_from_get_call_states (context,
states);
- g_hash_table_destroy (states);
+ g_hash_table_unref (states);
}
diff --git a/src/media-channel.c b/src/media-channel.c
index 4e2341a57..d57ed4012 100644
--- a/src/media-channel.c
+++ b/src/media-channel.c
@@ -450,9 +450,7 @@ gabble_media_channel_constructor (GType type, guint n_props,
/* If this is a Google session, let's set ImmutableStreams */
if (priv->session != NULL)
{
- JingleDialect d = gabble_jingle_session_get_dialect (priv->session);
-
- priv->immutable_streams = JINGLE_IS_GOOGLE_DIALECT (d);
+ priv->immutable_streams = !gabble_jingle_session_can_modify_contents (priv->session);
}
/* If there's no session yet, but we know who the peer will be, and we have
* presence for them, we can set ImmutableStreams using the same algorithm as
@@ -980,7 +978,7 @@ gabble_media_channel_dispose (GObject *object)
{
g_ptr_array_foreach (priv->delayed_request_streams,
(GFunc) destroy_request, NULL);
- g_ptr_array_free (priv->delayed_request_streams, TRUE);
+ g_ptr_array_unref (priv->delayed_request_streams);
priv->delayed_request_streams = NULL;
}
@@ -997,7 +995,7 @@ gabble_media_channel_dispose (GObject *object)
* removed when the call ended.
*/
g_assert (priv->streams->len == 0);
- g_ptr_array_free (priv->streams, TRUE);
+ g_ptr_array_unref (priv->streams);
priv->streams = NULL;
if (G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose)
@@ -1162,7 +1160,7 @@ gabble_media_channel_get_session_handlers (TpSvcChannelInterfaceMediaSignalling
tp_svc_channel_interface_media_signalling_return_from_get_session_handlers (
context, ret);
g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL);
- g_ptr_array_free (ret, TRUE);
+ g_ptr_array_unref (ret);
}
/**
@@ -1255,7 +1253,7 @@ gabble_media_channel_list_streams (TpSvcChannelTypeStreamedMedia *iface,
tp_svc_channel_type_streamed_media_return_from_list_streams (context, ret);
g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL);
- g_ptr_array_free (ret, TRUE);
+ g_ptr_array_unref (ret);
}
@@ -1391,7 +1389,7 @@ gabble_media_channel_remove_streams (TpSvcChannelTypeStreamedMedia *iface,
}
OUT:
- g_ptr_array_free (stream_objs, TRUE);
+ g_ptr_array_unref (stream_objs);
if (error)
{
@@ -1551,7 +1549,7 @@ pending_stream_request_maybe_satisfy (PendingStreamRequest *p,
p->succeeded_cb (p->context, ret);
g_ptr_array_foreach (ret, (GFunc) g_value_array_free, NULL);
- g_ptr_array_free (ret, TRUE);
+ g_ptr_array_unref (ret);
p->context = NULL;
return TRUE;
}
@@ -1799,7 +1797,7 @@ destroy_request (struct _delayed_request_streams_ctx *ctx,
g_error_free (error);
}
- g_array_free (ctx->types, TRUE);
+ g_array_unref (ctx->types);
g_slice_free (struct _delayed_request_streams_ctx, ctx);
}
@@ -1897,7 +1895,7 @@ media_channel_request_streams (GabbleMediaChannel *self,
DEBUG ("no streams to request");
succeeded_cb (context, empty);
- g_ptr_array_free (empty, TRUE);
+ g_ptr_array_unref (empty);
return;
}
@@ -1943,7 +1941,7 @@ media_channel_request_streams (GabbleMediaChannel *self,
context);
priv->pending_stream_requests = g_list_prepend (priv->pending_stream_requests,
psr);
- g_ptr_array_free (contents, TRUE);
+ g_ptr_array_unref (contents);
/* signal acceptance */
gabble_jingle_session_accept (priv->session);
@@ -2043,7 +2041,7 @@ gabble_media_channel_request_initial_streams (GabbleMediaChannel *chan,
media_channel_request_streams (chan, priv->initial_peer, types,
succeeded_cb, failed_cb, user_data);
- g_array_free (types, TRUE);
+ g_array_unref (types);
}
static gboolean
@@ -2442,7 +2440,7 @@ session_terminated_cb (GabbleJingleSession *session,
/* All the streams should have closed. */
g_assert (priv->streams->len == 0);
- g_ptr_array_free (tmp, TRUE);
+ g_ptr_array_unref (tmp);
}
/* remove the session */
@@ -3072,7 +3070,7 @@ gabble_media_channel_error (TpSvcMediaSessionHandler *iface,
gabble_media_stream_error (stream, errno, message, NULL);
}
- g_ptr_array_free (tmp, TRUE);
+ g_ptr_array_unref (tmp);
tp_svc_media_session_handler_return_from_error (context);
}
diff --git a/src/media-factory.c b/src/media-factory.c
index 4a2c7c704..4eb902c12 100644
--- a/src/media-factory.c
+++ b/src/media-factory.c
@@ -20,8 +20,6 @@
#include "config.h"
#include "media-factory.h"
-#define DBUS_API_SUBJECT_TO_CHANGE
-
#include <stdlib.h>
#include <string.h>
@@ -36,7 +34,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"
@@ -681,13 +679,13 @@ gabble_media_factory_type_foreach_channel_class (GType type,
func (type, table, named_channel_allowed_properties, user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
table = gabble_media_factory_call_channel_class ();
func (type, table, call_channel_allowed_properties, user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
}
@@ -1208,7 +1206,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/media-stream.c b/src/media-stream.c
index da22ebc5d..09570ebba 100644
--- a/src/media-stream.c
+++ b/src/media-stream.c
@@ -189,7 +189,7 @@ gabble_media_stream_new (const gchar *object_path,
NULL);
if (empty != NULL)
- g_ptr_array_free (empty, TRUE);
+ g_ptr_array_unref (empty);
return result;
}
@@ -743,15 +743,15 @@ gabble_media_stream_codec_choice (TpSvcMediaStreamHandler *iface,
gboolean
gabble_media_stream_error (GabbleMediaStream *self,
- guint errno,
+ guint errnum,
const gchar *message,
GError **error)
{
g_assert (GABBLE_IS_MEDIA_STREAM (self));
DEBUG ( "Media.StreamHandler::Error called, error %u (%s) -- emitting signal",
- errno, message);
- g_signal_emit (self, signals[ERROR], 0, errno, message);
+ errnum, message);
+ g_signal_emit (self, signals[ERROR], 0, errnum, message);
return TRUE;
}
@@ -765,14 +765,14 @@ gabble_media_stream_error (GabbleMediaStream *self,
*/
static void
gabble_media_stream_error_async (TpSvcMediaStreamHandler *iface,
- guint errno,
+ guint errnum,
const gchar *message,
DBusGMethodInvocation *context)
{
GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface);
GError *error = NULL;
- if (gabble_media_stream_error (self, errno, message, &error))
+ if (gabble_media_stream_error (self, errnum, message, &error))
{
tp_svc_media_stream_handler_return_from_error (context);
}
@@ -1681,7 +1681,7 @@ new_remote_candidates_cb (GabbleJingleContent *content,
g_free (candidate_id);
g_value_unset (&transport);
- g_ptr_array_free (transports, TRUE);
+ g_ptr_array_unref (transports);
g_ptr_array_add (candidates, g_value_get_boxed (&candidate));
}
diff --git a/src/message-util.c b/src/message-util.c
index c38efec42..e8832ae55 100644
--- a/src/message-util.c
+++ b/src/message-util.c
@@ -37,12 +37,12 @@
#include "util.h"
-static void
-_add_chat_state (LmMessage *msg,
+void
+gabble_message_util_add_chat_state (WockyStanza *stanza,
TpChannelChatState state)
{
WockyNode *node = NULL;
- WockyNode *n = wocky_stanza_get_top_node (msg);
+ WockyNode *n = wocky_stanza_get_top_node (stanza);
switch (state)
{
@@ -67,31 +67,32 @@ _add_chat_state (LmMessage *msg,
node->ns = g_quark_from_static_string (NS_CHAT_STATES);
}
-
/**
- * gabble_message_util_send_message:
- * @obj: a channel implementation featuring TpMessageMixin
- * @conn: the connection owning this channel
+ * gabble_message_util_build_stanza
* @message: the message to be sent
- * @flags: the flags used if sending is successful
+ * @conn: the connection owning this channel
* @subtype: the Loudmouth message subtype
* @state: the Telepathy chat state, or -1 if unknown or not applicable
* @recipient: the recipient's JID
* @send_nick: whether to include our own nick in the message
+ * @token: return the message id
+ * @error: return the error if operation failed
+ *
+ * Returns: The wocky stanza for the message
*/
-void
-gabble_message_util_send_message (GObject *obj,
+
+WockyStanza *
+gabble_message_util_build_stanza (TpMessage *message,
GabbleConnection *conn,
- TpMessage *message,
- TpMessageSendingFlags flags,
LmMessageSubType subtype,
TpChannelChatState state,
const char *recipient,
- gboolean send_nick)
+ gboolean send_nick,
+ gchar **token,
+ GError **error)
{
- GError *error = NULL;
const GHashTable *part;
- LmMessage *msg;
+ WockyStanza *stanza = NULL;
WockyNode *node;
guint type = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL;
gboolean result = TRUE;
@@ -99,12 +100,12 @@ gabble_message_util_send_message (GObject *obj,
gchar *id = NULL;
guint n_parts;
-#define INVALID_ARGUMENT(msg, ...) \
+#define RETURN_INVALID_ARGUMENT(msg, ...) \
G_STMT_START { \
DEBUG (msg , ## __VA_ARGS__); \
- g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, \
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, \
msg , ## __VA_ARGS__); \
- goto despair_island; \
+ return NULL; \
} G_STMT_END
part = tp_message_peek (message, 0);
@@ -113,15 +114,15 @@ gabble_message_util_send_message (GObject *obj,
type = tp_asv_get_uint32 (part, "message-type", &result);
if (!result)
- INVALID_ARGUMENT ("message-type must be a 32-bit unsigned integer");
+ RETURN_INVALID_ARGUMENT ("message-type must be a 32-bit unsigned integer");
if (type >= NUM_TP_CHANNEL_TEXT_MESSAGE_TYPES)
- INVALID_ARGUMENT ("invalid message type: %u", type);
+ RETURN_INVALID_ARGUMENT ("invalid message type: %u", type);
n_parts = tp_message_count_parts (message);
if (n_parts != 2)
- INVALID_ARGUMENT ("message must contain exactly 1 part, not %u",
+ RETURN_INVALID_ARGUMENT ("message must contain exactly 1 part, not %u",
(n_parts - 1));
part = tp_message_peek (message, 1);
@@ -129,12 +130,10 @@ gabble_message_util_send_message (GObject *obj,
text = tp_asv_get_string (part, "content");
if (content_type == NULL || tp_strdiff (content_type, "text/plain"))
- INVALID_ARGUMENT ("message must be text/plain");
+ RETURN_INVALID_ARGUMENT ("message must be text/plain");
if (text == NULL)
- INVALID_ARGUMENT ("content must be a UTF-8 string");
-
- /* Okay, it's valid. Let's send it. */
+ RETURN_INVALID_ARGUMENT ("content must be a UTF-8 string");
if (!subtype)
{
@@ -150,9 +149,9 @@ gabble_message_util_send_message (GObject *obj,
}
}
- msg = lm_message_new_with_sub_type (recipient, LM_MESSAGE_TYPE_MESSAGE,
+ stanza = lm_message_new_with_sub_type (recipient, LM_MESSAGE_TYPE_MESSAGE,
subtype);
- node = wocky_stanza_get_top_node (msg);
+ node = wocky_stanza_get_top_node (stanza);
/* Generate a UUID for the message */
id = gabble_generate_id ();
wocky_node_set_attribute (node, "id", id);
@@ -173,24 +172,15 @@ gabble_message_util_send_message (GObject *obj,
wocky_node_add_child_with_content (node, "body", text);
}
- _add_chat_state (msg, state);
+ gabble_message_util_add_chat_state (stanza, state);
- result = _gabble_connection_send (conn, msg, &error);
- lm_message_unref (msg);
-
- if (!result)
- goto despair_island;
-
- tp_message_mixin_sent (obj, message, flags, id, NULL);
- g_free (id);
-
- return;
+ if (token != NULL)
+ *token = id;
+ else
+ g_free (id);
-despair_island:
- g_assert (error != NULL);
- tp_message_mixin_sent (obj, message, 0, NULL, error);
- g_error_free (error);
- g_free (id);
+ gabble_connection_update_last_use (conn);
+ return stanza;
}
@@ -217,7 +207,7 @@ gabble_message_util_send_chat_state (GObject *obj,
LM_MESSAGE_TYPE_MESSAGE, subtype);
gboolean result;
- _add_chat_state (msg, state);
+ gabble_message_util_add_chat_state (msg, state);
result = _gabble_connection_send (conn, msg, error);
lm_message_unref (msg);
@@ -464,6 +454,14 @@ gabble_message_util_parse_incoming_message (LmMessage *message,
if (body != NULL)
{
+ if (lm_message_node_get_child_with_namespace (
+ wocky_stanza_get_top_node (message),
+ "google-rbc-announcement", "google:metadata") != NULL)
+ {
+ /* Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=36647 */
+ return FALSE;
+ }
+
if (type == NULL &&
lm_message_node_get_child_with_namespace (
wocky_stanza_get_top_node (message),
diff --git a/src/message-util.h b/src/message-util.h
index 7e8d743e6..8b63c7adb 100644
--- a/src/message-util.h
+++ b/src/message-util.h
@@ -24,16 +24,20 @@
#include <telepathy-glib/message-mixin.h>
#include <loudmouth/loudmouth.h>
+#include <wocky/wocky.h>
#include <wocky/wocky-xmpp-error.h>
#include "connection.h"
G_BEGIN_DECLS
-void gabble_message_util_send_message (GObject *obj,
- GabbleConnection *conn, TpMessage *message, TpMessageSendingFlags flags,
- LmMessageSubType subtype, TpChannelChatState state, const char *recipient,
- gboolean send_nick);
+void gabble_message_util_add_chat_state (WockyStanza *stanza,
+ TpChannelChatState state);
+
+WockyStanza * gabble_message_util_build_stanza (TpMessage *message,
+ GabbleConnection *conn, LmMessageSubType subtype,
+ TpChannelChatState state, const char *recipient, gboolean send_nick,
+ gchar **token, GError **error);
gboolean gabble_message_util_send_chat_state (GObject *obj,
GabbleConnection *conn, LmMessageSubType subtype, TpChannelChatState state,
diff --git a/src/muc-channel.c b/src/muc-channel.c
index a758399d2..5cb55d8b3 100644
--- a/src/muc-channel.c
+++ b/src/muc-channel.c
@@ -26,6 +26,7 @@
#include <string.h>
#include <wocky/wocky-muc.h>
+#include <wocky/wocky-utils.h>
#include <wocky/wocky-xmpp-error.h>
#include <dbus/dbus-glib.h>
@@ -40,17 +41,19 @@
#define DEBUG_FLAG GABBLE_DEBUG_MUC
#include "connection.h"
#include "conn-aliasing.h"
+#include "conn-util.h"
#include "debug.h"
#include "disco.h"
+#include "error.h"
#include "message-util.h"
+#include "room-config.h"
#include "namespaces.h"
#include "presence.h"
#include "util.h"
#include "presence-cache.h"
-
#include "call-muc-channel.h"
-
#include "gabble-signals-marshal.h"
+#include "gabble-enumtypes.h"
#define DEFAULT_JOIN_TIMEOUT 180
#define DEFAULT_LEAVE_TIMEOUT 180
@@ -61,6 +64,7 @@
static void password_iface_init (gpointer, gpointer);
static void chat_state_iface_init (gpointer, gpointer);
+static void subject_iface_init (gpointer, gpointer);
static void gabble_muc_channel_start_call_creation (GabbleMucChannel *gmuc,
GHashTable *request);
static void muc_call_channel_finish_requests (GabbleMucChannel *self,
@@ -69,8 +73,6 @@ static void muc_call_channel_finish_requests (GabbleMucChannel *self,
G_DEFINE_TYPE_WITH_CODE (GabbleMucChannel, gabble_muc_channel,
TP_TYPE_BASE_CHANNEL,
- G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_PROPERTIES_INTERFACE,
- tp_properties_mixin_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP,
tp_group_mixin_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_PASSWORD,
@@ -80,8 +82,13 @@ G_DEFINE_TYPE_WITH_CODE (GabbleMucChannel, gabble_muc_channel,
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MESSAGES,
tp_message_mixin_messages_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CHAT_STATE,
- chat_state_iface_init)
+ chat_state_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CONFERENCE, NULL);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_ROOM, NULL);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_ROOM_CONFIG,
+ tp_base_room_config_iface_init);
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_SUBJECT,
+ subject_iface_init);
)
static void gabble_muc_channel_send (GObject *obj, TpMessage *message,
@@ -91,10 +98,12 @@ static void gabble_muc_channel_close (TpBaseChannel *base);
static const gchar *gabble_muc_channel_interfaces[] = {
TP_IFACE_CHANNEL_INTERFACE_GROUP,
TP_IFACE_CHANNEL_INTERFACE_PASSWORD,
- TP_IFACE_PROPERTIES_INTERFACE,
TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE,
TP_IFACE_CHANNEL_INTERFACE_MESSAGES,
TP_IFACE_CHANNEL_INTERFACE_CONFERENCE,
+ TP_IFACE_CHANNEL_INTERFACE_ROOM,
+ TP_IFACE_CHANNEL_INTERFACE_ROOM_CONFIG,
+ TP_IFACE_CHANNEL_INTERFACE_SUBJECT,
NULL
};
@@ -127,10 +136,17 @@ enum
PROP_INITIAL_INVITEE_HANDLES,
PROP_INITIAL_INVITEE_IDS,
PROP_ORIGINAL_CHANNELS,
+ PROP_ROOM_NAME,
+ PROP_SERVER,
+
+ PROP_SUBJECT,
+ PROP_SUBJECT_ACTOR,
+ PROP_SUBJECT_TIMESTAMP,
+ PROP_CAN_SET_SUBJECT,
+
LAST_PROPERTY
};
-#ifdef ENABLE_DEBUG
static const gchar *muc_states[] =
{
"MUC_STATE_CREATED",
@@ -139,83 +155,6 @@ static const gchar *muc_states[] =
"MUC_STATE_JOINED",
"MUC_STATE_ENDED",
};
-#endif
-
-/* role and affiliation enums */
-typedef enum {
- ROLE_NONE = 0,
- ROLE_VISITOR,
- ROLE_PARTICIPANT,
- ROLE_MODERATOR,
-
- NUM_ROLES,
-
- INVALID_ROLE,
-} GabbleMucRole;
-
-typedef enum {
- AFFILIATION_NONE = 0,
- AFFILIATION_MEMBER,
- AFFILIATION_ADMIN,
- AFFILIATION_OWNER,
-
- NUM_AFFILIATIONS,
-
- INVALID_AFFILIATION,
-} GabbleMucAffiliation;
-
-/* room properties */
-enum
-{
- ROOM_PROP_ANONYMOUS = 0,
- ROOM_PROP_INVITE_ONLY,
- ROOM_PROP_INVITE_RESTRICTED,
- ROOM_PROP_MODERATED,
- ROOM_PROP_NAME,
- ROOM_PROP_DESCRIPTION,
- ROOM_PROP_PASSWORD,
- ROOM_PROP_PASSWORD_REQUIRED,
- ROOM_PROP_PERSISTENT,
- ROOM_PROP_PRIVATE,
- ROOM_PROP_SUBJECT,
- ROOM_PROP_SUBJECT_CONTACT,
- ROOM_PROP_SUBJECT_TIMESTAMP,
-
- NUM_ROOM_PROPS,
-
- INVALID_ROOM_PROP,
-};
-
-const TpPropertySignature room_property_signatures[NUM_ROOM_PROPS] = {
- /* Part of the room definition: modifiable by owners only */
- { "anonymous", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */
- { "invite-only", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */
- { "invite-restricted", G_TYPE_BOOLEAN }, /* impl: WRITE */
- { "moderated", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */
- { "name", G_TYPE_STRING }, /* impl: READ, WRITE */
-
- /* Part of the room definition: might be modifiable by the owner, or
- * not at all */
- { "description", G_TYPE_STRING }, /* impl: READ, WRITE */
-
- /* Part of the room definition: modifiable by owners only */
- { "password", G_TYPE_STRING }, /* impl: WRITE */
- { "password-required", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */
- { "persistent", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */
- { "private", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */
-
- /* fd.o#13157: currently assumed to be modifiable by everyone in the
- * room (role >= VISITOR). When that bug is fixed, it will be: */
- /* Modifiable via special <message/>s, if the user's role is high enough;
- * "high enough" is defined by the muc#roominfo_changesubject and
- * muc#roomconfig_changesubject settings. */
- { "subject", G_TYPE_STRING }, /* impl: READ, WRITE */
-
- /* Special: implicitly set to "myself" and "now", respectively, by
- * changing subject. */
- { "subject-contact", G_TYPE_UINT }, /* impl: READ */
- { "subject-timestamp", G_TYPE_UINT }, /* impl: READ */
-};
/* private structures */
struct _GabbleMucChannelPrivate
@@ -227,21 +166,33 @@ struct _GabbleMucChannelPrivate
guint poll_timer_id;
guint leave_timer_id;
- TpChannelPasswordFlags password_flags;
+ gboolean must_provide_password;
DBusGMethodInvocation *password_ctx;
- gchar *password;
const gchar *jid;
gboolean requested;
guint nick_retry_count;
GString *self_jid;
- GabbleMucRole self_role;
- GabbleMucAffiliation self_affil;
+ WockyMucRole self_role;
+ WockyMucAffiliation self_affil;
guint recv_id;
- TpPropertiesContext *properties_ctx;
+ TpBaseRoomConfig *room_config;
+ GHashTable *properties_being_updated;
+
+ /* Room interface */
+ gchar *room_name;
+ gchar *server;
+
+ /* Subject interface */
+ gchar *subject;
+ gchar *subject_actor;
+ gint64 subject_timestamp;
+ gboolean can_set_subject;
+ DBusGMethodInvocation *set_subject_context;
+ gchar *set_subject_stanza_id;
gboolean ready;
gboolean dispose_has_run;
@@ -268,6 +219,12 @@ struct _GabbleMucChannelPrivate
char **initial_ids;
};
+typedef struct {
+ GabbleMucChannel *channel;
+ TpMessage *message;
+ gchar *token;
+} _GabbleMUCSendMessageCtx;
+
static void
gabble_muc_channel_init (GabbleMucChannel *self)
{
@@ -290,7 +247,7 @@ static void handle_fill_presence (WockyMuc *muc,
static void handle_renamed (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
gpointer data);
static void handle_error (GObject *source,
@@ -301,12 +258,12 @@ static void handle_error (GObject *source,
static void handle_join (WockyMuc *muc,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
gpointer data);
static void handle_parted (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
const gchar *actor_jid,
const gchar *why,
const gchar *msg,
@@ -314,7 +271,7 @@ static void handle_parted (GObject *source,
static void handle_left (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
WockyMucMember *who,
const gchar *actor_jid,
const gchar *why,
@@ -323,7 +280,7 @@ static void handle_left (GObject *source,
static void handle_presence (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
WockyMucMember *who,
gpointer data);
@@ -339,7 +296,7 @@ static void handle_message (GObject *source,
WockyStanza *stanza,
WockyMucMsgType type,
const gchar *xmpp_id,
- time_t stamp,
+ GDateTime *datetime,
WockyMucMember *who,
const gchar *text,
const gchar *subject,
@@ -350,13 +307,25 @@ static void handle_errmsg (GObject *source,
WockyStanza *stanza,
WockyMucMsgType type,
const gchar *xmpp_id,
- time_t stamp,
+ GDateTime *datetime,
WockyMucMember *who,
const gchar *text,
WockyXmppError error,
WockyXmppErrorType etype,
gpointer data);
+/* Signatures for some other stuff. */
+
+static void _gabble_muc_channel_handle_subject (GabbleMucChannel *chan,
+ TpHandleType handle_type,
+ TpHandle sender, GDateTime *datetime, const gchar *subject,
+ LmMessage *msg);
+static void _gabble_muc_channel_receive (GabbleMucChannel *chan,
+ TpChannelTextMessageType msg_type, TpHandleType handle_type,
+ TpHandle sender, GDateTime *datetime, const gchar *id, const gchar *text,
+ LmMessage *msg, TpChannelTextSendError send_error,
+ TpDeliveryStatus delivery_status);
+
static void
gabble_muc_channel_constructed (GObject *obj)
{
@@ -366,6 +335,7 @@ gabble_muc_channel_constructed (GObject *obj)
TpBaseConnection *base_conn = tp_base_channel_get_connection (base);
TpHandleRepoIface *room_handles, *contact_handles;
TpHandle target, initiator, self_handle;
+ gchar *tmp;
TpChannelTextMessageType types[] = {
TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL,
TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION,
@@ -377,6 +347,7 @@ gabble_muc_channel_constructed (GObject *obj)
};
void (*chain_up) (GObject *) =
((GObjectClass *) gabble_muc_channel_parent_class)->constructed;
+ gboolean ok;
if (chain_up != NULL)
chain_up (obj);
@@ -403,8 +374,8 @@ gabble_muc_channel_constructed (GObject *obj)
* at the end of this function */
/* initialize our own role and affiliation */
- priv->self_role = ROLE_NONE;
- priv->self_affil = AFFILIATION_NONE;
+ priv->self_role = WOCKY_MUC_ROLE_NONE;
+ priv->self_affil = WOCKY_MUC_AFFILIATION_NONE;
/* initialise the wocky muc object */
{
@@ -456,10 +427,6 @@ gabble_muc_channel_constructed (GObject *obj)
TP_CHANNEL_GROUP_FLAG_CAN_ADD,
0);
- /* initialize properties mixin */
- tp_properties_mixin_init (obj, G_STRUCT_OFFSET (
- GabbleMucChannel, properties));
-
/* initialize message mixin */
tp_message_mixin_init (obj, G_STRUCT_OFFSET (GabbleMucChannel, message_mixin),
base_conn);
@@ -471,6 +438,54 @@ gabble_muc_channel_constructed (GObject *obj)
tp_group_mixin_add_handle_owner (obj, self_handle, base_conn->self_handle);
+ /* Room interface */
+ g_object_get (self,
+ "target-id", &tmp,
+ NULL);
+
+ if (priv->room_name != NULL)
+ ok = wocky_decode_jid (tmp, NULL, &(priv->server), NULL);
+ else
+ ok = wocky_decode_jid (tmp, &(priv->room_name), &(priv->server), NULL);
+ g_free (tmp);
+
+ /* Asserting here is fine because the target ID has already been
+ * checked so we know it's valid. */
+ g_assert (ok);
+
+ priv->subject = NULL;
+ priv->subject_actor = NULL;
+ priv->subject_timestamp = G_MAXINT64;
+ /* fd.o#13157: The subject is currently assumed to be modifiable by everyone
+ * in the room (role >= VISITOR). When that bug is fixed, it will be: */
+ /* Modifiable via special <message/>s, if the user's role is high enough;
+ * "high enough" is defined by the muc#roominfo_changesubject and
+ * muc#roomconfig_changesubject settings. */
+ priv->can_set_subject = TRUE;
+
+ {
+ TpBaseRoomConfigProperty mutable_properties[] = {
+ TP_BASE_ROOM_CONFIG_ANONYMOUS,
+ TP_BASE_ROOM_CONFIG_INVITE_ONLY,
+ TP_BASE_ROOM_CONFIG_MODERATED,
+ TP_BASE_ROOM_CONFIG_TITLE,
+ TP_BASE_ROOM_CONFIG_PERSISTENT,
+ TP_BASE_ROOM_CONFIG_PRIVATE,
+ TP_BASE_ROOM_CONFIG_PASSWORD_PROTECTED,
+ TP_BASE_ROOM_CONFIG_PASSWORD,
+ };
+ guint i;
+
+ priv->room_config =
+ (TpBaseRoomConfig *) gabble_room_config_new ((TpBaseChannel *) self);
+ for (i = 0; i < G_N_ELEMENTS (mutable_properties); i++)
+ tp_base_room_config_set_property_mutable (priv->room_config,
+ mutable_properties[i], TRUE);
+
+ /* Just to get those mutable properties out there. */
+ tp_base_room_config_emit_properties_changed (priv->room_config);
+ }
+
if (priv->invited)
{
/* invited: add ourself to local pending and the inviter to members */
@@ -504,12 +519,112 @@ gabble_muc_channel_constructed (GObject *obj)
g_array_append_val (members, self_handle);
tp_group_mixin_add_members (obj, members, "", &error);
g_assert (error == NULL);
- g_array_free (members, TRUE);
+ g_array_unref (members);
}
tp_handle_unref (contact_handles, self_handle);
}
+typedef struct {
+ const gchar *var;
+ const gchar *config_property_name;
+ gboolean value;
+} FeatureMapping;
+
+static FeatureMapping *
+lookup_feature (const gchar *var)
+{
+ static FeatureMapping features[] = {
+ { "muc_nonanonymous", "anonymous", FALSE },
+ { "muc_semianonymous", "anonymous", TRUE },
+ { "muc_anonymous", "anonymous", TRUE },
+
+ { "muc_open", "invite-only", FALSE },
+ { "muc_membersonly", "invite-only", TRUE },
+
+ { "muc_unmoderated", "moderated", FALSE },
+ { "muc_moderated", "moderated", TRUE },
+
+ { "muc_unsecure", "password-protected", FALSE },
+ { "muc_unsecured", "password-protected", FALSE },
+ { "muc_passwordprotected", "password-protected", TRUE },
+
+ { "muc_temporary", "persistent", FALSE },
+ { "muc_persistent", "persistent", TRUE },
+
+ { "muc_public", "private", FALSE },
+ { "muc_hidden", "private", TRUE },
+
+ /* The MUC namespace is included as a feature in disco results. We ignore
+ * it here.
+ */
+ { NS_MUC, NULL, FALSE },
+
+ { NULL }
+ };
+ FeatureMapping *f;
+
+ for (f = features; f->var != NULL; f++)
+ if (strcmp (var, f->var) == 0)
+ return f;
+
+ return NULL;
+}
+
+static const gchar *
+map_feature (
+ WockyNode *feature,
+ GValue *value)
+{
+ const gchar *var = wocky_node_get_attribute (feature, "var");
+ FeatureMapping *f;
+
+ if (var == NULL)
+ return NULL;
+
+ f = lookup_feature (var);
+
+ if (f == NULL)
+ {
+ DEBUG ("unhandled feature '%s'", var);
+ return NULL;
+ }
+
+ if (f->config_property_name != NULL)
+ {
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, f->value);
+ }
+
+ return f->config_property_name;
+}
+
+static const gchar *
+handle_form (
+ WockyNode *x,
+ GValue *value)
+{
+ WockyNodeIter j;
+ WockyNode *field;
+
+ wocky_node_iter_init (&j, x, "field", NULL);
+ while (wocky_node_iter_next (&j, &field))
+ {
+ const gchar *var = wocky_node_get_attribute (field, "var");
+ const gchar *description;
+
+ if (tp_strdiff (var, "muc#roominfo_description"))
+ continue;
+
+ description = wocky_node_get_content_from_child (field, "value");
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, description != NULL ? description : "");
+ return "description";
+ }
+
+ return NULL;
+}
+
static void
properties_disco_cb (GabbleDisco *disco,
GabbleDiscoRequest *request,
@@ -520,10 +635,8 @@ properties_disco_cb (GabbleDisco *disco,
gpointer user_data)
{
GabbleMucChannel *chan = user_data;
- TpIntSet *changed_props_val, *changed_props_flags;
+ GabbleMucChannelPrivate *priv = chan->priv;
WockyNode *lm_node;
- const gchar *str;
- GValue val = { 0, };
NodeIter i;
g_assert (GABBLE_IS_MUC_CHANNEL (chan));
@@ -534,9 +647,6 @@ properties_disco_cb (GabbleDisco *disco,
return;
}
- changed_props_val = tp_intset_sized_new (NUM_ROOM_PROPS);
- changed_props_flags = tp_intset_sized_new (NUM_ROOM_PROPS);
-
/*
* Update room definition.
*/
@@ -555,186 +665,38 @@ properties_disco_cb (GabbleDisco *disco,
!tp_strdiff (type, "text") &&
name != NULL)
{
- g_value_init (&val, G_TYPE_STRING);
- g_value_set_string (&val, name);
-
- tp_properties_mixin_change_value (G_OBJECT (chan), ROOM_PROP_NAME,
- &val, changed_props_val);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan), ROOM_PROP_NAME,
- TP_PROPERTY_FLAG_READ,
- 0, changed_props_flags);
-
- g_value_unset (&val);
+ g_object_set (priv->room_config, "title", name, NULL);
}
}
for (i = node_iter (query_result); i; i = node_iter_next (i))
{
- guint prop_id = INVALID_ROOM_PROP;
+ const gchar *config_property_name = NULL;
WockyNode *child = node_iter_data (i);
+ GValue val = { 0, };
if (strcmp (child->name, "feature") == 0)
{
- str = wocky_node_get_attribute (child, "var");
- if (str == NULL)
- continue;
-
- /* ROOM_PROP_ANONYMOUS */
- if (strcmp (str, "muc_nonanonymous") == 0)
- {
- prop_id = ROOM_PROP_ANONYMOUS;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, FALSE);
- }
- else if (strcmp (str, "muc_semianonymous") == 0 ||
- strcmp (str, "muc_anonymous") == 0)
- {
- prop_id = ROOM_PROP_ANONYMOUS;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, TRUE);
- }
-
- /* ROOM_PROP_INVITE_ONLY */
- else if (strcmp (str, "muc_open") == 0)
- {
- prop_id = ROOM_PROP_INVITE_ONLY;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, FALSE);
- }
- else if (strcmp (str, "muc_membersonly") == 0)
- {
- prop_id = ROOM_PROP_INVITE_ONLY;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, TRUE);
- }
-
- /* ROOM_PROP_MODERATED */
- else if (strcmp (str, "muc_unmoderated") == 0)
- {
- prop_id = ROOM_PROP_MODERATED;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, FALSE);
- }
- else if (strcmp (str, "muc_moderated") == 0)
- {
- prop_id = ROOM_PROP_MODERATED;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, TRUE);
- }
-
- /* ROOM_PROP_PASSWORD_REQUIRED */
- else if (strcmp (str, "muc_unsecure") == 0 ||
- strcmp (str, "muc_unsecured") == 0)
- {
- prop_id = ROOM_PROP_PASSWORD_REQUIRED;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, FALSE);
- }
- else if (strcmp (str, "muc_passwordprotected") == 0)
- {
- prop_id = ROOM_PROP_PASSWORD_REQUIRED;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, TRUE);
- }
-
- /* ROOM_PROP_PERSISTENT */
- else if (strcmp (str, "muc_temporary") == 0)
- {
- prop_id = ROOM_PROP_PERSISTENT;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, FALSE);
- }
- else if (strcmp (str, "muc_persistent") == 0)
- {
- prop_id = ROOM_PROP_PERSISTENT;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, TRUE);
- }
-
- /* ROOM_PROP_PRIVATE */
- else if (strcmp (str, "muc_public") == 0)
- {
- prop_id = ROOM_PROP_PRIVATE;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, FALSE);
- }
- else if (strcmp (str, "muc_hidden") == 0)
- {
- prop_id = ROOM_PROP_PRIVATE;
- g_value_init (&val, G_TYPE_BOOLEAN);
- g_value_set_boolean (&val, TRUE);
- }
-
- /* Ignored */
- else if (strcmp (str, NS_MUC) == 0)
- {
- }
-
- /* Unhandled */
- else
- {
- DEBUG ("unhandled feature '%s'", str);
- }
+ config_property_name = map_feature (child, &val);
}
- else if (strcmp (child->name, "x") == 0)
+ else if (strcmp (child->name, "x") == 0 &&
+ wocky_node_has_ns (child, NS_X_DATA))
{
- if (wocky_node_has_ns (child, NS_X_DATA))
- {
- NodeIter j;
-
- for (j = node_iter (child); j; j = node_iter_next (j))
- {
- WockyNode *field = node_iter_data (j);
- WockyNode *value_node;
-
- if (strcmp (field->name, "field") != 0)
- continue;
-
- str = wocky_node_get_attribute (field, "var");
- if (str == NULL)
- continue;
-
- if (strcmp (str, "muc#roominfo_description") != 0)
- continue;
-
- value_node = wocky_node_get_child (field, "value");
- if (value_node == NULL)
- continue;
-
- str = value_node->content;
- if (str == NULL)
- {
- str = "";
- }
-
- prop_id = ROOM_PROP_DESCRIPTION;
- g_value_init (&val, G_TYPE_STRING);
- g_value_set_string (&val, str);
- }
- }
+ config_property_name = handle_form (child, &val);
}
- if (prop_id != INVALID_ROOM_PROP)
+ if (config_property_name != NULL)
{
- tp_properties_mixin_change_value (G_OBJECT (chan), prop_id, &val,
- changed_props_val);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan), prop_id,
- TP_PROPERTY_FLAG_READ,
- 0, changed_props_flags);
+ g_object_set_property ((GObject *) priv->room_config, config_property_name, &val);
g_value_unset (&val);
}
}
- /*
- * Emit signals.
+ /* This could be the first time we've fetched the room properties, or it
+ * could be a later time; either way, this method does the right thing.
*/
- tp_properties_mixin_emit_changed (G_OBJECT (chan), changed_props_val);
- tp_properties_mixin_emit_flags (G_OBJECT (chan), changed_props_flags);
- tp_intset_destroy (changed_props_val);
- tp_intset_destroy (changed_props_flags);
+ tp_base_room_config_set_retrieved (priv->room_config);
}
static void
@@ -784,7 +746,7 @@ create_room_identity (GabbleMucChannel *chan)
*/
gchar *local_part;
- g_assert (gabble_decode_jid (alias, &local_part, NULL, NULL));
+ g_assert (wocky_decode_jid (alias, &local_part, NULL, NULL));
g_assert (local_part != NULL);
g_free (alias);
@@ -802,12 +764,10 @@ create_room_identity (GabbleMucChannel *chan)
}
static void
-send_join_request (GabbleMucChannel *gmuc,
- const gchar *password)
+send_join_request (GabbleMucChannel *gmuc)
{
GabbleMucChannelPrivate *priv = gmuc->priv;
- g_object_set (priv->wmuc, "password", password, NULL);
wocky_muc_join (priv->wmuc, NULL);
}
@@ -884,6 +844,24 @@ gabble_muc_channel_get_property (GObject *object,
* which we can't do anyway in XMPP. */
g_value_take_boxed (value, g_hash_table_new (NULL, NULL));
break;
+ case PROP_ROOM_NAME:
+ g_value_set_string (value, priv->room_name);
+ break;
+ case PROP_SERVER:
+ g_value_set_string (value, priv->server);
+ break;
+ case PROP_SUBJECT:
+ g_value_set_string (value, priv->subject);
+ break;
+ case PROP_SUBJECT_ACTOR:
+ g_value_set_string (value, priv->subject_actor);
+ break;
+ case PROP_SUBJECT_TIMESTAMP:
+ g_value_set_int64 (value, priv->subject_timestamp);
+ break;
+ case PROP_CAN_SET_SUBJECT:
+ g_value_set_boolean (value, priv->can_set_subject);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -931,6 +909,9 @@ gabble_muc_channel_set_property (GObject *object,
case PROP_INITIAL_INVITEE_IDS:
priv->initial_ids = g_value_dup_boxed (value);
break;
+ case PROP_ROOM_NAME:
+ priv->room_name = g_value_dup_string (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -943,8 +924,6 @@ static gboolean gabble_muc_channel_add_member (GObject *obj, TpHandle handle,
const gchar *message, GError **error);
static gboolean gabble_muc_channel_remove_member (GObject *obj,
TpHandle handle, const gchar *message, GError **error);
-static gboolean gabble_muc_channel_do_set_properties (GObject *obj,
- TpPropertiesContext *ctx, GError **error);
static void
gabble_muc_channel_fill_immutable_properties (
@@ -964,6 +943,8 @@ gabble_muc_channel_fill_immutable_properties (
TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "DeliveryReportingSupport",
TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "SupportedContentTypes",
TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessageTypes",
+ TP_IFACE_CHANNEL_INTERFACE_ROOM, "RoomName",
+ TP_IFACE_CHANNEL_INTERFACE_ROOM, "Server",
NULL);
}
@@ -979,6 +960,38 @@ gabble_muc_channel_class_init (GabbleMucChannelClass *gabble_muc_channel_class)
{ "OriginalChannels", "original-channels", NULL },
{ NULL }
};
+ static TpDBusPropertiesMixinPropImpl room_props[] = {
+ { "RoomName", "room-name", NULL, },
+ { "Server", "server", NULL },
+ { NULL }
+ };
+ static TpDBusPropertiesMixinPropImpl subject_props[] = {
+ { "Subject", "subject", NULL },
+ { "Actor", "subject-actor", NULL },
+ { "Timestamp", "subject-timestamp", NULL },
+ { "CanSet", "can-set-subject", NULL },
+ { NULL }
+ };
+
+ static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
+ { TP_IFACE_CHANNEL_INTERFACE_CONFERENCE,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ conference_props,
+ },
+ { TP_IFACE_CHANNEL_INTERFACE_ROOM,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ room_props,
+ },
+ { TP_IFACE_CHANNEL_INTERFACE_SUBJECT,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ subject_props,
+ },
+ { NULL }
+ };
+
GObjectClass *object_class = G_OBJECT_CLASS (gabble_muc_channel_class);
TpBaseChannelClass *base_class = TP_BASE_CHANNEL_CLASS (object_class);
GParamSpec *param_spec;
@@ -1065,6 +1078,50 @@ gabble_muc_channel_class_init (GabbleMucChannelClass *gabble_muc_channel_class)
g_object_class_install_property (object_class, PROP_ORIGINAL_CHANNELS,
param_spec);
+ param_spec = g_param_spec_string ("room-name",
+ "RoomName",
+ "The human-readable identifier of a chat room.",
+ "",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_ROOM_NAME,
+ param_spec);
+
+ param_spec = g_param_spec_string ("server",
+ "Server",
+ "the DNS name of the server hosting this channel",
+ "",
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_SERVER,
+ param_spec);
+
+ param_spec = g_param_spec_string ("subject",
+ "Subject.Subject", "The subject of the room",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_SUBJECT, param_spec);
+
+ param_spec = g_param_spec_string ("subject-actor",
+ "Subject.Actor", "The JID of the contact who last changed the subject",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_SUBJECT_ACTOR,
+ param_spec);
+
+ param_spec = g_param_spec_int64 ("subject-timestamp",
+ "Subject.Timestamp",
+ "The UNIX timestamp at which the subject was last changed",
+ G_MININT64, G_MAXINT64, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_SUBJECT_TIMESTAMP,
+ param_spec);
+
+ param_spec = g_param_spec_boolean ("can-set-subject",
+ "Subject.CanSet", "Whether we believe we can set the subject",
+ TRUE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_CAN_SET_SUBJECT,
+ param_spec);
+
signals[READY] =
g_signal_new ("ready",
G_OBJECT_CLASS_TYPE (gabble_muc_channel_class),
@@ -1128,19 +1185,12 @@ gabble_muc_channel_class_init (GabbleMucChannelClass *gabble_muc_channel_class)
GABBLE_TYPE_CALL_MUC_CHANNEL,
G_TYPE_POINTER);
- tp_properties_mixin_class_init (object_class,
- G_STRUCT_OFFSET (GabbleMucChannelClass,
- properties_class),
- room_property_signatures, NUM_ROOM_PROPS,
- gabble_muc_channel_do_set_properties);
-
-
- tp_dbus_properties_mixin_implement_interface (object_class,
- TP_IFACE_QUARK_CHANNEL_INTERFACE_CONFERENCE,
- tp_dbus_properties_mixin_getter_gobject_properties, NULL,
- conference_props);
+ gabble_muc_channel_class->dbus_props_class.interfaces = prop_interfaces;
+ tp_dbus_properties_mixin_class_init (object_class,
+ G_STRUCT_OFFSET (GabbleMucChannelClass, dbus_props_class));
tp_message_mixin_init_dbus_properties (object_class);
+ tp_base_room_config_register_class (base_class);
tp_group_mixin_class_init (object_class,
G_STRUCT_OFFSET (GabbleMucChannelClass, group_class),
@@ -1173,6 +1223,7 @@ gabble_muc_channel_dispose (GObject *object)
tp_clear_object (&priv->wmuc);
tp_clear_object (&priv->requests_cancellable);
+ tp_clear_object (&priv->room_config);
if (G_OBJECT_CLASS (gabble_muc_channel_parent_class)->dispose)
G_OBJECT_CLASS (gabble_muc_channel_parent_class)->dispose (object);
@@ -1193,8 +1244,6 @@ gabble_muc_channel_finalize (GObject *object)
g_string_free (priv->self_jid, TRUE);
}
- g_free (priv->password);
-
if (priv->initial_channels != NULL)
{
g_boxed_free (TP_ARRAY_TYPE_OBJECT_PATH_LIST, priv->initial_channels);
@@ -1213,7 +1262,11 @@ gabble_muc_channel_finalize (GObject *object)
priv->initial_ids = NULL;
}
- tp_properties_mixin_finalize (object);
+ g_free (priv->room_name);
+ g_free (priv->server);
+ g_free (priv->subject);
+ g_free (priv->subject_actor);
+
tp_group_mixin_finalize (object);
tp_message_mixin_finalize (object);
@@ -1257,9 +1310,9 @@ clear_leave_timer (GabbleMucChannel *chan)
}
static void
-change_password_flags (GabbleMucChannel *chan,
- TpChannelPasswordFlags add,
- TpChannelPasswordFlags del)
+change_must_provide_password (
+ GabbleMucChannel *chan,
+ gboolean must_provide_password)
{
GabbleMucChannelPrivate *priv;
TpChannelPasswordFlags added, removed;
@@ -1268,20 +1321,27 @@ change_password_flags (GabbleMucChannel *chan,
priv = chan->priv;
- added = add & ~priv->password_flags;
- priv->password_flags |= added;
+ if (priv->must_provide_password == !!must_provide_password)
+ return;
- removed = del & priv->password_flags;
- priv->password_flags &= ~removed;
+ priv->must_provide_password = !!must_provide_password;
- if (add != 0 || del != 0)
+ if (must_provide_password)
{
- DEBUG ("emitting password flags changed, added 0x%X, removed 0x%X",
- added, removed);
-
- tp_svc_channel_interface_password_emit_password_flags_changed (
- chan, added, removed);
+ added = TP_CHANNEL_PASSWORD_FLAG_PROVIDE;
+ removed = 0;
+ }
+ else
+ {
+ added = 0;
+ removed = TP_CHANNEL_PASSWORD_FLAG_PROVIDE;
}
+
+ DEBUG ("emitting password flags changed, added 0x%X, removed 0x%X",
+ added, removed);
+
+ tp_svc_channel_interface_password_emit_password_flags_changed (
+ chan, added, removed);
}
static void
@@ -1297,7 +1357,7 @@ provide_password_return_if_pending (GabbleMucChannel *chan, gboolean success)
if (success)
{
- change_password_flags (chan, 0, TP_CHANNEL_PASSWORD_FLAG_PROVIDE);
+ change_must_provide_password (chan, FALSE);
}
}
@@ -1364,10 +1424,6 @@ channel_state_changed (GabbleMucChannel *chan,
priv->poll_timer_id =
g_timeout_add_seconds (interval, timeout_poll, chan);
-
- /* no need to keep this around any longer, if it's set */
- g_free (priv->password);
- priv->password = NULL;
}
else if (new_state == MUC_STATE_ENDED)
{
@@ -1384,6 +1440,22 @@ channel_state_changed (GabbleMucChannel *chan,
}
}
+static void
+return_from_set_subject (
+ GabbleMucChannel *self,
+ const GError *error)
+{
+ GabbleMucChannelPrivate *priv = self->priv;
+
+ if (error == NULL)
+ tp_svc_channel_interface_subject_return_from_set_subject (
+ priv->set_subject_context);
+ else
+ dbus_g_method_return_error (priv->set_subject_context, error);
+
+ priv->set_subject_context = NULL;
+ tp_clear_pointer (&priv->set_subject_stanza_id, g_free);
+}
static void
close_channel (GabbleMucChannel *chan, const gchar *reason,
@@ -1437,7 +1509,10 @@ close_channel (GabbleMucChannel *chan, const gchar *reason,
handles = tp_handle_set_to_array (chan->group.members);
gabble_presence_cache_update_many (conn->presence_cache, handles,
NULL, GABBLE_PRESENCE_UNKNOWN, NULL, 0);
- g_array_free (handles, TRUE);
+ g_array_unref (handles);
+
+ if (priv->set_subject_context != NULL)
+ return_from_set_subject (chan, &error);
g_object_set (chan, "state", MUC_STATE_ENDED, NULL);
g_object_unref (chan);
@@ -1482,7 +1557,7 @@ handle_nick_conflict (GabbleMucChannel *chan,
*/
g_assert (from != NULL);
- if (index (from, '/') != NULL && tp_strdiff (from, priv->self_jid->str))
+ if (strchr (from, '/') != NULL && tp_strdiff (from, priv->self_jid->str))
{
DEBUG ("ignoring spurious conflict message for %s", from);
return TRUE;
@@ -1517,117 +1592,99 @@ handle_nick_conflict (GabbleMucChannel *chan,
tp_handle_unref (contact_repo, self_handle);
priv->nick_retry_count++;
- send_join_request (chan, priv->password);
+ send_join_request (chan);
return TRUE;
}
-static LmHandlerResult
-room_created_submit_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
- LmMessage *reply_msg, GObject *object,
- gpointer user_data)
+static void
+room_created_submit_reply_cb (
+ GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
- {
- DEBUG ("failed to submit room config");
- }
-
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ if (conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, NULL, NULL))
+ DEBUG ("failed to submit room config");
}
static WockyNode *
-config_form_get_form_node (LmMessage *msg)
+config_form_get_form_node (WockyStanza *stanza)
{
- WockyNode *node;
- NodeIter i;
+ WockyNode *query, *x;
+ WockyNodeIter i;
/* find the query node */
- node = wocky_node_get_child (wocky_stanza_get_top_node (msg), "query");
- if (node == NULL)
+ query = wocky_node_get_child (wocky_stanza_get_top_node (stanza), "query");
+ if (query == NULL)
return NULL;
/* then the form node */
- for (i = node_iter (node); i; i = node_iter_next (i))
+ wocky_node_iter_init (&i, query, "x", NS_X_DATA);
+ while (wocky_node_iter_next (&i, &x))
{
- WockyNode *child = node_iter_data (i);
-
- if (tp_strdiff (child->name, "x"))
- {
- continue;
- }
-
- if (!wocky_node_has_ns (child, NS_X_DATA))
- {
- continue;
- }
-
- if (tp_strdiff (wocky_node_get_attribute (child, "type"), "form"))
- {
- continue;
- }
-
- return child;
+ if (!tp_strdiff (wocky_node_get_attribute (x, "type"), "form"))
+ return x;
}
return NULL;
}
-static LmHandlerResult
-perms_config_form_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
- LmMessage *reply_msg, GObject *object,
- gpointer user_data)
+static void
+perms_config_form_reply_cb (
+ GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object);
- GabbleMucChannelPrivate *priv = chan->priv;
- WockyNode *form_node;
- NodeIter i;
+ GabbleMucChannel *self = GABBLE_MUC_CHANNEL (user_data);
+ GabbleMucChannelPrivate *priv = self->priv;
+ WockyStanza *reply = NULL;
+ WockyNode *form_node, *field;
+ WockyNodeIter i;
- if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
+ if (!conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, &reply, NULL))
{
- DEBUG ("request for config form denied, property permissions "
+ DEBUG ("request for config form failed, property permissions "
"will be inaccurate");
goto OUT;
}
/* just in case our affiliation has changed in the meantime */
- if (priv->self_affil != AFFILIATION_OWNER)
+ if (priv->self_affil != WOCKY_MUC_AFFILIATION_OWNER)
goto OUT;
- form_node = config_form_get_form_node (reply_msg);
+ form_node = config_form_get_form_node (reply);
if (form_node == NULL)
{
- DEBUG ("form node node found, property permissions will be inaccurate");
+ DEBUG ("form node not found, property permissions will be inaccurate");
goto OUT;
}
- for (i = node_iter (form_node); i; i = node_iter_next (i))
+ wocky_node_iter_init (&i, form_node, "field", NULL);
+ while (wocky_node_iter_next (&i, &field))
{
- const gchar *var;
- WockyNode *node = node_iter_data (i);
+ const gchar *var = wocky_node_get_attribute (field, "var");
- if (strcmp (node->name, "field") != 0)
- continue;
-
- var = wocky_node_get_attribute (node, "var");
- if (var == NULL)
- continue;
-
- if (strcmp (var, "muc#roomconfig_roomdesc") == 0 ||
- strcmp (var, "muc#owner_roomdesc") == 0)
+ if (!tp_strdiff (var, "muc#roomconfig_roomdesc") ||
+ !tp_strdiff (var, "muc#owner_roomdesc"))
{
- if (tp_properties_mixin_is_readable (G_OBJECT (chan),
- ROOM_PROP_DESCRIPTION))
- {
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_DESCRIPTION, TP_PROPERTY_FLAG_WRITE, 0,
- NULL);
-
- goto OUT;
- }
+ tp_base_room_config_set_property_mutable (priv->room_config,
+ TP_BASE_ROOM_CONFIG_DESCRIPTION, TRUE);
+ tp_base_room_config_emit_properties_changed (priv->room_config);
+ break;
}
}
OUT:
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ tp_clear_object (&reply);
+ g_object_unref (self);
+}
+
+static void
+emit_subject_changed (GabbleMucChannel *chan)
+{
+ const gchar *changed[] = { "Subject", "Actor", "Timestamp", NULL };
+
+ tp_dbus_properties_mixin_emit_properties_changed (G_OBJECT (chan),
+ TP_IFACE_CHANNEL_INTERFACE_SUBJECT, changed);
}
static void
@@ -1636,8 +1693,6 @@ update_permissions (GabbleMucChannel *chan)
GabbleMucChannelPrivate *priv = chan->priv;
TpBaseChannel *base = TP_BASE_CHANNEL (chan);
TpChannelGroupFlags grp_flags_add, grp_flags_rem;
- TpPropertyFlags prop_flags_add, prop_flags_rem;
- TpIntSet *changed_props_val, *changed_props_flags;
/*
* Update group flags.
@@ -1646,7 +1701,7 @@ update_permissions (GabbleMucChannel *chan)
TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD;
grp_flags_rem = 0;
- if (priv->self_role == ROLE_MODERATOR)
+ if (priv->self_role == WOCKY_MUC_ROLE_MODERATOR)
{
grp_flags_add |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE |
TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE;
@@ -1659,172 +1714,34 @@ update_permissions (GabbleMucChannel *chan)
tp_group_mixin_change_flags ((GObject *) chan, grp_flags_add, grp_flags_rem);
+ /* Update RoomConfig.CanUpdateConfiguration */
- /*
- * Update write capabilities based on room configuration
- * and own role and affiliation.
- */
-
- changed_props_val = tp_intset_sized_new (NUM_ROOM_PROPS);
- changed_props_flags = tp_intset_sized_new (NUM_ROOM_PROPS);
-
- /*
- * Subject
- *
- * FIXME: this might be allowed for participants/moderators only,
- * so for now just rely on the server making that call.
- */
-
- if (priv->self_role >= ROLE_VISITOR)
+ /* The room configuration is part of the "room definition", so is defined by
+ * the XEP to be editable only by owners. */
+ if (priv->self_affil == WOCKY_MUC_AFFILIATION_OWNER)
{
- prop_flags_add = TP_PROPERTY_FLAG_WRITE;
- prop_flags_rem = 0;
+ tp_base_room_config_set_can_update_configuration (priv->room_config, TRUE);
}
else
{
- prop_flags_add = 0;
- prop_flags_rem = TP_PROPERTY_FLAG_WRITE;
+ tp_base_room_config_set_can_update_configuration (priv->room_config, FALSE);
}
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_SUBJECT, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- /* The room properties below are part of the "room definition", so are
- * defined by the XEP to be editable only by owners. */
+ tp_base_room_config_emit_properties_changed (priv->room_config);
- if (priv->self_affil == AFFILIATION_OWNER)
- {
- prop_flags_add = TP_PROPERTY_FLAG_WRITE;
- prop_flags_rem = 0;
- }
- else
- {
- prop_flags_add = 0;
- prop_flags_rem = TP_PROPERTY_FLAG_WRITE;
- }
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_ANONYMOUS, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_INVITE_ONLY, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_INVITE_RESTRICTED, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_MODERATED, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_NAME, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_PASSWORD, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_PASSWORD_REQUIRED, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_PERSISTENT, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_PRIVATE, prop_flags_add, prop_flags_rem,
- changed_props_flags);
-
- if (priv->self_affil == AFFILIATION_OWNER)
+ if (priv->self_affil == WOCKY_MUC_AFFILIATION_OWNER)
{
/* request the configuration form purely to see if the description
* is writable by us in this room. sigh. GO MUC!!! */
- LmMessage *msg;
- WockyNode *node;
- GError *error = NULL;
- gboolean success;
-
- msg = lm_message_new_with_sub_type (priv->jid,
- LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET);
- node = wocky_node_add_child_with_content (
- wocky_stanza_get_top_node (msg), "query", NULL);
- node->ns = g_quark_from_string (NS_MUC_OWNER);
-
- success = _gabble_connection_send_with_reply (
- GABBLE_CONNECTION (tp_base_channel_get_connection (base)), msg,
- perms_config_form_reply_cb, G_OBJECT (chan), NULL,
- &error);
-
- lm_message_unref (msg);
-
- if (!success)
- {
- DEBUG ("failed to request config form: %s", error->message);
- g_error_free (error);
- }
- }
- else
- {
- /* mark description unwritable if we're no longer an owner */
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_DESCRIPTION, 0, TP_PROPERTY_FLAG_WRITE,
- changed_props_flags);
- }
-
- /*
- * Emit signals.
- */
- tp_properties_mixin_emit_changed (G_OBJECT (chan), changed_props_val);
- tp_properties_mixin_emit_flags (G_OBJECT (chan), changed_props_flags);
- tp_intset_destroy (changed_props_val);
- tp_intset_destroy (changed_props_flags);
-}
+ GabbleConnection *conn = GABBLE_CONNECTION (
+ tp_base_channel_get_connection (base));
+ WockyStanza *stanza = wocky_stanza_build (
+ WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET, NULL, priv->jid,
+ '(', "query", ':', WOCKY_NS_MUC_OWNER, ')', NULL);
-
-
-/* ************************************************************************* */
-/* wocky MUC implementation */
-static GabbleMucRole
-get_role_from_backend (WockyMucRole role)
-{
- switch (role)
- {
- case WOCKY_MUC_ROLE_NONE:
- return ROLE_NONE;
- case WOCKY_MUC_ROLE_VISITOR:
- return ROLE_VISITOR;
- case WOCKY_MUC_ROLE_PARTICIPANT:
- return ROLE_PARTICIPANT;
- case WOCKY_MUC_ROLE_MODERATOR:
- return ROLE_MODERATOR;
- default:
- DEBUG ("unknown role '%d' -- defaulting to ROLE_VISITOR", role);
- return ROLE_VISITOR;
- }
-}
-
-static GabbleMucAffiliation
-get_aff_from_backend (WockyMucAffiliation aff)
-{
- switch (aff)
- {
- case WOCKY_MUC_AFFILIATION_OUTCAST:
- case WOCKY_MUC_AFFILIATION_NONE:
- return AFFILIATION_NONE;
- case WOCKY_MUC_AFFILIATION_MEMBER:
- return AFFILIATION_MEMBER;
- case WOCKY_MUC_AFFILIATION_ADMIN:
- return AFFILIATION_ADMIN;
- case WOCKY_MUC_AFFILIATION_OWNER:
- return AFFILIATION_OWNER;
- default:
- DEBUG ("unknown affiliation %d -- defaulting to AFFILIATION_NONE", aff);
- return AFFILIATION_NONE;
+ conn_util_send_iq_async (conn, stanza, NULL, perms_config_form_reply_cb,
+ g_object_ref (chan));
+ g_object_unref (stanza);
}
}
@@ -1860,8 +1777,8 @@ handle_error (GObject *source,
return;
}
- DEBUG ("password required to join, changing password flags");
- change_password_flags (gmuc, TP_CHANNEL_PASSWORD_FLAG_PROVIDE, 0);
+ DEBUG ("password required to join; signalling");
+ change_must_provide_password (gmuc, TRUE);
g_object_set (gmuc, "state", MUC_STATE_AUTH, NULL);
}
else
@@ -1991,13 +1908,27 @@ handle_tube_presence (GabbleMucChannel *gmuc,
gabble_tubes_channel_presence_updated (priv->tube, from, node);
}
+static TpChannelGroupChangeReason
+muc_status_codes_to_change_reason (guint codes)
+{
+ if ((codes & WOCKY_MUC_CODE_BANNED) != 0)
+ return TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
+ else if ((codes & ( WOCKY_MUC_CODE_KICKED
+ | WOCKY_MUC_CODE_KICKED_AFFILIATION
+ | WOCKY_MUC_CODE_KICKED_ROOM_PRIVATISED
+ | WOCKY_MUC_CODE_KICKED_SHUTDOWN
+ )) != 0)
+ return TP_CHANNEL_GROUP_CHANGE_REASON_KICKED;
+ else
+ return TP_CHANNEL_GROUP_CHANGE_REASON_NONE;
+}
/* connect to wocky-muc:SIG_PARTED, which we will receive when the MUC tells *
* us that we have left the channel */
static void
handle_parted (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
const gchar *actor_jid,
const gchar *why,
const gchar *msg,
@@ -2014,14 +1945,6 @@ handle_parted (GObject *source,
TpIntSet *handles = NULL;
TpHandle member = 0;
TpHandle actor = 0;
- int x = 0;
- static const gpointer banned = GUINT_TO_POINTER (WOCKY_MUC_CODE_BANNED);
- static const gpointer const kicked[] =
- { GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED),
- GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_AFFILIATION),
- GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_ROOM_PRIVATISED),
- GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_SHUTDOWN),
- NULL };
const char *jid = wocky_muc_jid (wmuc);
DEBUG ("called with jid='%s'", jid);
@@ -2060,14 +1983,7 @@ handle_parted (GObject *source,
DEBUG ("ignoring invalid actor JID %s", actor_jid);
}
- if (g_hash_table_lookup (code, banned) != NULL)
- reason = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
- else
- for (x = 0; kicked[x] != NULL; x++)
- {
- if (g_hash_table_lookup (code, kicked[x]) != NULL)
- reason = TP_CHANNEL_GROUP_CHANGE_REASON_KICKED;
- }
+ reason = muc_status_codes_to_change_reason (codes);
/* handle_tube_presence creates tubes if need be, so bypass it here: */
if (priv->tube != NULL)
@@ -2088,7 +2004,7 @@ handle_parted (GObject *source,
static void
handle_left (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
WockyMucMember *who,
const gchar *actor_jid,
const gchar *why,
@@ -2105,14 +2021,6 @@ handle_left (GObject *source,
TpIntSet *handles = NULL;
TpHandle member = 0;
TpHandle actor = 0;
- int x = 0;
- static const gpointer banned = GUINT_TO_POINTER (WOCKY_MUC_CODE_BANNED);
- static const gpointer const kicked[] =
- { GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED),
- GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_AFFILIATION),
- GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_ROOM_PRIVATISED),
- GUINT_TO_POINTER (WOCKY_MUC_CODE_KICKED_SHUTDOWN),
- NULL };
member = tp_handle_ensure (contact_repo, who->from, NULL, NULL);
@@ -2132,14 +2040,7 @@ handle_left (GObject *source,
DEBUG ("ignoring invalid actor JID %s", actor_jid);
}
- if (g_hash_table_lookup (code, banned) != NULL)
- reason = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED;
- else
- for (x = 0; kicked[x] != NULL; x++)
- {
- if (g_hash_table_lookup (code, kicked[x]) != NULL)
- reason = TP_CHANNEL_GROUP_CHANGE_REASON_KICKED;
- }
+ reason = muc_status_codes_to_change_reason (codes);
/* handle_tube_presence creates tubes if need be, so bypass it here: */
if (priv->tube != NULL)
@@ -2170,8 +2071,8 @@ handle_perms (GObject *source,
GabbleMucChannelPrivate *priv = gmuc->priv;
TpHandle myself = TP_GROUP_MIXIN (gmuc)->self_handle;
- priv->self_role = get_role_from_backend (wocky_muc_role (wmuc));
- priv->self_affil = get_aff_from_backend (wocky_muc_affiliation (wmuc));
+ priv->self_role = wocky_muc_role (wmuc);
+ priv->self_affil = wocky_muc_affiliation (wmuc);
room_properties_update (gmuc);
update_permissions (gmuc);
@@ -2218,7 +2119,7 @@ handle_fill_presence (WockyMuc *muc,
static void
handle_renamed (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
gpointer data)
{
WockyMuc *wmuc = WOCKY_MUC (source);
@@ -2300,7 +2201,7 @@ update_roster_presence (GabbleMucChannel *gmuc,
static void
handle_join (WockyMuc *muc,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
gpointer data)
{
GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
@@ -2334,42 +2235,24 @@ handle_join (WockyMuc *muc,
tp_handle_set_peek (members), NULL, NULL, NULL, 0, 0);
/* accept the config of the room if it was created for us: */
- if (g_hash_table_lookup (code, (gpointer) WOCKY_MUC_CODE_NEW_ROOM))
+ if (codes & WOCKY_MUC_CODE_NEW_ROOM)
{
- GError *error = NULL;
- gboolean sent = FALSE;
WockyStanza *accept = wocky_stanza_build (
WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET,
- NULL, NULL,
+ NULL, gmuc->priv->jid,
'(', "query", ':', WOCKY_NS_MUC_OWNER,
'(', "x", ':', WOCKY_XMPP_NS_DATA,
'@', "type", "submit",
')',
')',
NULL);
-
- sent = _gabble_connection_send_with_reply (
- GABBLE_CONNECTION (base_conn), accept,
- room_created_submit_reply_cb, data, NULL, &error);
-
+ conn_util_send_iq_async (GABBLE_CONNECTION (base_conn), accept, NULL,
+ room_created_submit_reply_cb, NULL);
g_object_unref (accept);
-
- if (!sent)
- {
- DEBUG ("failed to send submit message: %s", error->message);
- g_error_free (error);
-
- g_object_unref (accept);
- close_channel (gmuc, NULL, TRUE, 0,
- TP_CHANNEL_GROUP_CHANGE_REASON_NONE);
-
- goto out;
- }
}
g_object_set (gmuc, "state", MUC_STATE_JOINED, NULL);
- out:
tp_handle_unref (contact_repo, myself);
tp_handle_set_destroy (members);
tp_handle_set_destroy (owners);
@@ -2382,7 +2265,7 @@ handle_join (WockyMuc *muc,
static void
handle_presence (GObject *source,
WockyStanza *stanza,
- GHashTable *code,
+ guint codes,
WockyMucMember *who,
gpointer data)
{
@@ -2456,7 +2339,7 @@ handle_message (GObject *source,
WockyStanza *stanza,
WockyMucMsgType type,
const gchar *xmpp_id,
- time_t stamp,
+ GDateTime *datetime,
WockyMucMember *who,
const gchar *text,
const gchar *subject,
@@ -2508,7 +2391,7 @@ handle_message (GObject *source,
if (text != NULL)
_gabble_muc_channel_receive (gmuc,
- msg_type, handle_type, from, stamp, xmpp_id, text, stanza,
+ msg_type, handle_type, from, datetime, xmpp_id, text, stanza,
GABBLE_TEXT_CHANNEL_SEND_NO_ERROR, TP_DELIVERY_STATUS_DELIVERED);
if (from_member && state != WOCKY_MUC_MSG_STATE_NONE)
@@ -2531,12 +2414,14 @@ handle_message (GObject *source,
default:
tp_msg_state = TP_CHANNEL_CHAT_STATE_ACTIVE;
}
- _gabble_muc_channel_state_receive (gmuc, tp_msg_state, from);
+
+ tp_svc_channel_interface_chat_state_emit_chat_state_changed (gmuc,
+ from, tp_msg_state);
}
if (subject != NULL)
- _gabble_muc_channel_handle_subject (gmuc, msg_type, handle_type, from,
- stamp, subject, stanza);
+ _gabble_muc_channel_handle_subject (gmuc, handle_type, from,
+ datetime, subject, stanza);
tp_handle_unref (repo, from);
}
@@ -2546,7 +2431,7 @@ handle_errmsg (GObject *source,
WockyStanza *stanza,
WockyMucMsgType type,
const gchar *xmpp_id,
- time_t stamp,
+ GDateTime *datetime,
WockyMucMember *who,
const gchar *text,
WockyXmppError error,
@@ -2554,6 +2439,7 @@ handle_errmsg (GObject *source,
gpointer data)
{
GabbleMucChannel *gmuc = GABBLE_MUC_CHANNEL (data);
+ GabbleMucChannelPrivate *priv = gmuc->priv;
TpBaseChannel *base = TP_BASE_CHANNEL (gmuc);
TpBaseConnection *conn = tp_base_channel_get_connection (base);
gboolean from_member = (who != NULL);
@@ -2562,6 +2448,7 @@ handle_errmsg (GObject *source,
TpHandleRepoIface *repo = NULL;
TpHandleType handle_type;
TpHandle from = 0;
+ const gchar *subject;
if (from_member)
{
@@ -2593,7 +2480,23 @@ handle_errmsg (GObject *source,
if (text != NULL)
_gabble_muc_channel_receive (gmuc, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE,
- handle_type, from, stamp, xmpp_id, text, stanza, tp_err, ds);
+ handle_type, from, datetime, xmpp_id, text, stanza, tp_err, ds);
+
+ /* FIXME: this is stupid. WockyMuc gives us the subject for non-errors, but
+ * doesn't bother for errors.
+ */
+ subject = wocky_node_get_content_from_child (
+ wocky_stanza_get_top_node (stanza), "subject");
+
+ /* The server is under no obligation to echo the <subject> element back if it
+ * sends us an error. Fortunately, it should preserve the id='' element so we
+ * can check for that instead.
+ */
+ if (subject != NULL ||
+ (priv->set_subject_stanza_id != NULL &&
+ !tp_strdiff (xmpp_id, priv->set_subject_stanza_id)))
+ _gabble_muc_channel_handle_subject (gmuc,
+ handle_type, from, datetime, subject, stanza);
tp_handle_unref (repo, from);
}
@@ -2604,43 +2507,34 @@ handle_errmsg (GObject *source,
*/
void
_gabble_muc_channel_handle_subject (GabbleMucChannel *chan,
- TpChannelTextMessageType msg_type,
TpHandleType handle_type,
TpHandle sender,
- time_t timestamp,
+ GDateTime *datetime,
const gchar *subject,
LmMessage *msg)
{
GabbleMucChannelPrivate *priv;
- TpIntSet *changed_values, *changed_flags;
- GValue val = { 0, };
+ const gchar *actor;
GError *error = NULL;
+ gint64 timestamp = datetime != NULL ?
+ g_date_time_to_unix (datetime) : G_MAXINT64;
g_assert (GABBLE_IS_MUC_CHANNEL (chan));
priv = chan->priv;
- if (priv->properties_ctx)
- {
- tp_properties_context_remove (priv->properties_ctx,
- ROOM_PROP_SUBJECT);
- }
-
if (wocky_stanza_extract_errors (msg, NULL, &error, NULL, NULL))
{
- if (priv->properties_ctx)
+ if (priv->set_subject_context != NULL)
{
- error->domain = TP_ERRORS;
- error->code = TP_ERROR_PERMISSION_DENIED;
+ GError *tp_error = NULL;
- if (tp_str_empty (error->message))
- {
- g_free (error->message);
- error->message = g_strdup ("failed to change subject");
- }
+ gabble_set_tp_error_from_wocky (error, &tp_error);
+ if (tp_str_empty (tp_error->message))
+ g_prefix_error (&tp_error, "failed to change subject");
- tp_properties_context_return (priv->properties_ctx, error);
- priv->properties_ctx = NULL;
+ return_from_set_subject (chan, tp_error);
+ g_clear_error (&tp_error);
/* Get the properties into a consistent state. */
room_properties_update (chan);
@@ -2650,71 +2544,35 @@ _gabble_muc_channel_handle_subject (GabbleMucChannel *chan,
return;
}
- DEBUG ("updating new property value for subject");
-
- changed_values = tp_intset_sized_new (NUM_ROOM_PROPS);
- changed_flags = tp_intset_sized_new (NUM_ROOM_PROPS);
-
- /* ROOM_PROP_SUBJECT */
- g_value_init (&val, G_TYPE_STRING);
- g_value_set_string (&val, subject);
-
- tp_properties_mixin_change_value (G_OBJECT (chan),
- ROOM_PROP_SUBJECT, &val, changed_values);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_SUBJECT, TP_PROPERTY_FLAG_READ, 0,
- changed_flags);
-
- g_value_unset (&val);
-
- /* ROOM_PROP_SUBJECT_CONTACT */
- g_value_init (&val, G_TYPE_UINT);
+ /* Channel.Interface.Subject properties */
if (handle_type == TP_HANDLE_TYPE_CONTACT)
{
- g_value_set_uint (&val, sender);
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (
+ tp_base_channel_get_connection (TP_BASE_CHANNEL (chan)),
+ handle_type);
+
+ actor = tp_handle_inspect (contact_handles, sender);
}
else
{
- g_value_set_uint (&val, 0);
+ actor = "";
}
- tp_properties_mixin_change_value (G_OBJECT (chan),
- ROOM_PROP_SUBJECT_CONTACT, &val, changed_values);
-
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_SUBJECT_CONTACT, TP_PROPERTY_FLAG_READ, 0,
- changed_flags);
-
- g_value_unset (&val);
-
- /* ROOM_PROP_SUBJECT_TIMESTAMP */
- g_value_init (&val, G_TYPE_UINT);
- g_value_set_uint (&val, timestamp);
-
- tp_properties_mixin_change_value (G_OBJECT (chan),
- ROOM_PROP_SUBJECT_TIMESTAMP, &val, changed_values);
+ g_free (priv->subject);
+ g_free (priv->subject_actor);
+ priv->subject = g_strdup (subject);
+ priv->subject_actor = g_strdup (actor);
+ priv->subject_timestamp = timestamp;
- tp_properties_mixin_change_flags (G_OBJECT (chan),
- ROOM_PROP_SUBJECT_TIMESTAMP, TP_PROPERTY_FLAG_READ, 0,
- changed_flags);
-
- g_value_unset (&val);
+ DEBUG ("Subject changed to '%s' by '%s' at %" G_GINT64_FORMAT "",
+ subject, actor, timestamp);
/* Emit signals */
- tp_properties_mixin_emit_changed (G_OBJECT (chan), changed_values);
- tp_properties_mixin_emit_flags (G_OBJECT (chan), changed_flags);
- tp_intset_destroy (changed_values);
- tp_intset_destroy (changed_flags);
+ emit_subject_changed (chan);
- if (priv->properties_ctx)
- {
- if (tp_properties_context_return_if_done (priv->properties_ctx))
- {
- priv->properties_ctx = NULL;
- }
- }
+ if (priv->set_subject_context != NULL)
+ return_from_set_subject (chan, NULL);
}
/**
@@ -2725,7 +2583,7 @@ _gabble_muc_channel_receive (GabbleMucChannel *chan,
TpChannelTextMessageType msg_type,
TpHandleType sender_handle_type,
TpHandle sender,
- time_t timestamp,
+ GDateTime *datetime,
const gchar *id,
const gchar *text,
LmMessage *msg,
@@ -2739,6 +2597,7 @@ _gabble_muc_channel_receive (GabbleMucChannel *chan,
gboolean is_echo;
gboolean is_error;
gchar *tmp;
+ gint64 timestamp = datetime != NULL ? g_date_time_to_unix (datetime): 0;
g_assert (GABBLE_IS_MUC_CHANNEL (chan));
@@ -2852,24 +2711,6 @@ _gabble_muc_channel_receive (GabbleMucChannel *chan,
}
}
-/**
- * _gabble_muc_channel_state_receive
- *
- * Send the D-BUS signal ChatStateChanged
- * on org.freedesktop.Telepathy.Channel.Interface.ChatState
- */
-void
-_gabble_muc_channel_state_receive (GabbleMucChannel *chan,
- guint state,
- guint from_handle)
-{
- g_assert (state < NUM_TP_CHANNEL_CHAT_STATES);
- g_assert (GABBLE_IS_MUC_CHANNEL (chan));
-
- tp_svc_channel_interface_chat_state_emit_chat_state_changed (chan,
- from_handle, state);
-}
-
static void
gabble_muc_channel_close (TpBaseChannel *base)
{
@@ -2897,7 +2738,7 @@ gabble_muc_channel_get_password_flags (TpSvcChannelInterfacePassword *iface,
priv = self->priv;
tp_svc_channel_interface_password_return_from_get_password_flags (context,
- priv->password_flags);
+ priv->must_provide_password ? TP_CHANNEL_PASSWORD_FLAG_PROVIDE : 0);
}
@@ -2922,7 +2763,7 @@ gabble_muc_channel_provide_password (TpSvcChannelInterfacePassword *iface,
priv = self->priv;
- if ((priv->password_flags & TP_CHANNEL_PASSWORD_FLAG_PROVIDE) == 0 ||
+ if (!priv->must_provide_password ||
priv->password_ctx != NULL)
{
GError error = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
@@ -2931,11 +2772,40 @@ gabble_muc_channel_provide_password (TpSvcChannelInterfacePassword *iface,
}
else
{
- send_join_request (self, password);
+ g_object_set (priv->wmuc, "password", password, NULL);
+ send_join_request (self);
priv->password_ctx = context;
}
}
+static void
+_gabble_muc_channel_message_sent_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ WockyPorter *porter = WOCKY_PORTER (source);
+ _GabbleMUCSendMessageCtx *context = user_data;
+ GabbleMucChannel *chan = context->channel;
+ TpMessage *message = context->message;
+ GError *error = NULL;
+
+ if (wocky_porter_send_finish (porter, res, &error))
+ {
+ tp_message_mixin_sent ((GObject *) chan, message,
+ TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY, context->token, NULL);
+ }
+ else
+ {
+ tp_message_mixin_sent ((GObject *) chan, context->message,
+ TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY, NULL, error);
+ g_free (error);
+ }
+
+ g_object_unref (context->channel);
+ g_object_unref (context->message);
+ g_free (context->token);
+ g_slice_free (_GabbleMUCSendMessageCtx, context);
+}
/**
* gabble_muc_channel_send
@@ -2952,14 +2822,35 @@ gabble_muc_channel_send (GObject *obj,
GabbleMucChannel *self = GABBLE_MUC_CHANNEL (obj);
TpBaseChannel *base = TP_BASE_CHANNEL (self);
GabbleMucChannelPrivate *priv = self->priv;
+ GabbleConnection *gabble_conn =
+ GABBLE_CONNECTION (tp_base_channel_get_connection (base));
+ _GabbleMUCSendMessageCtx *context = NULL;
+ WockyStanza *stanza = NULL;
+ WockyPorter *porter = NULL;
+ GError *error = NULL;
+ gchar *id = NULL;
- flags &= TP_MESSAGE_SENDING_FLAG_REPORT_DELIVERY;
-
- gabble_message_util_send_message (obj,
- GABBLE_CONNECTION (tp_base_channel_get_connection (base)),
- message, flags,
+ stanza = gabble_message_util_build_stanza (message, gabble_conn,
LM_MESSAGE_SUB_TYPE_GROUPCHAT, TP_CHANNEL_CHAT_STATE_ACTIVE,
- priv->jid, FALSE /* send nick */);
+ priv->jid, FALSE, &id, &error);
+
+ if (stanza != NULL)
+ {
+ context = g_slice_new0 (_GabbleMUCSendMessageCtx);
+ context->channel = g_object_ref (obj);
+ context->message = g_object_ref (message);
+ context->token = id;
+ porter = gabble_connection_dup_porter (gabble_conn);
+ wocky_porter_send_async (porter, stanza, NULL,
+ _gabble_muc_channel_message_sent_cb, context);
+ g_object_unref (stanza);
+ g_object_unref (porter);
+ }
+ else
+ {
+ tp_message_mixin_sent (obj, message, flags, NULL, error);
+ g_error_free (error);
+ }
}
gboolean
@@ -3048,7 +2939,7 @@ gabble_muc_channel_add_member (GObject *obj,
tp_intset_add (set_remove_members,
g_array_index (arr_members, guint, 0));
}
- g_array_free (arr_members, TRUE);
+ g_array_unref (arr_members);
tp_intset_add (set_remote_pending, handle);
@@ -3064,7 +2955,7 @@ gabble_muc_channel_add_member (GObject *obj,
tp_intset_destroy (set_remote_pending);
/* seek to enter the room */
- send_join_request (self, NULL);
+ send_join_request (self);
g_object_set (obj, "state", MUC_STATE_INITIATED, NULL);
/* deny adding */
@@ -3162,147 +3053,206 @@ gabble_muc_channel_remove_member (GObject *obj,
}
-static LmHandlerResult request_config_form_reply_cb (GabbleConnection *conn,
- LmMessage *sent_msg, LmMessage *reply_msg, GObject *object,
+static void request_config_form_reply_cb (
+ GObject *source,
+ GAsyncResult *result,
gpointer user_data);
-static gboolean
-gabble_muc_channel_do_set_properties (GObject *obj,
- TpPropertiesContext *ctx,
- GError **error)
+void
+gabble_muc_channel_update_configuration_async (
+ GabbleMucChannel *self,
+ GHashTable *validated_properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GabbleMucChannel *self = GABBLE_MUC_CHANNEL (obj);
GabbleMucChannelPrivate *priv = self->priv;
- TpBaseChannel *base = TP_BASE_CHANNEL (obj);
+ TpBaseChannel *base = (TpBaseChannel *) self;
GabbleConnection *conn =
GABBLE_CONNECTION (tp_base_channel_get_connection (base));
- LmMessage *msg;
- WockyNode *node;
- gboolean success;
+ WockyStanza *stanza;
+ GSimpleAsyncResult *result = g_simple_async_result_new ((GObject *) self,
+ callback, user_data, gabble_muc_channel_update_configuration_async);
- g_assert (priv->properties_ctx == NULL);
+ g_assert (priv->properties_being_updated == NULL);
- /* Changing subject? */
- if (tp_properties_context_has (ctx, ROOM_PROP_SUBJECT))
- {
- const gchar *str;
+ stanza = wocky_stanza_build (
+ WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET,
+ NULL, priv->jid,
+ '(', "query", ':', WOCKY_NS_MUC_OWNER, ')', NULL);
+ conn_util_send_iq_async (conn, stanza, NULL,
+ request_config_form_reply_cb, result);
+ g_object_unref (stanza);
- str = g_value_get_string (tp_properties_context_get (ctx,
- ROOM_PROP_SUBJECT));
+ priv->properties_being_updated = g_hash_table_ref (validated_properties);
+}
- msg = lm_message_new_with_sub_type (priv->jid,
- LM_MESSAGE_TYPE_MESSAGE, LM_MESSAGE_SUB_TYPE_GROUPCHAT);
- wocky_node_add_child_with_content (
- wocky_stanza_get_top_node (msg), "subject", str);
+gboolean
+gabble_muc_channel_update_configuration_finish (
+ GabbleMucChannel *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ wocky_implement_finish_void (self,
+ gabble_muc_channel_update_configuration_async);
+}
- success = _gabble_connection_send (conn, msg, error);
+typedef const gchar * (*MapFieldFunc) (const GValue *value);
- lm_message_unref (msg);
+typedef struct {
+ const gchar *var;
+ TpBaseRoomConfigProperty prop_id;
+ MapFieldFunc map;
+} ConfigFormMapping;
- if (!success)
- return FALSE;
- }
+static const gchar *
+map_bool (const GValue *value)
+{
+ return g_value_get_boolean (value) ? "1" : "0";
+}
- /* Changing any other properties? */
- if (tp_properties_context_has_other_than (ctx, ROOM_PROP_SUBJECT))
- {
- msg = lm_message_new_with_sub_type (priv->jid,
- LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET);
- node = wocky_node_add_child_with_content (
- wocky_stanza_get_top_node (msg), "query", NULL);
- node->ns = g_quark_from_string (NS_MUC_OWNER);
+static const gchar *
+map_bool_inverted (const GValue *value)
+{
+ return g_value_get_boolean (value) ? "0" : "1";
+}
+
+static const gchar *
+map_roomconfig_whois (const GValue *value)
+{
+ return g_value_get_boolean (value) ? "moderators" : "anyone";
+}
- success = _gabble_connection_send_with_reply (conn, msg,
- request_config_form_reply_cb, G_OBJECT (obj), NULL,
- error);
+static const gchar *
+map_owner_whois (const GValue *value)
+{
+ return g_value_get_boolean (value) ? "admins" : "anyone";
+}
- lm_message_unref (msg);
+static ConfigFormMapping form_mappings[] = {
+ { "anonymous", TP_BASE_ROOM_CONFIG_ANONYMOUS, map_bool },
+ { "muc#roomconfig_whois", TP_BASE_ROOM_CONFIG_ANONYMOUS, map_roomconfig_whois },
+ { "muc#owner_whois", TP_BASE_ROOM_CONFIG_ANONYMOUS, map_owner_whois },
- if (!success)
- return FALSE;
- }
+ { "members_only", TP_BASE_ROOM_CONFIG_INVITE_ONLY, map_bool },
+ { "muc#roomconfig_membersonly", TP_BASE_ROOM_CONFIG_INVITE_ONLY, map_bool },
+ { "muc#owner_inviteonly", TP_BASE_ROOM_CONFIG_INVITE_ONLY, map_bool },
- priv->properties_ctx = ctx;
- return TRUE;
+ { "moderated", TP_BASE_ROOM_CONFIG_MODERATED, map_bool },
+ { "muc#roomconfig_moderatedroom", TP_BASE_ROOM_CONFIG_MODERATED, map_bool },
+ { "muc#owner_moderatedroom", TP_BASE_ROOM_CONFIG_MODERATED, map_bool },
+
+ { "title", TP_BASE_ROOM_CONFIG_TITLE, g_value_get_string },
+ { "muc#roomconfig_roomname", TP_BASE_ROOM_CONFIG_TITLE, g_value_get_string },
+ { "muc#owner_roomname", TP_BASE_ROOM_CONFIG_TITLE, g_value_get_string },
+
+ { "muc#roomconfig_roomdesc", TP_BASE_ROOM_CONFIG_DESCRIPTION, g_value_get_string },
+ { "muc#owner_roomdesc", TP_BASE_ROOM_CONFIG_DESCRIPTION, g_value_get_string },
+
+ { "password", TP_BASE_ROOM_CONFIG_PASSWORD, g_value_get_string },
+ { "muc#roomconfig_roomsecret", TP_BASE_ROOM_CONFIG_PASSWORD, g_value_get_string },
+ { "muc#owner_roomsecret", TP_BASE_ROOM_CONFIG_PASSWORD, g_value_get_string },
+
+ { "password_protected", TP_BASE_ROOM_CONFIG_PASSWORD_PROTECTED, map_bool },
+ { "muc#roomconfig_passwordprotectedroom", TP_BASE_ROOM_CONFIG_PASSWORD_PROTECTED, map_bool },
+ { "muc#owner_passwordprotectedroom", TP_BASE_ROOM_CONFIG_PASSWORD_PROTECTED, map_bool },
+
+ { "persistent", TP_BASE_ROOM_CONFIG_PERSISTENT, map_bool },
+ { "muc#roomconfig_persistentroom", TP_BASE_ROOM_CONFIG_PERSISTENT, map_bool },
+ { "muc#owner_persistentroom", TP_BASE_ROOM_CONFIG_PERSISTENT, map_bool },
+
+ { "public", TP_BASE_ROOM_CONFIG_PRIVATE, map_bool_inverted },
+ { "muc#roomconfig_publicroom", TP_BASE_ROOM_CONFIG_PRIVATE, map_bool_inverted },
+ { "muc#owner_publicroom", TP_BASE_ROOM_CONFIG_PRIVATE, map_bool_inverted },
+
+ { NULL }
+};
+
+static ConfigFormMapping *
+lookup_config_form_field (const gchar *var)
+{
+ ConfigFormMapping *f;
+
+ for (f = form_mappings; f->var != NULL; f++)
+ if (strcmp (var, f->var) == 0)
+ return f;
+
+ DEBUG ("unknown field %s", var);
+
+ return NULL;
}
-static LmHandlerResult request_config_form_submit_reply_cb (
- GabbleConnection *conn, LmMessage *sent_msg, LmMessage *reply_msg,
- GObject *object, gpointer user_data);
+static void request_config_form_submit_reply_cb (
+ GObject *source,
+ GAsyncResult *result,
+ gpointer user_data);
-static LmHandlerResult
-request_config_form_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
- LmMessage *reply_msg, GObject *object,
- gpointer user_data)
+static void
+request_config_form_reply_cb (
+ GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object);
- TpBaseChannel *base = TP_BASE_CHANNEL (chan);
+ GabbleConnection *conn = GABBLE_CONNECTION (source);
+ GSimpleAsyncResult *update_result = G_SIMPLE_ASYNC_RESULT (user_data);
+ GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (
+ g_async_result_get_source_object ((GAsyncResult *) update_result));
GabbleMucChannelPrivate *priv = chan->priv;
- TpPropertiesContext *ctx = priv->properties_ctx;
+ GHashTable *properties = priv->properties_being_updated;
+ WockyStanza *reply = NULL;
+ WockyStanza *submit_iq = NULL;
+ WockyNode *form_node, *submit_node, *child;
GError *error = NULL;
- LmMessage *msg = NULL;
- WockyNode *submit_node, *form_node, *node;
guint i, props_left;
- NodeIter j;
+ WockyNodeIter j;
- if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
+ if (!conn_util_send_iq_finish (conn, result, &reply, &error))
{
- error = g_error_new (TP_ERRORS, TP_ERROR_PERMISSION_DENIED,
- "request for configuration form denied");
-
+ g_prefix_error (&error, "failed to request configuration form: ");
goto OUT;
}
- form_node = config_form_get_form_node (reply_msg);
+ form_node = config_form_get_form_node (reply);
if (form_node == NULL)
- goto PARSE_ERROR;
+ {
+ g_set_error (&error, TP_ERROR, TP_ERROR_SERVICE_CONFUSED,
+ "MUC configuration form didn't actually contain a form");
+ goto OUT;
+ }
/* initialize */
- msg = lm_message_new_with_sub_type (priv->jid, LM_MESSAGE_TYPE_IQ,
- LM_MESSAGE_SUB_TYPE_SET);
-
- node = wocky_node_add_child_with_content (
- wocky_stanza_get_top_node (msg), "query", NULL);
- node->ns = g_quark_from_string (NS_MUC_OWNER);
-
- submit_node = wocky_node_add_child_with_content (node, "x", NULL);
- submit_node->ns = g_quark_from_static_string (NS_X_DATA);
- wocky_node_set_attribute (submit_node,
- "type", "submit");
+ submit_iq = wocky_stanza_build (
+ WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET,
+ NULL, priv->jid,
+ '(',
+ "query", ':', WOCKY_NS_MUC_OWNER,
+ '(',
+ "x", ':', WOCKY_XMPP_NS_DATA,
+ '@', "type", "submit",
+ '*', &submit_node,
+ ')',
+ ')', NULL);
/* we assume that the number of props will fit in a guint on all supported
* platforms, so fail at compile time if this is no longer the case
*/
-#if NUM_ROOM_PROPS > 32
+#if TP_NUM_BASE_ROOM_CONFIG_PROPERTIES > 32
#error GabbleMUCChannel request_config_form_reply_cb needs porting to TpIntSet
#endif
props_left = 0;
- for (i = 0; i < NUM_ROOM_PROPS; i++)
+ for (i = 0; i < TP_NUM_BASE_ROOM_CONFIG_PROPERTIES; i++)
{
- if (i == ROOM_PROP_SUBJECT)
- continue;
-
- if (tp_properties_context_has (ctx, i))
+ if (g_hash_table_lookup (properties, GUINT_TO_POINTER (i)) != NULL)
props_left |= 1 << i;
}
- for (j = node_iter (form_node); j; j = node_iter_next (j))
+ wocky_node_iter_init (&j, form_node, "field", NULL);
+ while (wocky_node_iter_next (&j, &child))
{
- const gchar *var;
+ const gchar *var, *type_str;
WockyNode *field_node;
- WockyNode *child = node_iter_data (j);
- guint id;
- GType type;
- gboolean invert;
- const gchar *val_str = NULL, *type_str;
- gboolean val_bool;
-
- if (strcmp (child->name, "field") != 0)
- {
- DEBUG ("skipping node '%s'", child->name);
- continue;
- }
+ ConfigFormMapping *f;
+ GValue *value = NULL;
var = wocky_node_get_attribute (child, "var");
if (var == NULL) {
@@ -3311,252 +3261,132 @@ request_config_form_reply_cb (GabbleConnection *conn, LmMessage *sent_msg,
continue;
}
- id = INVALID_ROOM_PROP;
- type = G_TYPE_BOOLEAN;
- invert = FALSE;
-
- if (strcmp (var, "anonymous") == 0)
- {
- id = ROOM_PROP_ANONYMOUS;
- }
- else if (strcmp (var, "muc#roomconfig_whois") == 0)
- {
- id = ROOM_PROP_ANONYMOUS;
-
- if (tp_properties_context_has (ctx, id))
- {
- val_bool = g_value_get_boolean (
- tp_properties_context_get (ctx, id));
- val_str = (val_bool) ? "moderators" : "anyone";
- }
- }
- else if (strcmp (var, "muc#owner_whois") == 0)
- {
- id = ROOM_PROP_ANONYMOUS;
-
- if (tp_properties_context_has (ctx, id))
- {
- val_bool = g_value_get_boolean (
- tp_properties_context_get (ctx, id));
- val_str = (val_bool) ? "admins" : "anyone";
- }
- }
- else if (strcmp (var, "members_only") == 0 ||
- strcmp (var, "muc#roomconfig_membersonly") == 0 ||
- strcmp (var, "muc#owner_inviteonly") == 0)
- {
- id = ROOM_PROP_INVITE_ONLY;
- }
- else if (strcmp (var, "muc#roomconfig_allowinvites") == 0)
- {
- id = ROOM_PROP_INVITE_RESTRICTED;
- invert = TRUE;
- }
- else if (strcmp (var, "moderated") == 0 ||
- strcmp (var, "muc#roomconfig_moderatedroom") == 0 ||
- strcmp (var, "muc#owner_moderatedroom") == 0)
- {
- id = ROOM_PROP_MODERATED;
- }
- else if (strcmp (var, "title") == 0 ||
- strcmp (var, "muc#roomconfig_roomname") == 0 ||
- strcmp (var, "muc#owner_roomname") == 0)
- {
- id = ROOM_PROP_NAME;
- type = G_TYPE_STRING;
- }
- else if (strcmp (var, "muc#roomconfig_roomdesc") == 0 ||
- strcmp (var, "muc#owner_roomdesc") == 0)
- {
- id = ROOM_PROP_DESCRIPTION;
- type = G_TYPE_STRING;
- }
- else if (strcmp (var, "password") == 0 ||
- strcmp (var, "muc#roomconfig_roomsecret") == 0 ||
- strcmp (var, "muc#owner_roomsecret") == 0)
- {
- id = ROOM_PROP_PASSWORD;
- type = G_TYPE_STRING;
- }
- else if (strcmp (var, "password_protected") == 0 ||
- strcmp (var, "muc#roomconfig_passwordprotectedroom") == 0 ||
- strcmp (var, "muc#owner_passwordprotectedroom") == 0)
- {
- id = ROOM_PROP_PASSWORD_REQUIRED;
- }
- else if (strcmp (var, "persistent") == 0 ||
- strcmp (var, "muc#roomconfig_persistentroom") == 0 ||
- strcmp (var, "muc#owner_persistentroom") == 0)
- {
- id = ROOM_PROP_PERSISTENT;
- }
- else if (strcmp (var, "public") == 0 ||
- strcmp (var, "muc#roomconfig_publicroom") == 0 ||
- strcmp (var, "muc#owner_publicroom") == 0)
- {
- id = ROOM_PROP_PRIVATE;
- invert = TRUE;
- }
- else
- {
- DEBUG ("ignoring field '%s'", var);
- }
+ f = lookup_config_form_field (var);
/* add the corresponding field node to the reply message */
- field_node = wocky_node_add_child_with_content (submit_node, "field", NULL);
+ field_node = wocky_node_add_child (submit_node, "field");
wocky_node_set_attribute (field_node, "var", var);
type_str = wocky_node_get_attribute (child, "type");
- if (type_str)
+ if (type_str != NULL)
{
wocky_node_set_attribute (field_node, "type", type_str);
}
- if (id != INVALID_ROOM_PROP && tp_properties_context_has (ctx, id))
- {
- /* Known property and we have a value to set */
- DEBUG ("looking up %s... has=%d", room_property_signatures[id].name,
- tp_properties_context_has (ctx, id));
+ if (f != NULL)
+ value = g_hash_table_lookup (properties, GUINT_TO_POINTER (f->prop_id));
- if (!val_str)
- {
- const GValue *provided_value;
-
- provided_value = tp_properties_context_get (ctx, id);
-
- switch (type) {
- case G_TYPE_BOOLEAN:
- val_bool = g_value_get_boolean (provided_value);
- if (invert)
- val_bool = !val_bool;
- val_str = val_bool ? "1" : "0";
- break;
- case G_TYPE_STRING:
- val_str = g_value_get_string (provided_value);
- break;
- default:
- g_assert_not_reached ();
- }
- }
-
- DEBUG ("Setting value %s for %s", val_str, var);
+ if (value != NULL)
+ {
+ const gchar *val_str;
- props_left &= ~(1 << id);
+ /* Known property and we have a value to set */
+ DEBUG ("transforming %s...",
+ wocky_enum_to_nick (TP_TYPE_BASE_ROOM_CONFIG_PROPERTY,
+ f->prop_id));
+ g_assert (f->map != NULL);
+ val_str = f->map (value);
/* add the corresponding value node(s) to the reply message */
+ DEBUG ("Setting value %s for %s", val_str, var);
wocky_node_add_child_with_content (field_node, "value", val_str);
+
+ props_left &= ~(1 << f->prop_id);
}
else
{
/* Copy all the <value> nodes */
- NodeIter k;
-
- for (k = node_iter (child); k; k = node_iter_next (k))
- {
- WockyNode *value_node = node_iter_data (k);
+ WockyNodeIter k;
+ WockyNode *value_node;
- if (tp_strdiff (value_node->name, "value"))
- /* Not a value, skip it */
- continue;
-
- wocky_node_add_child_with_content (field_node, "value",
- value_node->content);
- }
+ wocky_node_iter_init (&k, child, "value", NULL);
+ while (wocky_node_iter_next (&k, &value_node))
+ wocky_node_add_child_with_content (field_node, "value",
+ value_node->content);
}
}
if (props_left != 0)
{
+ GString *unsubstituted = g_string_new ("");
+
printf (TP_ANSI_BOLD_ON TP_ANSI_FG_WHITE TP_ANSI_BG_RED
"\n%s: the following properties were not substituted:\n",
G_STRFUNC);
- for (i = 0; i < NUM_ROOM_PROPS; i++)
+ for (i = 0; i < TP_NUM_BASE_ROOM_CONFIG_PROPERTIES; i++)
{
if ((props_left & (1 << i)) != 0)
{
- printf (" %s\n", room_property_signatures[i].name);
+ const gchar *name = wocky_enum_to_nick (
+ TP_TYPE_BASE_ROOM_CONFIG_PROPERTY, i);
+ printf (" %s\n", name);
+
+ if (unsubstituted->len > 0)
+ g_string_append (unsubstituted, ", ");
+
+ g_string_append (unsubstituted, name);
}
}
printf ("\nthis is a MUC server compatibility bug in gabble, please "
"report it with a full debug log attached (running gabble "
- "with LM_DEBUG=net)" TP_ANSI_RESET "\n\n");
+ "with WOCKY_DEBUG=xmpp)" TP_ANSI_RESET "\n\n");
fflush (stdout);
- error = g_error_new (TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
- "not all properties were substituted");
+ error = g_error_new (TP_ERRORS, TP_ERROR_SERVICE_CONFUSED,
+ "Couldn't find fields corresponding to %s in the muc#owner form. "
+ "This is a MUC server compatibility bug in Gabble.",
+ unsubstituted->str);
+ g_string_free (unsubstituted, TRUE);
goto OUT;
}
- _gabble_connection_send_with_reply (
- GABBLE_CONNECTION (tp_base_channel_get_connection (base)), msg,
- request_config_form_submit_reply_cb, G_OBJECT (object),
- NULL, &error);
-
- goto OUT;
-
-PARSE_ERROR:
- error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "error parsing reply from server");
+ conn_util_send_iq_async (conn, submit_iq, NULL,
+ request_config_form_submit_reply_cb, update_result);
OUT:
- if (error)
+ if (error != NULL)
{
- tp_properties_context_return (ctx, error);
- priv->properties_ctx = NULL;
+ g_simple_async_result_set_from_error (update_result, error);
+ g_simple_async_result_complete (update_result);
+ g_object_unref (update_result);
+ tp_clear_pointer (&priv->properties_being_updated, g_hash_table_unref);
+ g_clear_error (&error);
}
- if (msg)
- lm_message_unref (msg);
-
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ tp_clear_object (&reply);
+ tp_clear_object (&submit_iq);
+ g_object_unref (chan);
}
-static LmHandlerResult
-request_config_form_submit_reply_cb (GabbleConnection *conn,
- LmMessage *sent_msg,
- LmMessage *reply_msg,
- GObject *object,
- gpointer user_data)
+static void
+request_config_form_submit_reply_cb (
+ GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object);
+ GSimpleAsyncResult *update_result = G_SIMPLE_ASYNC_RESULT (user_data);
+ GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (
+ g_async_result_get_source_object ((GAsyncResult *) update_result));
GabbleMucChannelPrivate *priv = chan->priv;
- TpPropertiesContext *ctx = priv->properties_ctx;
GError *error = NULL;
- gboolean returned;
-
- if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT)
- {
- error = g_error_new (TP_ERRORS, TP_ERROR_PERMISSION_DENIED,
- "submitted configuration form was rejected");
- }
- if (!error)
+ if (!conn_util_send_iq_finish (GABBLE_CONNECTION (source), result, NULL, &error))
{
- guint i;
-
- for (i = 0; i < NUM_ROOM_PROPS; i++)
- {
- if (i != ROOM_PROP_SUBJECT)
- tp_properties_context_remove (ctx, i);
- }
-
- returned = tp_properties_context_return_if_done (ctx);
+ g_prefix_error (&error, "submitted configuration form was rejected: ");
+ g_simple_async_result_set_from_error (update_result, error);
+ g_clear_error (&error);
}
- else
- {
- tp_properties_context_return (ctx, error);
- returned = TRUE;
- /* Get the properties into a consistent state. */
- room_properties_update (chan);
- }
+ g_simple_async_result_complete (update_result);
+ tp_clear_pointer (&priv->properties_being_updated, g_hash_table_unref);
- if (returned)
- priv->properties_ctx = NULL;
+ /* Get the properties into a consistent state. */
+ room_properties_update (chan);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ g_object_unref (chan);
+ g_object_unref (update_result);
}
/**
@@ -3893,6 +3723,96 @@ gabble_muc_channel_teardown (GabbleMucChannel *gmuc)
}
static void
+sent_subject_cb (
+ GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GabbleMucChannel *self = GABBLE_MUC_CHANNEL (user_data);
+ GabbleMucChannelPrivate *priv = self->priv;
+ GError *error = NULL;
+
+ if (!wocky_porter_send_finish (WOCKY_PORTER (source), result, &error))
+ {
+ DEBUG ("buh, failed to send a <message> to change the subject: %s",
+ error->message);
+
+ if (priv->set_subject_context != NULL)
+ {
+ GError *tp_error = NULL;
+
+ gabble_set_tp_error_from_wocky (error, &tp_error);
+ return_from_set_subject (self, tp_error);
+ g_clear_error (&tp_error);
+ }
+
+ g_clear_error (&error);
+ }
+ /* otherwise, we wait for a reply! */
+
+ g_object_unref (self);
+}
+
+static void
+gabble_muc_channel_set_subject (TpSvcChannelInterfaceSubject *iface,
+ const gchar *subject,
+ DBusGMethodInvocation *context)
+{
+ GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface);
+ GabbleMucChannelPrivate *priv = self->priv;
+ GabbleConnection *conn = GABBLE_CONNECTION (tp_base_channel_get_connection (
+ TP_BASE_CHANNEL (self)));
+ WockyPorter *porter = wocky_session_get_porter (conn->session);
+
+ if (priv->state < MUC_STATE_JOINED)
+ {
+ GError error = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "Steady on. You're not in the room yet" };
+
+ dbus_g_method_return_error (context, &error);
+ }
+ else if (priv->state > MUC_STATE_JOINED || priv->closing)
+ {
+ GError error = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "Already left/leaving the room" };
+
+ dbus_g_method_return_error (context, &error);
+ }
+ else if (priv->set_subject_context != NULL)
+ {
+ GError error = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "Hey! Stop changing the subject! (Your last request is still in "
+ "flight.)" };
+
+ dbus_g_method_return_error (context, &error);
+ }
+ else
+ {
+ WockyXmppConnection *xmpp_conn;
+ WockyStanza *stanza;
+
+ g_assert (priv->set_subject_stanza_id == NULL);
+ g_object_get (porter,
+ "connection", &xmpp_conn,
+ NULL);
+ priv->set_subject_stanza_id = wocky_xmpp_connection_new_id (xmpp_conn);
+ g_object_unref (xmpp_conn);
+
+ stanza = wocky_stanza_build (
+ WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_GROUPCHAT,
+ NULL, priv->jid,
+ '@', "id", priv->set_subject_stanza_id,
+ '(', "subject", '$', subject, ')',
+ NULL);
+
+ priv->set_subject_context = context;
+ wocky_porter_send_async (porter, stanza, NULL, sent_subject_cb,
+ g_object_ref (self));
+ g_object_unref (stanza);
+ }
+}
+
+static void
password_iface_init (gpointer g_iface, gpointer iface_data)
{
TpSvcChannelInterfacePasswordClass *klass =
@@ -3916,3 +3836,15 @@ chat_state_iface_init (gpointer g_iface, gpointer iface_data)
IMPLEMENT(set_chat_state);
#undef IMPLEMENT
}
+
+static void
+subject_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ TpSvcChannelInterfaceSubjectClass *klass =
+ (TpSvcChannelInterfaceSubjectClass *) g_iface;
+
+#define IMPLEMENT(x) tp_svc_channel_interface_subject_implement_##x (\
+ klass, gabble_muc_channel_##x)
+ IMPLEMENT(set_subject);
+#undef IMPLEMENT
+}
diff --git a/src/muc-channel.h b/src/muc-channel.h
index 770c3f02b..ca8ce2296 100644
--- a/src/muc-channel.h
+++ b/src/muc-channel.h
@@ -30,7 +30,6 @@
#include <telepathy-glib/dbus-properties-mixin.h>
#include <telepathy-glib/group-mixin.h>
#include <telepathy-glib/message-mixin.h>
-#include <telepathy-glib/properties-mixin.h>
#include "types.h"
#include "tubes-channel.h"
@@ -53,14 +52,13 @@ struct _GabbleMucChannelClass {
TpBaseChannelClass parent_class;
TpGroupMixinClass group_class;
- TpPropertiesMixinClass properties_class;
+ TpDBusPropertiesMixinClass dbus_props_class;
};
struct _GabbleMucChannel {
TpBaseChannel parent;
TpGroupMixin group;
- TpPropertiesMixin properties;
TpMessageMixin message_mixin;
GabbleMucChannelPrivate *priv;
@@ -86,22 +84,6 @@ GType gabble_muc_channel_get_type (void);
GabbleMucChannelClass))
gboolean _gabble_muc_channel_is_ready (GabbleMucChannel *chan);
-void _gabble_muc_channel_presence_error (GabbleMucChannel *chan,
- const gchar *jid, WockyNode *pres_node);
-void _gabble_muc_channel_member_presence_updated (GabbleMucChannel *chan,
- TpHandle handle, LmMessage *message, WockyNode *x_node,
- WockyNode *item_node);
-void _gabble_muc_channel_handle_subject (GabbleMucChannel *chan,
- TpChannelTextMessageType msg_type, TpHandleType handle_type,
- TpHandle sender, time_t timestamp, const gchar *subject, LmMessage *msg);
-void _gabble_muc_channel_receive (GabbleMucChannel *chan,
- TpChannelTextMessageType msg_type, TpHandleType handle_type,
- TpHandle sender, time_t timestamp, const gchar *id, const gchar *text,
- LmMessage *msg, TpChannelTextSendError send_error,
- TpDeliveryStatus delivery_status);
-
-void _gabble_muc_channel_state_receive (GabbleMucChannel *chan,
- guint state, guint from_handle);
void gabble_muc_channel_send_presence (GabbleMucChannel *chan);
@@ -130,6 +112,16 @@ gboolean gabble_muc_channel_request_call_finish (GabbleMucChannel *gmuc,
gboolean gabble_muc_channel_handle_jingle_session (GabbleMucChannel *channel,
GabbleJingleSession *session);
+void gabble_muc_channel_update_configuration_async (
+ GabbleMucChannel *self,
+ GHashTable *validated_properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean gabble_muc_channel_update_configuration_finish (
+ GabbleMucChannel *self,
+ GAsyncResult *result,
+ GError **error);
+
void gabble_muc_channel_teardown (GabbleMucChannel *gmuc);
void gabble_muc_channel_close_tube (GabbleMucChannel *gmuc);
diff --git a/src/muc-factory.c b/src/muc-factory.c
index b6bb486d5..f5c324968 100644
--- a/src/muc-factory.c
+++ b/src/muc-factory.c
@@ -20,13 +20,12 @@
#include "config.h"
#include "muc-factory.h"
-#define DBUS_API_SUBJECT_TO_CHANGE
-
#include <string.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <loudmouth/loudmouth.h>
+#include <wocky/wocky-utils.h>
#include <telepathy-glib/channel-manager.h>
#include <telepathy-glib/dbus.h>
#include <telepathy-glib/interfaces.h>
@@ -36,7 +35,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"
@@ -99,17 +98,14 @@ struct _GabbleMucFactoryPrivate
gboolean dispose_has_run;
};
-#define GABBLE_MUC_FACTORY_GET_PRIVATE(o) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MUC_FACTORY, \
- GabbleMucFactoryPrivate))
-
static GObject *gabble_muc_factory_constructor (GType type, guint n_props,
GObjectConstructParam *props);
static void
gabble_muc_factory_init (GabbleMucFactory *fac)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (fac,
+ GABBLE_TYPE_MUC_FACTORY, GabbleMucFactoryPrivate);
fac->priv = priv;
@@ -150,7 +146,7 @@ static void
gabble_muc_factory_dispose (GObject *object)
{
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
if (priv->dispose_has_run)
return;
@@ -166,7 +162,7 @@ gabble_muc_factory_dispose (GObject *object)
g_hash_table_foreach (priv->disco_requests, cancel_disco_request,
priv->conn->disco);
- g_hash_table_destroy (priv->disco_requests);
+ g_hash_table_unref (priv->disco_requests);
if (G_OBJECT_CLASS (gabble_muc_factory_parent_class)->dispose)
G_OBJECT_CLASS (gabble_muc_factory_parent_class)->dispose (object);
@@ -179,7 +175,7 @@ gabble_muc_factory_get_property (GObject *object,
GParamSpec *pspec)
{
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
switch (property_id) {
case PROP_CONNECTION:
@@ -198,7 +194,7 @@ gabble_muc_factory_set_property (GObject *object,
GParamSpec *pspec)
{
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
switch (property_id) {
case PROP_CONNECTION:
@@ -242,7 +238,7 @@ static void
muc_channel_closed_cb (GabbleMucChannel *chan, gpointer user_data)
{
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
TpHandle room_handle;
tp_channel_manager_emit_channel_closed_for_object (fac,
@@ -265,7 +261,7 @@ muc_ready_cb (GabbleMucChannel *text_chan,
gpointer data)
{
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
GabbleTubesChannel *tubes_chan;
GSList *requests_satisfied_text, *requests_satisfied_tubes = NULL;
gboolean text_requested;
@@ -340,7 +336,7 @@ muc_ready_cb (GabbleMucChannel *text_chan,
tp_channel_manager_emit_new_channels (fac, channels);
- g_hash_table_destroy (channels);
+ g_hash_table_unref (channels);
}
g_hash_table_remove (priv->tubes_needed_for_tube, tubes_chan);
@@ -354,7 +350,7 @@ muc_join_error_cb (GabbleMucChannel *chan,
gpointer data)
{
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
GabbleTubesChannel *tubes_chan;
GSList *requests_satisfied;
GSList *iter;
@@ -452,9 +448,10 @@ new_muc_channel (GabbleMucFactory *fac,
gboolean requested,
GHashTable *initial_channels,
GArray *initial_handles,
- char **initial_ids)
+ char **initial_ids,
+ const char *room_name)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
GabbleMucChannel *chan;
char *object_path;
@@ -497,6 +494,7 @@ new_muc_channel (GabbleMucFactory *fac,
"initial-channels", initial_channels_array,
"initial-invitee-handles", initial_handles,
"initial-invitee-ids", initial_ids,
+ "room-name", room_name,
NULL);
g_signal_connect (chan, "closed", (GCallback) muc_channel_closed_cb, fac);
@@ -507,7 +505,7 @@ new_muc_channel (GabbleMucFactory *fac,
g_hash_table_insert (priv->text_channels, GUINT_TO_POINTER (handle), chan);
g_free (object_path);
- g_ptr_array_free (initial_channels_array, TRUE);
+ g_ptr_array_unref (initial_channels_array);
g_array_unref (initial_handles);
if (_gabble_muc_channel_is_ready (chan))
@@ -529,7 +527,7 @@ do_invite (GabbleMucFactory *fac,
TpHandle inviter_handle,
const gchar *reason)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
TpHandleRepoIface *room_repo = tp_base_connection_get_handles (
(TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM);
TpHandle room_handle;
@@ -547,7 +545,7 @@ do_invite (GabbleMucFactory *fac,
GUINT_TO_POINTER (room_handle)) == NULL)
{
new_muc_channel (fac, room_handle, TRUE, inviter_handle, reason, FALSE,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
}
else
{
@@ -581,7 +579,7 @@ obsolete_invite_disco_cb (GabbleDisco *self,
struct DiscoInviteData *data = (struct DiscoInviteData *) user_data;
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data->factory);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
(TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT);
WockyNode *identity;
@@ -626,7 +624,7 @@ process_muc_invite (GabbleMucFactory *fac,
const gchar *from,
TpChannelTextSendError send_error)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
TP_HANDLE_TYPE_CONTACT);
@@ -700,7 +698,7 @@ process_obsolete_invite (GabbleMucFactory *fac,
const gchar *body,
TpChannelTextSendError send_error)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
TP_HANDLE_TYPE_CONTACT);
@@ -788,7 +786,7 @@ muc_factory_message_cb (LmMessageHandler *handler,
gpointer user_data)
{
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
const gchar *from, *body, *id;
time_t stamp;
@@ -824,7 +822,7 @@ muc_factory_message_cb (LmMessageHandler *handler,
void
gabble_muc_factory_broadcast_presence (GabbleMucFactory *self)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
GHashTableIter iter;
gpointer channel = NULL;
@@ -842,7 +840,7 @@ gabble_muc_factory_associate_request (GabbleMucFactory *self,
gpointer channel,
gpointer request)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
GSList *list = g_hash_table_lookup (priv->queued_requests, channel);
g_assert (TP_IS_EXPORTABLE_CHANNEL (channel));
@@ -880,7 +878,7 @@ cancel_queued_requests (gpointer k,
static void
gabble_muc_factory_close_all (GabbleMucFactory *self)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
DEBUG ("closing channels");
@@ -895,9 +893,9 @@ gabble_muc_factory_close_all (GabbleMucFactory *self)
g_hash_table_foreach_steal (priv->queued_requests,
cancel_queued_requests, self);
- tp_clear_pointer (&priv->queued_requests, g_hash_table_destroy);
- tp_clear_pointer (&priv->text_needed_for_tubes, g_hash_table_destroy);
- tp_clear_pointer (&priv->tubes_needed_for_tube, g_hash_table_destroy);
+ tp_clear_pointer (&priv->queued_requests, g_hash_table_unref);
+ tp_clear_pointer (&priv->text_needed_for_tubes, g_hash_table_unref);
+ tp_clear_pointer (&priv->tubes_needed_for_tube, g_hash_table_unref);
/* Use a temporary variable because we don't want
* muc_channel_closed_cb or tubes_channel_closed_cb to remove the channel
@@ -914,7 +912,7 @@ gabble_muc_factory_close_all (GabbleMucFactory *self)
while (g_hash_table_iter_next (&iter, NULL, &chan))
gabble_muc_channel_teardown (GABBLE_MUC_CHANNEL (chan));
- g_hash_table_destroy (tmp);
+ g_hash_table_unref (tmp);
}
if (priv->message_cb != NULL)
@@ -935,7 +933,7 @@ connection_status_changed_cb (GabbleConnection *conn,
guint reason,
GabbleMucFactory *self)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
switch (status)
{
@@ -964,7 +962,7 @@ gabble_muc_factory_constructor (GType type, guint n_props,
GObject *obj = G_OBJECT_CLASS (gabble_muc_factory_parent_class)->
constructor (type, n_props, props);
GabbleMucFactory *self = GABBLE_MUC_FACTORY (obj);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
priv->status_changed_id = g_signal_connect (priv->conn,
"status-changed", (GCallback) connection_status_changed_cb, obj);
@@ -1009,7 +1007,7 @@ gabble_muc_factory_foreach_channel (TpChannelManager *manager,
gpointer user_data)
{
GabbleMucFactory *fac = GABBLE_MUC_FACTORY (manager);
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac);
+ GabbleMucFactoryPrivate *priv = fac->priv;
struct _ForeachData data;
data.user_data = user_data;
@@ -1035,7 +1033,8 @@ ensure_muc_channel (GabbleMucFactory *fac,
gboolean requested,
GHashTable *initial_channels,
GArray *initial_handles,
- char **initial_ids)
+ char **initial_ids,
+ const char *room_name)
{
TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
@@ -1044,7 +1043,7 @@ ensure_muc_channel (GabbleMucFactory *fac,
if (*ret == NULL)
{
*ret = new_muc_channel (fac, handle, FALSE, base_conn->self_handle, NULL,
- requested, initial_channels, initial_handles, initial_ids);
+ requested, initial_channels, initial_handles, initial_ids, room_name);
return FALSE;
}
@@ -1062,7 +1061,7 @@ gabble_muc_factory_handle_si_stream_request (GabbleMucFactory *self,
const gchar *stream_id,
LmMessage *msg)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
TpHandleRepoIface *room_repo = tp_base_connection_get_handles (
(TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM);
GabbleMucChannel *gmuc = NULL;
@@ -1092,15 +1091,15 @@ GabbleMucChannel *
gabble_muc_factory_find_text_channel (GabbleMucFactory *self,
TpHandle handle)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
return g_hash_table_lookup (priv->text_channels, GUINT_TO_POINTER (handle));
}
static const gchar * const muc_channel_fixed_properties[] = {
- TP_IFACE_CHANNEL ".ChannelType",
- TP_IFACE_CHANNEL ".TargetHandleType",
+ TP_PROP_CHANNEL_CHANNEL_TYPE,
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
NULL
};
@@ -1108,18 +1107,20 @@ static const gchar * const * muc_tubes_channel_fixed_properties =
muc_channel_fixed_properties;
static const gchar * const muc_channel_allowed_properties[] = {
- TP_IFACE_CHANNEL ".TargetHandle",
- TP_IFACE_CHANNEL ".TargetID",
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialChannels",
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeHandles",
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeIDs",
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InvitationMessage",
+ TP_PROP_CHANNEL_TARGET_HANDLE,
+ TP_PROP_CHANNEL_TARGET_ID,
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS,
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_HANDLES,
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS,
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INVITATION_MESSAGE,
+ TP_PROP_CHANNEL_INTERFACE_ROOM_ROOM_NAME,
+ TP_PROP_CHANNEL_INTERFACE_ROOM_SERVER,
NULL
};
static const gchar * const muc_tubes_channel_allowed_properties[] = {
- TP_IFACE_CHANNEL ".TargetHandle",
- TP_IFACE_CHANNEL ".TargetID",
+ TP_PROP_CHANNEL_TARGET_HANDLE,
+ TP_PROP_CHANNEL_TARGET_ID,
NULL
};
@@ -1134,12 +1135,12 @@ gabble_muc_factory_type_foreach_channel_class (GType type,
channel_type_value = tp_g_value_slice_new (G_TYPE_STRING);
/* no string value yet - we'll change it for each channel class */
- g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType",
+ g_hash_table_insert (table, TP_PROP_CHANNEL_CHANNEL_TYPE,
channel_type_value);
handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (handle_type_value, TP_HANDLE_TYPE_ROOM);
- g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
+ g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
handle_type_value);
/* Channel.Type.Text */
@@ -1171,7 +1172,7 @@ gabble_muc_factory_type_foreach_channel_class (GType type,
gabble_media_factory_call_channel_allowed_properties (),
user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
}
/* return TRUE if the text_channel associated is ready */
@@ -1181,14 +1182,14 @@ ensure_tubes_channel (GabbleMucFactory *self,
GabbleTubesChannel **tubes_chan,
gboolean requested)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn;
GabbleMucChannel *text_chan;
TpHandle initiator = base_conn->self_handle;
gboolean result;
result = ensure_muc_channel (self, priv, handle, &text_chan, FALSE,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
/* this refs the tube channel object */
*tubes_chan = gabble_muc_channel_open_tube (text_chan, initiator, requested);
@@ -1207,7 +1208,7 @@ handle_text_channel_request (GabbleMucFactory *self,
TpHandle room,
GError **error)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
TpBaseConnection *conn = TP_BASE_CONNECTION (priv->conn);
GabbleMucChannel *text_chan;
TpHandleSet *handles;
@@ -1226,22 +1227,29 @@ handle_text_channel_request (GabbleMucFactory *self,
char **initial_ids, **final_ids;
const char *invite_msg;
+ const gchar *room_name, *server_prop;
+
if (tp_channel_manager_asv_has_unknown_properties (request_properties,
muc_channel_fixed_properties, muc_channel_allowed_properties,
error))
return FALSE;
initial_channels = tp_asv_get_boxed (request_properties,
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialChannels",
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS,
TP_ARRAY_TYPE_OBJECT_PATH_LIST);
initial_handles = tp_asv_get_boxed (request_properties,
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeHandles",
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_HANDLES,
DBUS_TYPE_G_UINT_ARRAY);
initial_ids = tp_asv_get_boxed (request_properties,
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeIDs",
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS,
G_TYPE_STRV);
invite_msg = tp_asv_get_string (request_properties,
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InvitationMessage");
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INVITATION_MESSAGE);
+
+ room_name = tp_asv_get_string (request_properties,
+ TP_PROP_CHANNEL_INTERFACE_ROOM_ROOM_NAME);
+ server_prop = tp_asv_get_string (request_properties,
+ TP_PROP_CHANNEL_INTERFACE_ROOM_SERVER);
handles = tp_handle_set_new (contact_handles);
continue_handles = tp_intset_new ();
@@ -1339,9 +1347,11 @@ handle_text_channel_request (GabbleMucFactory *self,
final_ids[i] = (char *) id;
}
+ /* TargetHandleType=None and TargetHandle=0 */
if (room == 0)
{
char *uuid, *id, *server = "";
+ gchar *tmp = NULL;
/* There's no super obvious way to tell.. you can't invite GMail users to
* a non-Google MUC (it just doesn't work), and if your own account is on
@@ -1365,15 +1375,29 @@ handle_text_channel_request (GabbleMucFactory *self,
}
}
- uuid = gabble_generate_id ();
- id = g_strdup_printf ("private-chat-%s%s", uuid, server);
+ if (server_prop != NULL)
+ {
+ tmp = g_strdup_printf ("@%s", server_prop);
+ server = tmp;
+ }
- DEBUG ("Creating PMUC '%s'", id);
+ if (room_name != NULL && room_name[0] != '\0')
+ {
+ id = g_strdup_printf ("%s%s", room_name, server);
+ }
+ else
+ {
+ uuid = gabble_generate_id ();
+ id = g_strdup_printf ("private-chat-%s%s", uuid, server);
+ g_free (uuid);
+
+ DEBUG ("Creating PMUC '%s'", id);
+ }
room = tp_handle_ensure (room_handles, id, NULL, error);
- g_free (uuid);
g_free (id);
+ g_free (tmp);
if (room == 0)
{
@@ -1387,8 +1411,66 @@ handle_text_channel_request (GabbleMucFactory *self,
tp_handle_ref (room_handles, room);
}
+ /* Make sure TargetID and RoomName don't conflict. */
+ if (room_name != NULL && room_name[0] != '\0')
+ {
+ const gchar *target_id = tp_handle_inspect (room_handles, room);
+ gchar *target_room = NULL;
+ gboolean ok;
+
+ /* JIDs that are handles must already be valid. */
+ ok = wocky_decode_jid (target_id, &target_room, NULL, NULL);
+ g_assert (ok);
+
+ ok = !tp_strdiff (target_room, room_name);
+
+ if (!ok)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "TargetID's node part (%s) doesn't match RoomName (%s)",
+ target_room, room_name);
+ ret = FALSE;
+ }
+
+ g_free (target_room);
+
+ if (!ok)
+ goto out;
+ }
+
+ /* Make sure TargetID and Server don't conflict. */
+ if (server_prop != NULL)
+ {
+ const gchar *target_id = tp_handle_inspect (room_handles, room);
+ gchar *target_server = NULL;
+ gboolean ok = TRUE;
+
+ /* JIDs that are handles must already be valid. */
+ ok = wocky_decode_jid (target_id, NULL, &target_server, NULL);
+ g_assert (ok);
+
+ if (target_server != NULL)
+ ok = !tp_strdiff (target_server, server_prop);
+ else
+ ok = TRUE;
+
+ if (!ok)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
+ "TargetID's domain part (%s) doesn't match Server (%s)",
+ target_server, server_prop);
+ ret = FALSE;
+ }
+
+ g_free (target_server);
+
+ if (!ok)
+ goto out;
+
+ }
+
if (ensure_muc_channel (self, priv, room, &text_chan, TRUE,
- final_channels, final_handles, final_ids))
+ final_channels, final_handles, final_ids, room_name))
{
/* channel exists */
@@ -1454,8 +1536,8 @@ out:
if (room != 0)
tp_handle_unref (room_handles, room);
- g_hash_table_destroy (final_channels);
- g_array_free (final_handles, TRUE);
+ g_hash_table_unref (final_channels);
+ g_array_unref (final_handles);
g_free (final_ids);
tp_handle_set_destroy (handles);
@@ -1472,7 +1554,7 @@ handle_tubes_channel_request (GabbleMucFactory *self,
TpHandle handle,
GError **error)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
GabbleTubesChannel *tube = NULL;
GabbleMucChannel *gmuc = NULL;
@@ -1529,7 +1611,7 @@ handle_tube_channel_request (GabbleMucFactory *self,
GError **error)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
gboolean can_announce_now = TRUE;
gboolean tubes_channel_created = FALSE;
GabbleTubesChannel *tube = NULL;
@@ -1575,7 +1657,7 @@ handle_tube_channel_request (GabbleMucFactory *self,
g_hash_table_insert (channels, new_channel, request_tokens);
tp_channel_manager_emit_new_channels (self, channels);
- g_hash_table_destroy (channels);
+ g_hash_table_unref (channels);
g_slist_free (request_tokens);
}
else
@@ -1617,12 +1699,12 @@ handle_stream_tube_channel_request (GabbleMucFactory *self,
/* "Service" is a mandatory, not-fixed property */
service = tp_asv_get_string (request_properties,
- TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service");
+ TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE);
if (service == NULL)
{
g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
"Request does not contain the mandatory property '%s'",
- TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service");
+ TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE);
return FALSE;
}
@@ -1648,12 +1730,12 @@ handle_dbus_tube_channel_request (GabbleMucFactory *self,
/* "ServiceName" is a mandatory, not-fixed property */
service = tp_asv_get_string (request_properties,
- TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName");
+ TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME);
if (service == NULL)
{
g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
"Request does not contain the mandatory property '%s'",
- TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName");
+ TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME);
return FALSE;
}
@@ -1695,7 +1777,7 @@ handle_call_channel_request (GabbleMucFactory *self,
TpHandle handle,
GError **error)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
gboolean initial_audio, initial_video;
GabbleMucChannel *muc;
GabbleCallMucChannel *call;
@@ -1719,7 +1801,7 @@ handle_call_channel_request (GabbleMucFactory *self,
return FALSE;
}
- ensure_muc_channel (self, priv, handle, &muc, FALSE, NULL, NULL, NULL);
+ ensure_muc_channel (self, priv, handle, &muc, FALSE, NULL, NULL, NULL, NULL);
call = gabble_muc_channel_get_call (muc);
@@ -1758,6 +1840,27 @@ error:
return FALSE;
}
+typedef gboolean (*ChannelTypeHandlerFunc) (
+ GabbleMucFactory *self,
+ gpointer request_token,
+ GHashTable *request_properties,
+ gboolean require_new,
+ TpHandle room,
+ GError **error);
+
+typedef struct {
+ const gchar *channel_type;
+ ChannelTypeHandlerFunc f;
+} ChannelTypeHandler;
+
+static ChannelTypeHandler channel_type_handlers[] = {
+ { TP_IFACE_CHANNEL_TYPE_TEXT, handle_text_channel_request },
+ { TP_IFACE_CHANNEL_TYPE_TUBES, handle_tubes_channel_request },
+ { TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, handle_stream_tube_channel_request },
+ { TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, handle_dbus_tube_channel_request },
+ { TPY_IFACE_CHANNEL_TYPE_CALL, handle_call_channel_request },
+ { NULL }
+};
static gboolean
gabble_muc_factory_request (GabbleMucFactory *self,
@@ -1768,80 +1871,57 @@ gabble_muc_factory_request (GabbleMucFactory *self,
GError *error = NULL;
TpHandleType handle_type;
TpHandle handle;
- gboolean conference;
+ gboolean conference, room;
const gchar *channel_type;
+ ChannelTypeHandler *h;
handle_type = tp_asv_get_uint32 (request_properties,
- TP_IFACE_CHANNEL ".TargetHandleType", NULL);
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL);
channel_type = tp_asv_get_string (request_properties,
- TP_IFACE_CHANNEL ".ChannelType");
+ TP_PROP_CHANNEL_CHANNEL_TYPE);
/* Conference channels can be anonymous (HandleTypeNone) */
conference = (handle_type == TP_HANDLE_TYPE_NONE &&
!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT) &&
(g_hash_table_lookup (request_properties,
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialChannels") ||
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS) ||
g_hash_table_lookup (request_properties,
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeHandles") ||
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_HANDLES) ||
g_hash_table_lookup (request_properties,
- TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeIDs")));
+ TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS)));
- /* the channel must either be a room, or a new conference */
- if (handle_type != TP_HANDLE_TYPE_ROOM && !conference)
- return FALSE;
+ room = (handle_type == TP_HANDLE_TYPE_NONE
+ && !tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)
+ && g_hash_table_lookup (request_properties,
+ TP_PROP_CHANNEL_INTERFACE_ROOM_ROOM_NAME));
- if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT) &&
- tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES) &&
- tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE) &&
- tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE) &&
- tp_strdiff (channel_type, TPY_IFACE_CHANNEL_TYPE_CALL))
+ /* the channel must either be a room, or a new conference */
+ if (handle_type != TP_HANDLE_TYPE_ROOM && !conference && !room)
return FALSE;
/* validity already checked by TpBaseConnection */
handle = tp_asv_get_uint32 (request_properties,
- TP_IFACE_CHANNEL ".TargetHandle", NULL);
- g_assert (conference || handle != 0);
+ TP_PROP_CHANNEL_TARGET_HANDLE, NULL);
+ g_assert (conference || room || handle != 0);
- if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT))
+ for (h = channel_type_handlers; h->channel_type != NULL; h++)
{
- if (handle_text_channel_request (self, request_token,
- request_properties, require_new, handle, &error))
- return TRUE;
- }
- else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES))
- {
- if (handle_tubes_channel_request (self, request_token,
- request_properties, require_new, handle, &error))
- return TRUE;
- }
- else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
- {
- if (handle_stream_tube_channel_request (self, request_token,
- request_properties, require_new, handle, &error))
- return TRUE;
- }
- else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE))
- {
- if (handle_dbus_tube_channel_request (self, request_token,
- request_properties, require_new, handle, &error))
- return TRUE;
- }
- else if (!tp_strdiff (channel_type, TPY_IFACE_CHANNEL_TYPE_CALL))
- {
- if (handle_call_channel_request (self, request_token,
- request_properties, require_new, handle, &error))
- return TRUE;
- }
- else
- {
- g_assert_not_reached ();
+ if (tp_strdiff (channel_type, h->channel_type))
+ continue;
+
+ if (!h->f (self, request_token, request_properties, require_new,
+ handle, &error))
+ {
+ tp_channel_manager_emit_request_failed (self, request_token,
+ error->domain, error->code, error->message);
+ g_error_free (error);
+ }
+
+ /* We've handled the request one way or another. */
+ return TRUE;
}
- /* Something failed */
- tp_channel_manager_emit_request_failed (self, request_token,
- error->domain, error->code, error->message);
- g_error_free (error);
- return TRUE;
+ return FALSE;
}
@@ -1884,7 +1964,7 @@ gboolean
gabble_muc_factory_handle_jingle_session (GabbleMucFactory *self,
GabbleJingleSession *session)
{
- GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self);
+ GabbleMucFactoryPrivate *priv = self->priv;
TpHandleRepoIface *room_repo = tp_base_connection_get_handles (
(TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM);
TpHandle room;
diff --git a/src/namespaces.h b/src/namespaces.h
index 08b00c6b4..4f8415c84 100644
--- a/src/namespaces.h
+++ b/src/namespaces.h
@@ -84,6 +84,7 @@
/* Jingle ICE-UDP transport */
#define NS_JINGLE_TRANSPORT_ICEUDP "urn:xmpp:jingle:transports:ice-udp:1"
+#define NS_LAST "jabber:iq:last"
#define NS_MUC "http://jabber.org/protocol/muc"
#define NS_MUC_BYTESTREAM "http://telepathy.freedesktop.org/xmpp/protocol/muc-bytestream"
#define NS_MUC_USER "http://jabber.org/protocol/muc#user"
@@ -117,8 +118,20 @@
#define NS_VERSION "jabber:iq:version"
#define NS_GEOLOC "http://jabber.org/protocol/geoloc"
#define NS_GOOGLE_MAIL_NOTIFY "google:mail:notify"
+#define NS_GOOGLE_SETTING "google:setting"
#define NS_TEMPPRES "urn:xmpp:temppres:0"
#define NS_GOOGLE_SHARED_STATUS "google:shared-status"
+/* This is used by the extension Facebook uses to push you messages you send
+ * using other devices (or the website).
+ *
+ * http://www.youtube.com/watch?v=rSnXE2791yg is not the song I was looking
+ * for, but it's not bad.
+ */
+#define NS_FACEBOOK_MESSAGES "http://www.facebook.com/xmpp/messages"
+
+#define NS_TP_FT_METADATA_SERVICE "http://telepathy.freedesktop.org/xmpp/file-transfer-service"
+#define NS_TP_FT_METADATA "http://telepathy.freedesktop.org/xmpp/file-transfer-metadata"
+
#endif /* __GABBLE_NAMESPACES__H__ */
diff --git a/src/olpc-activity.c b/src/olpc-activity.c
index 3bfb965af..7d8028e6d 100644
--- a/src/olpc-activity.c
+++ b/src/olpc-activity.c
@@ -95,7 +95,7 @@ gabble_olpc_activity_finalize (GObject *object)
if (self->properties != NULL)
{
- g_hash_table_destroy (self->properties);
+ g_hash_table_unref (self->properties);
self->properties = NULL;
}
@@ -154,7 +154,7 @@ gabble_olpc_activity_set_property (GObject *object,
break;
case PROP_PROPERTIES:
if (self->properties != NULL)
- g_hash_table_destroy (self->properties);
+ g_hash_table_unref (self->properties);
self->properties = g_value_get_boxed (value);
break;
diff --git a/src/plugin-loader.c b/src/plugin-loader.c
index ec0aa24a1..2568ff5db 100644
--- a/src/plugin-loader.c
+++ b/src/plugin-loader.c
@@ -108,7 +108,9 @@ static void
gabble_plugin_loader_probe (GabblePluginLoader *self)
{
GError *error = NULL;
- const gchar *directory_name = g_getenv ("GABBLE_PLUGIN_DIR");
+ const gchar *directory_names = g_getenv ("GABBLE_PLUGIN_DIR");
+ gchar **dir_array;
+ gchar **ptr;
GDir *d;
const gchar *file;
@@ -118,32 +120,43 @@ gabble_plugin_loader_probe (GabblePluginLoader *self)
return;
}
- if (directory_name == NULL)
- directory_name = PLUGIN_DIR;
+ if (directory_names == NULL)
+ directory_names = PLUGIN_DIR;
- DEBUG ("probing %s", directory_name);
- d = g_dir_open (directory_name, 0, &error);
+#ifdef G_OS_WIN32
+ dir_array = g_strsplit (directory_names, ";", 0);
+#else
+ dir_array = g_strsplit (directory_names, ":", 0);
+#endif
- if (d == NULL)
+ for (ptr = dir_array ; *ptr != NULL ; ptr++)
{
- DEBUG ("%s", error->message);
- g_error_free (error);
- return;
- }
+ DEBUG ("probing %s", *ptr);
+ d = g_dir_open (*ptr, 0, &error);
- while ((file = g_dir_read_name (d)) != NULL)
- {
- gchar *path;
+ if (d == NULL)
+ {
+ DEBUG ("%s", error->message);
+ g_error_free (error);
+ continue;
+ }
- if (!g_str_has_suffix (file, G_MODULE_SUFFIX))
- continue;
+ while ((file = g_dir_read_name (d)) != NULL)
+ {
+ gchar *path;
+
+ if (!g_str_has_suffix (file, G_MODULE_SUFFIX))
+ continue;
+
+ path = g_build_filename (*ptr, file, NULL);
+ plugin_loader_try_to_load (self, path);
+ g_free (path);
+ }
- path = g_build_filename (directory_name, file, NULL);
- plugin_loader_try_to_load (self, path);
- g_free (path);
+ g_dir_close (d);
}
- g_dir_close (d);
+ g_strfreev (dir_array);
}
#endif
@@ -386,7 +399,7 @@ gabble_plugin_loader_create_channel_managers (
continue;
g_ptr_array_foreach (managers, copy_to_other_array, out);
- g_ptr_array_free (managers, TRUE);
+ g_ptr_array_unref (managers);
}
return out;
diff --git a/src/presence-cache.c b/src/presence-cache.c
index 484cb5f60..1e8cbcc0f 100644
--- a/src/presence-cache.c
+++ b/src/presence-cache.c
@@ -40,11 +40,14 @@
#include <wocky/wocky-caps-cache.h>
#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"
@@ -250,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);
@@ -292,6 +313,8 @@ capability_info_recvd (GabblePresenceCache *cache,
info->client_types = client_types;
+ replace_data_forms (info, data_forms);
+
return info->trust;
}
@@ -464,7 +487,7 @@ gabble_presence_cache_init (GabblePresenceCache *cache)
decloak_context_free);
priv->location = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
- (GDestroyNotify) g_hash_table_destroy);
+ (GDestroyNotify) g_hash_table_unref);
}
static void gabble_presence_cache_add_bundle_caps (GabblePresenceCache *cache,
@@ -477,7 +500,9 @@ gabble_presence_cache_add_bundles (GabblePresenceCache *cache)
gabble_presence_cache_add_bundle_caps (cache, \
"http://www.google.com/xmpp/client/caps#" cap, features); \
gabble_presence_cache_add_bundle_caps (cache, \
- "http://talk.google.com/xmpp/client/caps#" cap, features);
+ "http://talk.google.com/xmpp/client/caps#" cap, features); \
+ gabble_presence_cache_add_bundle_caps (cache, \
+ "http://www.android.com/gtalk/client/caps#" cap, features);
/* Cache various bundle from the Google Talk clients as trusted. Some old
* versions of Google Talk do not reply correctly to discovery requests.
@@ -560,7 +585,7 @@ gabble_presence_cache_dispose (GObject *object)
priv->unsure_id = 0;
}
- tp_clear_pointer (&priv->decloak_requests, g_hash_table_destroy);
+ tp_clear_pointer (&priv->decloak_requests, g_hash_table_unref);
tp_clear_pointer (&priv->decloak_handles, tp_handle_set_destroy);
g_assert (priv->lm_message_cb == NULL);
@@ -568,11 +593,11 @@ gabble_presence_cache_dispose (GObject *object)
g_signal_handler_disconnect (priv->conn, priv->status_changed_cb);
- tp_clear_pointer (&priv->presence, g_hash_table_destroy);
- tp_clear_pointer (&priv->capabilities, g_hash_table_destroy);
- tp_clear_pointer (&priv->disco_pending, g_hash_table_destroy);
+ tp_clear_pointer (&priv->presence, g_hash_table_unref);
+ tp_clear_pointer (&priv->capabilities, g_hash_table_unref);
+ tp_clear_pointer (&priv->disco_pending, g_hash_table_unref);
tp_clear_pointer (&priv->presence_handles, tp_handle_set_destroy);
- tp_clear_pointer (&priv->location, g_hash_table_destroy);
+ tp_clear_pointer (&priv->location, g_hash_table_unref);
if (G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose)
G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose (object);
@@ -1008,7 +1033,8 @@ _parse_node (GabblePresence *presence,
DEBUG ("Client is Google Web Client");
gabble_capability_set_add (cap_set, QUIRK_GOOGLE_WEBMAIL_CLIENT);
- gabble_presence_set_capabilities (presence, resource, cap_set, serial);
+ gabble_capability_set_add (cap_set, QUIRK_OMITS_CONTENT_CREATORS);
+ gabble_presence_set_capabilities (presence, resource, cap_set, NULL, serial);
gabble_capability_set_free (cap_set);
}
}
@@ -1138,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)
{
@@ -1154,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);
@@ -1218,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)
@@ -1227,7 +1277,7 @@ _signal_presences_updated (GabblePresenceCache *cache,
handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1);
g_array_append_val (handles, handle);
g_signal_emit (cache, signals[PRESENCES_UPDATED], 0, handles);
- g_array_free (handles, TRUE);
+ g_array_unref (handles);
}
static void
@@ -1253,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;
@@ -1286,7 +1337,7 @@ _caps_disco_cb (GabbleDisco *disco,
}
/* If tp_handle_ensure () was happy with the jid, it's valid. */
- jid_is_valid = gabble_decode_jid (jid, NULL, NULL, &resource);
+ jid_is_valid = wocky_decode_jid (jid, NULL, NULL, &resource);
g_assert (jid_is_valid);
waiter_self = find_matching_waiter (waiters, handle, resource);
g_free (resource);
@@ -1301,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
@@ -1313,10 +1365,16 @@ _caps_disco_cb (GabbleDisco *disco,
computed_hash = wocky_caps_hash_compute_from_node (query_result);
- if (g_str_equal (waiter_self->ver, computed_hash))
+ if (computed_hash == NULL)
+ {
+ DEBUG ("Unable to compute caps hash for '%s'.", jid);
+ trust = 0;
+ bad_hash = TRUE;
+ }
+ 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
{
@@ -1331,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
@@ -1371,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);
}
@@ -1399,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);
@@ -1425,6 +1486,7 @@ _caps_disco_cb (GabbleDisco *disco,
}
gabble_capability_set_free (cap_set);
+ g_ptr_array_unref (data_forms);
OUT:
if (handle)
@@ -1491,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. */
@@ -1786,6 +1848,16 @@ gabble_presence_parse_presence_message (GabblePresenceCache *cache,
return ret;
}
+/* FIXME: this scrapes nicknames out of <messages>, and relies on im-channel.c
+ * setting keep_unavailable back to FALSE to make nicknames random peers send
+ * us disappear once we close the accompanying messages. As a side effect, it
+ * makes specifying <nick> in MUC messages work, which is questionable
+ * behaviour. See vcard/test-alias-message.py.
+ *
+ * It would be cleaner to make the IM channel stash the nickname if we want it
+ * to go away when the channel closes, rather than relying on this
+ * spooky-action-at-a-distance.
+ */
static LmHandlerResult
_parse_message_message (GabblePresenceCache *cache,
TpHandle handle,
@@ -1918,7 +1990,8 @@ gabble_presence_cache_maybe_remove (
if (NULL == presence)
return;
- if (presence->status == GABBLE_PRESENCE_OFFLINE &&
+ if ((presence->status == GABBLE_PRESENCE_OFFLINE ||
+ presence->status == GABBLE_PRESENCE_UNKNOWN) &&
presence->status_message == NULL &&
!presence->keep_unavailable)
{
@@ -1956,20 +2029,28 @@ gabble_presence_cache_do_update (
gboolean *update_client_types)
{
GabblePresenceCachePrivate *priv = cache->priv;
- TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
- (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT);
- const gchar *jid;
GabblePresence *presence;
GabbleCapabilitySet *old_cap_set;
const GabbleCapabilitySet *new_cap_set;
gboolean ret = FALSE;
- jid = tp_handle_inspect (contact_repo, handle);
- DEBUG ("%s (%d) resource %s prio %d presence %d message \"%s\"",
- jid, handle,
- resource == NULL ? "<null>" : resource,
- priority, presence_id,
- status_message == NULL ? "<null>" : status_message);
+ if (DEBUGGING)
+ {
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT);
+ const gchar *jid = tp_handle_inspect (contact_repo, handle);
+ const gchar *presence_name = wocky_enum_to_nick (
+ GABBLE_TYPE_PRESENCE_ID, presence_id);
+
+ if (presence_name == NULL)
+ presence_name = "plugin-specific, not an element of GabblePresenceId";
+
+ DEBUG ("%s (%d) resource %s prio %d presence %d (%s) message \"%s\"",
+ jid, handle,
+ resource == NULL ? "<null>" : resource,
+ priority, presence_id, presence_name,
+ status_message == NULL ? "<null>" : status_message);
+ }
presence = gabble_presence_cache_get (cache, handle);
@@ -2045,7 +2126,7 @@ gabble_presence_cache_update_many (
if (updated->len > 0)
g_signal_emit (cache, signals[PRESENCES_UPDATED], 0, updated);
- g_array_free (updated, TRUE);
+ g_array_unref (updated);
for (i = 0 ; i < contact_handles->len ; i++)
{
@@ -2081,7 +2162,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);
@@ -2116,6 +2198,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:
@@ -2210,8 +2294,8 @@ gabble_presence_cache_contacts_added_to_olpc_view (GabblePresenceCache *self,
g_signal_emit (self, signals[PRESENCES_UPDATED], 0, changed);
}
- g_array_free (tmp, TRUE);
- g_array_free (changed, TRUE);
+ g_array_unref (tmp);
+ g_array_unref (changed);
}
void
@@ -2251,8 +2335,8 @@ gabble_presence_cache_contacts_removed_from_olpc_view (
g_signal_emit (self, signals[PRESENCES_UPDATED], 0, changed);
}
- g_array_free (tmp, TRUE);
- g_array_free (changed, TRUE);
+ g_array_unref (tmp);
+ g_array_unref (changed);
}
static gboolean
@@ -2478,3 +2562,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 44af23fb0..0e5283967 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 41c7c7b7d..9f1a6bfaf 100644
--- a/src/presence.c
+++ b/src/presence.c
@@ -23,8 +23,10 @@
#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"
@@ -35,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;
@@ -43,6 +50,7 @@ struct _Resource {
gchar *name;
guint client_type;
GabbleCapabilitySet *cap_set;
+ GPtrArray *data_forms;
guint caps_serial;
GabblePresenceId status;
gchar *status_message;
@@ -57,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;
@@ -71,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;
@@ -86,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);
}
@@ -102,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);
@@ -127,7 +142,11 @@ 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;
}
GabblePresence *
@@ -195,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)
{
@@ -263,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;
@@ -285,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;
}
@@ -312,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)
@@ -319,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 *
@@ -636,13 +685,19 @@ gabble_presence_dump (GabblePresence *presence)
GString *ret = g_string_new ("");
gchar *tmp;
GabblePresencePrivate *priv = presence->priv;
+ const gchar *presence_name = wocky_enum_to_nick (GABBLE_TYPE_PRESENCE_ID,
+ presence->status);
+
+ if (presence_name == NULL)
+ presence_name = "plugin-specific, not an element of GabblePresenceId";
g_string_append_printf (ret,
"nickname: %s\n"
- "accumulated status: %d\n"
+ "accumulated status: %d (%s)\n"
"accumulated status msg: %s\n"
"kept while unavailable: %d\n",
- presence->nickname, presence->status,
+ presence->nickname,
+ presence->status, presence_name,
presence->status_message,
presence->keep_unavailable);
@@ -846,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 4f9126d09..50d6945a5 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 00d05324e..fc8e3ea6f 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"
@@ -92,14 +91,14 @@ struct _GabblePrivateTubesFactoryPrivate
#define GABBLE_PRIVATE_TUBES_FACTORY_GET_PRIVATE(obj) ((obj)->priv)
static const gchar * const tubes_channel_fixed_properties[] = {
- TP_IFACE_CHANNEL ".ChannelType",
- TP_IFACE_CHANNEL ".TargetHandleType",
+ TP_PROP_CHANNEL_CHANNEL_TYPE,
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
NULL
};
static const gchar * const old_tubes_channel_allowed_properties[] = {
- TP_IFACE_CHANNEL ".TargetHandle",
- TP_IFACE_CHANNEL ".TargetID",
+ TP_PROP_CHANNEL_TARGET_HANDLE,
+ TP_PROP_CHANNEL_TARGET_ID,
NULL
};
@@ -373,7 +372,7 @@ gabble_private_tubes_factory_close_all (GabblePrivateTubesFactory *fac)
/* Use a temporary variable (the macro does this) because we don't want
* tubes_channel_closed_cb to remove the channel from the hash table a
* second time */
- tp_clear_pointer (&priv->tubes_channels, g_hash_table_destroy);
+ tp_clear_pointer (&priv->tubes_channels, g_hash_table_unref);
}
static void
@@ -388,8 +387,8 @@ add_service_to_array (const gchar *service,
GValue *target_handle_type_value;
gchar *tube_allowed_properties[] =
{
- TP_IFACE_CHANNEL ".TargetHandle",
- TP_IFACE_CHANNEL ".TargetID",
+ TP_PROP_CHANNEL_TARGET_HANDLE,
+ TP_PROP_CHANNEL_TARGET_ID,
NULL
};
@@ -410,23 +409,23 @@ add_service_to_array (const gchar *service,
else
g_value_set_static_string (channel_type_value,
TP_IFACE_CHANNEL_TYPE_DBUS_TUBE);
- g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".ChannelType",
+ g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_CHANNEL_TYPE,
channel_type_value);
target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT);
g_hash_table_insert (fixed_properties,
- TP_IFACE_CHANNEL ".TargetHandleType", target_handle_type_value);
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, target_handle_type_value);
target_handle_type_value = tp_g_value_slice_new (G_TYPE_STRING);
g_value_set_string (target_handle_type_value, service);
if (type == TP_TUBE_TYPE_STREAM)
g_hash_table_insert (fixed_properties,
- TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service",
+ TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE,
target_handle_type_value);
else
g_hash_table_insert (fixed_properties,
- TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName",
+ TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME,
target_handle_type_value);
dbus_g_type_struct_set (&monster,
@@ -434,7 +433,7 @@ add_service_to_array (const gchar *service,
1, tube_allowed_properties,
G_MAXUINT);
- g_hash_table_destroy (fixed_properties);
+ g_hash_table_unref (fixed_properties);
g_ptr_array_add (arr, g_value_get_boxed (&monster));
}
@@ -460,20 +459,20 @@ add_generic_tube_caps (GPtrArray *arr)
g_value_set_static_string (channel_type_value,
TP_IFACE_CHANNEL_TYPE_STREAM_TUBE);
- g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".ChannelType",
+ g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_CHANNEL_TYPE,
channel_type_value);
target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT);
g_hash_table_insert (fixed_properties,
- TP_IFACE_CHANNEL ".TargetHandleType", target_handle_type_value);
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, target_handle_type_value);
dbus_g_type_struct_set (&monster1,
0, fixed_properties,
1, gabble_tube_stream_channel_get_allowed_properties (),
G_MAXUINT);
- g_hash_table_destroy (fixed_properties);
+ g_hash_table_unref (fixed_properties);
g_ptr_array_add (arr, g_value_get_boxed (&monster1));
/* DBusTube */
@@ -489,20 +488,20 @@ add_generic_tube_caps (GPtrArray *arr)
g_value_set_static_string (channel_type_value,
TP_IFACE_CHANNEL_TYPE_DBUS_TUBE);
- g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL ".ChannelType",
+ g_hash_table_insert (fixed_properties, TP_PROP_CHANNEL_CHANNEL_TYPE,
channel_type_value);
target_handle_type_value = tp_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (target_handle_type_value, TP_HANDLE_TYPE_CONTACT);
g_hash_table_insert (fixed_properties,
- TP_IFACE_CHANNEL ".TargetHandleType", target_handle_type_value);
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, target_handle_type_value);
dbus_g_type_struct_set (&monster2,
0, fixed_properties,
1, gabble_tube_dbus_channel_get_allowed_properties (),
G_MAXUINT);
- g_hash_table_destroy (fixed_properties);
+ g_hash_table_unref (fixed_properties);
g_ptr_array_add (arr, g_value_get_boxed (&monster2));
}
@@ -563,8 +562,13 @@ gabble_private_tubes_factory_add_cap (GabbleCapsChannelManager *manager,
const gchar *channel_type, *service;
gchar *ns = NULL;
+ /* capabilities mean being able to RECEIVE said kinds of tubes. hence,
+ * skip Requested=true (locally initiated) channel classes */
+ if (tp_asv_get_boolean (cap, TP_PROP_CHANNEL_REQUESTED, FALSE))
+ return;
+
channel_type = tp_asv_get_string (cap,
- TP_IFACE_CHANNEL ".ChannelType");
+ TP_PROP_CHANNEL_CHANNEL_TYPE);
/* this channel is not for this factory */
if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES) &&
@@ -573,13 +577,13 @@ gabble_private_tubes_factory_add_cap (GabbleCapsChannelManager *manager,
return;
if (tp_asv_get_uint32 (cap,
- TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_CONTACT)
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT)
return;
if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE))
{
service = tp_asv_get_string (cap,
- TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service");
+ TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE);
if (service != NULL)
ns = g_strconcat (STREAM_CAP_PREFIX, service, NULL);
@@ -587,7 +591,7 @@ gabble_private_tubes_factory_add_cap (GabbleCapsChannelManager *manager,
else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE))
{
service = tp_asv_get_string (cap,
- TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName");
+ TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME);
if (service != NULL)
ns = g_strconcat (DBUS_CAP_PREFIX, service, NULL);
@@ -607,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;
@@ -805,17 +810,17 @@ gabble_private_tubes_factory_type_foreach_channel_class (GType type,
value = tp_g_value_slice_new (G_TYPE_STRING);
g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TUBES);
- g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType",
+ g_hash_table_insert (table, TP_PROP_CHANNEL_CHANNEL_TYPE,
value);
value = tp_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
- g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
+ g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
value);
func (type, table, old_tubes_channel_allowed_properties, user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
/* 1-1 Channel.Type.StreamTube */
table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
@@ -823,18 +828,18 @@ gabble_private_tubes_factory_type_foreach_channel_class (GType type,
value = tp_g_value_slice_new (G_TYPE_STRING);
g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE);
- g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType",
+ g_hash_table_insert (table, TP_PROP_CHANNEL_CHANNEL_TYPE,
value);
value = tp_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
- g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
+ g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
value);
func (type, table, gabble_tube_stream_channel_get_allowed_properties (),
user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
/* 1-1 Channel.Type.DBusTube */
table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
@@ -842,18 +847,18 @@ gabble_private_tubes_factory_type_foreach_channel_class (GType type,
value = tp_g_value_slice_new (G_TYPE_STRING);
g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE);
- g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType",
+ g_hash_table_insert (table, TP_PROP_CHANNEL_CHANNEL_TYPE,
value);
value = tp_g_value_slice_new (G_TYPE_UINT);
g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT);
- g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType",
+ g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
value);
func (type, table, gabble_tube_dbus_channel_get_allowed_properties (),
user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
}
@@ -870,11 +875,11 @@ gabble_private_tubes_factory_requestotron (GabblePrivateTubesFactory *self,
GabbleTubesChannel *channel;
if (tp_asv_get_uint32 (request_properties,
- TP_IFACE_CHANNEL ".TargetHandleType", NULL) != TP_HANDLE_TYPE_CONTACT)
+ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT)
return FALSE;
channel_type = tp_asv_get_string (request_properties,
- TP_IFACE_CHANNEL ".ChannelType");
+ TP_PROP_CHANNEL_CHANNEL_TYPE);
if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES) &&
tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE) &&
@@ -901,12 +906,12 @@ gabble_private_tubes_factory_requestotron (GabblePrivateTubesFactory *self,
/* "Service" is a mandatory, not-fixed property */
service = tp_asv_get_string (request_properties,
- TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service");
+ TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE);
if (service == NULL)
{
g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
"Request does not contain the mandatory property '%s'",
- TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service");
+ TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE);
goto error;
}
}
@@ -923,12 +928,12 @@ gabble_private_tubes_factory_requestotron (GabblePrivateTubesFactory *self,
/* "ServiceName" is a mandatory, not-fixed property */
service = tp_asv_get_string (request_properties,
- TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName");
+ TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME);
if (service == NULL)
{
g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
"Request does not contain the mandatory property '%s'",
- TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName");
+ TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME);
goto error;
}
@@ -944,7 +949,7 @@ gabble_private_tubes_factory_requestotron (GabblePrivateTubesFactory *self,
/* validity already checked by TpBaseConnection */
handle = tp_asv_get_uint32 (request_properties,
- TP_IFACE_CHANNEL ".TargetHandle", NULL);
+ TP_PROP_CHANNEL_TARGET_HANDLE, NULL);
g_assert (handle != 0);
/* Don't support opening a channel to our self handle */
@@ -1014,7 +1019,7 @@ gabble_private_tubes_factory_requestotron (GabblePrivateTubesFactory *self,
g_hash_table_insert (channels, new_channel, request_tokens);
tp_channel_manager_emit_new_channels (self, channels);
- g_hash_table_destroy (channels);
+ g_hash_table_unref (channels);
g_slist_free (request_tokens);
}
else
diff --git a/src/protocol.c b/src/protocol.c
index a1b98c1d6..a6e343188 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -19,11 +19,15 @@
#include "protocol.h"
+#include <string.h>
#include <telepathy-glib/base-connection-manager.h>
#include <dbus/dbus-protocol.h>
#include <dbus/dbus-glib.h>
+#include "extensions/extensions.h"
+
#include "conn-presence.h"
+
#include "connection.h"
#include "connection-manager.h"
#include "im-factory.h"
@@ -32,15 +36,19 @@
#include "roomlist-manager.h"
#include "search-manager.h"
#include "util.h"
+#include "addressing-util.h"
#define PROTOCOL_NAME "jabber"
#define ICON_NAME "im-" PROTOCOL_NAME
#define VCARD_FIELD_NAME "x-" PROTOCOL_NAME
#define ENGLISH_NAME "Jabber"
-G_DEFINE_TYPE (GabbleJabberProtocol,
- gabble_jabber_protocol,
- TP_TYPE_BASE_PROTOCOL)
+static void addressing_iface_init (TpProtocolAddressingInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GabbleJabberProtocol, gabble_jabber_protocol,
+ TP_TYPE_BASE_PROTOCOL,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_PROTOCOL_ADDRESSING, addressing_iface_init);
+ )
static TpCMParamSpec jabber_params[] = {
{ "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING,
@@ -76,7 +84,7 @@ static TpCMParamSpec jabber_params[] = {
0 /* unused */, NULL, NULL },
{ "require-encryption", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN,
- TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE),
+ TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(TRUE),
0 /* unused */, NULL, NULL },
{ "register", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN,
@@ -149,7 +157,9 @@ static TpCMParamSpec jabber_params[] = {
{ GABBLE_PROP_CONNECTION_INTERFACE_GABBLE_DECLOAK_DECLOAK_AUTOMATICALLY,
DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN,
- TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER (FALSE),
+ TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT |
+ TP_CONN_MGR_PARAM_FLAG_DBUS_PROPERTY,
+ GINT_TO_POINTER (TRUE),
0 /* unused */, NULL, NULL },
{ "fallback-servers", "as", 0,
@@ -301,6 +311,7 @@ get_interfaces (TpBaseProtocol *self)
{
const gchar * const interfaces[] = {
TP_IFACE_PROTOCOL_INTERFACE_PRESENCE,
+ TP_IFACE_PROTOCOL_INTERFACE_ADDRESSING,
NULL };
return g_strdupv ((GStrv) interfaces);
@@ -329,7 +340,9 @@ get_connection_details (TpBaseProtocol *self,
if (channel_managers != NULL)
{
GType types[] = {
+#ifdef ENABLE_FILE_TRANSFER
GABBLE_TYPE_FT_MANAGER,
+#endif
GABBLE_TYPE_IM_FACTORY,
GABBLE_TYPE_MEDIA_FACTORY,
GABBLE_TYPE_MUC_FACTORY,
@@ -368,6 +381,59 @@ dup_authentication_types (TpBaseProtocol *self)
return g_strdupv ((GStrv) types);
}
+static GStrv
+dup_supported_uri_schemes (TpBaseProtocol *self)
+{
+ return g_strdupv ((gchar **) gabble_get_addressable_uri_schemes ());
+}
+
+static GStrv
+dup_supported_vcard_fields (TpBaseProtocol *self)
+{
+ return g_strdupv ((gchar **) gabble_get_addressable_vcard_fields ());
+}
+
+static gchar *
+addressing_normalize_vcard_address (TpBaseProtocol *self,
+ const gchar *vcard_field,
+ const gchar *vcard_address,
+ GError **error)
+{
+ gchar *normalized_address = gabble_normalize_vcard_address (vcard_field, vcard_address, error);
+
+ if (normalized_address == NULL)
+ {
+ /* InvalidHandle makes no sense in Protocol */
+ if (error != NULL && g_error_matches (*error, TP_ERROR, TP_ERROR_INVALID_HANDLE))
+ {
+ (*error)->code = TP_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ return normalized_address;
+}
+
+static gchar *
+addressing_normalize_contact_uri (TpBaseProtocol *self,
+ const gchar *uri,
+ GError **error)
+{
+ gchar *normalized_address = NULL;
+
+ normalized_address = gabble_normalize_contact_uri (uri, error);
+
+ if (normalized_address == NULL)
+ {
+ /* InvalidHandle makes no sense in Protocol */
+ if (error != NULL && g_error_matches (*error, TP_ERROR, TP_ERROR_INVALID_HANDLE))
+ {
+ (*error)->code = TP_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ return normalized_address;
+}
+
static void
gabble_jabber_protocol_class_init (GabbleJabberProtocolClass *klass)
{
@@ -392,3 +458,11 @@ gabble_jabber_protocol_new (void)
NULL);
}
+static void
+addressing_iface_init (TpProtocolAddressingInterface *iface)
+{
+ iface->dup_supported_vcard_fields = dup_supported_vcard_fields;
+ iface->dup_supported_uri_schemes = dup_supported_uri_schemes;
+ iface->normalize_vcard_address = addressing_normalize_vcard_address;
+ iface->normalize_contact_uri = addressing_normalize_contact_uri;
+}
diff --git a/src/room-config.c b/src/room-config.c
new file mode 100644
index 000000000..6345fc767
--- /dev/null
+++ b/src/room-config.c
@@ -0,0 +1,101 @@
+/*
+ * room-config.c - Channel.Interface.RoomConfig1 implementation
+ * Copyright ©2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "room-config.h"
+#include "muc-channel.h"
+
+#define DEBUG_FLAG GABBLE_DEBUG_MUC
+#include "debug.h"
+
+static void gabble_room_config_update_configuration_async (
+ TpBaseRoomConfig *base_config,
+ GHashTable *validated_properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+struct _GabbleRoomConfigPrivate {
+ gpointer hi_dere;
+};
+
+G_DEFINE_TYPE (GabbleRoomConfig, gabble_room_config, TP_TYPE_BASE_ROOM_CONFIG)
+
+static void
+gabble_room_config_init (GabbleRoomConfig *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GABBLE_TYPE_ROOM_CONFIG,
+ GabbleRoomConfigPrivate);
+}
+
+static void
+gabble_room_config_class_init (GabbleRoomConfigClass *klass)
+{
+ TpBaseRoomConfigClass *parent_class = TP_BASE_ROOM_CONFIG_CLASS (klass);
+
+ parent_class->update_async = gabble_room_config_update_configuration_async;
+ g_type_class_add_private (klass, sizeof (GabbleRoomConfigPrivate));
+}
+
+GabbleRoomConfig *
+gabble_room_config_new (
+ TpBaseChannel *channel)
+{
+ g_return_val_if_fail (TP_IS_BASE_CHANNEL (channel), NULL);
+
+ return g_object_new (GABBLE_TYPE_ROOM_CONFIG,
+ "channel", channel,
+ NULL);
+}
+
+static void
+updated_configuration_cb (
+ GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GabbleMucChannel *channel = GABBLE_MUC_CHANNEL (source);
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+ GError *error = NULL;
+
+ if (!gabble_muc_channel_update_configuration_finish (channel, result, &error))
+ {
+ g_simple_async_result_set_from_error (simple, error);
+ g_clear_error (&error);
+ }
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+gabble_room_config_update_configuration_async (
+ TpBaseRoomConfig *base_config,
+ GHashTable *validated_properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ TpBaseChannel *base_channel = tp_base_room_config_dup_channel (base_config);
+ GSimpleAsyncResult *simple = g_simple_async_result_new (
+ G_OBJECT (base_config), callback, user_data,
+ gabble_room_config_update_configuration_async);
+
+ gabble_muc_channel_update_configuration_async (
+ GABBLE_MUC_CHANNEL (base_channel), validated_properties,
+ updated_configuration_cb, simple);
+ g_object_unref (base_channel);
+}
diff --git a/src/room-config.h b/src/room-config.h
new file mode 100644
index 000000000..a2981c186
--- /dev/null
+++ b/src/room-config.h
@@ -0,0 +1,61 @@
+/*
+ * room-config.h - header for Channel.I.RoomConfig1 implementation
+ * Copyright ©2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef GABBLE_ROOM_CONFIG_H
+#define GABBLE_ROOM_CONFIG_H
+
+#include <glib-object.h>
+#include <telepathy-glib/base-room-config.h>
+
+typedef struct _GabbleRoomConfig GabbleRoomConfig;
+typedef struct _GabbleRoomConfigClass GabbleRoomConfigClass;
+typedef struct _GabbleRoomConfigPrivate GabbleRoomConfigPrivate;
+
+struct _GabbleRoomConfigClass {
+ TpBaseRoomConfigClass parent_class;
+};
+
+struct _GabbleRoomConfig {
+ TpBaseRoomConfig parent;
+
+ GabbleRoomConfigPrivate *priv;
+};
+
+GabbleRoomConfig *gabble_room_config_new (
+ TpBaseChannel *channel);
+
+/* TYPE MACROS */
+GType gabble_room_config_get_type (void);
+
+#define GABBLE_TYPE_ROOM_CONFIG \
+ (gabble_room_config_get_type ())
+#define GABBLE_ROOM_CONFIG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_ROOM_CONFIG, GabbleRoomConfig))
+#define GABBLE_ROOM_CONFIG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_ROOM_CONFIG,\
+ GabbleRoomConfigClass))
+#define GABBLE_IS_ROOM_CONFIG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_ROOM_CONFIG))
+#define GABBLE_IS_ROOM_CONFIG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_ROOM_CONFIG))
+#define GABBLE_ROOM_CONFIG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_ROOM_CONFIG, \
+ GabbleRoomConfigClass))
+
+#endif /* GABBLE_ROOM_CONFIG_H */
diff --git a/src/roomlist-channel.c b/src/roomlist-channel.c
index cc1a29500..642c983d1 100644
--- a/src/roomlist-channel.c
+++ b/src/roomlist-channel.c
@@ -231,7 +231,7 @@ gabble_roomlist_channel_dispose (GObject *object)
g_assert (priv->pending_room_signals != NULL);
g_assert (priv->pending_room_signals->len == 0);
- g_ptr_array_free (priv->pending_room_signals, TRUE);
+ g_ptr_array_unref (priv->pending_room_signals);
priv->pending_room_signals = NULL;
if (G_OBJECT_CLASS (gabble_roomlist_channel_parent_class)->dispose)
@@ -429,7 +429,7 @@ room_info_cb (gpointer pipeline, GabbleDiscoItem *item, gpointer user_data)
DEBUG ("adding new room signal data to pending: %s", jid);
g_ptr_array_add (priv->pending_room_signals, g_value_get_boxed (&room));
- g_hash_table_destroy (keys);
+ g_hash_table_unref (keys);
}
static void
diff --git a/src/roomlist-manager.c b/src/roomlist-manager.c
index 76abee688..e3cb09a4a 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"
@@ -113,7 +113,7 @@ gabble_roomlist_manager_close_all (GabbleRoomlistManager *self)
g_object_unref (channel);
}
- g_ptr_array_free (tmp, TRUE);
+ g_ptr_array_unref (tmp);
}
}
@@ -289,7 +289,7 @@ gabble_roomlist_manager_type_foreach_channel_class (GType type,
func (type, table, roomlist_channel_allowed_properties, user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
}
diff --git a/src/roster.c b/src/roster.c
index 075957eaf..fec216bdf 100644
--- a/src/roster.c
+++ b/src/roster.c
@@ -22,8 +22,6 @@
#include "config.h"
#include "roster.h"
-#define DBUS_API_SUBJECT_TO_CHANGE
-
#include <string.h>
#include <dbus/dbus-glib.h>
@@ -37,7 +35,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"
@@ -52,7 +50,7 @@
/* signal enum */
enum
{
- NICKNAME_UPDATE,
+ NICKNAMES_UPDATE,
LAST_SIGNAL
};
@@ -114,8 +112,12 @@ struct _GabbleRosterItemEdit
/* if these are ..._INVALID, that means don't edit */
GabbleRosterSubscription new_subscription;
GoogleItemType new_google_type;
- /* owned by the GabbleRosterItemEdit; if NULL, that means don't edit */
+
+ /* owned by the GabbleRosterItemEdit. If NULL, that means don't edit... */
gchar *new_name;
+ /* if TRUE, disregard new_name and remove name='' from the roster item */
+ gboolean remove_name;
+
TpHandleSet *add_to_groups;
TpHandleSet *remove_from_groups;
gboolean remove_from_all_other_groups;
@@ -234,7 +236,7 @@ gabble_roster_finalize (GObject *object)
DEBUG ("called with %p", object);
g_hash_table_foreach (priv->items, item_handle_unref_foreach, priv);
- g_hash_table_destroy (priv->items);
+ g_hash_table_unref (priv->items);
G_OBJECT_CLASS (gabble_roster_parent_class)->finalize (object);
}
@@ -525,7 +527,8 @@ static GabbleRosterItem *
_gabble_roster_item_update (GabbleRoster *roster,
TpHandle contact_handle,
WockyNode *node,
- gboolean google_roster_mode)
+ gboolean google_roster_mode,
+ gboolean *nickname_updated)
{
GabbleRosterPrivate *priv = roster->priv;
GabbleRosterItem *item;
@@ -572,8 +575,12 @@ _gabble_roster_item_update (GabbleRoster *roster,
DEBUG ("name for contact#%u changed to %s", contact_handle,
name);
- g_signal_emit (G_OBJECT (roster), signals[NICKNAME_UPDATE], 0,
- contact_handle);
+
+ *nickname_updated = TRUE;
+ }
+ else
+ {
+ *nickname_updated = FALSE;
}
new_groups_handle_set = _parse_item_groups (node,
@@ -616,7 +623,7 @@ _gabble_roster_item_update (GabbleRoster *roster,
tp_base_contact_list_groups_created ((TpBaseContactList *) roster,
(const gchar * const *) strv->pdata, strv->len);
- g_ptr_array_free (strv, TRUE);
+ g_ptr_array_unref (strv);
}
tp_clear_pointer (&created_groups, tp_intset_destroy);
@@ -1085,6 +1092,7 @@ process_roster (
TpBaseConnection *conn = (TpBaseConnection *) priv->conn;
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn,
TP_HANDLE_TYPE_CONTACT);
+ GArray *updated_nicknames = g_array_new (FALSE, FALSE, sizeof (TpHandle));
/* asymmetry is because we don't get locally pending subscription
* requests via <roster>, we get it via <presence> */
@@ -1109,6 +1117,7 @@ process_roster (
const char *jid;
TpHandle handle;
GabbleRosterItem *item;
+ gboolean nickname_updated;
handle = validate_roster_item (contact_repo, item_node, &jid);
@@ -1120,7 +1129,7 @@ process_roster (
tp_handle_unref (contact_repo, handle);
item = _gabble_roster_item_update (roster, handle, item_node,
- google_roster);
+ google_roster, &nickname_updated);
#ifdef ENABLE_DEBUG
if (DEBUGGING)
{
@@ -1130,6 +1139,9 @@ process_roster (
}
#endif
+ if (nickname_updated)
+ g_array_append_val (updated_nicknames, handle);
+
/* handle publish list changes */
switch (item->subscription)
{
@@ -1233,12 +1245,17 @@ process_roster (
case GABBLE_ROSTER_SUBSCRIPTION_FROM:
case GABBLE_ROSTER_SUBSCRIPTION_BOTH:
if (google_roster &&
- /* Don't hide contacts from stored if they're remote pending.
- * This works around Google Talk flickering ask="subscribe"
- * when you try to subscribe to someone; see
- * test-google-roster.py.
+ /* Don't hide contacts from stored if they're pending.
+ * This works around two Google Talk issues:
+ * - When you try to subscribe to someone, you get a flickering
+ * ask="subscribe";
+ * - When somebody tries to subscribe to you, you get a presence
+ * with type="subscribe" followed by a roster update with
+ * subscribe="none".
+ * See test-google-roster.py for more details.
*/
item->subscribe != TP_SUBSCRIPTION_STATE_ASK &&
+ item->publish != TP_SUBSCRIPTION_STATE_ASK &&
!_google_roster_item_should_keep (jid, item))
{
tp_handle_set_remove (changed, handle);
@@ -1303,6 +1320,9 @@ process_roster (
_gabble_roster_item_maybe_remove (roster, handle);
}
+ if (updated_nicknames->len > 0)
+ g_signal_emit (roster, signals[NICKNAMES_UPDATE], 0, updated_nicknames);
+
tp_base_contact_list_contacts_changed ((TpBaseContactList *) roster,
changed, removed);
@@ -1313,6 +1333,7 @@ process_roster (
tp_handle_set_destroy (blocking_changed);
}
+ g_array_unref (updated_nicknames);
tp_handle_set_destroy (changed);
tp_handle_set_destroy (removed);
tp_handle_set_destroy (referenced_handles);
@@ -1359,6 +1380,16 @@ got_roster_iq (GabbleRoster *roster,
return FALSE;
}
+ if (sub_type == WOCKY_STANZA_SUB_TYPE_RESULT && priv->received)
+ {
+ /* <https://bugs.freedesktop.org/show_bug.cgi?id=42186>: some super-buggy
+ * XMPP server running on vk.com sends its reply to our roster query twice.
+ */
+ DEBUG ("The server sent replied to our roster query more than once! "
+ "Ignoring this reply");
+ return FALSE;
+ }
+
process_roster (roster, query_node);
if (sub_type == WOCKY_STANZA_SUB_TYPE_RESULT)
@@ -1380,16 +1411,36 @@ got_roster_iq (GabbleRoster *roster,
{
GabbleRosterItem *item = v;
TpHandle contact = GPOINTER_TO_UINT (k);
+ GabblePresence *presence = gabble_presence_cache_get (
+ priv->conn->presence_cache, contact);
+
+ if (item->subscribe == TP_SUBSCRIPTION_STATE_YES &&
+ (presence == NULL || presence->status == GABBLE_PRESENCE_UNKNOWN))
+ {
+ /* The contact might be in the presence cache with UNKNOWN
+ * presence if we've received a message from them before the
+ * roster arrived: an item is forcibly added to stash the
+ * nickname which might have been included in the <message/> in
+ * the presence cache. (This seems like a rather illogical place
+ * to stash such nicknames—if anything, they should live in
+ * GabbleImFactory—but there we go.)
+ *
+ * So if this is the case, we flip their status to OFFLINE. We
+ * don't use gabble_presence_update() because we want to signal
+ * all the unknown→offline transitions together.
+ */
+ if (presence != NULL)
+ presence->status = GABBLE_PRESENCE_OFFLINE;
- if (item->subscribe == TP_SUBSCRIPTION_STATE_YES)
- g_array_append_val (members, contact);
+ g_array_append_val (members, contact);
+ }
if (item->unsent_edits != NULL)
edited_items = g_slist_prepend (edited_items, item);
}
conn_presence_emit_presence_update (priv->conn, members);
- g_array_free (members, TRUE);
+ g_array_unref (members);
/* The roster is now complete and we can emit signals... */
tp_base_contact_list_set_list_received ((TpBaseContactList *) roster);
@@ -1708,6 +1759,8 @@ roster_received_cb (GObject *source_object,
result, &response, &error))
{
got_roster_iq (self, response);
+
+ g_object_unref (response);
}
else
{
@@ -2017,7 +2070,13 @@ roster_item_apply_edits (GabbleRoster *roster,
}
}
- if (edits->new_name != NULL && tp_strdiff (item->name, edits->new_name))
+ if (edits->remove_name)
+ {
+ DEBUG ("Removing name='' (was '%s')", item->name);
+ altered = TRUE;
+ edited_item.name = NULL;
+ }
+ else if (edits->new_name != NULL && tp_strdiff (item->name, edits->new_name))
{
DEBUG ("Changing name from %s to %s", item->name, edits->new_name);
altered = TRUE;
@@ -2284,7 +2343,6 @@ gabble_roster_handle_set_name (GabbleRoster *roster,
g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE);
g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL),
FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
item = _gabble_roster_item_ensure (roster, handle);
g_return_val_if_fail (item != NULL, FALSE);
@@ -2292,10 +2350,19 @@ gabble_roster_handle_set_name (GabbleRoster *roster,
if (item->unsent_edits == NULL)
item->unsent_edits = item_edit_new (contact_repo, handle);
- DEBUG ("queue edit to contact#%u - change name to \"%s\"",
- handle, name);
- g_free (item->unsent_edits->new_name);
- item->unsent_edits->new_name = g_strdup (name);
+ tp_clear_pointer (&item->unsent_edits->new_name, g_free);
+
+ if (name == NULL)
+ {
+ DEBUG ("queue edit to contact#%u - remove name", handle);
+ item->unsent_edits->remove_name = TRUE;
+ }
+ else
+ {
+ DEBUG ("queue edit to contact#%u - set name='%s'", handle, name);
+ item->unsent_edits->remove_name = FALSE;
+ item->unsent_edits->new_name = g_strdup (name);
+ }
/* maybe we can apply the edit immediately? */
roster_item_apply_edits (roster, handle, item);
@@ -2680,19 +2747,15 @@ gabble_roster_authorize_publication_async (TpBaseContactList *base,
TpIntSetFastIter iter;
TpHandle contact;
GError *error = NULL;
-#ifdef ENABLE_DEBUG
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
(TpBaseConnection *) self->priv->conn, TP_HANDLE_TYPE_CONTACT);
-#endif
tp_intset_fast_iter_init (&iter, tp_handle_set_peek (contacts));
while (tp_intset_fast_iter_next (&iter, &contact))
{
GabbleRosterItem *item = _gabble_roster_item_lookup (self, contact);
-#ifdef ENABLE_DEBUG
const gchar *contact_id = tp_handle_inspect (contact_repo, contact);
-#endif
if (item == NULL || item->publish == TP_SUBSCRIPTION_STATE_NO
|| item->publish == TP_SUBSCRIPTION_STATE_REMOVED_REMOTELY)
@@ -3134,7 +3197,7 @@ gabble_roster_set_contact_groups_async (TpBaseContactList *base,
(const gchar * const *) groups_created->pdata, groups_created->len);
}
- g_ptr_array_free (groups_created, TRUE);
+ g_ptr_array_unref (groups_created);
if (item->unsent_edits == NULL)
item->unsent_edits = item_edit_new (contact_repo, contact);
@@ -3492,13 +3555,13 @@ gabble_roster_class_init (GabbleRosterClass *cls)
base_class->dup_states = gabble_roster_dup_states;
base_class->dup_contacts = gabble_roster_dup_contacts;
- signals[NICKNAME_UPDATE] = g_signal_new (
- "nickname-update",
+ signals[NICKNAMES_UPDATE] = g_signal_new (
+ "nicknames-update",
G_TYPE_FROM_CLASS (cls),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
- g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
+ g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DBUS_TYPE_G_UINT_ARRAY);
}
gboolean
diff --git a/src/search-channel.c b/src/search-channel.c
index c7dd1d02f..05915db1e 100644
--- a/src/search-channel.c
+++ b/src/search-channel.c
@@ -237,7 +237,7 @@ parse_unextended_field_response (
g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
"server is broken: %s is not a field defined in XEP 0055",
field->name);
- g_ptr_array_free (search_keys, TRUE);
+ g_ptr_array_unref (search_keys);
return NULL;
}
}
@@ -350,7 +350,7 @@ parse_data_form (
return search_keys;
fail:
- g_ptr_array_free (search_keys, TRUE);
+ g_ptr_array_unref (search_keys);
return NULL;
}
@@ -661,7 +661,7 @@ parse_result_item (GabbleSearchChannel *chan,
}
add_search_result (chan, info);
- g_hash_table_destroy (info);
+ g_hash_table_unref (info);
}
static void
@@ -714,7 +714,7 @@ parse_extended_result_item (GabbleSearchChannel *chan,
add_search_result (chan, info);
}
- g_hash_table_destroy (info);
+ g_hash_table_unref (info);
}
static gboolean
@@ -1059,7 +1059,7 @@ gabble_search_channel_finalize (GObject *obj)
g_free (priv->server);
tp_handle_set_destroy (priv->result_handles);
- g_hash_table_destroy (chan->priv->tp_to_xmpp);
+ g_hash_table_unref (chan->priv->tp_to_xmpp);
g_free (chan->priv->available_search_keys);
@@ -1067,9 +1067,9 @@ gabble_search_channel_finalize (GObject *obj)
{
g_free (g_ptr_array_index (priv->boolean_keys, i));
}
- g_ptr_array_free (priv->boolean_keys, TRUE);
+ g_ptr_array_unref (priv->boolean_keys);
- g_hash_table_destroy (chan->priv->results);
+ g_hash_table_unref (chan->priv->results);
if (G_OBJECT_CLASS (gabble_search_channel_parent_class)->finalize)
G_OBJECT_CLASS (gabble_search_channel_parent_class)->finalize (obj);
diff --git a/src/search-manager.c b/src/search-manager.c
index b1a173b3c..b96bd5e54 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"
@@ -230,7 +230,7 @@ gabble_search_manager_finalize (GObject *object)
/* close_all removed all the channels from the hash table */
g_assert_cmpuint (g_hash_table_size (priv->channels), ==, 0);
- g_hash_table_destroy (priv->channels);
+ g_hash_table_unref (priv->channels);
if (G_OBJECT_CLASS (gabble_search_manager_parent_class)->finalize)
G_OBJECT_CLASS (gabble_search_manager_parent_class)->finalize (object);
@@ -341,7 +341,7 @@ gabble_search_manager_type_foreach_channel_class (GType type,
func (type, table, search_channel_allowed_properties, user_data);
- g_hash_table_destroy (table);
+ g_hash_table_unref (table);
}
static void
diff --git a/src/server-tls-channel.c b/src/server-tls-channel.c
index 2d23bf823..e6f8267fc 100644
--- a/src/server-tls-channel.c
+++ b/src/server-tls-channel.c
@@ -63,7 +63,7 @@ struct _GabbleServerTLSChannelPrivate {
GabbleTLSCertificate *server_cert;
gchar *server_cert_path;
gchar *hostname;
- GPtrArray *reference_identities;
+ GStrv reference_identities;
gboolean dispose_has_run;
};
@@ -85,7 +85,7 @@ gabble_server_tls_channel_get_property (GObject *object,
g_value_set_string (value, self->priv->hostname);
break;
case PROP_REFERENCE_IDENTITIES:
- g_value_set_boxed (value, self->priv->reference_identities->pdata);
+ g_value_set_boxed (value, self->priv->reference_identities);
break;
case PROP_TLS_SESSION:
g_value_set_object (value, self->priv->tls_session);
@@ -112,6 +112,9 @@ gabble_server_tls_channel_set_property (GObject *object,
case PROP_HOSTNAME:
self->priv->hostname = g_value_dup_string (value);
break;
+ case PROP_REFERENCE_IDENTITIES:
+ self->priv->reference_identities = g_value_dup_boxed (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@@ -127,7 +130,7 @@ gabble_server_tls_channel_finalize (GObject *object)
g_free (self->priv->server_cert_path);
g_free (self->priv->hostname);
- g_ptr_array_free (self->priv->reference_identities, TRUE);
+ g_strfreev (self->priv->reference_identities);
G_OBJECT_CLASS (gabble_server_tls_channel_parent_class)->finalize (object);
}
@@ -182,10 +185,6 @@ gabble_server_tls_channel_constructed (GObject *object)
const gchar *path;
gchar *cert_object_path;
GPtrArray *certificates;
- gchar *connect_server = NULL;
- gchar *explicit_server = NULL;
- gchar **extra_certificate_identities = NULL;
- gint i;
if (chain_up != NULL)
chain_up (object);
@@ -206,44 +205,6 @@ gabble_server_tls_channel_constructed (GObject *object)
NULL);
self->priv->server_cert_path = cert_object_path;
- /* Build up the identities we can check against */
- self->priv->reference_identities = g_ptr_array_new_with_free_func (g_free);
- g_object_get (tp_base_channel_get_connection (TP_BASE_CHANNEL (self)),
- "connect-server", &connect_server,
- "explicit-server", &explicit_server,
- "extra-certificate_identities", &extra_certificate_identities,
- NULL);
-
- /* First the domain part of the JID, which we were initialied with */
- g_ptr_array_add (self->priv->reference_identities,
- g_strdup (self->priv->hostname));
-
- /* And secondly the an explicitly overridden server (if in use) */
- if (!tp_str_empty (explicit_server) &&
- !tp_strdiff (connect_server, explicit_server))
- {
- g_ptr_array_add (self->priv->reference_identities,
- g_strdup (explicit_server));
- }
-
- /* Lastly extra identities added to the account as a result of user choices */
- if (extra_certificate_identities != NULL)
- {
- for (i = 0; extra_certificate_identities[i] != NULL; ++i)
- {
- if (!tp_str_empty (extra_certificate_identities[i]))
- g_ptr_array_add (self->priv->reference_identities,
- g_strdup (extra_certificate_identities[i]));
- }
- }
-
- /* Null terminate, since this is a gchar** */
- g_ptr_array_add (self->priv->reference_identities, NULL);
-
- g_free (explicit_server);
- g_free (connect_server);
- g_strfreev (extra_certificate_identities);
-
DEBUG ("Server TLS channel constructed at %s", path);
}
@@ -323,7 +284,7 @@ gabble_server_tls_channel_class_init (GabbleServerTLSChannelClass *klass)
"The various identities to check the certificate against",
"The server certificate identity should match one of these identities.",
G_TYPE_STRV,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (oclass, PROP_REFERENCE_IDENTITIES, pspec);
pspec = g_param_spec_object ("tls-session", "The WockyTLSSession",
diff --git a/src/server-tls-manager.c b/src/server-tls-manager.c
index a5cef2295..a82814281 100644
--- a/src/server-tls-manager.c
+++ b/src/server-tls-manager.c
@@ -25,7 +25,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"
@@ -54,6 +54,7 @@ struct _GabbleServerTLSManagerPrivate {
GabbleServerTLSChannel *channel;
gchar *peername;
+ GStrv reference_identities;
WockyTLSSession *tls_session;
GSimpleAsyncResult *async_result;
@@ -146,8 +147,8 @@ server_tls_channel_closed_cb (GabbleServerTLSChannel *channel,
WOCKY_TLS_HANDLER_CLASS
(gabble_server_tls_manager_parent_class)->verify_async_func (
WOCKY_TLS_HANDLER (self), self->priv->tls_session,
- self->priv->peername, self->priv->async_callback,
- self->priv->async_data);
+ self->priv->peername, self->priv->reference_identities,
+ self->priv->async_callback, self->priv->async_data);
}
tp_clear_object (&self->priv->async_result);
@@ -205,9 +206,73 @@ tls_certificate_rejected_cb (GabbleTLSCertificate *certificate,
}
static void
+extend_string_ptr_array (GPtrArray *array,
+ GStrv new_elements)
+{
+ gint i;
+
+ if (new_elements != NULL)
+ {
+ for (i = 0; new_elements[i] != NULL; i++)
+ {
+ if (!tp_str_empty (new_elements[i]))
+ g_ptr_array_add (array, g_strdup (new_elements[i]));
+ }
+ }
+}
+
+static void
+fill_reference_identities (GabbleServerTLSManager *self,
+ const gchar *peername,
+ GStrv original_extra_identities)
+{
+ GPtrArray *identities;
+ gchar *connect_server = NULL;
+ gchar *explicit_server = NULL;
+ GStrv extra_certificate_identities = NULL;
+
+ g_return_if_fail (self->priv->reference_identities == NULL);
+
+ g_object_get (self->priv->connection,
+ "connect-server", &connect_server,
+ "explicit-server", &explicit_server,
+ "extra-certificate-identities", &extra_certificate_identities,
+ NULL);
+
+ identities = g_ptr_array_new ();
+
+ /* The peer name, i.e, the domain part of the JID */
+ g_ptr_array_add (identities, g_strdup (peername));
+
+ /* The extra identities that the caller of verify_async() passed */
+ extend_string_ptr_array (identities, original_extra_identities);
+
+ /* The explicitly overridden server (if in use) */
+ if (!tp_str_empty (explicit_server) &&
+ !tp_strdiff (connect_server, explicit_server))
+ {
+ g_ptr_array_add (identities, g_strdup (explicit_server));
+ }
+
+ /* Extra identities added to the account as a result of user choices */
+ extend_string_ptr_array (identities, extra_certificate_identities);
+
+ /* Null terminate, since this is a gchar** */
+ g_ptr_array_add (identities, NULL);
+
+ self->priv->reference_identities = (GStrv) g_ptr_array_free (identities,
+ FALSE);
+
+ g_strfreev (extra_certificate_identities);
+ g_free (explicit_server);
+ g_free (connect_server);
+}
+
+static void
gabble_server_tls_manager_verify_async (WockyTLSHandler *handler,
WockyTLSSession *tls_session,
const gchar *peername,
+ GStrv extra_identities,
GAsyncReadyCallback callback,
gpointer user_data)
{
@@ -222,19 +287,6 @@ gabble_server_tls_manager_verify_async (WockyTLSHandler *handler,
self->priv->verify_async_called = TRUE;
- if (!self->priv->interactive_tls)
- {
- DEBUG ("ignore-ssl-errors is set, fallback to non-interactive "
- "verification.");
-
- WOCKY_TLS_HANDLER_CLASS
- (gabble_server_tls_manager_parent_class)->verify_async_func (
- WOCKY_TLS_HANDLER (self), tls_session, peername,
- callback, user_data);
-
- return;
- }
-
result = g_simple_async_result_new (G_OBJECT (self),
callback, user_data, gabble_server_tls_manager_verify_async);
@@ -248,6 +300,23 @@ gabble_server_tls_manager_verify_async (WockyTLSHandler *handler,
return;
}
+ fill_reference_identities (self, peername, extra_identities);
+
+ if (!self->priv->interactive_tls)
+ {
+ DEBUG ("ignore-ssl-errors is set, fallback to non-interactive "
+ "verification.");
+
+ g_object_unref (result);
+
+ WOCKY_TLS_HANDLER_CLASS
+ (gabble_server_tls_manager_parent_class)->verify_async_func (
+ WOCKY_TLS_HANDLER (self), tls_session, peername,
+ self->priv->reference_identities, callback, user_data);
+
+ return;
+ }
+
self->priv->async_result = result;
self->priv->tls_session = g_object_ref (tls_session);
self->priv->peername = g_strdup (peername);
@@ -258,6 +327,7 @@ gabble_server_tls_manager_verify_async (WockyTLSHandler *handler,
"connection", self->priv->connection,
"tls-session", tls_session,
"hostname", peername,
+ "reference-identities", self->priv->reference_identities,
NULL);
g_signal_connect (self->priv->channel, "closed",
@@ -332,6 +402,7 @@ gabble_server_tls_manager_finalize (GObject *object)
tp_base_channel_close (TP_BASE_CHANNEL (self->priv->channel));
g_free (self->priv->peername);
+ g_strfreev (self->priv->reference_identities);
G_OBJECT_CLASS (gabble_server_tls_manager_parent_class)->finalize (object);
}
@@ -390,13 +461,9 @@ gabble_server_tls_manager_foreach_channel (TpChannelManager *manager,
{
GabbleServerTLSManager *self = GABBLE_SERVER_TLS_MANAGER (manager);
- DEBUG ("Foreach channel");
-
- if (self->priv->channel == NULL)
- return;
-
/* there's only one channel of this kind */
- func (TP_EXPORTABLE_CHANNEL (self->priv->channel), user_data);
+ if (self->priv->channel != NULL)
+ func (TP_EXPORTABLE_CHANNEL (self->priv->channel), user_data);
}
static void
diff --git a/src/tls-certificate.c b/src/tls-certificate.c
index 86e40d58d..6d5748568 100644
--- a/src/tls-certificate.c
+++ b/src/tls-certificate.c
@@ -134,7 +134,7 @@ gabble_tls_certificate_finalize (GObject *object)
g_free (self->priv->object_path);
g_free (self->priv->cert_type);
- g_ptr_array_free (self->priv->cert_data, TRUE);
+ g_ptr_array_unref (self->priv->cert_data);
G_OBJECT_CLASS (gabble_tls_certificate_parent_class)->finalize (object);
}
diff --git a/src/tube-dbus.c b/src/tube-dbus.c
index 173f36a4d..7622b076c 100644
--- a/src/tube-dbus.c
+++ b/src/tube-dbus.c
@@ -27,7 +27,7 @@
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <loudmouth/loudmouth.h>
-
+#include <wocky/wocky-utils.h>
#include <telepathy-glib/channel-iface.h>
#include <telepathy-glib/dbus.h>
#include <telepathy-glib/exportable-channel.h>
@@ -591,8 +591,8 @@ gabble_tube_dbus_dispose (GObject *object)
contact_repo);
}
- tp_clear_pointer (&priv->dbus_names, g_hash_table_destroy);
- tp_clear_pointer (&priv->dbus_name_to_handle, g_hash_table_destroy);
+ tp_clear_pointer (&priv->dbus_names, g_hash_table_unref);
+ tp_clear_pointer (&priv->dbus_name_to_handle, g_hash_table_unref);
if (priv->reassembly_buffer)
g_string_free (priv->reassembly_buffer, TRUE);
@@ -612,8 +612,8 @@ gabble_tube_dbus_finalize (GObject *object)
g_free (priv->object_path);
g_free (priv->stream_id);
g_free (priv->service);
- g_hash_table_destroy (priv->parameters);
- g_array_free (priv->supported_access_controls, TRUE);
+ g_hash_table_unref (priv->parameters);
+ g_array_unref (priv->supported_access_controls);
if (priv->muc != NULL)
{
@@ -841,7 +841,7 @@ gabble_tube_dbus_set_property (GObject *object,
break;
case PROP_PARAMETERS:
if (priv->parameters != NULL)
- g_hash_table_destroy (priv->parameters);
+ g_hash_table_unref (priv->parameters);
priv->parameters = g_value_dup_boxed (value);
break;
case PROP_MUC:
@@ -904,7 +904,7 @@ gabble_tube_dbus_constructor (GType type,
priv->dbus_name_to_handle = g_hash_table_new_full (g_str_hash,
g_str_equal, NULL, NULL);
- g_assert (gabble_decode_jid (
+ g_assert (wocky_decode_jid (
tp_handle_inspect (contact_repo, priv->self_handle),
NULL, NULL, &nick));
g_assert (nick != NULL);
@@ -1681,7 +1681,7 @@ gabble_tube_dbus_add_name (GabbleTubeDBus *self,
const gchar *jid;
jid = tp_handle_inspect (contact_repo, handle);
- g_assert (gabble_decode_jid (jid, NULL, NULL, &nick));
+ g_assert (wocky_decode_jid (jid, NULL, NULL, &nick));
supposed_name = _gabble_generate_dbus_unique_name (nick);
g_free (nick);
@@ -1712,8 +1712,8 @@ gabble_tube_dbus_add_name (GabbleTubeDBus *self,
tp_svc_channel_type_dbus_tube_emit_dbus_names_changed (self, added,
removed);
- g_hash_table_destroy (added);
- g_array_free (removed, TRUE);
+ g_hash_table_unref (added);
+ g_array_unref (removed);
return TRUE;
}
@@ -1750,8 +1750,8 @@ gabble_tube_dbus_remove_name (GabbleTubeDBus *self,
tp_svc_channel_type_dbus_tube_emit_dbus_names_changed (self, added,
removed);
- g_hash_table_destroy (added);
- g_array_free (removed, TRUE);
+ g_hash_table_unref (added);
+ g_array_unref (removed);
tp_handle_unref (contact_repo, handle);
return TRUE;
}
diff --git a/src/tube-stream.c b/src/tube-stream.c
index 94c4d6fa9..07ff7a5f2 100644
--- a/src/tube-stream.c
+++ b/src/tube-stream.c
@@ -1035,7 +1035,7 @@ tube_stream_open (GabbleTubeStream *self,
priv->address = tp_g_value_slice_new (DBUS_TYPE_G_UCHAR_ARRAY);
g_value_set_boxed (priv->address, array);
- g_array_free (array, TRUE);
+ g_array_unref (array);
ret = gibber_listener_listen_socket (priv->local_listener, path, FALSE,
error);
@@ -1207,9 +1207,9 @@ gabble_tube_stream_dispose (GObject *object)
g_string_free (path, TRUE);
}
- tp_clear_pointer (&priv->transport_to_bytestream, g_hash_table_destroy);
- tp_clear_pointer (&priv->bytestream_to_transport, g_hash_table_destroy);
- tp_clear_pointer (&priv->transport_to_id, g_hash_table_destroy);
+ tp_clear_pointer (&priv->transport_to_bytestream, g_hash_table_unref);
+ tp_clear_pointer (&priv->bytestream_to_transport, g_hash_table_unref);
+ tp_clear_pointer (&priv->transport_to_id, g_hash_table_unref);
tp_handle_unref (contact_repo, priv->initiator);
@@ -1234,7 +1234,7 @@ gabble_tube_stream_finalize (GObject *object)
g_free (priv->object_path);
g_free (priv->service);
- g_hash_table_destroy (priv->parameters);
+ g_hash_table_unref (priv->parameters);
if (priv->address != NULL)
{
@@ -1446,7 +1446,7 @@ gabble_tube_stream_set_property (GObject *object,
break;
case PROP_PARAMETERS:
if (priv->parameters != NULL)
- g_hash_table_destroy (priv->parameters);
+ g_hash_table_unref (priv->parameters);
priv->parameters = g_value_dup_boxed (value);
break;
case PROP_ADDRESS_TYPE:
@@ -2327,7 +2327,7 @@ static void
destroy_socket_control_list (gpointer data)
{
GArray *tab = data;
- g_array_free (tab, TRUE);
+ g_array_unref (tab);
}
/**
diff --git a/src/tubes-channel.c b/src/tubes-channel.c
index a4fa88d6b..ac7011649 100644
--- a/src/tubes-channel.c
+++ b/src/tubes-channel.c
@@ -353,8 +353,8 @@ d_bus_names_changed_added (GabbleTubesChannel *self,
for (i = 0; i < added->len; i++)
g_boxed_free (TP_STRUCT_TYPE_DBUS_TUBE_MEMBER, added->pdata[i]);
- g_ptr_array_free (added, TRUE);
- g_array_free (removed, TRUE);
+ g_ptr_array_unref (added);
+ g_array_unref (removed);
}
static void
@@ -377,8 +377,8 @@ d_bus_names_changed_removed (GabbleTubesChannel *self,
tp_svc_channel_type_tubes_emit_d_bus_names_changed (self,
tube_id, added, removed);
- g_ptr_array_free (added, TRUE);
- g_array_free (removed, TRUE);
+ g_ptr_array_unref (added);
+ g_array_unref (removed);
}
static void
@@ -532,7 +532,7 @@ tube_offered_cb (GabbleTubeIface *tube,
update_tubes_presence (self);
g_free (service);
- g_hash_table_destroy (parameters);
+ g_hash_table_unref (parameters);
}
static GabbleTubeIface *
@@ -781,10 +781,8 @@ contact_left_muc (GabbleTubesChannel *self,
TpHandle contact)
{
GabbleTubesChannelPrivate *priv = self->priv;
-#ifdef ENABLE_DEBUG
TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
(TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT);
-#endif
GHashTable *old_dbus_tubes;
struct _add_in_old_dbus_tubes_data add_data;
struct _emit_d_bus_names_changed_foreach_data emit_data;
@@ -805,7 +803,7 @@ contact_left_muc (GabbleTubesChannel *self,
g_hash_table_foreach (old_dbus_tubes, emit_d_bus_names_changed_foreach,
&emit_data);
- g_hash_table_destroy (old_dbus_tubes);
+ g_hash_table_unref (old_dbus_tubes);
}
/* Called when we receive a presence from a contact who is
@@ -916,7 +914,7 @@ gabble_tubes_channel_presence_updated (GabbleTubesChannel *self,
/* the tube has reffed its initiator, no need to keep a ref */
tp_handle_unref (contact_repo, initiator_handle);
- g_hash_table_destroy (parameters);
+ g_hash_table_unref (parameters);
}
}
else
@@ -963,7 +961,7 @@ gabble_tubes_channel_presence_updated (GabbleTubesChannel *self,
g_hash_table_foreach (old_dbus_tubes, emit_d_bus_names_changed_foreach,
&emit_data);
- g_hash_table_destroy (old_dbus_tubes);
+ g_hash_table_unref (old_dbus_tubes);
}
static void
@@ -1053,7 +1051,7 @@ gabble_tubes_channel_get_available_tube_types (TpSvcChannelTypeTubes *iface,
tp_svc_channel_type_tubes_return_from_get_available_tube_types (context,
ret);
- g_array_free (ret, TRUE);
+ g_array_unref (ret);
}
/**
@@ -1081,7 +1079,7 @@ gabble_tubes_channel_list_tubes (TpSvcChannelTypeTubes *iface,
for (i = 0; i < ret->len; i++)
g_boxed_free (TP_STRUCT_TYPE_TUBE_INFO, ret->pdata[i]);
- g_ptr_array_free (ret, TRUE);
+ g_ptr_array_unref (ret);
}
struct _i_hate_g_hash_table_foreach
@@ -1216,7 +1214,7 @@ gabble_tubes_channel_tube_si_offered (GabbleTubesChannel *self,
NODE_DEBUG (tube_node, e.message);
gabble_bytestream_iface_close (bytestream, &e);
- g_hash_table_destroy (parameters);
+ g_hash_table_unref (parameters);
return;
}
@@ -1237,7 +1235,7 @@ gabble_tubes_channel_tube_si_offered (GabbleTubesChannel *self,
tp_channel_manager_emit_new_channel (priv->conn->private_tubes_factory,
TP_EXPORTABLE_CHANNEL (tube), NULL);
- g_hash_table_destroy (parameters);
+ g_hash_table_unref (parameters);
}
/* Called when we receive a SI request,
@@ -1415,7 +1413,7 @@ tube_msg_offered (GabbleTubesChannel *self,
tp_channel_manager_emit_new_channel (priv->conn->private_tubes_factory,
TP_EXPORTABLE_CHANNEL (tube), NULL);
- g_hash_table_destroy (parameters);
+ g_hash_table_unref (parameters);
}
static void
@@ -1550,7 +1548,7 @@ GabbleTubeIface *gabble_tubes_channel_tube_request (GabbleTubesChannel *self,
tube = create_new_tube (self, type, priv->self_handle, service,
parameters, stream_id, tube_id, NULL, TRUE);
g_free (stream_id);
- g_hash_table_destroy (parameters);
+ g_hash_table_unref (parameters);
return tube;
}
@@ -2020,7 +2018,7 @@ gabble_tubes_channel_get_d_bus_names (TpSvcChannelTypeTubes *iface,
for (i = 0; i < ret->len; i++)
g_boxed_free (TP_STRUCT_TYPE_DBUS_TUBE_MEMBER, ret->pdata[i]);
g_hash_table_unref (names);
- g_ptr_array_free (ret, TRUE);
+ g_ptr_array_unref (ret);
}
/**
@@ -2100,7 +2098,7 @@ gabble_tubes_channel_get_available_stream_tube_types (TpSvcChannelTypeTubes *ifa
tp_svc_channel_type_tubes_return_from_get_available_stream_tube_types (
context, ret);
- g_hash_table_destroy (ret);
+ g_hash_table_unref (ret);
}
static void
@@ -2133,7 +2131,7 @@ gabble_tubes_channel_close (GabbleTubesChannel *self)
priv->closed = TRUE;
g_hash_table_foreach (priv->tubes, emit_tube_closed_signal, self);
- g_hash_table_destroy (priv->tubes);
+ g_hash_table_unref (priv->tubes);
priv->tubes = NULL;
diff --git a/src/util.c b/src/util.c
index 891509cee..dc107e0ff 100644
--- a/src/util.c
+++ b/src/util.c
@@ -349,35 +349,6 @@ lm_message_build_with_sub_type (const gchar *to, LmMessageType type,
}
/**
- * gabble_decode_jid
- *
- * Parses a JID which may be one of the following forms:
- *
- * domain
- * domain/resource
- * node@domain
- * node@domain/resource
- *
- * If the JID is valid, returns TRUE and sets the caller's
- * node/domain/resource pointers if they are not NULL. The node and resource
- * pointers will be set to NULL if the respective part is not present in the
- * JID. The node and domain are lower-cased because the Jabber protocol treats
- * them case-insensitively.
- *
- * XXX: Do nodeprep/resourceprep and length checking.
- *
- * See RFC 3920 §3.
- */
-gboolean
-gabble_decode_jid (const gchar *jid,
- gchar **node,
- gchar **domain,
- gchar **resource)
-{
- return wocky_decode_jid (jid, node, domain, resource);
-}
-
-/**
* gabble_get_room_handle_from_jid:
* @room_repo: The %TP_HANDLE_TYPE_ROOM handle repository
* @jid: A JID
@@ -442,7 +413,7 @@ gabble_normalize_room (TpHandleRepoIface *repo,
qualified_name = g_strdup (jid);
}
- if (!gabble_decode_jid (qualified_name, NULL, NULL, &resource))
+ if (!wocky_decode_jid (qualified_name, NULL, NULL, &resource))
{
INVALID_HANDLE (error, "room JID %s is invalid", qualified_name);
return NULL;
@@ -525,7 +496,7 @@ gabble_normalize_contact (TpHandleRepoIface *repo,
gchar *username = NULL, *server = NULL, *resource = NULL;
gchar *ret = NULL;
- if (!gabble_decode_jid (jid, &username, &server, &resource) || !username)
+ if (!wocky_decode_jid (jid, &username, &server, &resource) || !username)
{
INVALID_HANDLE (error,
"JID %s is invalid or has no node part", jid);
diff --git a/src/util.h b/src/util.h
index 1ab39657f..b5896cfc2 100644
--- a/src/util.h
+++ b/src/util.h
@@ -35,8 +35,6 @@
#include "types.h"
-#define CHECK_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0')
-
typedef GSList * NodeIter;
#define node_iter(node) (node->children)
#define node_iter_next(i) (g_slist_next (i))
@@ -62,7 +60,6 @@ G_GNUC_NULL_TERMINATED LmMessage * lm_message_build_with_sub_type (
guint spec, ...);
G_GNUC_WARN_UNUSED_RESULT
-gboolean gabble_decode_jid (const gchar *jid, gchar **a, gchar **b, gchar **c);
gchar *gabble_encode_jid (const gchar *node, const gchar *domain,
const gchar *resource);
diff --git a/src/vcard-manager.c b/src/vcard-manager.c
index b78802354..25297fe3b 100644
--- a/src/vcard-manager.c
+++ b/src/vcard-manager.c
@@ -627,7 +627,7 @@ gabble_vcard_manager_dispose (GObject *object)
g_hash_table_foreach (priv->cache, disconnect_entry_foreach, NULL);
tp_heap_destroy (priv->timed_cache);
- g_hash_table_destroy (priv->cache);
+ g_hash_table_unref (priv->cache);
if (priv->edit_pipeline_item)
gabble_request_pipeline_item_cancel (priv->edit_pipeline_item);
@@ -884,7 +884,7 @@ observe_vcard (GabbleConnection *conn,
{
const gchar *fn = fn_node->content;
- if (!CHECK_STR_EMPTY(fn))
+ if (!tp_str_empty (fn))
{
field = "<FN>";
alias = g_strdup (fn);
diff --git a/src/write-mgr-file.c b/src/write-mgr-file.c
index e37b3cea4..b097316ba 100644
--- a/src/write-mgr-file.c
+++ b/src/write-mgr-file.c
@@ -27,6 +27,7 @@
#include <dbus/dbus-protocol.h>
#include <telepathy-glib/telepathy-glib.h>
+#include "extensions/extensions.h"
#include "protocol.h"
@@ -293,19 +294,33 @@ mgr_file_contents (const char *busname,
while (protocols != NULL)
{
- TpBaseProtocol *protocol = protocols->data;
- GHashTable *props =
- tp_base_protocol_get_immutable_properties (protocol);
- gchar *section_name = g_strdup_printf ("Protocol %s",
- tp_base_protocol_get_name (protocol));
- const gchar * const *ifaces = tp_asv_get_strv (props,
- TP_PROP_PROTOCOL_INTERFACES);
- const gchar * const *c_ifaces = tp_asv_get_strv (props,
+ GabbleJabberProtocol *protocol = protocols->data;
+ gchar *section_name;
+ GHashTable *props;
+ const gchar * const *ifaces;
+ const gchar * const *c_ifaces;
+ const gchar * const *addr_vcard_fields;
+ const gchar * const *addr_uri_schemes;
+ const gchar * const *auth_types;
+
+ g_object_get (G_OBJECT (protocol),
+ "immutable-properties", &props,
+ NULL);
+
+ section_name = g_strdup_printf ("Protocol %s",
+ tp_base_protocol_get_name (TP_BASE_PROTOCOL (protocol)));
+
+ ifaces = tp_asv_get_strv (props, TP_PROP_PROTOCOL_INTERFACES);
+ c_ifaces = tp_asv_get_strv (props,
TP_PROP_PROTOCOL_CONNECTION_INTERFACES);
- const gchar * const *auth_types = tp_asv_get_strv (props,
+ auth_types = tp_asv_get_strv (props,
TP_PROP_PROTOCOL_AUTHENTICATION_TYPES);
+ addr_vcard_fields = tp_asv_get_strv (props,
+ TP_PROP_PROTOCOL_INTERFACE_ADDRESSING_ADDRESSABLE_VCARD_FIELDS);
+ addr_uri_schemes = tp_asv_get_strv (props,
+ TP_PROP_PROTOCOL_INTERFACE_ADDRESSING_ADDRESSABLE_URI_SCHEMES);
- write_parameters (f, section_name, protocol);
+ write_parameters (f, section_name, TP_BASE_PROTOCOL (protocol));
write_rccs (f, section_name, props);
g_key_file_set_string_list (f, section_name, "Interfaces",
@@ -314,13 +329,17 @@ mgr_file_contents (const char *busname,
c_ifaces, g_strv_length ((gchar **) c_ifaces));
g_key_file_set_string_list (f, section_name, "AuthenticationTypes",
auth_types, g_strv_length ((gchar **) auth_types));
+ g_key_file_set_string_list (f, section_name, "AddressableVCardFields",
+ addr_vcard_fields, g_strv_length ((gchar **) addr_vcard_fields));
+ g_key_file_set_string_list (f, section_name, "AddressableURISchemes",
+ addr_uri_schemes, g_strv_length ((gchar **) addr_uri_schemes));
WRITE_STR (TP_PROP_PROTOCOL_VCARD_FIELD, "VCardField");
WRITE_STR (TP_PROP_PROTOCOL_ENGLISH_NAME, "EnglishName");
WRITE_STR (TP_PROP_PROTOCOL_ICON, "Icon");
g_free (section_name);
- g_hash_table_destroy (props);
+ g_hash_table_unref (props);
protocols = protocols->next;
}