diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2013-11-11 19:45:13 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2013-11-11 19:45:13 +0000 |
commit | fc228b814e01a9cd231d4b9d8e4709effafd42fe (patch) | |
tree | bc1538923274ef61ea8ec303e06782d0c65091f7 | |
parent | 7155875747057e6a5c4bac068a59d433720a42cb (diff) | |
parent | 1aa78d2cf4d9b44c15c5b627eef210943646e3fb (diff) |
Merge branch 'master' into next
Conflicts:
docs/reference/telepathy-glib-sections.txt
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | docs/reference/telepathy-glib/telepathy-glib-sections.txt | 23 | ||||
-rw-r--r-- | examples/client/python/inspect-cm.py | 23 | ||||
-rw-r--r-- | examples/cm/echo-message-parts/protocol.c | 70 | ||||
-rw-r--r-- | telepathy-glib/presence-mixin.c | 219 | ||||
-rw-r--r-- | telepathy-glib/presence-mixin.h | 35 | ||||
-rw-r--r-- | telepathy-glib/protocol.c | 638 | ||||
-rw-r--r-- | telepathy-glib/protocol.h | 63 | ||||
-rw-r--r-- | tests/dbus/protocol-objects.c | 152 |
9 files changed, 1166 insertions, 61 deletions
diff --git a/configure.ac b/configure.ac index ec4379e2b..d695f11d9 100644 --- a/configure.ac +++ b/configure.ac @@ -238,10 +238,10 @@ AC_SUBST(tpglibtestsdir) dnl Check for Glib PKG_CHECK_MODULES(GLIB, - [glib-2.0 >= 2.34.0, gobject-2.0 >= 2.34.0, gio-2.0 >= 2.34.0]) + [glib-2.0 >= 2.36, gobject-2.0 >= 2.36, gio-2.0 >= 2.36]) AC_DEFINE([GLIB_VERSION_MIN_REQUIRED], [GLIB_VERSION_2_34], [Ignore post 2.34 deprecations]) -AC_DEFINE([GLIB_VERSION_MAX_ALLOWED], [GLIB_VERSION_2_34], [Prevent post 2.34 APIs]) +AC_DEFINE([GLIB_VERSION_MAX_ALLOWED], [GLIB_VERSION_2_36], [Prevent post 2.36 APIs]) dnl Check for GIO-Unix PKG_CHECK_MODULES(GIO_UNIX, [gio-unix-2.0], diff --git a/docs/reference/telepathy-glib/telepathy-glib-sections.txt b/docs/reference/telepathy-glib/telepathy-glib-sections.txt index e7c96f7f7..a5a5d0ff4 100644 --- a/docs/reference/telepathy-glib/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib/telepathy-glib-sections.txt @@ -1701,6 +1701,13 @@ TP_CONTACTS_MIXIN <FILE>presence-mixin</FILE> TpPresenceStatusOptionalArgumentSpec TpPresenceStatusSpec +tp_presence_status_spec_can_set_on_self +tp_presence_status_spec_get_name +tp_presence_status_spec_get_presence_type +tp_presence_status_spec_has_message +tp_presence_status_spec_new +tp_presence_status_spec_copy +tp_presence_status_spec_free TpPresenceMixinStatusAvailableFunc TpPresenceMixinGetContactStatusesFunc TpPresenceMixinSetOwnStatusFunc @@ -1731,6 +1738,8 @@ TpPresenceMixinClassPrivate <SUBSECTION Standard> TP_PRESENCE_MIXIN_CLASS TP_PRESENCE_MIXIN +TpPresenceStatusSpecPrivate +tp_presence_status_spec_get_type </SECTION> <SECTION> @@ -4904,7 +4913,21 @@ tp_protocol_get_english_name tp_protocol_get_icon_name tp_protocol_get_vcard_field tp_protocol_get_authentication_types +tp_protocol_identify_account_async +tp_protocol_identify_account_finish +tp_protocol_normalize_contact_async +tp_protocol_normalize_contact_finish +<SUBSECTION> tp_protocol_get_avatar_requirements +<SUBSECTION> +tp_protocol_dup_presence_statuses +<SUBSECTION> +tp_protocol_get_addressable_uri_schemes +tp_protocol_get_addressable_vcard_fields +tp_protocol_normalize_contact_uri_async +tp_protocol_normalize_contact_uri_finish +tp_protocol_normalize_vcard_address_async +tp_protocol_normalize_vcard_address_finish <SUBSECTION Standard> tp_protocol_get_type TP_PROTOCOL diff --git a/examples/client/python/inspect-cm.py b/examples/client/python/inspect-cm.py index c653cf716..0564078a9 100644 --- a/examples/client/python/inspect-cm.py +++ b/examples/client/python/inspect-cm.py @@ -28,9 +28,11 @@ def describe(cm): print("\t\tNo default") def manager_prepared_cb(cm, result, loop): - cm.prepare_finish(result) - describe(cm) - loop.quit() + try: + cm.prepare_finish(result) + describe(cm) + finally: + loop.quit() def inspect(name): cm = Tp.ConnectionManager( @@ -41,13 +43,14 @@ def inspect(name): cm.prepare_async(None, cm, loop) def cms_cb(source, result, loop): - cms = Tp.list_connection_managers_finish(result) - - for cm in cms: - describe(cm) - print("") - - loop.quit() + try: + cms = Tp.list_connection_managers_finish(result) + + for cm in cms: + describe(cm) + print("") + finally: + loop.quit() if __name__ == '__main__': loop = GObject.MainLoop() diff --git a/examples/cm/echo-message-parts/protocol.c b/examples/cm/echo-message-parts/protocol.c index ef3135063..16cef0920 100644 --- a/examples/cm/echo-message-parts/protocol.c +++ b/examples/cm/echo-message-parts/protocol.c @@ -116,7 +116,7 @@ identify_account (TpBaseProtocol *self G_GNUC_UNUSED, const gchar *account = tp_asv_get_string (asv, "account"); if (account != NULL) - return g_strdup (account); + return g_utf8_strdown (account, -1); g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "'account' parameter not given"); @@ -227,6 +227,72 @@ dup_supported_vcard_fields (TpBaseProtocol *self) return g_strdupv ((GStrv) addressing_vcard_fields); } +static gchar * +normalize_vcard_address (TpBaseProtocol *self, + const gchar *vcard_field, + const gchar *vcard_address, + GError **error) +{ + if (g_ascii_strcasecmp (vcard_field, "x-jabber") == 0) + { + /* This is not really how you normalize a JID but it's good enough + * for an example. In real life you'd do syntax-checking beyond + * "is it empty?", stringprep, and so on. Here, we just assume + * non-empty means valid, and lower-case means normalized. */ + + if (tp_str_empty (vcard_address)) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "The empty string is not a valid JID"); + return NULL; + } + + return g_utf8_strdown (vcard_address, -1); + } + else + { + g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "Don't know how to normalize vCard field: %s", vcard_field); + return NULL; + } +} + +static gchar * +normalize_contact_uri (TpBaseProtocol *self, + const gchar *uri, + GError **error) +{ + gchar *scheme = g_uri_parse_scheme (uri); + + if (g_ascii_strcasecmp (scheme, "xmpp") == 0) + { + gchar *ret = NULL; + gchar *id; + + id = normalize_vcard_address (self, "x-jabber", uri + 5, error); + + if (id != NULL) + ret = g_strdup_printf ("%s:%s", scheme, id); + + g_free (scheme); + g_free (id); + return ret; + } + else if (scheme == NULL) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Not a valid URI: %s", uri); + return NULL; + } + else + { + g_set_error (error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, + "Don't know how to normalize URIs of that scheme: %s", scheme); + g_free (scheme); + return NULL; + } +} + static void example_echo_2_protocol_class_init ( ExampleEcho2ProtocolClass *klass) @@ -249,4 +315,6 @@ 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 = normalize_vcard_address; + iface->normalize_contact_uri = normalize_contact_uri; } diff --git a/telepathy-glib/presence-mixin.c b/telepathy-glib/presence-mixin.c index 44e57ebae..f25663181 100644 --- a/telepathy-glib/presence-mixin.c +++ b/telepathy-glib/presence-mixin.c @@ -96,7 +96,10 @@ * @optional_arguments: An array of #TpPresenceStatusOptionalArgumentSpec * structures representing the optional arguments for this status, terminated * by a NULL name. If there are no optional arguments for a status, this can - * be NULL. + * be NULL. In modern Telepathy connection managers, the only optional + * argument should be a string (type "s") named "message" on statuses + * that have an optional human-readable message. All other optional arguments + * are deprecated. * * Structure specifying a supported presence status. * @@ -118,9 +121,13 @@ * In addition to the fields documented here, there are two gpointer fields * which must currently be %NULL. A meaning may be defined for these in a * future version of telepathy-glib. + * + * In modern Telepathy connection managers, the only optional + * argument should be a %G_TYPE_STRING named "message", on statuses + * that have an optional human-readable message. All other optional arguments + * are deprecated. */ - /** * TpPresenceMixinStatusAvailableFunc: * @obj: An instance of a #TpBaseConnection subclass implementing the presence @@ -298,6 +305,11 @@ deep_copy_hashtable (GHashTable *hash_table) * Construct a presence status structure. You should free the returned * structure with #tp_presence_status_free. * + * In modern Telepathy connection managers, the only optional + * argument should be a %G_TYPE_STRING named "message", on statuses + * that have an optional human-readable message. All other optional arguments + * are deprecated. + * * Returns: A pointer to the newly allocated presence status structure. */ TpPresenceStatus * @@ -669,27 +681,17 @@ tp_presence_mixin_get_dbus_property (GObject *object, for (i=0; mixin_cls->statuses[i].name != NULL; i++) { - const TpPresenceStatusOptionalArgumentSpec *specs; - int j; - gboolean message = FALSE; + gboolean message; /* we include statuses here even if they're not available * to set on yourself */ if (!check_status_available (object, mixin_cls, i, NULL, FALSE)) continue; - specs = mixin_cls->statuses[i].optional_arguments; - - for (j = 0; specs != NULL && specs[j].name != NULL; j++) - { - if (!tp_strdiff (specs[j].name, "message")) - { - message = TRUE; - break; - } - } + message = tp_presence_status_spec_has_message ( + &mixin_cls->statuses[i]); - status = tp_value_array_build (3, + status = tp_value_array_build (3, G_TYPE_UINT, (guint) mixin_cls->statuses[i].presence_type, G_TYPE_BOOLEAN, mixin_cls->statuses[i].self, G_TYPE_BOOLEAN, message, @@ -933,3 +935,188 @@ tp_presence_mixin_register_with_contacts_mixin (GObject *obj) tp_presence_mixin_fill_contact_attributes); } +/* For now, self->priv is just self if heap-allocated, NULL if not. */ +static gboolean +_tp_presence_status_spec_is_heap_allocated (const TpPresenceStatusSpec *self) +{ + return (self->priv == (TpPresenceStatusSpecPrivate *) self); +} + +/** + * tp_presence_status_spec_get_presence_type: + * @self: a presence status specification + * + * Return the category into which this presence type falls. For instance, + * for XMPP's "" (do not disturb) status, this would return + * %TP_CONNECTION_PRESENCE_TYPE_BUSY. + * + * Returns: a #TpConnectionPresenceType + * Since: 0.UNRELEASED + */ +TpConnectionPresenceType +tp_presence_status_spec_get_presence_type (const TpPresenceStatusSpec *self) +{ + g_return_val_if_fail (self != NULL, TP_CONNECTION_PRESENCE_TYPE_UNSET); + + return self->presence_type; +} + +/** + * tp_presence_status_spec_get_name: + * @self: a presence status specification + * + * <!-- --> + * + * Returns: (transfer none): the name of this presence status, + * such as "available" or "out-to-lunch". + * Since: 0.UNRELEASED + */ +const gchar * +tp_presence_status_spec_get_name (const TpPresenceStatusSpec *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return self->name; +} + +/** + * tp_presence_status_spec_can_set_on_self: + * @self: a presence status specification + * + * <!-- --> + * + * Returns: %TRUE if the user can set this presence status on themselves (most + * statuses), or %FALSE if they cannot directly set it on + * themselves (typically used for %TP_CONNECTION_PRESENCE_TYPE_OFFLINE + * and %TP_CONNECTION_PRESENCE_TYPE_ERROR) + * Since: 0.UNRELEASED + */ +gboolean +tp_presence_status_spec_can_set_on_self (const TpPresenceStatusSpec *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + + return self->self; +} + +/** + * tp_presence_status_spec_has_message: + * @self: a presence status specification + * + * <!-- --> + * + * Returns: %TRUE if this presence status is accompanied by an optional + * human-readable message + * Since: 0.UNRELEASED + */ +gboolean +tp_presence_status_spec_has_message (const TpPresenceStatusSpec *self) +{ + const TpPresenceStatusOptionalArgumentSpec *arg; + + g_return_val_if_fail (self != NULL, FALSE); + + if (self->optional_arguments == NULL) + return FALSE; + + for (arg = self->optional_arguments; arg->name != NULL; arg++) + { + if (!tp_strdiff (arg->name, "message") && !tp_strdiff (arg->dtype, "s")) + return TRUE; + } + + return FALSE; +} + +/** + * tp_presence_status_spec_new: + * @name: the name of the new presence status + * @type: the category into which this presence status falls + * @can_set_on_self: %TRUE if the user can set this presence status + * on themselves + * @has_message: %TRUE if this presence status is accompanied by an + * optional human-readable message + * + * <!-- --> + * + * Returns: (transfer full): a new #TpPresenceStatusSpec + * Since: 0.UNRELEASED + */ +TpPresenceStatusSpec * +tp_presence_status_spec_new (const gchar *name, + TpConnectionPresenceType type, + gboolean can_set_on_self, + gboolean has_message) +{ + TpPresenceStatusSpec *ret; + static const TpPresenceStatusOptionalArgumentSpec yes_it_has_a_message[] = { + { "message", "s" }, + { NULL } + }; + + g_return_val_if_fail (!tp_str_empty (name), NULL); + g_return_val_if_fail (type >= 0 && type < TP_NUM_CONNECTION_PRESENCE_TYPES, + NULL); + + ret = g_slice_new0 (TpPresenceStatusSpec); + + ret->name = g_strdup (name); + ret->presence_type = type; + ret->self = can_set_on_self; + + if (has_message) + ret->optional_arguments = yes_it_has_a_message; + else + ret->optional_arguments = NULL; + + /* dummy marker for "this is on the heap" rather than a real struct */ + ret->priv = (TpPresenceStatusSpecPrivate *) ret; + + return ret; +} + +/** + * tp_presence_status_spec_copy: + * @self: a presence status specification + * + * Copy a presence status specification. + * + * If @self has optional arguments other than a string named "message", + * they are not copied. Optional arguments with other names or types + * are deprecated. + * + * Returns: (transfer full): a new #TpPresenceStatusSpec resembling @self + * Since: 0.UNRELEASED + */ +TpPresenceStatusSpec * +tp_presence_status_spec_copy (const TpPresenceStatusSpec *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return tp_presence_status_spec_new (self->name, self->presence_type, + self->self, tp_presence_status_spec_has_message (self)); +} + +/** + * tp_presence_status_spec_free: + * @self: (transfer full): a presence status specification + * + * Free a presence status specification produced by + * tp_presence_status_spec_new() or tp_presence_status_spec_copy(). + * + * Since: 0.UNRELEASED + */ +void +tp_presence_status_spec_free (TpPresenceStatusSpec *self) +{ + g_return_if_fail (_tp_presence_status_spec_is_heap_allocated (self)); + + /* This struct was designed to always be on the stack, so freeing this + * needs a non-const-correct cast */ + g_free ((gchar *) self->name); + + g_slice_free (TpPresenceStatusSpec, self); +} + +G_DEFINE_BOXED_TYPE (TpPresenceStatusSpec, tp_presence_status_spec, + tp_presence_status_spec_copy, tp_presence_status_spec_free) diff --git a/telepathy-glib/presence-mixin.h b/telepathy-glib/presence-mixin.h index f94bf6579..6e7b98432 100644 --- a/telepathy-glib/presence-mixin.h +++ b/telepathy-glib/presence-mixin.h @@ -34,6 +34,7 @@ G_BEGIN_DECLS typedef struct _TpPresenceStatusOptionalArgumentSpec TpPresenceStatusOptionalArgumentSpec; typedef struct _TpPresenceStatusSpec TpPresenceStatusSpec; +typedef struct _TpPresenceStatusSpecPrivate TpPresenceStatusSpecPrivate; struct _TpPresenceStatusOptionalArgumentSpec { const gchar *name; @@ -52,9 +53,41 @@ struct _TpPresenceStatusSpec { /*<private>*/ gpointer _future1; - gpointer _future2; + TpPresenceStatusSpecPrivate *priv; }; +_TP_AVAILABLE_IN_UNRELEASED +TpConnectionPresenceType tp_presence_status_spec_get_presence_type ( + const TpPresenceStatusSpec *self); + +_TP_AVAILABLE_IN_UNRELEASED +const gchar *tp_presence_status_spec_get_name ( + const TpPresenceStatusSpec *self); + +_TP_AVAILABLE_IN_UNRELEASED +gboolean tp_presence_status_spec_can_set_on_self ( + const TpPresenceStatusSpec *self); + +_TP_AVAILABLE_IN_UNRELEASED +gboolean tp_presence_status_spec_has_message ( + const TpPresenceStatusSpec *self); + +_TP_AVAILABLE_IN_UNRELEASED +GType tp_presence_status_spec_get_type (void); + +_TP_AVAILABLE_IN_UNRELEASED +TpPresenceStatusSpec *tp_presence_status_spec_new (const gchar *name, + TpConnectionPresenceType type, + gboolean can_set_on_self, + gboolean has_message); + +_TP_AVAILABLE_IN_UNRELEASED +TpPresenceStatusSpec *tp_presence_status_spec_copy ( + const TpPresenceStatusSpec *self); + +_TP_AVAILABLE_IN_UNRELEASED +void tp_presence_status_spec_free (TpPresenceStatusSpec *self); + typedef struct _TpPresenceStatus TpPresenceStatus; struct _TpPresenceStatus { diff --git a/telepathy-glib/protocol.c b/telepathy-glib/protocol.c index 3d98b3830..1f508a418 100644 --- a/telepathy-glib/protocol.c +++ b/telepathy-glib/protocol.c @@ -47,6 +47,8 @@ #include "telepathy-glib/cli-misc.h" #include "telepathy-glib/debug-internal.h" #include "telepathy-glib/proxy-internal.h" +#include "telepathy-glib/util-internal.h" +#include "telepathy-glib/variant-util-internal.h" #include <string.h> @@ -146,6 +148,10 @@ struct _TpProtocolPrivate TpCapabilities *capabilities; TpAvatarRequirements *avatar_req; gchar *cm_name; + GStrv addressable_vcard_fields; + GStrv addressable_uri_schemes; + /* (transfer container) (element-type utf8 Simple_Status_Spec) */ + GHashTable *presence_statuses; }; enum @@ -160,6 +166,8 @@ enum PROP_AUTHENTICATION_TYPES, PROP_AVATAR_REQUIREMENTS, PROP_CM_NAME, + PROP_ADDRESSABLE_VCARD_FIELDS, + PROP_ADDRESSABLE_URI_SCHEMES, N_PROPS }; @@ -279,6 +287,15 @@ tp_protocol_get_property (GObject *object, g_value_set_string (value, tp_protocol_get_cm_name (self)); break; + case PROP_ADDRESSABLE_VCARD_FIELDS: + g_value_set_boxed (value, tp_protocol_get_addressable_vcard_fields ( + self)); + break; + + case PROP_ADDRESSABLE_URI_SCHEMES: + g_value_set_boxed (value, tp_protocol_get_addressable_uri_schemes (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -354,6 +371,11 @@ tp_protocol_finalize (GObject *object) g_free (self->priv->english_name); g_free (self->priv->icon_name); g_free (self->priv->cm_name); + g_strfreev (self->priv->addressable_vcard_fields); + g_strfreev (self->priv->addressable_uri_schemes); + + if (self->priv->presence_statuses != NULL) + g_hash_table_unref (self->priv->presence_statuses); if (self->priv->protocol_properties != NULL) g_hash_table_unref (self->priv->protocol_properties); @@ -414,6 +436,19 @@ title_case (const gchar *s) return g_strdup_printf ("%s%s", buf, g_utf8_next_char (s)); } +static GStrv +asv_strdupv_or_empty (const GHashTable *asv, + const gchar *key) +{ + const gchar * const *strings = tp_asv_get_boxed (asv, key, G_TYPE_STRV); + static const gchar * const no_strings[] = { NULL }; + + if (strings != NULL) + return g_strdupv ((GStrv) strings); + else + return g_strdupv ((GStrv) no_strings); +} + static void tp_protocol_constructed (GObject *object) { @@ -424,7 +459,6 @@ tp_protocol_constructed (GObject *object) const gchar *s; const GPtrArray *rccs; gboolean had_immutables = TRUE; - const gchar * const *auth_types = NULL; const gchar * const *interfaces; if (chain_up != NULL) @@ -444,7 +478,21 @@ tp_protocol_constructed (GObject *object) } else { + GHashTableIter iter; + gpointer k, v; + DEBUG ("immutable properties already supplied"); + + g_hash_table_iter_init (&iter, self->priv->protocol_properties); + + while (g_hash_table_iter_next (&iter, &k, &v)) + { + gchar *printed; + + printed = g_strdup_value_contents (v); + DEBUG ("%s = %s", (const gchar *) k, printed); + g_free (printed); + } } self->priv->params = tp_protocol_params_from_param_specs ( @@ -486,19 +534,9 @@ tp_protocol_constructed (GObject *object) if (rccs != NULL) self->priv->capabilities = _tp_capabilities_new (rccs, FALSE); - auth_types = tp_asv_get_boxed ( + self->priv->authentication_types = asv_strdupv_or_empty ( self->priv->protocol_properties, - TP_PROP_PROTOCOL_AUTHENTICATION_TYPES, G_TYPE_STRV); - - if (auth_types != NULL) - { - self->priv->authentication_types = g_strdupv ((GStrv) auth_types); - } - else - { - gchar *tmp[] = { NULL }; - self->priv->authentication_types = g_strdupv (tmp); - } + TP_PROP_PROTOCOL_AUTHENTICATION_TYPES); interfaces = tp_asv_get_strv (self->priv->protocol_properties, TP_PROP_PROTOCOL_INTERFACES); @@ -508,6 +546,9 @@ tp_protocol_constructed (GObject *object) if (tp_proxy_has_interface_by_id (self, TP_IFACE_QUARK_PROTOCOL_INTERFACE_AVATARS1)) { + DEBUG ("%s/%s implements Avatars", self->priv->cm_name, + self->priv->name); + self->priv->avatar_req = tp_avatar_requirements_new ( (GStrv) tp_asv_get_strv (self->priv->protocol_properties, TP_PROP_PROTOCOL_INTERFACE_AVATARS1_SUPPORTED_AVATAR_MIME_TYPES), @@ -527,6 +568,58 @@ tp_protocol_constructed (GObject *object) TP_PROP_PROTOCOL_INTERFACE_AVATARS1_MAXIMUM_AVATAR_BYTES, NULL)); } + if (tp_proxy_has_interface_by_id (self, + TP_IFACE_QUARK_PROTOCOL_INTERFACE_ADDRESSING1)) + { + DEBUG ("%s/%s implements Addressing", self->priv->cm_name, + self->priv->name); + + self->priv->addressable_vcard_fields = asv_strdupv_or_empty ( + self->priv->protocol_properties, + TP_PROP_PROTOCOL_INTERFACE_ADDRESSING1_ADDRESSABLE_VCARD_FIELDS); + self->priv->addressable_uri_schemes = asv_strdupv_or_empty ( + self->priv->protocol_properties, + TP_PROP_PROTOCOL_INTERFACE_ADDRESSING1_ADDRESSABLE_URI_SCHEMES); + } + + if (tp_proxy_has_interface_by_id (self, + TP_IFACE_QUARK_PROTOCOL_INTERFACE_PRESENCE1)) + { + DEBUG ("%s/%s implements Presence", self->priv->cm_name, + self->priv->name); + + self->priv->presence_statuses = tp_asv_get_boxed ( + self->priv->protocol_properties, + TP_PROP_PROTOCOL_INTERFACE_PRESENCE1_STATUSES, + TP_HASH_TYPE_STATUS_SPEC_MAP); + + if (self->priv->presence_statuses != NULL) + { + GHashTableIter iter; + gpointer k, v; + + g_hash_table_ref (self->priv->presence_statuses); + + DEBUG ("%s/%s presence statuses:", self->priv->cm_name, + self->priv->name); + g_hash_table_iter_init (&iter, self->priv->presence_statuses); + + while (g_hash_table_iter_next (&iter, &k, &v)) + { + guint type; + gboolean on_self, message; + + tp_value_array_unpack (v, 3, + &type, + &on_self, + &message); + DEBUG ("\tstatus '%s': type %u%s%s", + (const gchar *) k, type, on_self ? ", can set on self" : "", + message ? ", has message" : ""); + } + } + } + /* become ready immediately */ _tp_proxy_set_feature_prepared (proxy, TP_PROTOCOL_FEATURE_PARAMETERS, had_immutables); @@ -739,6 +832,45 @@ tp_protocol_class_init (TpProtocolClass *klass) NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * TpProtocol:addressable-vcard-fields: + * + * A non-%NULL #GStrv of vCard fields supported by this protocol. + * If this protocol does not support addressing contacts by a vCard field, + * the list is empty. + * + * For instance, a SIP connection manager that supports calling contacts + * by SIP URI (vCard field SIP) or telephone number (vCard field TEL) + * might have { "sip", "tel", NULL }. + * + * Since: 0.UNRELEASED + */ + g_object_class_install_property (object_class, PROP_ADDRESSABLE_VCARD_FIELDS, + g_param_spec_boxed ("addressable-vcard-fields", + "AddressableVCardFields", + "A list of vCard fields", + G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * TpProtocol:addressable-uri-schemes: + * + * A non-%NULL #GStrv of URI schemes supported by this protocol. + * If this protocol does not support addressing contacts by URI, + * the list is empty. + * + * For instance, a SIP connection manager that supports calling contacts + * by SIP URI (sip:alice@example.com, sips:bob@example.com) + * or telephone number (tel:+1-555-0123) might have + * { "sip", "sips", "tel", NULL }. + * + * Since: 0.UNRELEASED + */ + g_object_class_install_property (object_class, PROP_ADDRESSABLE_URI_SCHEMES, + g_param_spec_boxed ("addressable-uri-schemes", + "AddressableURISchemes", + "A list of URI schemes", + G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + proxy_class->list_features = tp_protocol_list_features; proxy_class->must_have_unique_name = FALSE; proxy_class->interface = TP_IFACE_QUARK_PROTOCOL; @@ -1194,7 +1326,7 @@ init_gvalue_from_dbus_sig (const gchar *sig, static gboolean parse_default_value (GValue *value, const gchar *sig, - gchar *string, + gchar *raw_value, GKeyFile *file, const gchar *group, const gchar *key) @@ -1217,31 +1349,28 @@ parse_default_value (GValue *value, * So, on error, let's fall back to more lenient parsing that explicitly * allows everything we historically allowed. */ g_error_free (error); - s = g_key_file_get_value (file, group, key, NULL); - if (s == NULL) + if (raw_value == NULL) return FALSE; - for (p = s; *p != '\0'; p++) + for (p = raw_value; *p != '\0'; p++) { *p = g_ascii_tolower (*p); } - if (!tp_strdiff (s, "1") || !tp_strdiff (s, "true")) + if (!tp_strdiff (raw_value, "1") || !tp_strdiff (raw_value, "true")) { g_value_set_boolean (value, TRUE); } - else if (!tp_strdiff (s, "0") || !tp_strdiff (s, "false")) + else if (!tp_strdiff (raw_value, "0") || !tp_strdiff (raw_value, "false")) { g_value_set_boolean (value, TRUE); } else { - g_free (s); return FALSE; } - g_free (s); return TRUE; case 's': @@ -1290,7 +1419,7 @@ parse_default_value (GValue *value, case 'n': case 'i': case 'x': - if (string[0] == '\0') + if (raw_value[0] == '\0') { return FALSE; } @@ -1437,20 +1566,34 @@ _tp_protocol_parse_channel_class (GKeyFile *file, const gchar *dbus_type; GValue *v = g_slice_new0 (GValue); - value = g_key_file_get_string (file, group, *key, NULL); + value = g_key_file_get_value (file, group, *key, NULL); /* keys without a space are reserved */ if (space == NULL) - goto cleanup; + { + DEBUG ("\t'%s' isn't a fixed property", *key); + goto cleanup; + } property = g_strndup (*key, space - *key); dbus_type = space + 1; if (!init_gvalue_from_dbus_sig (dbus_type, v)) - goto cleanup; + { + DEBUG ("\tunable to parse D-Bus type '%s' for '%s' in a " + ".manager file", dbus_type, property); + goto cleanup; + } if (!parse_default_value (v, dbus_type, value, file, group, *key)) - goto cleanup; + { + DEBUG ("\tunable to parse '%s' as a value of type '%s' for '%s'", + value, dbus_type, property); + goto cleanup; + } + + DEBUG ("\tfixed: '%s' of type '%s' = '%s'", + property, dbus_type, value); /* transfer ownership to @ret */ g_hash_table_insert (ret, property, v); @@ -1476,16 +1619,27 @@ cleanup: } static GValueArray * -_tp_protocol_parse_rcc (GKeyFile *file, +_tp_protocol_parse_rcc (const gchar *cm_debug_name, + const gchar *protocol_debug_name, + GKeyFile *file, const gchar *group) { GHashTable *fixed; GStrv allowed; GValueArray *ret; + guint i; + + DEBUG ("%s/%s: parsing requestable channel class '%s'", cm_debug_name, + protocol_debug_name, group); fixed = _tp_protocol_parse_channel_class (file, group); allowed = g_key_file_get_string_list (file, group, "allowed", NULL, NULL); + for (i = 0; allowed != NULL && allowed[i] != NULL; i++) + { + DEBUG ("\tallowed: '%s'", allowed[i]); + } + ret = tp_value_array_build (2, TP_HASH_TYPE_CHANNEL_CLASS, fixed, G_TYPE_STRV, allowed, @@ -1504,6 +1658,7 @@ _tp_protocol_parse_manager_file (GKeyFile *file, gchar **protocol_name) { GHashTable *immutables; + GHashTable *status_specs; GPtrArray *param_specs, *rccs; const gchar *name; gchar **rcc_groups, **rcc_group; @@ -1580,7 +1735,7 @@ _tp_protocol_parse_manager_file (GKeyFile *file, } def = g_strdup_printf ("default-%s", param.name); - value = g_key_file_get_string (file, group, def, NULL); + value = g_key_file_get_value (file, group, def, NULL); init_gvalue_from_dbus_sig (param.dbus_signature, ¶m.default_value); @@ -1622,8 +1777,6 @@ _tp_protocol_parse_manager_file (GKeyFile *file, } } - g_strfreev (keys); - immutables = tp_asv_new ( TP_PROP_PROTOCOL_PARAMETERS, TP_ARRAY_TYPE_PARAM_SPEC_LIST, param_specs, NULL); @@ -1683,11 +1836,89 @@ _tp_protocol_parse_manager_file (GKeyFile *file, if (rcc_groups != NULL) { for (rcc_group = rcc_groups; *rcc_group != NULL; rcc_group++) - g_ptr_array_add (rccs, _tp_protocol_parse_rcc (file, *rcc_group)); + g_ptr_array_add (rccs, + _tp_protocol_parse_rcc (cm_debug_name, name, file, *rcc_group)); } g_strfreev (rcc_groups); + /* Statuses */ + status_specs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) tp_value_array_free); + + for (key = keys; key != NULL && *key != NULL; key++) + { + if (g_str_has_prefix (*key, "status-")) + { + GValueArray *ubb; + gint64 type; + gboolean on_self = FALSE, has_message = FALSE; + gchar *value, *endptr; + gchar **strv, **iter; + + if (!tp_strdiff (*key, "status-")) + { + DEBUG ("'status-' is not a valid status"); + continue; + } + + value = g_key_file_get_value (file, group, *key, NULL); + strv = g_strsplit (value, " ", 0); + g_free (value); + + type = g_ascii_strtoll (strv[0], &endptr, 10); + + if (endptr <= strv[0] || *endptr != '\0') + { + DEBUG ("invalid (non-numeric?) status type %s", strv[0]); + goto next_status; + } + + if (type == TP_CONNECTION_PRESENCE_TYPE_UNSET || + type < 0 || type >= TP_NUM_CONNECTION_PRESENCE_TYPES) + { + DEBUG ("presence type out of range: %" G_GINT64_FORMAT, + type); + goto next_status; + } + + for (iter = strv + 1; *iter != NULL; iter++) + { + if (!tp_strdiff (*iter, "settable")) + on_self = TRUE; + else if (!tp_strdiff (*iter, "message")) + has_message = TRUE; + else + DEBUG ("unknown status modifier '%s'", *iter); + } + + ubb = tp_value_array_build (3, + G_TYPE_UINT, (guint) type, + G_TYPE_BOOLEAN, on_self, + G_TYPE_BOOLEAN, has_message, + G_TYPE_INVALID); + + /* strlen ("status-") == 7 */ + g_hash_table_insert (status_specs, g_strdup (*key + 7), + ubb); + DEBUG ("Status '%s': type %u%s%s", *key + 7, (guint) type, + on_self ? ", can set on self" : "", + has_message ? ", has message" : ""); + +next_status: + g_strfreev (strv); + } + } + + if (g_hash_table_size (status_specs) > 0) + tp_asv_take_boxed (immutables, + TP_PROP_PROTOCOL_INTERFACE_PRESENCE1_STATUSES, + TP_HASH_TYPE_STATUS_SPEC_MAP, status_specs); + else + g_hash_table_unref (status_specs); + + g_strfreev (keys); + tp_asv_take_boxed (immutables, TP_PROP_PROTOCOL_REQUESTABLE_CHANNEL_CLASSES, TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, rccs); @@ -1732,3 +1963,348 @@ tp_protocol_get_cm_name (TpProtocol *self) return self->priv->cm_name; } + +/* + * Handle the result from a tp_cli_protocol_* function that + * returns one string. user_data is a #GTask. + */ +static void +tp_protocol_async_string_cb (TpProxy *proxy, + const gchar *normalized, + const GError *error, + gpointer user_data, + GObject *weak_object G_GNUC_UNUSED) +{ + if (error == NULL) + g_task_return_pointer (user_data, g_strdup (normalized), g_free); + else + g_task_return_error (user_data, g_error_copy (error)); +} + +/** + * tp_protocol_normalize_contact_async: + * @self: a protocol + * @contact: a contact identifier, possibly invalid + * @cancellable: (allow-none): may be used to cancel the async request + * @callback: (scope async): a callback to call when + * the request is satisfied + * @user_data: (closure) (allow-none): data to pass to @callback + * + * Perform best-effort offline contact normalization. This does syntactic + * normalization (e.g. transforming case-insensitive text to lower-case), + * but does not query servers or anything similar. + * + * Since: 0.UNRELEASED + */ +void +tp_protocol_normalize_contact_async (TpProtocol *self, + const gchar *contact, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_return_if_fail (TP_IS_PROTOCOL (self)); + g_return_if_fail (contact != NULL); + /* this makes no sense to call for its side-effects */ + g_return_if_fail (callback != NULL); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, tp_protocol_normalize_contact_async); + + tp_cli_protocol_call_normalize_contact (self, -1, contact, + tp_protocol_async_string_cb, task, g_object_unref, NULL); +} + +/** + * tp_protocol_normalize_contact_finish: + * @self: a protocol + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Interpret the result of tp_protocol_normalize_contact_async(). + * + * Returns: (transfer full): the normalized form of @contact, + * or %NULL on error + * Since: 0.UNRELEASED + */ +gchar * +tp_protocol_normalize_contact_finish (TpProtocol *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, + tp_protocol_normalize_contact_async), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + +/** + * tp_protocol_identify_account_async: + * @self: a protocol + * @vardict: the account parameters as a #GVariant of + * type %G_VARIANT_TYPE_VARDICT. If it is floating, ownership will + * be taken, as if via g_variant_ref_sink(). + * @cancellable: (allow-none): may be used to cancel the async request + * @callback: (scope async): a callback to call when + * the request is satisfied + * @user_data: (closure) (allow-none): data to pass to @callback + * + * Return a string that could identify the account with the given + * parameters. In most protocols that string is a normalized 'account' + * parameter, but some protocols have more complex requirements; + * for instance, on IRC, the 'account' (nickname) is insufficient, + * and must be combined with a server or network name. + * + * Since: 0.UNRELEASED + */ +void +tp_protocol_identify_account_async (TpProtocol *self, + GVariant *vardict, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + GHashTable *asv; + + g_return_if_fail (TP_IS_PROTOCOL (self)); + g_return_if_fail (vardict != NULL); + g_return_if_fail (g_variant_is_of_type (vardict, G_VARIANT_TYPE_VARDICT)); + /* this makes no sense to call for its side-effects */ + g_return_if_fail (callback != NULL); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, tp_protocol_identify_account_async); + g_variant_ref_sink (vardict); + asv = _tp_asv_from_vardict (vardict); + tp_cli_protocol_call_identify_account (self, -1, asv, + tp_protocol_async_string_cb, task, g_object_unref, NULL); + g_hash_table_unref (asv); + g_variant_unref (vardict); +} + +/** + * tp_protocol_identify_account_finish: + * @self: a protocol + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Interpret the result of tp_protocol_identify_account_async(). + * + * Returns: (transfer full): a string identifying the account, + * or %NULL on error + * Since: 0.UNRELEASED + */ +gchar * +tp_protocol_identify_account_finish (TpProtocol *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, + tp_protocol_identify_account_async), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + +/** + * tp_protocol_normalize_contact_uri_async: + * @self: a protocol + * @uri: a contact URI, possibly invalid + * @cancellable: (allow-none): may be used to cancel the async request + * @callback: (scope async): a callback to call when the request is satisfied + * @user_data: (closure) (allow-none): data to pass to @callback + * + * Perform best-effort offline contact normalization, for a contact in + * the form of a URI. This method will fail if the URI is not in a + * scheme supported by this protocol or connection manager. + * + * Since: 0.UNRELEASED + */ +void +tp_protocol_normalize_contact_uri_async (TpProtocol *self, + const gchar *uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_return_if_fail (TP_IS_PROTOCOL (self)); + g_return_if_fail (uri != NULL); + /* this makes no sense to call for its side-effects */ + g_return_if_fail (callback != NULL); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, tp_protocol_normalize_contact_uri_async); + + tp_cli_protocol_interface_addressing1_call_normalize_contact_uri (self, -1, + uri, tp_protocol_async_string_cb, task, g_object_unref, NULL); +} + +/** + * tp_protocol_normalize_contact_uri_finish: + * @self: a protocol + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Interpret the result of tp_protocol_normalize_contact_uri_async(). + * + * Returns: (transfer full): the normalized form of @uri, + * or %NULL on error + * Since: 0.UNRELEASED + */ +gchar * +tp_protocol_normalize_contact_uri_finish (TpProtocol *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, + tp_protocol_normalize_contact_uri_async), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + +/** + * tp_protocol_normalize_vcard_address_async: + * @self: a protocol + * @field: a vCard field + * @value: an address that is a value of @field + * @cancellable: (allow-none): may be used to cancel the async request + * @callback: (scope async): a callback to call when the request is satisfied + * @user_data: (closure) (allow-none): data to pass to @callback + * + * Perform best-effort offline contact normalization, for a contact in + * the form of a vCard field. This method will fail if the vCard field + * is not supported by this protocol or connection manager. + * + * Since: 0.UNRELEASED + */ +void +tp_protocol_normalize_vcard_address_async (TpProtocol *self, + const gchar *field, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_return_if_fail (TP_IS_PROTOCOL (self)); + g_return_if_fail (!tp_str_empty (field)); + g_return_if_fail (value != NULL); + /* this makes no sense to call for its side-effects */ + g_return_if_fail (callback != NULL); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, tp_protocol_normalize_vcard_address_async); + + tp_cli_protocol_interface_addressing1_call_normalize_vcard_address (self, -1, + field, value, tp_protocol_async_string_cb, task, g_object_unref, NULL); +} + +/** + * tp_protocol_normalize_vcard_address_finish: + * @self: a protocol + * @result: a #GAsyncResult + * @error: a #GError to fill + * + * Interpret the result of tp_protocol_normalize_vcard_address_async(). + * + * Returns: (transfer full): the normalized form of @value, + * or %NULL on error + * Since: 0.UNRELEASED + */ +gchar * +tp_protocol_normalize_vcard_address_finish (TpProtocol *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, self), NULL); + g_return_val_if_fail (g_async_result_is_tagged (result, + tp_protocol_normalize_vcard_address_async), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + +/** + * tp_protocol_get_addressable_vcard_fields: + * @self: a protocol object + * + * <!-- --> + * + * Returns: (transfer none): the value of #TpProtocol:addressable-vcard-fields + * Since: 0.UNRELEASED + */ +const gchar * const * +tp_protocol_get_addressable_vcard_fields (TpProtocol *self) +{ + g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL); + return (const gchar * const *) self->priv->addressable_vcard_fields; +} + +/** + * tp_protocol_get_addressable_uri_schemes: + * @self: a protocol object + * + * <!-- --> + * + * Returns: (transfer none): the value of #TpProtocol:addressable-uri-schemes + * Since: 0.UNRELEASED + */ +const gchar * const * +tp_protocol_get_addressable_uri_schemes (TpProtocol *self) +{ + g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL); + return (const gchar * const *) self->priv->addressable_uri_schemes; +} + +/** + * tp_protocol_dup_presence_statuses: + * @self: a protocol object + * + * Return the presence statuses that might be supported by connections + * to this protocol. + * + * It is possible that some of these statuses will not actually be supported + * by a connection: for instance, an XMPP connection manager would + * include "hidden" in this list, even though not all XMPP servers allow + * users to be online-but-hidden. + * + * Returns: (transfer full) (element-type TelepathyGLib.PresenceStatusSpec): a + * list of statuses, or %NULL if unknown + */ +GList * +tp_protocol_dup_presence_statuses (TpProtocol *self) +{ + GHashTableIter iter; + gpointer k, v; + GList *l = NULL; + + g_return_val_if_fail (TP_IS_PROTOCOL (self), NULL); + + if (self->priv->presence_statuses == NULL) + return NULL; + + g_hash_table_iter_init (&iter, self->priv->presence_statuses); + + while (g_hash_table_iter_next (&iter, &k, &v)) + { + guint type; + gboolean on_self, message; + + tp_value_array_unpack (v, 3, + &type, + &on_self, + &message); + + l = g_list_prepend (l, tp_presence_status_spec_new (k, type, + on_self, message)); + } + + return g_list_reverse (l); +} diff --git a/telepathy-glib/protocol.h b/telepathy-glib/protocol.h index 51dd904b5..eef1895c6 100644 --- a/telepathy-glib/protocol.h +++ b/telepathy-glib/protocol.h @@ -98,6 +98,20 @@ const gchar * const * /* gtk-doc sucks */ tp_protocol_get_authentication_types (TpProtocol *self); +_TP_AVAILABLE_IN_UNRELEASED +const gchar * const * +/* ... */ +tp_protocol_get_addressable_vcard_fields (TpProtocol *self); + +_TP_AVAILABLE_IN_UNRELEASED +const gchar * const * +/* ... */ +tp_protocol_get_addressable_uri_schemes (TpProtocol *self); + +_TP_AVAILABLE_IN_UNRELEASED +GList *tp_protocol_dup_presence_statuses (TpProtocol *self) + G_GNUC_WARN_UNUSED_RESULT; + #define TP_PROTOCOL_FEATURE_CORE \ (tp_protocol_get_feature_quark_core ()) GQuark tp_protocol_get_feature_quark_core (void) G_GNUC_CONST; @@ -110,6 +124,55 @@ TpCapabilities *tp_protocol_get_capabilities (TpProtocol *self); _TP_AVAILABLE_IN_0_16 TpAvatarRequirements * tp_protocol_get_avatar_requirements (TpProtocol *self); +_TP_AVAILABLE_IN_UNRELEASED +void tp_protocol_normalize_contact_async (TpProtocol *self, + const gchar *contact, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +_TP_AVAILABLE_IN_UNRELEASED +gchar *tp_protocol_normalize_contact_finish (TpProtocol *self, + GAsyncResult *result, + GError **error); + +_TP_AVAILABLE_IN_UNRELEASED +void tp_protocol_identify_account_async (TpProtocol *self, + GVariant *vardict, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +_TP_AVAILABLE_IN_UNRELEASED +gchar *tp_protocol_identify_account_finish (TpProtocol *self, + GAsyncResult *result, + GError **error); + +_TP_AVAILABLE_IN_UNRELEASED +void tp_protocol_normalize_contact_uri_async (TpProtocol *self, + const gchar *uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +_TP_AVAILABLE_IN_UNRELEASED +gchar *tp_protocol_normalize_contact_uri_finish (TpProtocol *self, + GAsyncResult *result, + GError **error); + +_TP_AVAILABLE_IN_UNRELEASED +void tp_protocol_normalize_vcard_address_async (TpProtocol *self, + const gchar *field, + const gchar *value, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +_TP_AVAILABLE_IN_UNRELEASED +gchar *tp_protocol_normalize_vcard_address_finish (TpProtocol *self, + GAsyncResult *result, + GError **error); + G_END_DECLS #endif diff --git a/tests/dbus/protocol-objects.c b/tests/dbus/protocol-objects.c index 5ccadfbbe..5cba6dff9 100644 --- a/tests/dbus/protocol-objects.c +++ b/tests/dbus/protocol-objects.c @@ -442,6 +442,154 @@ test_protocol_object_from_file (Test *test, check_avatar_requirements (req); } +static void +test_normalize (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + GAsyncResult *result = NULL; + gchar *s; + + tp_tests_proxy_run_until_prepared (test->cm, NULL); + test->protocol = g_object_ref ( + tp_connection_manager_get_protocol (test->cm, "example")); + + tp_protocol_normalize_contact_async (test->protocol, + "MiXeDcAsE", NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_normalize_contact_finish (test->protocol, result, + &test->error); + g_assert_no_error (test->error); + g_assert_cmpstr (s, ==, "mixedcase"); + g_clear_object (&result); + g_free (s); + + tp_protocol_normalize_contact_async (test->protocol, + "", NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_normalize_contact_finish (test->protocol, result, + &test->error); + g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_HANDLE); + g_assert_cmpstr (s, ==, NULL); + g_clear_object (&result); + g_clear_error (&test->error); + + tp_protocol_normalize_contact_uri_async (test->protocol, + "xmpp:MiXeDcAsE", NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_normalize_contact_uri_finish (test->protocol, result, + &test->error); + g_assert_no_error (test->error); + g_assert_cmpstr (s, ==, "xmpp:mixedcase"); + g_clear_object (&result); + g_free (s); + + tp_protocol_normalize_contact_uri_async (test->protocol, + "xmpp:", NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_normalize_contact_uri_finish (test->protocol, result, + &test->error); + g_assert_cmpstr (s, ==, NULL); + g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT); + g_clear_object (&result); + g_clear_error (&test->error); + + tp_protocol_normalize_contact_uri_async (test->protocol, + "http://example.com", NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_normalize_contact_uri_finish (test->protocol, result, + &test->error); + g_assert_cmpstr (s, ==, NULL); + g_assert_error (test->error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED); + g_clear_object (&result); + g_clear_error (&test->error); + + tp_protocol_normalize_vcard_address_async (test->protocol, + "x-jabber", "MiXeDcAsE", NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_normalize_vcard_address_finish (test->protocol, result, + &test->error); + g_assert_no_error (test->error); + g_assert_cmpstr (s, ==, "mixedcase"); + g_clear_object (&result); + g_free (s); + + tp_protocol_normalize_vcard_address_async (test->protocol, + "x-jabber", "", NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_normalize_vcard_address_finish (test->protocol, result, + &test->error); + g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT); + g_assert_cmpstr (s, ==, NULL); + g_clear_object (&result); + g_clear_error (&test->error); + + tp_protocol_normalize_vcard_address_async (test->protocol, + "x-skype", "", NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_normalize_vcard_address_finish (test->protocol, result, + &test->error); + g_assert_error (test->error, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED); + g_assert_cmpstr (s, ==, NULL); + g_clear_object (&result); + g_clear_error (&test->error); +} + +static void +test_id (Test *test, + gconstpointer data G_GNUC_UNUSED) +{ + GAsyncResult *result = NULL; + gchar *s; + + tp_tests_proxy_run_until_prepared (test->cm, NULL); + test->protocol = g_object_ref ( + tp_connection_manager_get_protocol (test->cm, "example")); + + tp_protocol_identify_account_async (test->protocol, + g_variant_new_parsed ("{ 'account': <'Hello'> }"), + NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_identify_account_finish (test->protocol, result, + &test->error); + g_assert_no_error (test->error); + g_assert_cmpstr (s, ==, "hello"); + g_clear_object (&result); + g_free (s); + + tp_protocol_identify_account_async (test->protocol, + g_variant_new_parsed ("{ 'account': <'Hello'>, 'unknown-param': <42> }"), + NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_identify_account_finish (test->protocol, result, + &test->error); + g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT); + g_assert_cmpstr (s, ==, NULL); + g_clear_object (&result); + g_clear_error (&test->error); + + tp_protocol_identify_account_async (test->protocol, + g_variant_new_parsed ("@a{sv} {}"), + NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_identify_account_finish (test->protocol, result, + &test->error); + g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT); + g_assert_cmpstr (s, ==, NULL); + g_clear_object (&result); + g_clear_error (&test->error); + + tp_protocol_identify_account_async (test->protocol, + g_variant_new_parsed ("@a{sv} { 'account': <''> }"), + NULL, tp_tests_result_ready_cb, &result); + tp_tests_run_until_result (&result); + s = tp_protocol_identify_account_finish (test->protocol, result, + &test->error); + g_assert_error (test->error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT); + g_assert_cmpstr (s, ==, NULL); + g_clear_object (&result); + g_clear_error (&test->error); +} + int main (int argc, char **argv) @@ -461,6 +609,10 @@ main (int argc, test_protocol_object, teardown); g_test_add ("/protocol-objects/object-from-file", Test, NULL, setup, test_protocol_object_from_file, teardown); + g_test_add ("/protocol-objects/normalize", Test, NULL, setup, + test_normalize, teardown); + g_test_add ("/protocol-objects/id", Test, NULL, setup, + test_id, teardown); return tp_tests_run_with_bus (); } |