summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Staudinger <robsta@linux.intel.com>2012-01-26 09:53:02 +0100
committerRob Staudinger <robsta@linux.intel.com>2012-01-26 09:53:02 +0100
commitb004f03c6500e39021cee38f327507bb40af03c3 (patch)
tree5925d7920f43cb94a59534d6e9b2030303efbee2
parent45a01f95eecaaee49ab94ccdc3211d3c917f056e (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.c2
-rw-r--r--examples/status.c16
-rw-r--r--ytstenut/Makefile.am4
-rw-r--r--ytstenut/yts-caps.h178
-rw-r--r--ytstenut/yts-client-status.c325
-rw-r--r--ytstenut/yts-client-status.h110
-rw-r--r--ytstenut/yts-client.c395
-rw-r--r--ytstenut/yts-client.h28
-rw-r--r--ytstenut/yts-xml.h28
-rw-r--r--ytstenut/ytstenut.sym1
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