diff options
author | Rob Staudinger <robsta@linux.intel.com> | 2012-01-26 09:53:02 +0100 |
---|---|---|
committer | Rob Staudinger <robsta@linux.intel.com> | 2012-01-26 09:53:02 +0100 |
commit | b004f03c6500e39021cee38f327507bb40af03c3 (patch) | |
tree | 5925d7920f43cb94a59534d6e9b2030303efbee2 | |
parent | 45a01f95eecaaee49ab94ccdc3211d3c917f056e (diff) |
Rewrite capability handling in YtsClient
The new class YtsClientStatus holds interests, capabilities and the
per-capability status. It also caches those when telepathy is not ready
yet, for example before a program enters the mainloop. Once the
TpYtsClient and TpYtsStatus are ready the interests, capabilities and
respective status is published.
Old-style YtsCaps have been removed.
This implements/fixes status change notification for C2S.
-rw-r--r-- | examples/dictionary-message.c | 2 | ||||
-rw-r--r-- | examples/status.c | 16 | ||||
-rw-r--r-- | ytstenut/Makefile.am | 4 | ||||
-rw-r--r-- | ytstenut/yts-caps.h | 178 | ||||
-rw-r--r-- | ytstenut/yts-client-status.c | 325 | ||||
-rw-r--r-- | ytstenut/yts-client-status.h | 110 | ||||
-rw-r--r-- | ytstenut/yts-client.c | 395 | ||||
-rw-r--r-- | ytstenut/yts-client.h | 28 | ||||
-rw-r--r-- | ytstenut/yts-xml.h | 28 | ||||
-rw-r--r-- | ytstenut/ytstenut.sym | 1 |
10 files changed, 669 insertions, 418 deletions
diff --git a/examples/dictionary-message.c b/examples/dictionary-message.c index 8041e73..d8f02d4 100644 --- a/examples/dictionary-message.c +++ b/examples/dictionary-message.c @@ -162,7 +162,7 @@ run_server (void) GMainLoop *mainloop; client = yts_client_new_p2p (SERVER_UID); - yts_client_add_capability (client, CAPABILITY); + yts_client_add_capability (client, CAPABILITY, YTS_CAPABILITY_MODE_PROVIDED); g_signal_connect (client, "authenticated", G_CALLBACK (_server_authenticated), NULL); g_signal_connect (client, "ready", diff --git a/examples/status.c b/examples/status.c index a819740..b289e85 100644 --- a/examples/status.c +++ b/examples/status.c @@ -111,6 +111,7 @@ run_client (bool p2p) else client = yts_client_new_c2s (CLIENT_JID, CLIENT_UID); + yts_client_add_capability (client, CAPABILITY, YTS_CAPABILITY_MODE_CONSUMED); g_signal_connect (client, "authenticated", G_CALLBACK (_client_authenticated), NULL); g_signal_connect (client, "ready", @@ -163,11 +164,18 @@ _server_text_message (YtsClient *client, char const *text, void *data) { - g_debug ("%s()", __FUNCTION__); + GTimeVal time; + char *date; + + /* Got pinged, set current date as status, that should be random enough. */ - /* Got pinged, set some status */ + g_get_current_time (&time); + date = g_time_val_to_iso8601 (&time); - yts_client_set_status_by_capability (client, CAPABILITY, "Foo"); + g_debug ("%s() setting %s", __FUNCTION__, date); + + yts_client_set_status_by_capability (client, CAPABILITY, date, NULL); + g_free (date); } static int @@ -181,7 +189,7 @@ run_server (bool p2p) else client = yts_client_new_c2s (SERVER_JID, SERVER_UID); - yts_client_add_capability (client, CAPABILITY); + yts_client_add_capability (client, CAPABILITY, YTS_CAPABILITY_MODE_PROVIDED); g_signal_connect (client, "authenticated", G_CALLBACK (_server_authenticated), NULL); g_signal_connect (client, "ready", diff --git a/ytstenut/Makefile.am b/ytstenut/Makefile.am index 6d909dc..b0b886b 100644 --- a/ytstenut/Makefile.am +++ b/ytstenut/Makefile.am @@ -38,6 +38,7 @@ source_c = \ $(srcdir)/empathy-tp-file.c \ $(srcdir)/yts-capability.c \ $(srcdir)/yts-client.c \ + $(srcdir)/yts-client-status.c \ $(srcdir)/yts-contact.c \ $(srcdir)/yts-contact-impl.c \ $(srcdir)/yts-enum-types.c \ @@ -91,6 +92,7 @@ private_h = \ $(srcdir)/empathy-tp-file.h \ $(srcdir)/yts-adapter-factory.h \ $(srcdir)/yts-client-internal.h \ + $(srcdir)/yts-client-status.h \ $(srcdir)/yts-contact-impl.h \ $(srcdir)/yts-contact-internal.h \ $(srcdir)/yts-error.h \ @@ -107,9 +109,9 @@ private_h = \ $(srcdir)/yts-service-factory.h \ $(srcdir)/yts-service-impl.h \ $(srcdir)/yts-service-internal.h \ + $(srcdir)/yts-xml.h \ \ $(srcdir)/yts-capability-status.h \ - $(srcdir)/yts-caps.h \ $(srcdir)/yts-message.h \ $(srcdir)/yts-metadata.h \ $(srcdir)/yts-error-message.h \ diff --git a/ytstenut/yts-caps.h b/ytstenut/yts-caps.h deleted file mode 100644 index 43b8a0d..0000000 --- a/ytstenut/yts-caps.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright © 2011 Intel Corp. - * - * 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 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, see - * <http://www.gnu.org/licenses/>. - * - * Authored by: Tomas Frydrych <tf@linux.intel.com> - */ - -/** - * SECTION:yts-caps - * @short_description: represents application capability. - * - * #YtsCaps represents application capability. - */ - -#ifndef YTS_CAPS_H -#define YTS_CAPS_H - -#include <glib.h> - -G_BEGIN_DECLS - -/** - * YtsCaps: - * - * Represents client capablilites. - */ -typedef GQuark YtsCaps; - -/** - * YTS_CAPS_S_UNDEFINED: - * - * Ytstenut string representing unknown capabilities - */ -#define YTS_CAPS_S_UNDEFINED NULL - -/** - * YTS_CAPS_UI_UNDEFINED: - * - * Ytstenut UI string representing unknown capabilities - */ -#define YTS_CAPS_UI_UNDEFINED "Unknown" - -/** - * YTS_CAPS_UNDEFINED: - * - * Ytstenut GQuark representing unknown capabilties - */ -#define YTS_CAPS_UNDEFINED 0 - -/** - * YTS_CAPS_S_AUDIO: - * - * Ytstenut string representing audio capabilities - */ -#define YTS_CAPS_S_AUDIO "yts-caps-audio" - -/** - * YTS_CAPS_UI_AUDIO: - * - * Ytstenut UI string representing audio capabilities - */ -#define YTS_CAPS_UI_AUDIO "Audio" - -/** - * YTS_CAPS_AUDIO: - * - * Ytstenut GQuark representing audio capabilties - */ -#define YTS_CAPS_AUDIO g_quark_from_static_string (YTS_CAPS_S_AUDIO) - -/** - * YTS_CAPS_S_VIDEO: - * - * Ytstenut string representing video capabilities - */ -#define YTS_CAPS_S_VIDEO "yts-caps-video" - -/** - * YTS_CAPS_UI_VIDEO: - * - * Ytstenut UI string representing video capabilities - */ -#define YTS_CAPS_UI_VIDEO "Video" - - -/** - * YTS_CAPS_VIDEO: - * - * Ytstenut GQuark representing video capabilties - */ -#define YTS_CAPS_VIDEO g_quark_from_static_string (YTS_CAPS_S_VIDEO) - -/** - * YTS_CAPS_S_IMAGE: - * - * Ytstenut string representing image capabilities - */ -#define YTS_CAPS_S_IMAGE "yts-caps-image" - -/** - * YTS_CAPS_UI_IMAGE: - * - * Ytstenut UI string representing unknown capabilities - */ -#define YTS_CAPS_UI_IMAGE "Image" - -/** - * YTS_CAPS_IMAGE: - * - * Ytstenut GQuark representing image capabilties - */ -#define YTS_CAPS_IMAGE g_quark_from_static_string (YTS_CAPS_S_IMAGE) - -/** - * YTS_CAPS_S_HTML: - * - * Ytstenut string representing html capabilities - */ -#define YTS_CAPS_S_HTML "yts-caps-html" - -/** - * YTS_CAPS_UI_HTML: - * - * Ytstenut UI string representing unknown capabilities - */ -#define YTS_CAPS_UI_HTML "Web page" - -/** - * YTS_CAPS_HTML: - * - * Ytstenut GQuark representing html capabilties - */ -#define YTS_CAPS_HTML g_quark_from_static_string (YTS_CAPS_S_HTML) - -/** - * YTS_CAPS_S_CONTROL: - * - * Ytstenut string representing control capabilities - */ -#define YTS_CAPS_S_CONTROL "yts-caps-control" - -/** - * YTS_CAPS_UI_CONTROL: - * - * Ytstenut UI string representing unknown capabilities - */ -#define YTS_CAPS_UI_CONTROL "Control" - - -/** - * YTS_CAPS_CONTROL: - * - * Ytstenut GQuark representing control capabilties. This is a special value for - * applications the sole purpose of which is to be Ytstenut remote - * control. Clients that set their #YtsClient status using this value will - * receive unfiltered roster and in turn will be added to the roster of every - * other connected client. Clients that implement regular capabilities should - * fitler out clients with YTS_CAPS_CONTROL capability from their UI (though - * they need to respond to commands received from them). - */ -#define YTS_CAPS_CONTROL g_quark_from_static_string (YTS_CAPS_S_CONTROL) - -G_END_DECLS - -#endif /* YTS_CAPS_H */ diff --git a/ytstenut/yts-client-status.c b/ytstenut/yts-client-status.c new file mode 100644 index 0000000..d19f06d --- /dev/null +++ b/ytstenut/yts-client-status.c @@ -0,0 +1,325 @@ +/* + * Copyright © 2012 Intel Corp. + * + * 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 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, see + * <http://www.gnu.org/licenses/>. + * + * Authored by: Rob Staudinger <robsta@linux.intel.com> + */ + +#include "yts-client-status.h" +#include "yts-xml.h" + +#include "config.h" + +G_DEFINE_TYPE (YtsClientStatus, yts_client_status, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), YTS_TYPE_CLIENT_STATUS, YtsClientStatusPrivate)) + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN PACKAGE"\0client-status\0"G_STRLOC + +enum { + PROP_0, + PROP_SERVICE_ID +}; + +typedef struct { + char *service_id; + GHashTable *status; + GList *interests; +} YtsClientStatusPrivate; + +static void +_get_property (GObject *object, + unsigned property_id, + GValue *value, + GParamSpec *pspec) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (object); + + switch (property_id) { + case PROP_SERVICE_ID: + g_value_set_string (value, priv->service_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +_set_property (GObject *object, + unsigned property_id, + const GValue *value, + GParamSpec *pspec) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (object); + + switch (property_id) { + case PROP_SERVICE_ID: + /* Construct-only */ + priv->service_id = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +_finalize (GObject *object) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (object); + + g_free (priv->service_id); + priv->service_id = NULL; + + g_hash_table_destroy (priv->status); + priv->status = NULL; + + G_OBJECT_CLASS (yts_client_status_parent_class)->finalize (object); +} + +static void +yts_client_status_class_init (YtsClientStatusClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (YtsClientStatusPrivate)); + + object_class->get_property = _get_property; + object_class->set_property = _set_property; + object_class->finalize = _finalize; + + pspec = g_param_spec_string ("service-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_SERVICE_ID, pspec); +} + +static void +yts_client_status_init (YtsClientStatus *self) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (self); + + priv->status = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); +} + +YtsClientStatus * +yts_client_status_new (char const *service_id) +{ + return g_object_new (YTS_TYPE_CLIENT_STATUS, + "service-id", service_id, + NULL); +} + +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); + + 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; + } + + return false; +} + +bool +yts_client_status_revoke_capability (YtsClientStatus *self, + char const *capability) +{ + 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 * +yts_client_status_set (YtsClientStatus *self, + char const *capability, + char const *const *attribs, + char const *xml_payload) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (self); + GString *attribs_str; + char *status_xml; + + g_return_val_if_fail (YTS_IS_CLIENT_STATUS (self), NULL); + g_return_val_if_fail (capability, NULL); + g_return_val_if_fail (g_str_has_prefix (capability, + YTS_XML_CAPABILITY_NAMESPACE), + NULL); + + attribs_str = g_string_new (""); + for (unsigned i = 0; attribs && attribs[i] && attribs[i+1]; i += 2) { + g_string_append_printf (attribs_str, "%s='%s' ", attribs[i], attribs[i+1]); + } + + status_xml = g_strdup_printf ("<status xmlns='%s' " + "from-service='%s' " + "capability='%s' " + "%s>" + "%s" + "</status>", + YTS_XML_STATUS_NAMESPACE, + priv->service_id, + capability, + attribs_str->str, + xml_payload ? xml_payload : ""); + + g_string_free (attribs_str, true); + + g_hash_table_replace (priv->status, g_strdup (capability), status_xml); + + return status_xml; +} + +bool +yts_client_status_clear (YtsClientStatus *self, + char const *capability) +{ + 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_foreach_capability (YtsClientStatus *self, + YtsClientStatusCapabilityIterator iterator, + void *user_data) +{ + YtsClientStatusPrivate *priv = GET_PRIVATE (self); + GHashTableIter iter; + char const *capability; + char const *status_xml; + bool ret = true; + + g_return_val_if_fail (YTS_IS_CLIENT_STATUS (self), false); + g_return_val_if_fail (iterator, false); + + g_hash_table_iter_init (&iter, priv->status); + while (ret && + g_hash_table_iter_next (&iter, + (gpointer *) &capability, + (gpointer *) &status_xml)) { + + ret = iterator (self, capability, status_xml, user_data); + } + + return ret; +} + diff --git a/ytstenut/yts-client-status.h b/ytstenut/yts-client-status.h new file mode 100644 index 0000000..e516374 --- /dev/null +++ b/ytstenut/yts-client-status.h @@ -0,0 +1,110 @@ +/* + * Copyright © 2012 Intel Corp. + * + * 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 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, see + * <http://www.gnu.org/licenses/>. + * + * Authored by: Rob Staudinger <robsta@linux.intel.com> + */ + +#ifndef YTS_CLIENT_STATUS_H +#define YTS_CLIENT_STATUS_H + +#include <stdbool.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define YTS_TYPE_CLIENT_STATUS yts_client_status_get_type() + +#define YTS_CLIENT_STATUS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), YTS_TYPE_CLIENT_STATUS, YtsClientStatus)) + +#define YTS_CLIENT_STATUS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), YTS_TYPE_CLIENT_STATUS, YtsClientStatusClass)) + +#define YTS_IS_CLIENT_STATUS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YTS_TYPE_CLIENT_STATUS)) + +#define YTS_IS_CLIENT_STATUS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), YTS_TYPE_CLIENT_STATUS)) + +#define YTS_CLIENT_STATUS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), YTS_TYPE_CLIENT_STATUS, YtsClientStatusClass)) + +typedef struct { + GObject parent; +} YtsClientStatus; + +typedef struct { + GObjectClass parent; +} YtsClientStatusClass; + +GType +yts_client_status_get_type (void); + +YtsClientStatus * +yts_client_status_new (char const *service_id); + +bool +yts_client_status_add_capability (YtsClientStatus *self, + char const *capability); + +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, + char const *const *attribs, + char const *xml_payload); + +bool +yts_client_status_clear (YtsClientStatus *self, + char const *capability); + +typedef bool +(*YtsClientStatusCapabilityIterator) (YtsClientStatus const *self, + char const *capability, + char const *status_xml, + void *data); + +bool +yts_client_status_foreach_capability (YtsClientStatus *self, + YtsClientStatusCapabilityIterator iterator, + void *user_data); + +G_END_DECLS + +#endif /* YTS_CLIENT_STATUS_H */ + diff --git a/ytstenut/yts-client.c b/ytstenut/yts-client.c index c9f46cd..b556d98 100644 --- a/ytstenut/yts-client.c +++ b/ytstenut/yts-client.c @@ -36,8 +36,8 @@ #include "empathy-tp-file.h" #include "ytstenut-internal.h" #include "yts-adapter-factory.h" -#include "yts-caps.h" #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 +49,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 +81,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 +735,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 +1156,31 @@ yts_client_setup_debug (YtsClient *self) g_free (busname); } +static bool +_client_status_foreach_interest_add (YtsClientStatus const *client_status, + char const *capability, + YtsClient *self) +{ + YtsClientPrivate *priv = GET_PRIVATE (self); + + tp_yts_client_add_interest (priv->tp_client, capability); + + return true; +} + +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 +1196,17 @@ 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)); - } - } + /* Publish capabilities */ + yts_client_status_foreach_capability ( + priv->client_status, + (YtsClientStatusCapabilityIterator) _client_status_foreach_capability_add, + self); + + /* Publish interests */ + yts_client_status_foreach_interest ( + priv->client_status, + (YtsClientStatusInterestIterator) _client_status_foreach_interest_add, + self); /* * If connection has been requested already, make one @@ -1342,6 +1387,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 +1545,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 +1574,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 +2304,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 +2314,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 +2328,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 +2357,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 +2472,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 +2532,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 +2814,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) @@ -2904,11 +2842,11 @@ yts_client_refresh_roster (YtsClient *self) * Since: 0.3 */ void -yts_client_add_capability (YtsClient *self, - char const *c) +yts_client_add_capability (YtsClient *self, + char const *c, + YtsCapabilityMode mode) { YtsClientPrivate *priv = GET_PRIVATE (self); - GQuark cap_quark; char *capability; /* FIXME check that there's no collision with service owned capabilities. */ @@ -2916,27 +2854,40 @@ yts_client_add_capability (YtsClient *self, g_return_if_fail (YTS_IS_CLIENT (self)); g_return_if_fail (c); - // TODO error reporting // TODO make sure c doesn't have prefix already - capability = g_strdup_printf ("urn:ytstenut:capabilities:%s", c); - cap_quark = g_quark_from_string (capability); - if (yts_client_has_capability (self, cap_quark)) - { + if (YTS_CAPABILITY_MODE_PROVIDED == mode) { + + 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; } - if (priv->tp_client) - tp_yts_client_add_capability (priv->tp_client, capability); + yts_client_refresh_roster (self); - if (!priv->caps) - priv->caps = g_array_sized_new (FALSE, FALSE, sizeof (YtsCaps), 1); + } else if (YTS_CAPABILITY_MODE_CONSUMED == mode) { - g_array_append_val (priv->caps, cap_quark); + 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; + } - yts_client_refresh_roster (self); + } else { + g_critical ("Invalid capability mode %d", mode); + } g_free (capability); } @@ -3095,40 +3046,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 @@ -3136,39 +3053,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; - - g_return_if_fail (YTS_IS_CLIENT (self) && c); - g_return_if_fail (c); - - g_return_if_fail (priv->caps && priv->caps->len); + char *capability; + char const *capability_status_xml; + char const *attribs[] = { + "activity", activity, + NULL + }; - 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_return_if_fail (YTS_IS_CLIENT (self)); + g_return_if_fail (cap_value); + g_return_if_fail (activity); - g_message ("Constructing status for %s, %s, %s", - capability, activity, priv->service_id); + capability = g_strdup_printf ("%s%s", + YTS_XML_CAPABILITY_NAMESPACE, + cap_value); - 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 @@ -3605,7 +3544,7 @@ yts_client_publish_service (YtsClient *self, g_hash_table_insert (priv->services, g_strdup (fqc_ids[i]), adapter); - yts_client_add_capability (self, fqc_ids[i]); + yts_client_add_capability (self, fqc_ids[i], YTS_CAPABILITY_MODE_PROVIDED); /* Keep the proxy management service up to date. */ adapter = g_hash_table_lookup (priv->services, YTS_PROFILE_FQC_ID); diff --git a/ytstenut/yts-client.h b/ytstenut/yts-client.h index d417793..32bcfe2 100644 --- a/ytstenut/yts-client.h +++ b/ytstenut/yts-client.h @@ -95,7 +95,7 @@ yts_client_get_type (void) G_GNUC_CONST; * @YTS_PROTOCOL_XMPP: Jabber * @YTS_PROTOCOL_LOCAL_XMPP: XEP-0174 serverless messaging. * - * YtsProtocol represents the xmpp protocol to use + * YtsProtocol represents the XMPP variant to use. */ typedef enum { /*< prefix=YTS_PROTOCOL >*/ YTS_PROTOCOL_XMPP = 0, @@ -115,9 +115,24 @@ yts_client_disconnect (YtsClient *self); void yts_client_connect (YtsClient *self); +/** + * YtsCapabilityMode: + * @YTS_CAPABILITY_MODE_PROVIDED: @capability is provided by this client. + * @YTS_CAPABILITY_MODE_CONSUMED: @capability is going to be consumed by this + * client so track other services providing it. + * + * YtsCapabilityMode is used to determine whether a capability is provided or + * sought after. + */ +typedef enum { /*< prefix=YTS_CAPABILITY_MODE >*/ + YTS_CAPABILITY_MODE_PROVIDED = 0, + YTS_CAPABILITY_MODE_CONSUMED +} YtsCapabilityMode; /* FIXME better naming? */ + void -yts_client_add_capability (YtsClient *self, - char const *capability); +yts_client_add_capability (YtsClient *self, + char const *capability, + YtsCapabilityMode mode); YtsRoster *const yts_client_get_roster (YtsClient const *self); @@ -136,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, diff --git a/ytstenut/yts-xml.h b/ytstenut/yts-xml.h new file mode 100644 index 0000000..71012b0 --- /dev/null +++ b/ytstenut/yts-xml.h @@ -0,0 +1,28 @@ +/* + * Copyright © 2012 Intel Corp. + * + * 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 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, see + * <http://www.gnu.org/licenses/>. + * + * Authored by: Rob Staudinger <robsta@linux.intel.com> + */ + +#ifndef YTS_XML +#define YTS_XML + +#define YTS_XML_CAPABILITY_NAMESPACE "urn:ytstenut:capabilities:" +#define YTS_XML_STATUS_NAMESPACE "urn:ytstenut:status" + +#endif /* YTS_XML */ + diff --git a/ytstenut/ytstenut.sym b/ytstenut/ytstenut.sym index 0dd8510..032010a 100644 --- a/ytstenut/ytstenut.sym +++ b/ytstenut/ytstenut.sym @@ -3,6 +3,7 @@ yts_capability_get_fqc_ids yts_capability_has_fqc_id yts_capability_status_get_type yts_capability_status_new +yts_capability_mode_get_type yts_client_connect yts_client_disconnect yts_client_foreach_service |