[Telepathy-commits] [telepathy-gabble/master] commit initial location interface implemenation
Dafydd Harries
daf at rhydd.org
Sun Feb 1 05:23:54 PST 2009
---
src/Makefile.am | 2 +
src/conn-location.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++
src/conn-location.h | 14 +++
src/connection.c | 5 +-
tests/test-location.py | 46 +++++++++
5 files changed, 303 insertions(+), 1 deletions(-)
create mode 100644 src/conn-location.c
create mode 100644 src/conn-location.h
create mode 100644 tests/test-location.py
diff --git a/src/Makefile.am b/src/Makefile.am
index 619d9ee..b2549d9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,6 +33,8 @@ libgabble_convenience_la_SOURCES = \
conn-aliasing.c \
conn-avatars.h \
conn-avatars.c \
+ conn-location.h \
+ conn-location.c \
conn-olpc.h \
conn-olpc.c \
conn-presence.h \
diff --git a/src/conn-location.c b/src/conn-location.c
new file mode 100644
index 0000000..f962e65
--- /dev/null
+++ b/src/conn-location.c
@@ -0,0 +1,237 @@
+
+#include "config.h"
+#include "conn-location.h"
+
+#include <stdlib.h>
+
+#include "namespaces.h"
+#include "pubsub.h"
+
+struct request_location_ctx
+{
+ DBusGMethodInvocation *call;
+ guint pending_replies;
+ GHashTable *results;
+};
+
+/* XXX: similar to conn-olpc.c's inspect_contact(), except that it assumes
+ * that the handle is valid. (Does tp_handle_inspect check validity anyway?)
+ * Reduce duplication.
+ */
+static const gchar *
+inspect_contact (TpBaseConnection *base,
+ guint contact)
+{
+ TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (
+ base, TP_HANDLE_TYPE_CONTACT);
+
+ return tp_handle_inspect (contact_repo, contact);
+}
+
+static gboolean
+validate_contacts (TpBaseConnection *base,
+ DBusGMethodInvocation *context,
+ const GArray *contacts)
+{
+ TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base,
+ TP_HANDLE_TYPE_CONTACT);
+ GError *error;
+
+ if (!tp_handles_are_valid (contact_handles, contacts, TRUE, &error))
+ {
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+lm_message_node_get_double (LmMessageNode *node,
+ gdouble *d)
+{
+ const gchar *value;
+ gchar *end;
+
+ value = lm_message_node_get_value (node);
+
+ if (value == NULL)
+ return FALSE;
+
+ *d = strtod (value, &end);
+
+ if (end == value)
+ return FALSE;
+
+ return TRUE;
+}
+
+static LmHandlerResult
+pep_reply_cb (GabbleConnection *conn,
+ LmMessage *sent_msg,
+ LmMessage *reply_msg,
+ GObject *object,
+ gpointer user_data)
+{
+ TpBaseConnection *base = (TpBaseConnection *) conn;
+ TpHandleRepoIface *contact_repo =
+ tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT);
+ LmMessageNode *geoloc;
+ LmMessageNode *lat_node;
+ LmMessageNode *lon_node;
+ GHashTable *result = NULL;
+ struct request_location_ctx *ctx = user_data;
+ const gchar *from;
+ gdouble lat;
+ gdouble lon;
+ guint contact;
+
+ ctx->pending_replies--;
+ result = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) tp_g_value_slice_free);
+ from = lm_message_node_get_attribute (reply_msg->node, "from");
+
+ if (from == NULL)
+ goto FAIL;
+
+ contact = tp_handle_lookup (contact_repo, from, NULL, NULL);
+ /* XXX: ref all the handles */
+ g_assert (contact);
+ geoloc = lm_message_node_find_child (reply_msg->node, "geoloc");
+
+ if (geoloc == NULL)
+ goto FAIL;
+
+ lat_node = lm_message_node_find_child (geoloc, "lat");
+ lon_node = lm_message_node_find_child (geoloc, "lon");
+
+ if (lat_node == NULL &&
+ lon_node == NULL)
+ goto FAIL;
+
+ if (lat_node != NULL && lm_message_node_get_double (lat_node, &lat))
+ {
+ GValue *value = g_slice_new0 (GValue);
+
+ g_value_init (value, G_TYPE_DOUBLE);
+ g_value_set_double (value, lat);
+ g_hash_table_insert (result, g_strdup ("lat"), value);
+ }
+
+ if (lon_node != NULL && lm_message_node_get_double (lon_node, &lon))
+ {
+ GValue *value = g_slice_new0 (GValue);
+
+ g_value_init (value, G_TYPE_DOUBLE);
+ g_value_set_double (value, lon);
+ g_hash_table_insert (result, g_strdup ("lon"), value);
+ }
+
+ g_hash_table_insert (ctx->results, GINT_TO_POINTER (contact), result);
+ goto END;
+
+FAIL:
+ g_hash_table_destroy (result);
+
+END:
+ if (ctx->pending_replies == 0)
+ {
+ gabble_svc_location_return_from_request_locations (ctx->call, ctx->results);
+ g_hash_table_destroy (ctx->results);
+ g_slice_free (struct request_location_ctx, ctx);
+ }
+
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static void
+location_request_locations (GabbleSvcLocation *iface,
+ const GArray *contacts,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (iface);
+ TpBaseConnection *base = (TpBaseConnection *) conn;
+ struct request_location_ctx *ctx = NULL;
+ guint i;
+
+ if (!validate_contacts (base, context, contacts))
+ return;
+
+ ctx = g_slice_new0 (struct request_location_ctx);
+ ctx->call = context;
+ ctx->pending_replies = contacts->len;
+ ctx->results = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
+ (GDestroyNotify) g_hash_table_destroy);
+
+ for (i = 0; i < contacts->len; i++)
+ {
+ GError error = { TP_ERRORS, TP_ERROR_NETWORK_ERROR,
+ "Sending PEP location query failed" };
+ guint contact = g_array_index (contacts, guint, i);
+ const gchar *jid = inspect_contact (base, contact);
+
+ if (!pubsub_query (conn, jid, NS_GEOLOC, pep_reply_cb, ctx))
+ {
+ dbus_g_method_return_error (context, &error);
+ g_hash_table_destroy (ctx->results);
+ g_slice_free (struct request_location_ctx, ctx);
+ return;
+ }
+ }
+}
+
+static void
+location_set_location (GabbleSvcLocation *iface,
+ GHashTable *location,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (iface);
+ LmMessage *msg;
+ LmMessageNode *geoloc;
+ GValue *lat_val;
+ GValue *lon_val;
+
+ msg = pubsub_make_publish_msg (NULL, NS_GEOLOC, NS_GEOLOC, "geoloc",
+ &geoloc);
+
+ lat_val = g_hash_table_lookup (location, "lat");
+ lon_val = g_hash_table_lookup (location, "lon");
+
+ if (lat_val && G_VALUE_TYPE (lat_val) == G_TYPE_DOUBLE)
+ {
+ gchar *lat_str;
+
+ lat_str = g_strdup_printf ("%.6f", g_value_get_double (lat_val));
+ lm_message_node_add_child (geoloc, "lat", lat_str);
+ g_free (lat_str);
+ }
+
+ if (lon_val && G_VALUE_TYPE (lon_val) == G_TYPE_DOUBLE)
+ {
+ gchar *lon_str;
+
+ lon_str = g_strdup_printf ("%.6f", g_value_get_double (lon_val));
+ lm_message_node_add_child (geoloc, "lon", lon_str);
+ g_free (lon_str);
+ }
+
+ /* XXX: use _ignore_reply */
+ if (!_gabble_connection_send (conn, msg, NULL))
+ /* XXX: return error */
+ return;
+
+ dbus_g_method_return (context);
+}
+
+void
+location_iface_init (gpointer g_iface, gpointer iface_data)
+{
+ GabbleSvcLocationClass *klass = g_iface;
+
+#define IMPLEMENT(x) gabble_svc_location_implement_##x (klass, location_##x)
+ IMPLEMENT(request_locations);
+ IMPLEMENT(set_location);
+#undef IMPLEMENT
+}
+
diff --git a/src/conn-location.h b/src/conn-location.h
new file mode 100644
index 0000000..3ec03e0
--- /dev/null
+++ b/src/conn-location.h
@@ -0,0 +1,14 @@
+
+#ifndef __CONN_LOCATION_H__
+#define __CONN_LOCATION_H__
+
+#include <extensions/extensions.h>
+
+G_BEGIN_DECLS
+
+void
+location_iface_init (gpointer g_iface, gpointer iface_data);
+
+G_END_DECLS
+
+#endif /* __CONN_LOCATION_H__ */
diff --git a/src/connection.c b/src/connection.c
index 5f71e72..f6ae00e 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -50,6 +50,7 @@
#include "caps-hash.h"
#include "conn-aliasing.h"
#include "conn-avatars.h"
+#include "conn-location.h"
#include "conn-presence.h"
#include "conn-olpc.h"
#include "debug.h"
@@ -101,6 +102,8 @@ G_DEFINE_TYPE_WITH_CODE(GabbleConnection,
tp_presence_mixin_simple_presence_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE,
conn_presence_iface_init);
+ G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_LOCATION,
+ location_iface_init);
G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_BUDDY_INFO,
olpc_buddy_info_iface_init);
G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_OLPC_ACTIVITY_PROPERTIES,
@@ -2028,7 +2031,7 @@ connection_disco_cb (GabbleDisco *disco,
if (conn->features & GABBLE_CONNECTION_FEATURES_PEP)
{
const gchar *ifaces[] = { GABBLE_IFACE_OLPC_BUDDY_INFO,
- GABBLE_IFACE_OLPC_ACTIVITY_PROPERTIES, NULL };
+ GABBLE_IFACE_OLPC_ACTIVITY_PROPERTIES, GABBLE_IFACE_LOCATION, NULL };
tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces);
}
diff --git a/tests/test-location.py b/tests/test-location.py
new file mode 100644
index 0000000..43845aa
--- /dev/null
+++ b/tests/test-location.py
@@ -0,0 +1,46 @@
+
+from gabbletest import exec_test, make_result_iq
+from servicetest import call_async
+
+def test(q, bus, conn, stream):
+ # hack
+ import dbus
+ conn.interfaces['Location'] = \
+ dbus.Interface(conn, 'org.freedesktop.Telepathy.Location')
+
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
+
+ # discard activities request
+ q.expect('stream-iq', iq_type='set',
+ query_ns='http://jabber.org/protocol/pubsub')
+
+ conn.Location.SetLocation({
+ 'lat': dbus.Double(0.0, variant_level=1), 'lon': 0.0})
+
+ event = q.expect('stream-iq', iq_type='set',
+ query_ns='http://jabber.org/protocol/pubsub')
+
+ handle = conn.RequestHandles(1, ['bob at foo.com'])[0]
+ call_async(q, conn.Location, 'GetLocations', [handle])
+
+ # XXX this is made up
+ event = q.expect('stream-iq', iq_type='get',
+ query_ns='http://jabber.org/protocol/pubsub')
+ result = make_result_iq(stream, event.stanza)
+ result['from'] = 'bob at foo.com'
+ query = result.firstChildElement()
+ geoloc = query.addElement(('http://jabber.org/protocol/geoloc', 'geoloc'))
+ geoloc.addElement('lat', content='1.234')
+ geoloc.addElement('lon', content='5.678')
+ stream.send(result)
+
+ q.expect('dbus-return', method='GetLocations')
+
+ conn.Disconnect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
+ return True
+
+if __name__ == '__main__':
+ exec_test(test)
+
--
1.5.6.5
More information about the Telepathy-commits
mailing list