/* Object representing the capabilities a Connection or a Contact supports. * * Copyright (C) 2010-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 "config.h" #include "telepathy-glib/capabilities.h" #include "telepathy-glib/capabilities-internal.h" #include #include #include #include #include #include #define DEBUG_FLAG TP_DEBUG_CONNECTION #include "telepathy-glib/debug-internal.h" #include "telepathy-glib/variant-util-internal.h" /** * SECTION:capabilities * @title: TpCapabilities * @short_description: object representing capabilities * * #TpCapabilities objects represent the capabilities a #TpConnection * or a #TpContact supports. * * Since: 0.11.3 */ /** * TpCapabilities: * * An object representing capabilities a #TpConnection or #TpContact supports. * * Since: 0.11.3 */ struct _TpCapabilitiesClass { /**/ GObjectClass parent_class; }; struct _TpCapabilities { /**/ GObject parent; TpCapabilitiesPrivate *priv; }; G_DEFINE_TYPE (TpCapabilities, tp_capabilities, G_TYPE_OBJECT) enum { PROP_CHANNEL_CLASSES = 1, PROP_CONTACT_SPECIFIC, PROP_CHANNEL_CLASSES_VARIANT, N_PROPS }; struct _TpCapabilitiesPrivate { GPtrArray *classes; gboolean contact_specific; GVariant *classes_variant; }; /** * tp_capabilities_get_channel_classes: * @self: a #TpCapabilities object * * * * Returns: (transfer none) (element-type GHashTable): the same #GPtrArray as * the #TpCapabilities:channel-classes property * * Since: 0.11.3 */ GPtrArray * tp_capabilities_get_channel_classes (TpCapabilities *self) { g_return_val_if_fail (self != NULL, NULL); return self->priv->classes; } /** * tp_capabilities_is_specific_to_contact: * @self: a #TpCapabilities object * * * * Returns: the same #gboolean as the #TpCapabilities:contact-specific property * * Since: 0.11.3 */ gboolean tp_capabilities_is_specific_to_contact (TpCapabilities *self) { g_return_val_if_fail (TP_IS_CAPABILITIES (self), FALSE); return self->priv->contact_specific; } static void tp_capabilities_constructed (GObject *object) { void (*chain_up) (GObject *) = ((GObjectClass *) tp_capabilities_parent_class)->constructed; TpCapabilities *self = TP_CAPABILITIES (object); g_assert (self->priv->classes != NULL); if (chain_up != NULL) chain_up (object); } static void tp_capabilities_dispose (GObject *object) { TpCapabilities *self = TP_CAPABILITIES (object); if (self->priv->classes != NULL) { g_boxed_free (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, self->priv->classes); self->priv->classes = NULL; } tp_clear_pointer (&self->priv->classes_variant, g_variant_unref); ((GObjectClass *) tp_capabilities_parent_class)->dispose (object); } static void tp_capabilities_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TpCapabilities *self = TP_CAPABILITIES (object); switch (property_id) { case PROP_CHANNEL_CLASSES: g_value_set_boxed (value, self->priv->classes); break; case PROP_CONTACT_SPECIFIC: g_value_set_boolean (value, self->priv->contact_specific); break; case PROP_CHANNEL_CLASSES_VARIANT: g_value_set_variant (value, self->priv->classes_variant); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void tp_capabilities_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { TpCapabilities *self = TP_CAPABILITIES (object); switch (property_id) { case PROP_CHANNEL_CLASSES: self->priv->classes = g_value_dup_boxed (value); self->priv->classes_variant = _tp_boxed_to_variant ( TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, "a(a{sv}as)", self->priv->classes); break; case PROP_CONTACT_SPECIFIC: self->priv->contact_specific = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void tp_capabilities_class_init (TpCapabilitiesClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (TpCapabilitiesPrivate)); object_class->get_property = tp_capabilities_get_property; object_class->set_property = tp_capabilities_set_property; object_class->constructed = tp_capabilities_constructed; object_class->dispose = tp_capabilities_dispose; /** * TpCapabilities:channel-classes: * * The underlying data structure used by Telepathy to represent the * requests that can succeed. * * This can be used by advanced clients to determine whether an unusually * complex request would succeed. See the Telepathy D-Bus API Specification * for details of how to interpret the returned #GPtrArray of * #TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS. * * The higher-level methods like * tp_capabilities_supports_text_chats() are likely to be more useful to * the majority of clients. */ param_spec = g_param_spec_boxed ("channel-classes", "GPtrArray of TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS", "The channel classes supported", TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CHANNEL_CLASSES, param_spec); /** * TpCapabilities:contact-specific: * * Whether this object accurately describes the capabilities of a particular * contact, or if it's only a guess based on the capabilities of the * underlying connection. */ param_spec = g_param_spec_boolean ("contact-specific", "contact specific", "TRUE if this object describes the capabilities of a particular contact", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONTACT_SPECIFIC, param_spec); /** * TpCapabilities:channel-classes-variant: * * The underlying data structure used by Telepathy to represent the * requests that can succeed. * * This can be used by advanced clients to determine whether an unusually * complex request would succeed. See the Telepathy D-Bus API Specification * for details of how to interpret the returned #GVariant of type * a(a{sv}as). * * The higher-level methods like * tp_capabilities_supports_text_chats() are likely to be more useful to * the majority of clients. * * Since: 0.19.0 */ param_spec = g_param_spec_variant ("channel-classes-variant", "GVariant of type a(a{sv}as)", "The channel classes supported", G_VARIANT_TYPE ("a(a{sv}as)"), NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CHANNEL_CLASSES_VARIANT, param_spec); } static void tp_capabilities_init (TpCapabilities *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TYPE_CAPABILITIES, TpCapabilitiesPrivate); } /* NULL-safe for @classes */ TpCapabilities * _tp_capabilities_new (const GPtrArray *classes, gboolean contact_specific) { GPtrArray *empty = NULL; TpCapabilities *self; if (classes == NULL) { empty = g_ptr_array_sized_new (0); classes = empty; } self = g_object_new (TP_TYPE_CAPABILITIES, "channel-classes", classes, "contact-specific", contact_specific, NULL); if (empty != NULL) g_ptr_array_unref (empty); return self; } static gboolean supports_simple_channel (TpCapabilities *self, const gchar *expected_chan_type, TpHandleType expected_handle_type) { guint i; g_return_val_if_fail (TP_IS_CAPABILITIES (self), FALSE); for (i = 0; i < self->priv->classes->len; i++) { GValueArray *arr = g_ptr_array_index (self->priv->classes, i); GHashTable *fixed; const gchar * const *allowed; const gchar *chan_type; TpHandleType handle_type; gboolean valid; tp_value_array_unpack (arr, 2, &fixed, &allowed); if (g_hash_table_size (fixed) != 2) continue; chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); handle_type = tp_asv_get_uint32 (fixed, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &valid); if (!valid) continue; if (!tp_strdiff (chan_type, expected_chan_type) && handle_type == expected_handle_type) return TRUE; } return FALSE; } /** * tp_capabilities_supports_text_chats: * @self: a #TpCapabilities object * * Return whether private text channels can be established by providing * a contact identifier, for instance by calling * tp_account_channel_request_new_text() followed by * tp_account_channel_request_set_target_contact(). * * If the protocol is such that text chats can be established, but only via a * more elaborate D-Bus API than normal (because more information is needed), * then this method will return %FALSE. * * Returns: %TRUE if a channel request containing Text as ChannelType, * HandleTypeContact as TargetHandleType and a contact identifier can be * expected to work, %FALSE otherwise. * * Since: 0.11.3 */ gboolean tp_capabilities_supports_text_chats (TpCapabilities *self) { return supports_simple_channel (self, TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_CONTACT); } /** * tp_capabilities_supports_text_chatrooms: * @self: a #TpCapabilities object * * If the #TpCapabilities:contact-specific property is %FALSE, this function * checks if named text chatrooms can be joined by providing a chatroom * identifier, for instance by calling * tp_account_channel_request_new_text() followed by * tp_account_channel_request_set_target_id() with %TP_HANDLE_TYPE_ROOM. * * If the #TpCapabilities:contact-specific property is %TRUE, this function * checks if the contact associated with this #TpCapabilities can be invited * to named text chatrooms. * * If the protocol is such that chatrooms can be joined or contacts can be * invited, but only via a more elaborate D-Bus API than normal * (because more information is needed), then this method will return %FALSE. * * Returns: %TRUE if a channel request containing Text as ChannelType, * HandleTypeRoom as TargetHandleType and a channel identifier can be * expected to work, %FALSE otherwise. * * Since: 0.11.3 */ gboolean tp_capabilities_supports_text_chatrooms (TpCapabilities *self) { return supports_simple_channel (self, TP_IFACE_CHANNEL_TYPE_TEXT, TP_HANDLE_TYPE_ROOM); } /** * tp_capabilities_supports_sms: * @self: a #TpCapabilities object * * If the #TpCapabilities:contact-specific property is %FALSE, this function * checks if SMS text channels can be requested with the connection associated * with this #TpCapabilities. * * If the #TpCapabilities:contact-specific property is %TRUE, this function * checks if the contact associated with this #TpCapabilities supports * SMS text channels. * * Returns: %TRUE if a channel request containing Text as ChannelType, * HandleTypeContact as TargetHandleType, a channel identifier and * #TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL set to %TRUE can be * expected to work, %FALSE otherwise. * * Since: 0.19.0 */ gboolean tp_capabilities_supports_sms (TpCapabilities *self) { guint i; g_return_val_if_fail (TP_IS_CAPABILITIES (self), FALSE); for (i = 0; i < self->priv->classes->len; i++) { GValueArray *arr = g_ptr_array_index (self->priv->classes, i); GHashTable *fixed; const gchar * const *allowed; const gchar *chan_type; TpHandleType handle_type; gboolean valid; guint nb_fixed_props; tp_value_array_unpack (arr, 2, &fixed, &allowed); handle_type = tp_asv_get_uint32 (fixed, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &valid); if (!valid) continue; if (handle_type != TP_HANDLE_TYPE_CONTACT) continue; chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); if (tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT)) continue; /* SMSChannel be either in fixed or allowed properties */ if (tp_asv_get_boolean (fixed, TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL, NULL)) { /* In fixed, succeed if there is no more fixed properties required */ nb_fixed_props = 3; } else { /* Not in fixed; check allowed */ if (!tp_strv_contains (allowed, TP_PROP_CHANNEL_INTERFACE_SMS_SMS_CHANNEL)) continue; nb_fixed_props = 2; } if (g_hash_table_size (fixed) == nb_fixed_props) return TRUE; } return FALSE; } static gboolean supports_call_full (TpCapabilities *self, TpHandleType expected_handle_type, gboolean expected_initial_audio, gboolean expected_initial_video) { guint i; g_return_val_if_fail (TP_IS_CAPABILITIES (self), FALSE); for (i = 0; i < self->priv->classes->len; i++) { GValueArray *arr = g_ptr_array_index (self->priv->classes, i); GHashTable *fixed_prop; const gchar * const *allowed_prop; const gchar *chan_type; TpHandleType handle_type; gboolean valid; guint nb_fixed_props = 2; tp_value_array_unpack (arr, 2, &fixed_prop, &allowed_prop); chan_type = tp_asv_get_string (fixed_prop, TP_PROP_CHANNEL_CHANNEL_TYPE); if (tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_CALL)) continue; handle_type = tp_asv_get_uint32 (fixed_prop, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &valid); if (!valid || handle_type != expected_handle_type) continue; if (expected_initial_audio) { /* We want audio, INITIAL_AUDIO must be in either fixed or allowed */ if (tp_asv_get_boolean (fixed_prop, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL)) { nb_fixed_props++; } else if (!tp_strv_contains (allowed_prop, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO)) { continue; } } if (expected_initial_video) { /* We want video, INITIAL_VIDEO must be in either fixed or allowed */ if (tp_asv_get_boolean (fixed_prop, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL)) { nb_fixed_props++; } else if (!tp_strv_contains (allowed_prop, TP_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO)) { continue; } } /* We found the right class */ if (g_hash_table_size (fixed_prop) == nb_fixed_props) return TRUE; } return FALSE; } /** * tp_capabilities_supports_audio_call: * @self: a #TpCapabilities object * @handle_type: the handle type of the call; #TP_HANDLE_TYPE_CONTACT for * private, #TP_HANDLE_TYPE_ROOM or #TP_HANDLE_TYPE_NONE for conference * (depending on the protocol) * * Return whether audio calls can be established, for instance by calling * tp_account_channel_request_new_audio_call(), followed by * tp_account_channel_request_set_target_id() with @handle_type. * * To check whether requests using * tp_account_channel_request_set_target_contact() would work, set * @handle_type to %TP_HANDLE_TYPE_CONTACT. * * Returns: %TRUE if a channel request containing Call as ChannelType, * @handle_type as TargetHandleType, a True value for InitialAudio and an * identifier of the appropriate type can be expected to work, %FALSE otherwise. * * Since: 0.17.6 */ gboolean tp_capabilities_supports_audio_call (TpCapabilities *self, TpHandleType handle_type) { return supports_call_full (self, handle_type, TRUE, FALSE); } /** * tp_capabilities_supports_audio_video_call: * @self: a #TpCapabilities object * @handle_type: the handle type of the call; #TP_HANDLE_TYPE_CONTACT for * private, #TP_HANDLE_TYPE_ROOM or #TP_HANDLE_TYPE_NONE for conference * (depending on the protocol) * * Return whether audio/video calls can be established, for instance by calling * tp_account_channel_request_new_audio_video_call(), followed by * tp_account_channel_request_set_target_id() with @handle_type. * * To check whether requests using * tp_account_channel_request_set_target_contact() would work, set * @handle_type to %TP_HANDLE_TYPE_CONTACT. * * Returns: %TRUE if a channel request containing Call as ChannelType, * @handle_type as TargetHandleType, a True value for * InitialAudio/InitialVideo and an identifier of the appropriate type can be * expected to work, * %FALSE otherwise. * * Since: 0.17.6 */ gboolean tp_capabilities_supports_audio_video_call (TpCapabilities *self, TpHandleType handle_type) { return supports_call_full (self, handle_type, TRUE, TRUE); } typedef enum { FT_CAP_FLAGS_NONE = 0, FT_CAP_FLAG_URI = (1<<0), FT_CAP_FLAG_OFFSET = (1<<1), FT_CAP_FLAG_DATE = (1<<2), FT_CAP_FLAG_DESCRIPTION = (1<<3) } FTCapFlags; static gboolean supports_file_transfer (TpCapabilities *self, FTCapFlags flags) { guint i; g_return_val_if_fail (TP_IS_CAPABILITIES (self), FALSE); for (i = 0; i < self->priv->classes->len; i++) { GValueArray *arr = g_ptr_array_index (self->priv->classes, i); GHashTable *fixed; const gchar *chan_type; TpHandleType handle_type; gboolean valid; guint n_fixed = 2; const gchar * const *allowed; tp_value_array_unpack (arr, 2, &fixed, &allowed); chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); if (tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) continue; handle_type = tp_asv_get_uint32 (fixed, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &valid); if (!valid) continue; if (handle_type != TP_HANDLE_TYPE_CONTACT) continue; /* ContentType, Filename, Size are mandatory. In principle we could check * that the CM allows them, but not allowing them would be ridiculous, * so we don't. */ if ((flags & FT_CAP_FLAG_DESCRIPTION) != 0) { /* Description makes no sense as a fixed property so we assume * the CM won't be ridiculous */ if (!tp_strv_contains (allowed, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DESCRIPTION)) continue; } if ((flags & FT_CAP_FLAG_DATE) != 0) { /* makes no sense as a fixed property */ if (!tp_strv_contains (allowed, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_DATE)) continue; } if ((flags & FT_CAP_FLAG_URI) != 0) { /* makes no sense as a fixed property */ if (!tp_strv_contains (allowed, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_URI)) continue; } if ((flags & FT_CAP_FLAG_OFFSET) != 0) { /* makes no sense as a fixed property */ if (!tp_strv_contains (allowed, TP_PROP_CHANNEL_TYPE_FILE_TRANSFER_INITIAL_OFFSET)) continue; } if (n_fixed != tp_asv_size (fixed)) continue; return TRUE; } return FALSE; } /** * tp_capabilities_supports_file_transfer: * @self: a #TpCapabilities object * * Return whether private file transfer can be established by providing * a contact identifier. * * Returns: %TRUE if a channel request containing FileTransfer as ChannelType, * HandleTypeContact as TargetHandleType and a contact identifier can be * expected to work, %FALSE otherwise. * * Since: 0.17.6 */ gboolean tp_capabilities_supports_file_transfer (TpCapabilities *self) { return supports_file_transfer (self, FT_CAP_FLAGS_NONE); } /** * tp_capabilities_supports_file_transfer_uri: * @self: a #TpCapabilities object * * * * Returns: %TRUE if requests as described for * tp_capabilities_supports_file_transfer() can also specify the outgoing * file's URI * * Since: 0.19.0 */ gboolean tp_capabilities_supports_file_transfer_uri (TpCapabilities *self) { return supports_file_transfer (self, FT_CAP_FLAG_URI); } /** * tp_capabilities_supports_file_transfer_description: * @self: a #TpCapabilities object * * * * Returns: %TRUE if requests as described for * tp_capabilities_supports_file_transfer() can also specify the outgoing * file's description * * Since: 0.19.0 */ gboolean tp_capabilities_supports_file_transfer_description (TpCapabilities *self) { return supports_file_transfer (self, FT_CAP_FLAG_DESCRIPTION); } /** * tp_capabilities_supports_file_transfer_initial_offset: * @self: a #TpCapabilities object * * Return whether an initial offset other than 0 can be specified on * outgoing file transfers. This can be used to resume partial transfers, * by omitting the part that has already been sent. * * Returns: %TRUE if requests as described for * tp_capabilities_supports_file_transfer() can also specify an * initial offset greater than 0 * * Since: 0.19.0 */ gboolean tp_capabilities_supports_file_transfer_initial_offset (TpCapabilities *self) { return supports_file_transfer (self, FT_CAP_FLAG_OFFSET); } /** * tp_capabilities_supports_file_transfer_timestamp: * @self: a #TpCapabilities object * * * * Returns: %TRUE if requests as described for * tp_capabilities_supports_file_transfer() can also specify the outgoing * file's timestamp * * Since: 0.19.0 */ gboolean tp_capabilities_supports_file_transfer_timestamp (TpCapabilities *self) { return supports_file_transfer (self, FT_CAP_FLAG_DATE); } static gboolean tp_capabilities_supports_tubes_common (TpCapabilities *self, const gchar *expected_channel_type, TpHandleType expected_handle_type, const gchar *service_prop, const gchar *expected_service) { guint i; g_return_val_if_fail (TP_IS_CAPABILITIES (self), FALSE); g_return_val_if_fail (expected_handle_type == TP_HANDLE_TYPE_CONTACT || expected_handle_type == TP_HANDLE_TYPE_ROOM, FALSE); for (i = 0; i < self->priv->classes->len; i++) { GValueArray *arr = g_ptr_array_index (self->priv->classes, i); GHashTable *fixed; const gchar * const *allowed; const gchar *chan_type; TpHandleType handle_type; gboolean valid; guint nb_fixed_props = 2; tp_value_array_unpack (arr, 2, &fixed, &allowed); chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); if (tp_strdiff (chan_type, expected_channel_type)) continue; handle_type = tp_asv_get_uint32 (fixed, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &valid); if (!valid || handle_type != expected_handle_type) continue; if (expected_service != NULL && self->priv->contact_specific) { const gchar *service; nb_fixed_props++; service = tp_asv_get_string (fixed, service_prop); if (tp_strdiff (service, expected_service)) continue; } if (g_hash_table_size (fixed) == nb_fixed_props) return TRUE; } return FALSE; } /** * tp_capabilities_supports_stream_tubes: * @self: a #TpCapabilities object * @handle_type: the handle type of the tube (either #TP_HANDLE_TYPE_CONTACT * or #TP_HANDLE_TYPE_ROOM) * @service: the service of the tube, or %NULL * * If the #TpCapabilities:contact-specific property is %TRUE, this function * checks if the contact associated with this #TpCapabilities supports * stream tubes with @handle_type as TargetHandleType. * If @service is not %NULL, it also checks if it supports stream tubes * with @service as #TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE. * * If the #TpCapabilities:contact-specific property is %FALSE, this function * checks if the connection supports requesting stream tube channels with * @handle_type as ChannelType. The @service argument is unused in this case. * * Returns: %TRUE if the contact or connection supports this type of stream * tubes. * * Since: 0.13.0 */ gboolean tp_capabilities_supports_stream_tubes (TpCapabilities *self, TpHandleType handle_type, const gchar *service) { return tp_capabilities_supports_tubes_common (self, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, handle_type, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE, service); } /** * tp_capabilities_supports_dbus_tubes: * @self: a #TpCapabilities object * @handle_type: the handle type of the tube (either #TP_HANDLE_TYPE_CONTACT * or #TP_HANDLE_TYPE_ROOM) * @service_name: the service name of the tube, or %NULL * * If the #TpCapabilities:contact-specific property is %TRUE, this function * checks if the contact associated with this #TpCapabilities supports * D-Bus tubes with @handle_type as TargetHandleType. * If @service_name is not %NULL, it also checks if it supports stream tubes * with @service as #TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME. * * If the #TpCapabilities:contact-specific property is %FALSE, this function * checks if the connection supports requesting D-Bus tube channels with * @handle_type as ChannelType. The @service_name argument is unused in * this case. * * Returns: %TRUE if the contact or connection supports this type of D-Bus * tubes. * * Since: 0.13.0 */ gboolean tp_capabilities_supports_dbus_tubes (TpCapabilities *self, TpHandleType handle_type, const gchar *service_name) { return tp_capabilities_supports_tubes_common (self, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, handle_type, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, service_name); } /** * tp_capabilities_supports_contact_search: * @self: a #TpCapabilities object * @with_limit: (out): if not %NULL, used to return %TRUE if the limit * parameter to tp_contact_search_new_async() and * tp_contact_search_reset_async() can be nonzero * @with_server: (out): if not %NULL, used to return %TRUE if the server * parameter to tp_contact_search_new_async() and * tp_contact_search_reset_async() can be non-%NULL * * Return whether this protocol or connection can perform contact * searches. Optionally, also return whether a limited number of * results can be specified, and whether alternative servers can be * searched. * * Returns: %TRUE if #TpContactSearch can be used. * * Since: 0.13.11 */ gboolean tp_capabilities_supports_contact_search (TpCapabilities *self, gboolean *with_limit, gboolean *with_server) { gboolean ret = FALSE; guint i, j; g_return_val_if_fail (TP_IS_CAPABILITIES (self), FALSE); if (with_limit) *with_limit = FALSE; if (with_server) *with_server = FALSE; for (i = 0; i < self->priv->classes->len; i++) { GValueArray *arr = g_ptr_array_index (self->priv->classes, i); GHashTable *fixed; const gchar *chan_type; const gchar **allowed_properties; tp_value_array_unpack (arr, 2, &fixed, &allowed_properties); /* ContactSearch channel should have ChannelType and TargetHandleType=NONE * but CM implementations are wrong and omitted TargetHandleType, * so it's set in stone now. */ if (g_hash_table_size (fixed) != 1) continue; chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); if (tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_CONTACT_SEARCH)) continue; ret = TRUE; for (j = 0; allowed_properties[j] != NULL; j++) { if (with_limit) { if (!tp_strdiff (allowed_properties[j], TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_LIMIT)) *with_limit = TRUE; } if (with_server) { if (!tp_strdiff (allowed_properties[j], TP_PROP_CHANNEL_TYPE_CONTACT_SEARCH_SERVER)) *with_server = TRUE; } } } return ret; } /** * tp_capabilities_supports_room_list: * @self: a #TpCapabilities object * @with_server: (out): if not %NULL, used to return %TRUE if the * #TP_PROP_CHANNEL_TYPE_ROOM_LIST_SERVER property can be defined when * requesting a RoomList channel. * * Discovers whether this protocol or connection supports listing rooms. * Specifically, if this function returns %TRUE, a room list channel can be * requested as follows: * |[ * GHashTable *request; * TpAccountChannelRequest *req; * * request = tp_asv_new ( * TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, * TP_IFACE_CHANNEL_TYPE_ROOM_LIST, * TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_NONE, * NULL); * * req = tp_account_channel_request_new (account, request, * TP_USER_ACTION_TIME_CURRENT_TIME); * * tp_account_channel_request_create_and_handle_channel_async (req, NULL, * create_channel_cb, NULL); * * g_object_unref (req); * g_hash_table_unref (request); * ]| * * If @with_server is set to %TRUE, a list of rooms on a particular server can * be requested as follows: * |[ * /\* Same code as above but with request defined using: *\/ * request = tp_asv_new ( * TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, * TP_IFACE_CHANNEL_TYPE_ROOM_LIST, * TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_NONE, * TP_PROP_CHANNEL_TYPE_ROOM_LIST_SERVER, G_TYPE_STRING, * "characters.shakespeare.lit", * NULL); * ]| * * Returns: %TRUE if a channel request containing RoomList as ChannelType, * HandleTypeNone as TargetHandleType can be expected to work, * %FALSE otherwise. * * Since: 0.13.14 */ gboolean tp_capabilities_supports_room_list (TpCapabilities *self, gboolean *with_server) { gboolean result = FALSE; gboolean server = FALSE; guint i; for (i = 0; i < self->priv->classes->len; i++) { GValueArray *arr = g_ptr_array_index (self->priv->classes, i); GHashTable *fixed; const gchar *chan_type; const gchar **allowed_properties; TpHandleType handle_type; gboolean valid; tp_value_array_unpack (arr, 2, &fixed, &allowed_properties); if (g_hash_table_size (fixed) != 2) continue; chan_type = tp_asv_get_string (fixed, TP_PROP_CHANNEL_CHANNEL_TYPE); if (tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_ROOM_LIST)) continue; handle_type = tp_asv_get_uint32 (fixed, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, &valid); if (!valid || handle_type != TP_HANDLE_TYPE_NONE) continue; result = TRUE; server = tp_strv_contains (allowed_properties, TP_PROP_CHANNEL_TYPE_ROOM_LIST_SERVER); break; } if (with_server != NULL) *with_server = server; return result; } /** * tp_capabilities_dup_channel_classes_variant: * @self: a #TpCapabilities * * Return the #TpCapabilities:channel-classes-variant property * * Returns: (transfer full): the value of the * #TpCapabilities:channel-classes-variant property * * Since: 0.19.0 */ GVariant * tp_capabilities_dup_channel_classes_variant (TpCapabilities *self) { return g_variant_ref (self->priv->classes_variant); }