diff options
-rw-r--r-- | extensions/Connection_Interface_Addressing.xml | 245 | ||||
-rw-r--r-- | extensions/Makefile.am | 1 | ||||
-rw-r--r-- | extensions/all.xml | 6 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/addressing-util.c | 412 | ||||
-rw-r--r-- | src/addressing-util.h | 70 | ||||
-rw-r--r-- | src/conn-addressing.c | 166 | ||||
-rw-r--r-- | src/conn-addressing.h | 34 | ||||
-rw-r--r-- | src/connection.c | 5 | ||||
-rw-r--r-- | src/protocol.c | 77 | ||||
-rw-r--r-- | tests/twisted/addressing.py | 70 | ||||
-rw-r--r-- | tests/twisted/constants.py | 1 | ||||
-rw-r--r-- | tests/twisted/servicetest.py | 1 |
13 files changed, 1028 insertions, 64 deletions
diff --git a/extensions/Connection_Interface_Addressing.xml b/extensions/Connection_Interface_Addressing.xml new file mode 100644 index 000000000..db8155f23 --- /dev/null +++ b/extensions/Connection_Interface_Addressing.xml @@ -0,0 +1,245 @@ +<?xml version="1.0" ?> +<node name="/Connection_Interface_Addressing" xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"> + <tp:copyright> Copyright (C) 2010 Collabora Limited </tp:copyright> + <tp:license xmlns="http://www.w3.org/1999/xhtml"> + <p>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.</p> + + <p>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.</p> + + <p>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 Street, Fifth Floor, Boston, MA 02110-1301, USA.</p> + </tp:license> + <interface name="org.freedesktop.Telepathy.Connection.Interface.Addressing.DRAFT" + tp:causes-havoc="experimental"> + <tp:requires interface="org.freedesktop.Telepathy.Connection"/> + <tp:requires interface="org.freedesktop.Telepathy.Connection.Interface.Contacts"/> + <tp:added version="0.19.12">(as draft)</tp:added> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>This interface deals with the multiple address types that can + refer to the same contact, such as vCard fields and URIs.</p> + + <p>It can be used to retrieve contacts with a specific addresses + through <tp:member-ref>GetContactsByVCardField</tp:member-ref> and + <tp:member-ref>GetContactsByURI</tp:member-ref>, as well as + defining the various addressing methods for a given contact + through this interface's contact attributes.</p> + </tp:docstring> + + <method name="GetContactsByVCardField" + tp:name-for-bindings="Get_Contacts_By_VCard_Field"> + <arg direction="in" name="Field" type="s"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>The vCard field of the addresses we are requesting. The + field name SHOULD be in lower case. Supported + fields can be found in + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Protocol.Interface.Addressing">AddressableVCardFields</tp:dbus-ref>.</p> + + <p>The <code>url</code> vCard field MUST NOT appear here; see + <tp:member-ref>GetContactsByURI</tp:member-ref> instead.</p> + + <tp:rationale> + <p>In practice, protocols have a limited set of URI + schemes that make sense to resolve as a contact.</p> + </tp:rationale> + + </tp:docstring> + </arg> + <arg direction="in" name="Addresses" type="as"> + <tp:docstring> + The addresses to get contact handles for. The address types + should match the given vCard field. + </tp:docstring> + </arg> + <arg direction="in" name="Interfaces" type="as" + tp:type="DBus_Interface[]"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of strings indicating which D-Bus interfaces the calling + process is interested in. All supported attributes from these + interfaces, whose values can be obtained without additional network + activity, will be in the reply.</p> + + <p>Attributes from this interface and from + <tp:dbus-ref>org.freedesktop.Telepathy.Connection</tp:dbus-ref> + are always returned, and need not be requested + explicitly.</p> + + <p>The behavior of this parameter is similar to the same + parameter in + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Connection.Interface">Contacts.GetContactAttributes</tp:dbus-ref>.</p> + </tp:docstring> + </arg> + + <arg direction="out" type="a{su}" name="Requested" + tp:type="Addressing_Normalization_Map"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A mapping from requested vCard addresses to the corresponding + contact handles.</p> + + <p>Requested addresses that are not valid or understood for this protocol + MUST be omitted from the mapping.</p> + </tp:docstring> + </arg> + + <arg direction="out" type="a{ua{sv}}" name="Attributes" + tp:type="Contact_Attributes_Map"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A dictionary mapping the contact handles to contact attributes. + If any of the requested addresses are in fact invalid, they are + simply omitted from this mapping. If contact attributes are not + immediately known, the behaviour is defined by the interface; + the attribute should either be omitted from the result or + replaced with a default value.</p> + + <p>Requested addresses that are not valid or understood for this protocol + MUST be omitted from the mapping.</p> + + <p>Each contact's attributes will always include at least the + identifier that would be obtained by inspecting the handle + (<code>org.freedesktop.Telepathy.Connection/contact-id</code>). + </p> + </tp:docstring> + </arg> + + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Request contacts and retrieve their attributes using a given field + in their vCards.</p> + + <p>The connection manager should record that these handles are in + use by the client who invokes this method, and must not + deallocate the handles until the client disconnects from the + bus or calls the + <tp:dbus-ref namespace="org.freedesktop.Telepathy">Connection.ReleaseHandles</tp:dbus-ref> + method.</p> + </tp:docstring> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/> + </tp:possible-errors> + </method> + + <method name="GetContactsByURI" + tp:name-for-bindings="Get_Contacts_By_URI"> + <arg direction="in" name="URIs" type="as"> + <tp:docstring> + The URI addresses to get contact handles for. Supported + schemes can be found in + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Protocol.Interface.Addressing">AddressableURISchemes</tp:dbus-ref>. + </tp:docstring> + </arg> + <arg direction="in" name="Interfaces" type="as" + tp:type="DBus_Interface[]"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A list of strings indicating which D-Bus interfaces the calling + process is interested in. All supported attributes from these + interfaces, whose values can be obtained without additional network + activity, will be in the reply.</p> + + <p>Attributes from this interface and from + <tp:dbus-ref>org.freedesktop.Telepathy.Connection</tp:dbus-ref> + are always returned, and need not be requested + explicitly.</p> + + <p>The behavior of this parameter is similar to the same + parameter in + <tp:dbus-ref namespace="org.freedesktop.Telepathy.Connection.Interface">Contacts.GetContactAttributes</tp:dbus-ref>.</p> + </tp:docstring> + </arg> + + <arg direction="out" type="a{su}" name="Requested" + tp:type="Addressing_Normalization_Map"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A mapping of requested URIs to the corresponding contact handles.</p> + + <p>Requested URIs that are not valid or understood for this protocol + MUST be omitted from the mapping.</p> + </tp:docstring> + </arg> + + <arg direction="out" type="a{ua{sv}}" name="Attributes" + tp:type="Contact_Attributes_Map"> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A dictionary mapping the contact handles to contact attributes. + If any of the requested addresses are in fact invalid, they are + simply omitted from this mapping. If contact attributes are not + immediately known, the behaviour is defined by the interface; + the attribute should either be omitted from the result or + replaced with a default value.</p> + + <p>Requested URIs that are not valid or understood for this protocol + MUST be omitted from the mapping.</p> + + <p>Each contact's attributes will always include at least the + identifier that would be obtained by inspecting the handle + (<code>org.freedesktop.Telepathy.Connection/contact-id</code>). + </p> + </tp:docstring> + </arg> + + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>Request contacts and retrieve their attributes using URI addresses.</p> + + <p>The connection manager should record that these handles are in + use by the client who invokes this method, and must not + deallocate the handles until the client disconnects from the + bus or calls the + <tp:dbus-ref namespace="org.freedesktop.Telepathy">Connection.ReleaseHandles</tp:dbus-ref> + method.</p> + </tp:docstring> + + <tp:possible-errors> + <tp:error name="org.freedesktop.Telepathy.Error.Disconnected"/> + </tp:possible-errors> + </method> + + <tp:mapping name="VCard_Field_Address_Map" array-name=""> + <tp:docstring xmlns="http://www.w3.org/1999/xhtml"> + <p>A mapping of vCard fields and addresses that repreent + the given contact.</p> + </tp:docstring> + <tp:member type="s" name="VCard_Field"/> + <tp:member type="s" name="Address"/> + </tp:mapping> + + <tp:contact-attribute name="addresses" type="a{ss}" + tp:type="VCard_Field_Address_Map"> + <tp:docstring> + The various vCard addresses that identify this contact. + </tp:docstring> + </tp:contact-attribute> + + <tp:contact-attribute name="uris" type="as"> + <tp:docstring> + The various URI addresses that identify this contact. + </tp:docstring> + </tp:contact-attribute> + + <tp:mapping name="Addressing_Normalization_Map"> + <tp:docstring> + A map from URIs/vCard addresses to the corresponding handle. + </tp:docstring> + <tp:added version="0.25.UNRELEASED"/> + + <tp:member type="s" name="Requested_String"> + <tp:docstring> + The URI or vCard address that has been requested by + <tp:member-ref>GetContactsByVCardField</tp:member-ref> or + <tp:member-ref>GetContactsByURI</tp:member-ref>. + </tp:docstring> + </tp:member> + <tp:member type="u" name="Handle" tp:type="Contact_Handle"> + <tp:docstring> + A nonzero handle. + </tp:docstring> + </tp:member> + </tp:mapping> + + </interface> +</node> +<!-- vim:set sw=2 sts=2 et ft=xml: --> diff --git a/extensions/Makefile.am b/extensions/Makefile.am index 47b51b350..8e50ffbce 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -4,6 +4,7 @@ EXTRA_DIST = \ all.xml \ Channel_Type_FileTransfer_Future.xml \ Connection_Future.xml \ + Connection_Interface_Addressing.xml \ Connection_Interface_Gabble_Decloak.xml \ Gabble_Plugin_Console.xml \ Gabble_Plugin_Gateways.xml \ diff --git a/extensions/all.xml b/extensions/all.xml index 90f52a89b..99997d6db 100644 --- a/extensions/all.xml +++ b/extensions/all.xml @@ -33,6 +33,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p> from="Telepathy specification"/> </tp:generic-types> +<xi:include href="Connection_Interface_Addressing.xml"/> + <xi:include href="OLPC_Buddy_Info.xml"/> <xi:include href="OLPC_Activity_Properties.xml"/> @@ -81,6 +83,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA</p> <tp:external-type name="String_Variant_Map" type="a{sv}" from="Telepathy specification"/> + <!-- use types from Connection_Interface_Contacts --> + <tp:external-type name="Contact_Attributes_Map" type="a{ua{sv}}" + from="Telepathy specification"/> + <!-- use types from Connection_Interface_Requests --> <tp:external-type name="Channel_Class" type="a{sv}" from="Telepathy specification"/> diff --git a/src/Makefile.am b/src/Makefile.am index 36477041e..c24f2a867 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 \ @@ -51,6 +53,8 @@ libgabble_convenience_la_SOURCES = \ caps-hash.c \ $(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 \ diff --git a/src/addressing-util.c b/src/addressing-util.c new file mode 100644 index 000000000..7ad84d9c0 --- /dev/null +++ b/src/addressing-util.c @@ -0,0 +1,412 @@ +/* + * 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", 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_parse_vcard_address (const gchar *vcard_field, + const gchar *vcard_address, + GError **error) +{ + gchar *normalized_address = 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_address = 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 + { + 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) +{ + TpHandle handle; + + gchar *normalized_address = gabble_parse_vcard_address (vcard_field, vcard_address, error); + + if (normalized_address == NULL) + return 0; + + handle = tp_handle_ensure (repo, vcard_address, NULL, error); + + g_free (normalized_address); + + 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_parse_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..d4354a3fa --- /dev/null +++ b/src/addressing-util.h @@ -0,0 +1,70 @@ +/* + * 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_parse_vcard_address (const gchar *vcard_field, + const gchar *vcard_address, + 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/conn-addressing.c b/src/conn-addressing.c new file mode 100644 index 000000000..465699494 --- /dev/null +++ b/src/conn-addressing.c @@ -0,0 +1,166 @@ +/* + * 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 "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/connection.c b/src/connection.c index c3a28f7da..25c185ebf 100644 --- a/src/connection.c +++ b/src/connection.c @@ -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; @@ -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 */ @@ -420,6 +423,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, @@ -838,6 +842,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; diff --git a/src/protocol.c b/src/protocol.c index ea10746c6..5576c7cfe 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -34,6 +34,7 @@ #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 @@ -44,7 +45,8 @@ 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)) + G_IMPLEMENT_INTERFACE (TP_TYPE_PROTOCOL_ADDRESSING, addressing_iface_init); + ) static TpCMParamSpec jabber_params[] = { { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, @@ -378,17 +380,13 @@ dup_authentication_types (TpBaseProtocol *self) static GStrv dup_supported_uri_schemes (TpBaseProtocol *self) { - const gchar * const addressing_uri_schemes[] = {"xmpp", NULL}; - - return g_strdupv ((GStrv) addressing_uri_schemes); + return g_strdupv ((gchar **) gabble_get_addressable_uri_schemes ()); } static GStrv dup_supported_vcard_fields (TpBaseProtocol *self) { - const gchar * const addressing_vcard_fields[] = {"x-jabber", NULL}; - - return g_strdupv ((GStrv) addressing_vcard_fields); + return g_strdupv ((gchar **) gabble_get_addressable_vcard_fields ()); } static gchar * @@ -397,28 +395,16 @@ addressing_normalize_vcard_address (TpBaseProtocol *self, const gchar *vcard_address, GError **error) { - gchar *normalized_address = NULL; + gchar *normalized_address = gabble_parse_vcard_address (vcard_field, vcard_address, error); - if (g_ascii_strcasecmp (vcard_field, "x-jabber") == 0) + if (normalized_address == NULL) { - GError *gabble_error = NULL; - normalized_address = gabble_normalize_contact (NULL, - vcard_address, GUINT_TO_POINTER (GABBLE_JID_GLOBAL), - &gabble_error); - - if (gabble_error != NULL) + /* InvalidHandle makes no sense in Protocol */ + if (error != NULL && g_error_matches (*error, TP_ERROR, TP_ERROR_INVALID_HANDLE)) { - 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); + (*error)->code = TP_ERROR_INVALID_ARGUMENT; } } - else - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, - "'x-jabber' is the only vCard field supported by this protocol"); - } return normalized_address; } @@ -428,47 +414,20 @@ addressing_normalize_contact_uri (TpBaseProtocol *self, const gchar *uri, GError **error) { - gchar *scheme = g_uri_parse_scheme (uri); - gchar *normalized_uri = NULL; + gchar *normalized_address = NULL; - if (scheme == NULL) - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "'%s' is not a valid URI", uri); - } - else if (g_ascii_strcasecmp (scheme, "xmpp") == 0) - { - GError *gabble_error = NULL; - const gchar *address = uri + strlen (scheme) + 1; /* Strip the scheme */ - gchar *normalized_address = gabble_normalize_contact (NULL, - address, GUINT_TO_POINTER (GABBLE_JID_GLOBAL), &gabble_error); + normalized_address = gabble_normalize_contact_uri (uri, error); - if (gabble_error != NULL) - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "'%s' is an invalid address: %s", address, - gabble_error->message); - g_error_free (gabble_error); - } - else + if (normalized_address == NULL) + { + /* InvalidHandle makes no sense in Protocol */ + if (error != NULL && g_error_matches (*error, TP_ERROR, TP_ERROR_INVALID_HANDLE)) { - gchar *normalized_scheme = g_ascii_strdown (scheme, -1); - normalized_uri = g_strdup_printf ("%s:%s", normalized_scheme, - normalized_address); - - g_free (normalized_scheme); - g_free (normalized_address); + (*error)->code = TP_ERROR_INVALID_ARGUMENT; } } - else - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, - "'xmpp' is the only URI scheme supported by this protocol"); - } - g_free (scheme); - - return normalized_uri; + return normalized_address; } static void diff --git a/tests/twisted/addressing.py b/tests/twisted/addressing.py index f8291f9e7..d09462a24 100644 --- a/tests/twisted/addressing.py +++ b/tests/twisted/addressing.py @@ -3,12 +3,14 @@ Test Gabble's different addressing interfaces. """ import dbus -from servicetest import unwrap, tp_path_prefix, assertEquals, ProxyWrapper +from servicetest import unwrap, tp_path_prefix, assertEquals, ProxyWrapper, \ + assertContains, assertSameSets, assertDoesNotContain from gabbletest import exec_test, call_async import constants as cs +import ns import time -def test(q, bus, conn, stream): +def test_protocol(q, bus, conn, stream): proto = ProxyWrapper( bus.get_object(cs.CM + '.gabble', tp_path_prefix + '/ConnectionManager/gabble/jabber'), @@ -44,9 +46,14 @@ def test(q, bus, conn, stream): # NormalizeContactURI normalized_uri = proto.Addressing.NormalizeContactURI( - "xmpp:EITAN@example.COM/resource") + "xmpp:EITAN?@example.COM/resource") - assertEquals("xmpp:eitan@example.com", normalized_uri) + assertEquals("xmpp:eitan%3F@example.com", normalized_uri) + + normalized_uri = proto.Addressing.NormalizeContactURI( + "xmpp:EITAN?@example.COM/resourc?e") + + assertEquals("xmpp:eitan%3F@example.com", normalized_uri) call_async(q, proto.Addressing, "NormalizeContactURI", "Something that is far from a URI") @@ -66,5 +73,58 @@ def test(q, bus, conn, stream): q.expect('dbus-error', method="NormalizeContactURI", name=cs.INVALID_ARGUMENT) +def test_connection(q, bus, conn, stream): + event = q.expect('stream-iq', query_ns=ns.ROSTER) + event.stanza['type'] = 'result' + + normalized_buddies = ['amy@foo.com', 'bob@foo.com', 'che@foo.com'] + buddies = ['AMY@foo.com', 'bob@FOO.com', 'che@foo.com/resource'] + + for buddy in normalized_buddies: + item = event.query.addElement('item') + item['jid'] = buddy + item['subscription'] = 'both' + + stream.send(event.stanza) + + requested, attributes = conn.Addressing.GetContactsByVCardField( + "X-JABBER", buddies[:2] + ['bad!jid'] + buddies[2:], []) + + addresses = [] + + assertEquals(3, len(attributes)) + assertEquals(3, len(requested)) + + for attr in attributes.values(): + assertContains(cs.CONN_IFACE_ADDRESSING + '/addresses', attr.keys()) + assertContains('x-jabber', attr[cs.CONN_IFACE_ADDRESSING + '/addresses'].keys()) + addresses.append(attr[cs.CONN_IFACE_ADDRESSING + '/addresses']['x-jabber']) + + assertSameSets(normalized_buddies, addresses) + assertSameSets(buddies, requested.keys()); + + normalized_buddies = ['amy%3F@foo.com', 'bob@foo.com', 'che@foo.com'] + buddies = ['AMY?@foo.com', 'bob@FOO.com', 'che@foo.com/resource'] + + normalized_schemes = ["xmpp", "xmpp", "http"] + schemes = ["xmpp", "XMPP", "http"] + valid_schemes = ["xmpp", "XMPP"] + + request_uris = [a + ":" + b for a, b in zip(schemes, buddies)] + valid_request_uris = [a + ":" + b for a, b in zip(valid_schemes, buddies)] + normalized_request_uris = [a + ":" + b for a, b in zip(normalized_schemes, normalized_buddies)] + + requested, attributes = conn.Addressing.GetContactsByURI(request_uris, []) + + assertEquals(2, len(attributes)) + assertEquals(2, len(requested)) + + for attr in attributes.values(): + assertContains(attr[cs.CONN_IFACE_ADDRESSING + '/uris'][0], normalized_request_uris) + assertContains(cs.CONN_IFACE_ADDRESSING + '/uris', attr.keys()) + + assertSameSets(valid_request_uris, requested.keys()) + if __name__ == '__main__': - exec_test(test) + exec_test(test_protocol) + exec_test(test_connection) diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py index b580cd932..e869a4a97 100644 --- a/tests/twisted/constants.py +++ b/tests/twisted/constants.py @@ -139,6 +139,7 @@ CONN_IFACE_CONTACT_GROUPS = CONN + '.Interface.ContactGroups' CONN_IFACE_CLIENT_TYPES = CONN + '.Interface.ClientTypes' CONN_IFACE_POWER_SAVING = CONN + '.Interface.PowerSaving' CONN_IFACE_CONTACT_BLOCKING = CONN + '.Interface.ContactBlocking' +CONN_IFACE_ADDRESSING = CONN + '.Interface.Addressing.DRAFT' ATTR_CONTACT_CAPABILITIES = CONN_IFACE_CONTACT_CAPS + '/capabilities' diff --git a/tests/twisted/servicetest.py b/tests/twisted/servicetest.py index 0531ba638..0fee466e6 100644 --- a/tests/twisted/servicetest.py +++ b/tests/twisted/servicetest.py @@ -465,6 +465,7 @@ def wrap_connection(conn): ('ContactList', cs.CONN_IFACE_CONTACT_LIST), ('ContactGroups', cs.CONN_IFACE_CONTACT_GROUPS), ('PowerSaving', cs.CONN_IFACE_POWER_SAVING), + ('Addressing', cs.CONN_IFACE_ADDRESSING), ])) def wrap_channel(chan, type_, extra=None): |