From f66ae01f6394824198ab9bc92288fd0512e61865 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 7 May 2010 18:17:16 +0200 Subject: [PATCH] Add tp_connection_get_allowed_presence_statuses and tp_connection_set_self_presence_async https://bugs.freedesktop.org/show_bug.cgi?id=24107 --- docs/reference/telepathy-glib-sections.txt | 12 + telepathy-glib/connection-internal.h | 3 + telepathy-glib/connection.c | 350 ++++++++++++++++++++++++++++ telepathy-glib/connection.h | 38 +++ 4 files changed, 403 insertions(+), 0 deletions(-) diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt index 4d79193..300ceb3 100644 --- a/docs/reference/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib-sections.txt @@ -2791,16 +2791,28 @@ TP_IS_CONNECTION TP_IS_CONNECTION_CLASS TpConnectionPrivate tp_avatar_requirements_get_type +tp_status_spec_get_type tp_connection_get_feature_quark_core tp_connection_get_feature_quark_connected tp_connection_get_feature_quark_capabilities tp_connection_get_feature_quark_avatar_requirements +tp_connection_get_feature_quark_self_presence TP_TYPE_AVATAR_REQUIREMENTS TpAvatarRequirements tp_avatar_requirements_new tp_avatar_requirements_copy tp_avatar_requirements_destroy + +TP_CONNECTION_FEATURE_SELF_PRESENCE +tp_connection_get_allowed_presence_statuses +tp_connection_set_self_presence_async +tp_connection_set_self_presence_finish +TP_TYPE_STATUS_SPEC +TpStatusSpec +tp_status_spec_new +tp_status_spec_copy +tp_status_spec_destroy tp_cli_connection_callback_for_connect tp_cli_connection_call_connect diff --git a/telepathy-glib/connection-internal.h b/telepathy-glib/connection-internal.h index f96cfb9..c32d5ca 100644 --- a/telepathy-glib/connection-internal.h +++ b/telepathy-glib/connection-internal.h @@ -51,9 +51,12 @@ struct _TpConnectionPrivate { TpAvatarRequirements *avatar_requirements; + GHashTable *allowed_presence_statuses; + TpProxyPendingCall *introspection_call; unsigned fetching_rcc:1; unsigned fetching_avatar_requirements:1; + unsigned fetching_allowed_presence_statuses:1; unsigned ready:1; unsigned tracking_aliases_changed:1; diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c index cd4b1a1..8df88a3 100644 --- a/telepathy-glib/connection.c +++ b/telepathy-glib/connection.c @@ -60,6 +60,7 @@ * * connection status tracking * calling GetInterfaces() automatically + * Getting the valid presence statuses automatically * * * Since: 0.7.1 @@ -193,6 +194,28 @@ tp_connection_get_feature_quark_avatar_requirements (void) } /** + * TP_CONNECTION_FEATURE_SELF_PRESENCE: + * + * Expands to a call to a function that returns a #GQuark representing the + * "self-presence" feature. + * + * When this feature is prepared, the allowed presence statuses for this + * Connection have been retrieved. Use + * tp_connection_get_allowed_presence_statuses() to get them once prepared. + * + * One can ask for a feature to be prepared using the + * tp_proxy_prepare_async() function, and waiting for it to callback. + * + * Since: 0.11.UNRELEASED + */ + +GQuark +tp_connection_get_feature_quark_self_presence (void) +{ + return g_quark_from_static_string ("tp-connection-feature-self-presence"); +} + +/** * TP_ERRORS_DISCONNECTED: * * #GError domain representing a Telepathy connection becoming disconnected. @@ -453,6 +476,93 @@ tp_connection_maybe_prepare_avatar_requirements (TpProxy *proxy) } static void +allowed_presence_statuses_foreach (gpointer k, + gpointer v, + gpointer d) +{ + GHashTable *dict = (GHashTable *) d; + GValueArray *value = (GValueArray *) v; + TpStatusSpec *status = tp_status_spec_new ( + g_value_get_uint (value->values + 0), + k, + g_value_get_boolean (value->values + 1), + g_value_get_boolean (value->values + 2)); + + g_hash_table_insert (dict, g_strdup (k), status); +} + +static void +tp_connection_get_allowed_presence_statuses_cb (TpProxy *proxy, + GHashTable *properties, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + TpConnection *self = (TpConnection *) proxy; + GValue *value; + GHashTable *statuses; + + self->priv->fetching_allowed_presence_statuses = FALSE; + + if (error != NULL) + { + DEBUG ("Failed to get allowed presence statuses: %s", error->message); + goto finally; + } + + g_assert (self->priv->allowed_presence_statuses == NULL); + + DEBUG ("SELF PRESENCE ready"); + + if (self->priv->allowed_presence_statuses != NULL) + g_hash_table_unref (self->priv->allowed_presence_statuses); + self->priv->allowed_presence_statuses = g_hash_table_new_full (g_str_hash, + g_str_equal, g_free, (GDestroyNotify) tp_status_spec_destroy); + + value = g_hash_table_lookup (properties, "Statuses"); + statuses = g_value_get_boxed (value); + + g_hash_table_foreach (statuses, allowed_presence_statuses_foreach, + self->priv->allowed_presence_statuses); + +finally: + _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_SELF_PRESENCE, + self->priv->allowed_presence_statuses != NULL); +} + +static void +tp_connection_maybe_prepare_allowed_presence_statuses (TpProxy *proxy) +{ + TpConnection *self = (TpConnection *) proxy; + + if (self->priv->allowed_presence_statuses != NULL) + return; /* already done */ + + if (!_tp_proxy_is_preparing (proxy, TP_CONNECTION_FEATURE_SELF_PRESENCE)) + return; /* not interested right now */ + + if (!self->priv->ready) + return; /* will try again when ready */ + + if (self->priv->fetching_allowed_presence_statuses) + return; /* Another Get operation is running */ + + if (!tp_proxy_has_interface_by_id (proxy, + TP_IFACE_QUARK_CONNECTION_INTERFACE_SIMPLE_PRESENCE)) + { + _tp_proxy_set_feature_prepared (proxy, TP_CONNECTION_FEATURE_SELF_PRESENCE, + FALSE); + return; + } + + self->priv->fetching_allowed_presence_statuses = TRUE; + + tp_cli_dbus_properties_call_get_all (self, -1, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, + tp_connection_get_allowed_presence_statuses_cb, NULL, NULL, NULL); +} + +static void tp_connection_continue_introspection (TpConnection *self) { if (tp_proxy_get_invalidated (self) != NULL) @@ -490,6 +600,7 @@ tp_connection_continue_introspection (TpConnection *self) tp_connection_maybe_prepare_capabilities ((TpProxy *) self); tp_connection_maybe_prepare_avatar_requirements ((TpProxy *) self); + tp_connection_maybe_prepare_allowed_presence_statuses ((TpProxy *) self); } else { @@ -1082,6 +1193,12 @@ tp_connection_dispose (GObject *object) self->priv->avatar_requirements = NULL; } + if (self->priv->allowed_presence_statuses != NULL) + { + g_hash_table_unref (self->priv->allowed_presence_statuses); + self->priv->allowed_presence_statuses = NULL; + } + ((GObjectClass *) tp_connection_parent_class)->dispose (object); } @@ -1090,6 +1207,7 @@ enum { FEAT_CONNECTED, FEAT_CAPABILITIES, FEAT_AVATAR_REQUIREMENTS, + FEAT_SELF_PRESENCE, N_FEAT }; @@ -1114,6 +1232,10 @@ tp_connection_list_features (TpProxyClass *cls G_GNUC_UNUSED) features[FEAT_AVATAR_REQUIREMENTS].start_preparing = tp_connection_maybe_prepare_avatar_requirements; + features[FEAT_SELF_PRESENCE].name = TP_CONNECTION_FEATURE_SELF_PRESENCE; + features[FEAT_SELF_PRESENCE].start_preparing = + tp_connection_maybe_prepare_allowed_presence_statuses; + /* assert that the terminator at the end is there */ g_assert (features[N_FEAT].name == 0); @@ -2273,3 +2395,231 @@ tp_connection_get_detailed_error (TpConnection *self, } } } + +/** + * TpStatusSpec: + * @type: The type of a presence. This should not be used as a way to set + * statuses that the client does not recognise (see + * + * SetPresence in the Telepathy D-Bus Interface Specification), but + * may be used to check that the client's assumptions about a particular status + * name match the connection manager's. + * @status: The string identifier of this status. + * @may_set_on_self: If true, the user can set this status on themselves using + * tp_connection_set_self_presence_async(). + * @can_have_message: If true, a non-empty message can be set for this status. + * Otherwise, the empty string is the only acceptable message. (For instance, + * on IRC you can be Away with a status message, but if you are available you + * cannot set a status message.) + * + * The possible presence statuses for this connection in its current status. + * + * Since: 0.11.UNRELEASED + */ + +/** + * TP_TYPE_STATUS_SPEC: + * + * The boxed type of a #TpStatusSpec. + * + * Since: 0.11.UNRELEASED + */ +GType +tp_status_spec_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) + { + type = g_boxed_type_register_static ( + g_intern_static_string ("TpStatusSpec"), + (GBoxedCopyFunc) tp_status_spec_copy, + (GBoxedFreeFunc) tp_status_spec_destroy); + } + + return type; +} + +/** + * tp_status_spec_new: + * @type: The type of a presence. + * @status: The string identifier of this status. + * @may_set_on_self: If true, the user can set this status on themselves using + * tp_connection_set_self_presence. + * @can_have_message: If true, a non-empty message can be set for this status. + * + * + * + * Returns: a newly allocated #TpStatusSpec, free it with + * tp_status_spec_destroy() + * Since: 0.11.UNRELEASED + */ +TpStatusSpec * +tp_status_spec_new (TpConnectionPresenceType type, + const gchar *status, + gboolean may_set_on_self, + gboolean can_have_message) +{ + TpStatusSpec *self; + + self = g_slice_new0 (TpStatusSpec); + self->type = type; + self->status = g_strdup (status); + self->may_set_on_self = may_set_on_self; + self->can_have_message = can_have_message; + + return self; +} + +/** + * tp_status_spec_copy: + * @self: a #TpStatusSpec + * + * + * + * Returns: a newly allocated #TpStatusSpec, free it with + * tp_status_spec_destroy() + * Since: 0.11.UNRELEASED + */ +TpStatusSpec * +tp_status_spec_copy (TpStatusSpec *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return tp_status_spec_new (self->type, + self->status, + self->may_set_on_self, + self->can_have_message); +} + +/** + * tp_status_spec_destroy: + * @self: a #TpStatusSpec + * + * Free all memory used by the #TpStatusSpec. + * + * Since: 0.11.UNRELEASED + */ +void +tp_status_spec_destroy (TpStatusSpec *self) +{ + g_return_if_fail (self != NULL); + + g_free (self->status); + g_slice_free (TpStatusSpec, self); +} + +/** + * tp_connection_get_allowed_presence_statuses: + * @self: a connection + * + * Return a dictionary of presence statuses valid for use in this connection. + * + * The value may have changed arbitrarily during the time the Connection + * spends in status %TP_CONNECTION_STATUS_CONNECTING, again staying fixed for + * the entire time in %TP_CONNECTION_STATUS_CONNECTED. + * + * This method requires %TP_CONNECTION_FEATURE_SELF_PRESENCE to be enabled. + * To wait for the allowed presence statuses, call tp_proxy_prepare_async() + * with the feature %TP_CONNECTION_FEATURE_SELF_PRESENCE. + * + * Returns: (element-type utf8 TelepathyGLib.StatusSpec): Dictionary from + * string identifiers to structs for each valid status. + * + * Since: 0.11.UNRELEASED + */ +GHashTable * +tp_connection_get_allowed_presence_statuses (TpConnection *self) +{ + g_return_val_if_fail (TP_IS_CONNECTION (self), NULL); + + if (self->priv->allowed_presence_statuses != NULL) + g_hash_table_ref (self->priv->allowed_presence_statuses); + return self->priv->allowed_presence_statuses; +} + +static void +tp_connection_set_self_presence_cb (TpConnection *proxy, + const GError *error, + gpointer user_data, + GObject *weak_object) +{ + GSimpleAsyncResult *result = (GSimpleAsyncResult *) user_data; + + if (error != NULL) + g_simple_async_result_set_from_error (result, error); + + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +/** + * tp_connection_set_self_presence_async: + * @self: a connection + * @status: the desired status, one of the statuses returned by + * tp_connection_get_allowed_presence_statuses() + * @status_message: desired status message + * @callback: (allow-none): if not %NULL, called exactly once, when the + * operation has been finished or has failed + * @user_data: user-supplied data + * + * Set the self presence status. + * + * Note that clients SHOULD set the status message for the local user to the + * empty string, unless the user has actually provided a specific message (i.e. + * one that conveys more information than the Status). + * + * Since: 0.11.UNRELEASED + */ +void +tp_connection_set_self_presence_async (TpConnection *self, + const gchar *status, + const gchar *status_message, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result = NULL; + + if (callback != NULL) + result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + tp_connection_set_self_presence_async); + + tp_cli_connection_interface_simple_presence_call_set_presence (self, -1, + status, status_message, tp_connection_set_self_presence_cb, result, NULL, + G_OBJECT (self)); +} + +/** + * tp_connection_set_self_presence_finish: + * @self: a connection + * @result: the result passed to the callback of + * tp_connection_set_self_presence_async() + * @error: used to return an error if %FALSE is returned + * + * Check for error in a call to tp_connection_set_self_presence_finish(). + * + * Returns: %FALSE (setting @error) if tp_connection_set_self_presence_async() + * failed or was cancelled. + * + * Since: 0.11.UNRELEASED + */ +gboolean +tp_connection_set_self_presence_finish (TpConnection *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); + simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail ( + g_simple_async_result_is_valid (result, G_OBJECT (self), + tp_connection_set_self_presence_async), + FALSE); + + return TRUE; +} diff --git a/telepathy-glib/connection.h b/telepathy-glib/connection.h index 13737ab..d596362 100644 --- a/telepathy-glib/connection.h +++ b/telepathy-glib/connection.h @@ -87,6 +87,31 @@ TpAvatarRequirements * tp_avatar_requirements_copy ( void tp_avatar_requirements_destroy (TpAvatarRequirements *self); +typedef struct _TpStatusSpec TpStatusSpec; +struct _TpStatusSpec +{ + TpConnectionPresenceType type; + gchar *status; + gboolean may_set_on_self; + gboolean can_have_message; + + /**/ + gpointer priv; +}; + +#define TP_TYPE_STATUS_SPEC (tp_status_spec_get_type ()) +GType tp_status_spec_get_type (void); + +TpStatusSpec *tp_status_spec_new (TpConnectionPresenceType type, + const gchar *status, gboolean may_set_on_self, gboolean can_have_message) + G_GNUC_WARN_UNUSED_RESULT; + +TpStatusSpec *tp_status_spec_copy (TpStatusSpec *self) + G_GNUC_WARN_UNUSED_RESULT; + +void tp_status_spec_destroy (TpStatusSpec *self); + + GType tp_connection_get_type (void); #define TP_ERRORS_DISCONNECTED (tp_errors_disconnected_quark ()) @@ -175,6 +200,19 @@ GQuark tp_connection_get_feature_quark_capabilities (void) G_GNUC_CONST; (tp_connection_get_feature_quark_avatar_requirements ()) GQuark tp_connection_get_feature_quark_avatar_requirements (void) G_GNUC_CONST; +#define TP_CONNECTION_FEATURE_SELF_PRESENCE \ + (tp_connection_get_feature_quark_self_presence ()) +GQuark tp_connection_get_feature_quark_self_presence (void) G_GNUC_CONST; + +GHashTable *tp_connection_get_allowed_presence_statuses (TpConnection *self); + +void tp_connection_set_self_presence_async (TpConnection *self, + const gchar *status, const gchar *status_message, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean tp_connection_set_self_presence_finish (TpConnection *self, + GAsyncResult *result, GError **error); + /* connection-handles.c */ typedef void (*TpConnectionHoldHandlesCb) (TpConnection *connection, -- 1.6.6.1