diff options
-rw-r--r-- | ytstenut/yts-client-status.c | 115 | ||||
-rw-r--r-- | ytstenut/yts-client-status.h | 38 | ||||
-rw-r--r-- | ytstenut/yts-client.c | 368 | ||||
-rw-r--r-- | ytstenut/yts-client.h | 7 |
4 files changed, 273 insertions, 255 deletions
diff --git a/ytstenut/yts-client-status.c b/ytstenut/yts-client-status.c index 3c22f9e..d19f06d 100644 --- a/ytstenut/yts-client-status.c +++ b/ytstenut/yts-client-status.c @@ -39,6 +39,7 @@ enum { typedef struct { char *service_id; GHashTable *status; + GList *interests; } YtsClientStatusPrivate; static void @@ -128,25 +129,117 @@ yts_client_status_new (char const *service_id) NULL); } -void +bool yts_client_status_add_capability (YtsClientStatus *self, char const *capability) { YtsClientStatusPrivate *priv = GET_PRIVATE (self); + bool have_capability; + + g_return_val_if_fail (YTS_IS_CLIENT_STATUS (self), false); + g_return_val_if_fail (capability, false); + g_return_val_if_fail (g_str_has_prefix (capability, + YTS_XML_CAPABILITY_NAMESPACE), + false); - g_return_if_fail (YTS_IS_CLIENT_STATUS (self)); - g_return_if_fail (capability); - g_return_if_fail (g_str_has_prefix (capability, - YTS_XML_CAPABILITY_NAMESPACE)); + have_capability = g_hash_table_lookup_extended (priv->status, + capability, + NULL, NULL); + if (!have_capability) { + g_hash_table_insert (priv->status, g_strdup (capability), NULL); + return true; + } - g_hash_table_insert (priv->status, g_strdup (capability), NULL); + return false; } -void +bool yts_client_status_revoke_capability (YtsClientStatus *self, char const *capability) { - g_warning ("It is not yet possible to revoke capabilities"); + YtsClientStatusPrivate *priv = GET_PRIVATE (self); + + g_return_val_if_fail (YTS_IS_CLIENT_STATUS (self), false); + g_return_val_if_fail (capability, false); + g_return_val_if_fail (g_str_has_prefix (capability, + YTS_XML_CAPABILITY_NAMESPACE), + false); + + return g_hash_table_remove (priv->status, capability); +} + +bool +yts_client_status_add_interest (YtsClientStatus *self, + char const *interest) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (self); + GList *element; + + g_return_val_if_fail (YTS_IS_CLIENT_STATUS (self), false); + g_return_val_if_fail (interest, false); + g_return_val_if_fail (g_str_has_prefix (interest, + YTS_XML_CAPABILITY_NAMESPACE), + false); + + /* We could do better with something else but a list, but list is small. */ + element = g_list_find_custom (priv->interests, + interest, + (GCompareFunc) g_strcmp0); + if (!element) { + priv->interests = g_list_prepend (priv->interests, g_strdup (interest)); + return true; + } + + return false; +} + +bool +yts_client_status_revoke_interest (YtsClientStatus *self, + char const *interest) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (self); + GList *element; + + g_return_val_if_fail (YTS_IS_CLIENT_STATUS (self), false); + g_return_val_if_fail (interest, false); + g_return_val_if_fail (g_str_has_prefix (interest, + YTS_XML_CAPABILITY_NAMESPACE), + false); + + /* We could do better with something else but a list, but list is small. */ + element = g_list_find_custom (priv->interests, + interest, + (GCompareFunc) g_strcmp0); + if (element) { + g_free (element->data); + priv->interests = g_list_delete_link (priv->interests, element); + return true; + } else { + g_warning ("Interest '%s' not set in the first place, can not be revoked.", + interest); + } + + return false; +} + +bool +yts_client_status_foreach_interest (YtsClientStatus *self, + YtsClientStatusInterestIterator iterator, + void *user_data) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (self); + GList *iter; + bool ret = true; + + g_return_val_if_fail (YTS_IS_CLIENT_STATUS (self), false); + + for (iter = priv->interests; + iter && ret; + iter = iter->next) { + ret = iterator (self, (char const *) iter->data, user_data); + } + + return ret; } char const * @@ -205,9 +298,9 @@ yts_client_status_clear (YtsClientStatus *self, } bool -yts_client_status_foreach (YtsClientStatus *self, - YtsClientStatusIterator iterator, - void *user_data) +yts_client_status_foreach_capability (YtsClientStatus *self, + YtsClientStatusCapabilityIterator iterator, + void *user_data) { YtsClientStatusPrivate *priv = GET_PRIVATE (self); GHashTableIter iter; diff --git a/ytstenut/yts-client-status.h b/ytstenut/yts-client-status.h index a0d239f..e516374 100644 --- a/ytstenut/yts-client-status.h +++ b/ytstenut/yts-client-status.h @@ -57,14 +57,32 @@ yts_client_status_get_type (void); YtsClientStatus * yts_client_status_new (char const *service_id); -void +bool yts_client_status_add_capability (YtsClientStatus *self, char const *capability); -void +bool yts_client_status_revoke_capability (YtsClientStatus *self, char const *capability); +bool +yts_client_status_add_interest (YtsClientStatus *self, + char const *interest); + +bool +yts_client_status_revoke_interest (YtsClientStatus *self, + char const *interest); + +typedef bool +(*YtsClientStatusInterestIterator) (YtsClientStatus const *self, + char const *interest, + void *user_data); + +bool +yts_client_status_foreach_interest (YtsClientStatus *self, + YtsClientStatusInterestIterator iterator, + void *user_data); + char const * yts_client_status_set (YtsClientStatus *self, char const *capability, @@ -75,16 +93,16 @@ bool yts_client_status_clear (YtsClientStatus *self, char const *capability); -typedef bool -(*YtsClientStatusIterator) (YtsClientStatus const *self, - char const *capability, - char const *status_xml, - void *data); +typedef bool +(*YtsClientStatusCapabilityIterator) (YtsClientStatus const *self, + char const *capability, + char const *status_xml, + void *data); bool -yts_client_status_foreach (YtsClientStatus *self, - YtsClientStatusIterator iterator, - void *user_data); +yts_client_status_foreach_capability (YtsClientStatus *self, + YtsClientStatusCapabilityIterator iterator, + void *user_data); G_END_DECLS diff --git a/ytstenut/yts-client.c b/ytstenut/yts-client.c index 57f2e05..7b94ab1 100644 --- a/ytstenut/yts-client.c +++ b/ytstenut/yts-client.c @@ -36,8 +36,9 @@ #include "empathy-tp-file.h" #include "ytstenut-internal.h" #include "yts-adapter-factory.h" -#include "yts-caps.h" +#include "yts-caps.h" // TODO remove #include "yts-client-internal.h" +#include "yts-client-status.h" #include "yts-contact-internal.h" #include "yts-enum-types.h" #include "yts-error-message.h" @@ -49,7 +50,7 @@ #include "yts-roster-impl.h" #include "yts-service.h" #include "yts-service-adapter.h" -#include "yts-status.h" +#include "yts-xml.h" #include "profile/yts-profile.h" #include "profile/yts-profile-adapter.h" @@ -81,10 +82,9 @@ G_DEFINE_TYPE (YtsClient, yts_client, G_TYPE_OBJECT) */ typedef struct { - YtsRoster *roster; /* the roster of this client */ - YtsRoster *unwanted; /* roster of unwanted items */ - YtsStatus *status; - GArray *caps; + YtsRoster *roster; /* the roster of this client */ + YtsRoster *unwanted; /* roster of unwanted items */ + YtsClientStatus *client_status; /* connection parameters */ char *account_id; @@ -736,36 +736,56 @@ yts_client_authenticated (YtsClient *self) } static void -yts_client_ready (YtsClient *self) +_tp_yts_status_advertise_status_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + TpYtsStatus *status = TP_YTS_STATUS (source_object); + GError *error = NULL; + + if (!tp_yts_status_advertise_status_finish (status, result, &error)) { + g_critical ("Failed to advertise status: %s", error->message); + } else { + g_message ("Advertising of status succeeded"); + } + + g_clear_error (&error); +} + +static bool +_client_status_foreach_capability_advertise_status (YtsClientStatus const *client_status, + char const *capability, + char const *status_xml, + YtsClient *self) { YtsClientPrivate *priv = GET_PRIVATE (self); - priv->ready = TRUE; + tp_yts_status_advertise_status_async (priv->tp_status, + capability, + priv->service_id, + status_xml, + NULL, + _tp_yts_status_advertise_status_cb, + self); - g_message ("YtsClient is ready"); + return true; +} - if (priv->tp_status && priv->status) - { - char *xml = yts_metadata_to_string ((YtsMetadata*)priv->status); - unsigned i; +static void +yts_client_ready (YtsClient *self) +{ + YtsClientPrivate *priv = GET_PRIVATE (self); - for (i = 0; i < priv->caps->len; ++i) - { - char const *c; + g_return_if_fail (priv->tp_status); - c = g_quark_to_string (g_array_index (priv->caps, YtsCaps, i)); + priv->ready = TRUE; - tp_yts_status_advertise_status_async (priv->tp_status, - c, - priv->service_id, - xml, - NULL, - NULL, - NULL); - } + g_message ("YtsClient is ready"); - g_free (xml); - } + yts_client_status_foreach_capability ( + priv->client_status, + (YtsClientStatusCapabilityIterator) _client_status_foreach_capability_advertise_status, + self); } static void @@ -1137,6 +1157,19 @@ yts_client_setup_debug (YtsClient *self) g_free (busname); } +static bool +_client_status_foreach_capability_add (YtsClientStatus const *client_status, + char const *capability, + char const *status_xml, + YtsClient *self) +{ + YtsClientPrivate *priv = GET_PRIVATE (self); + + tp_yts_client_add_capability (priv->tp_client, capability); + + return true; +} + static void setup_tp_client (YtsClient *self, TpAccount *account) @@ -1152,16 +1185,10 @@ setup_tp_client (YtsClient *self, yts_client_setup_debug (self); } - if (priv->caps) - { - unsigned int i; - for (i = 0; i < priv->caps->len; i++) - { - GQuark cap = g_array_index (priv->caps, GQuark, i); - tp_yts_client_add_capability (priv->tp_client, - g_quark_to_string (cap)); - } - } + yts_client_status_foreach_capability ( + priv->client_status, + (YtsClientStatusCapabilityIterator) _client_status_foreach_capability_add, + self); /* * If connection has been requested already, make one @@ -1342,6 +1369,8 @@ yts_client_constructed (GObject *object) return; } + priv->client_status = yts_client_status_new (priv->service_id); + priv->tp_am = tp_yts_account_manager_dup (); if (!TP_IS_YTS_ACCOUNT_MANAGER (priv->tp_am)) { g_error ("Missing Account Manager"); @@ -1498,12 +1527,6 @@ yts_client_dispose (GObject *object) priv->tp_debug_proxy = NULL; } - if (priv->status) - { - g_object_unref (priv->status); - priv->status = NULL; - } - if (priv->services) { g_hash_table_destroy (priv->services); @@ -1533,9 +1556,7 @@ yts_client_finalize (GObject *object) g_free (priv->account_id); g_free (priv->service_id); g_free (priv->incoming_dir); - - if (priv->caps) - g_array_free (priv->caps, TRUE); + g_object_unref (priv->client_status); G_OBJECT_CLASS (yts_client_parent_class)->finalize (object); } @@ -2265,31 +2286,6 @@ yts_client_status_cb (TpConnection *proxy, } static gboolean -yts_client_caps_overlap (GArray *mycaps, char **caps) -{ - unsigned i; - - /* TODO -- this is not nice, maybe YtsClient:caps should also be just a - * char** - */ - for (i = 0; i < mycaps->len; ++i) - { - char **p; - - for (p = caps; *p; ++p) - { - if (!g_strcmp0 (g_quark_to_string (g_array_index (mycaps, YtsCaps, i)), - *p)) - { - return TRUE; - } - } - } - - return FALSE; -} - -static gboolean yts_client_process_one_service (YtsClient *self, char const *jid, char const *service_id, @@ -2300,7 +2296,6 @@ yts_client_process_one_service (YtsClient *self, GHashTable *names; char **caps; GHashTable *service_statuses; - YtsRoster *roster; if (service_info->n_values != 3) { @@ -2315,15 +2310,6 @@ yts_client_process_one_service (YtsClient *self, names = g_value_get_boxed (&service_info->values[1]); caps = g_value_get_boxed (&service_info->values[2]); - if (!priv->caps || !caps || !*caps || - yts_client_caps_overlap (priv->caps, caps)) - roster = priv->roster; - else - roster = priv->unwanted; - - g_message ("Using roster %s", - roster == priv->roster ? "wanted" : "unwanted"); - service_statuses = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, @@ -2353,7 +2339,7 @@ yts_client_process_one_service (YtsClient *self, } } - yts_roster_add_service (roster, + yts_roster_add_service (priv->roster, priv->tp_conn, jid, service_id, @@ -2468,61 +2454,17 @@ yts_client_process_status (YtsClient *self) g_message ("No discovered services"); } -static void -yts_client_advertise_status_cb (GObject *source_object, - GAsyncResult *result, - gpointer data) -{ - TpYtsStatus *status = TP_YTS_STATUS (source_object); - GError *error = NULL; - - if (!tp_yts_status_advertise_status_finish (status, result, &error)) - { - g_critical ("Failed to advertise status: %s", error->message); - } - else - { - g_message ("Advertising of status succeeded"); - } - - g_clear_error (&error); -} - +/* FIXME is this really needed or can we just advertise the new + * per-capability status on any change? */ static void yts_client_dispatch_status (YtsClient *self) { YtsClientPrivate *priv = GET_PRIVATE (self); - char *xml = NULL; - unsigned i; - - // TODO something is fishy here, why are we setting the same status to all the caps? - - g_return_if_fail (priv->caps && priv->caps->len); - - if (priv->status) { - xml = yts_metadata_to_string ((YtsMetadata*)priv->status); - } - - for (i = 0; i < priv->caps->len; ++i) - { - char const *c; - - c = g_quark_to_string (g_array_index (priv->caps, YtsCaps, i)); - - g_message ("Setting status of capability '%s' to\n %s", - c, - xml); - - tp_yts_status_advertise_status_async (priv->tp_status, - c, - priv->service_id, - xml, - NULL, - yts_client_advertise_status_cb, - self); - } - g_free (xml); + yts_client_status_foreach_capability ( + priv->client_status, + (YtsClientStatusCapabilityIterator) _client_status_foreach_capability_advertise_status, + self); } static void @@ -2572,9 +2514,7 @@ yts_client_yts_status_cb (GObject *obj, self, 0); - if (priv->status) - yts_client_dispatch_status (self); - + yts_client_dispatch_status (self); yts_client_process_status (self); if (!priv->ready) @@ -2856,26 +2796,6 @@ yts_client_connect (YtsClient *self) yts_client_make_connection (self); } -static gboolean -yts_client_has_capability (YtsClient *self, YtsCaps cap) -{ - YtsClientPrivate *priv = GET_PRIVATE (self); - unsigned i; - - if (!priv->caps) - return FALSE; - - for (i = 0; i < priv->caps->len; ++i) - { - YtsCaps c = g_array_index (priv->caps, YtsCaps, i); - - if (c == cap || c == YTS_CAPS_CONTROL) - return TRUE; - } - - return FALSE; -} - // TODO get rid of this, it invalidates the proxies static void yts_client_refresh_roster (YtsClient *self) @@ -2909,7 +2829,6 @@ yts_client_add_capability (YtsClient *self, YtsCapabilityMode mode) { YtsClientPrivate *priv = GET_PRIVATE (self); - GQuark cap_quark; char *capability; /* FIXME check that there's no collision with service owned capabilities. */ @@ -2917,37 +2836,36 @@ yts_client_add_capability (YtsClient *self, g_return_if_fail (YTS_IS_CLIENT (self)); g_return_if_fail (c); - // TODO error reporting - if (NULL == priv->tp_client) { - g_warning ("tp_client == NULL, could not add capability, ytstenut not ready yet."); - return; - } - // TODO make sure c doesn't have prefix already capability = g_strdup_printf ("urn:ytstenut:capabilities:%s", c); if (YTS_CAPABILITY_MODE_PROVIDED == mode) { - - cap_quark = g_quark_from_string (capability); - if (yts_client_has_capability (self, cap_quark)) { + if (yts_client_status_add_capability (priv->client_status, capability)) { + /* Advertise right away if possible, otherwise the advertising will + * happen when the tp_client is ready. */ + if (priv->tp_client) { + tp_yts_client_add_capability (priv->tp_client, capability); + } + } else { g_message ("Capablity '%s' already set", capability); return; } - // FIXME maybe queue up? - tp_yts_client_add_capability (priv->tp_client, capability); - - if (!priv->caps) - priv->caps = g_array_sized_new (FALSE, FALSE, sizeof (YtsCaps), 1); - - g_array_append_val (priv->caps, cap_quark); - yts_client_refresh_roster (self); } else if (YTS_CAPABILITY_MODE_CONSUMED == mode) { - tp_yts_client_add_interest (priv->tp_client, capability); + if (yts_client_status_add_interest (priv->client_status, capability)) { + /* Advertise right away if possible, otherwise the advertising will + * happen when the tp_client is ready. */ + if (priv->tp_client) { + tp_yts_client_add_interest (priv->tp_client, capability); + } + } else { + g_message ("Interest '%s' already set", capability); + return; + } } else { g_critical ("Invalid capability mode %d", mode); @@ -3110,40 +3028,6 @@ yts_client_get_tp_status (YtsClient *self) } /** - * yts_client_set_status: - * @self: object on which to invoke this method. - * @status: new #YtsStatus - * - * Changes the status of the service represented by this client to status; - */ -static void -yts_client_set_status (YtsClient *self, YtsStatus *status) -{ - YtsClientPrivate *priv = GET_PRIVATE (self); - - g_return_if_fail (YTS_IS_CLIENT (self)); - g_return_if_fail (YTS_IS_STATUS (status)); - - g_return_if_fail (priv->caps && priv->caps->len); - - if (status) - g_object_ref (status); - - if (priv->status) - { - g_object_unref (priv->status); - priv->status = NULL; - } - - priv->status = status; - - if (priv->tp_status) - { - yts_client_dispatch_status (self); - } -} - -/** * yts_client_set_status_by_capability: * @self: object on which to invoke this method. * @capability: the capability to set status for @@ -3151,39 +3035,61 @@ yts_client_set_status (YtsClient *self, YtsStatus *status) * * Set the status of the service represented by this client to @activity for * @capability. + * + * FIXME: Maybe this should be named yts_client_set_status_on_capability() or + * yts_client_set_status_for_capability() ? + * Also maybe the "activity" should not be exposed any more because we're + * kinda moving away from it, instead allow setting the xml payload? + * Will things work at all without the activity attribut -- to to check + * the spec. */ void -yts_client_set_status_by_capability (YtsClient *self, - char const *c, - char const *activity) +yts_client_set_status_by_capability (YtsClient *self, + char const *cap_value, + char const *activity, + char const *status_xml) { YtsClientPrivate *priv = GET_PRIVATE (self); - YtsStatus *status = NULL; + char *capability; + char const *capability_status_xml; + char const *attribs[] = { + "activity", activity, + NULL + }; g_return_if_fail (YTS_IS_CLIENT (self)); - g_return_if_fail (c); + g_return_if_fail (cap_value); + g_return_if_fail (activity); - g_return_if_fail (priv->caps && priv->caps->len); + capability = g_strdup_printf ("%s%s", + YTS_XML_CAPABILITY_NAMESPACE, + cap_value); - if (activity) - { - char *capability = g_strdup_printf ("urn:ytstenut:capabilities:%s", c); - char const *attributes[] = - { - "capability", capability, - "activity", activity, - "from-service", priv->service_id, - NULL - }; - - g_message ("Constructing status for %s, %s, %s", - capability, activity, priv->service_id); - - status = yts_status_new ((char const**)&attributes); - g_free (capability); + /* Check if the capability is already advertised. */ + if (yts_client_status_add_capability (priv->client_status, capability)) { + /* Add capability if we already have a tp_client, + * otherwise that's done when it's ready. */ + if (priv->tp_client) { + tp_yts_client_add_capability (priv->tp_client, capability); } + } - yts_client_set_status (self, status); + capability_status_xml = yts_client_status_set (priv->client_status, + capability, + attribs, + status_xml); + + /* Advertise if we already have a tp_status, + * otherwise that's done when it's ready. */ + if (priv->client_status) { + tp_yts_status_advertise_status_async (priv->tp_status, + capability, + priv->service_id, + capability_status_xml, + NULL, + _tp_yts_status_advertise_status_cb, + self); + } } struct YtsCLChannelData diff --git a/ytstenut/yts-client.h b/ytstenut/yts-client.h index 6d5ae59..32bcfe2 100644 --- a/ytstenut/yts-client.h +++ b/ytstenut/yts-client.h @@ -151,9 +151,10 @@ char const * yts_client_get_service_id (YtsClient const *self); void -yts_client_set_status_by_capability (YtsClient *self, - char const *capability, - char const *activity); +yts_client_set_status_by_capability (YtsClient *self, + char const *capability, + char const *activity, + char const *status_xml); bool yts_client_publish_service (YtsClient *self, |