From b81df5f0da5a86fe5bef05f3d8d627f6a01cee76 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 6 Feb 2014 19:18:35 +0000 Subject: Revert to commit 822ddd0 Some commits intended for 'next' appear to have ended up on master. --- .gitignore | 4 +- configure.ac | 31 +- data/Makefile.am | 4 +- data/salut.service.in | 2 +- extensions/Connection_Future.xml | 12 +- extensions/Makefile.am | 2 + extensions/OLPC_Activity_Properties.xml | 124 ++ extensions/OLPC_Buddy_Info.xml | 285 ++++ extensions/Salut_Plugin_Test.xml | 2 +- extensions/connection.xml | 3 + plugins/test.c | 2 +- salut/capabilities.h | 1 + src/Makefile.am | 14 + src/avahi-contact.c | 72 + src/avahi-discovery-client.c | 32 +- src/avahi-olpc-activity-manager.c | 376 +++++ src/avahi-olpc-activity-manager.h | 68 + src/avahi-olpc-activity.c | 546 +++++++ src/avahi-olpc-activity.h | 70 + src/avahi-self.c | 134 +- src/avahi-self.h | 3 +- src/bonjour-discovery-client.c | 6 +- src/bonjour-self.c | 12 +- src/bonjour-self.h | 3 +- src/capability-set.c | 23 + src/connection-contact-info.c | 49 + src/connection.c | 1720 +++++++++++++++++++- src/connection.h | 10 + src/contact-manager.c | 5 +- src/contact.c | 255 +++ src/contact.h | 43 + src/debug.c | 1 + src/debug.h | 5 +- src/discovery-client.c | 21 +- src/discovery-client.h | 18 +- src/dummy-discovery-client.c | 7 +- src/file-transfer-channel.c | 4 +- src/im-channel.c | 23 +- src/muc-channel.c | 134 +- src/muc-manager.c | 9 +- src/muc-tube-dbus.c | 2 - src/muc-tube-stream.c | 2 - src/namespaces.h | 8 + src/olpc-activity-manager.c | 349 ++++ src/olpc-activity-manager.h | 98 ++ src/olpc-activity.c | 680 ++++++++ src/olpc-activity.h | 98 ++ src/presence.h | 1 + src/protocol.c | 3 +- src/protocol.h | 4 +- src/roomlist-channel.c | 8 +- src/self.c | 480 ++++++ src/self.h | 44 + src/text-helper.h | 2 +- src/tube-dbus.c | 7 +- src/tube-iface.c | 4 +- src/tube-stream.c | 14 +- src/tubes-manager.c | 34 +- src/util.h | 6 - src/write-mgr-file.c | 12 +- tests/twisted/Makefile.am | 8 + tests/twisted/avahi/aliases.py | 11 +- tests/twisted/avahi/caps-file-transfer.py | 8 +- tests/twisted/avahi/caps-self.py | 2 +- tests/twisted/avahi/close-local-pending-room.py | 10 +- .../avahi/file-transfer/file_transfer_helper.py | 10 +- .../twisted/avahi/file-transfer/ft-client-caps.py | 14 +- tests/twisted/avahi/ichat-composing.py | 8 +- tests/twisted/avahi/ichat-incoming-msg.py | 10 +- tests/twisted/avahi/muc-invite.py | 11 +- tests/twisted/avahi/olpc-activity-announcements.py | 115 ++ tests/twisted/avahi/request-im.py | 8 +- tests/twisted/avahi/request-muc.py | 6 +- tests/twisted/avahi/roomlist.py | 11 +- tests/twisted/avahi/set-presence.py | 4 +- tests/twisted/avahi/text-channel.py | 15 +- tests/twisted/avahi/tubes/request-muc-tubes.py | 6 +- tests/twisted/avahi/tubes/tubetestutil.py | 10 +- tests/twisted/avahi/tubes/two-muc-dbus-tubes.py | 4 +- tests/twisted/cm/protocol.py | 6 +- tests/twisted/constants.py | 205 +-- tests/twisted/ns.py | 10 + tests/twisted/saluttest.py | 24 +- tests/twisted/servicetest.py | 249 +-- tests/twisted/sidecars.py | 2 +- tests/twisted/tools/Makefile.am | 4 +- tests/twisted/tools/salut.service.in | 2 +- 87 files changed, 6118 insertions(+), 646 deletions(-) create mode 100644 extensions/OLPC_Activity_Properties.xml create mode 100644 extensions/OLPC_Buddy_Info.xml create mode 100644 src/avahi-olpc-activity-manager.c create mode 100644 src/avahi-olpc-activity-manager.h create mode 100644 src/avahi-olpc-activity.c create mode 100644 src/avahi-olpc-activity.h create mode 100644 src/olpc-activity-manager.c create mode 100644 src/olpc-activity-manager.h create mode 100644 src/olpc-activity.c create mode 100644 src/olpc-activity.h create mode 100644 tests/twisted/avahi/olpc-activity-announcements.py diff --git a/.gitignore b/.gitignore index ec554dd4..d87d4ead 100644 --- a/.gitignore +++ b/.gitignore @@ -81,7 +81,7 @@ m4/lt~obsolete.m4 /coverage/ -data/im.telepathy1.ConnectionManager.salut.service +data/org.freedesktop.Telepathy.ConnectionManager.salut.service data/*.service data/salut.manager @@ -118,7 +118,7 @@ tests/twisted/tools/core tests/twisted/tools/exec-with-log.sh tests/twisted/tools/salut-testing.log* tests/twisted/tools/tmp-session-bus.conf -tests/twisted/tools/im.telepathy1.ConnectionManager.salut.service +tests/twisted/tools/org.freedesktop.Telepathy.ConnectionManager.salut.service tests/twisted/tools/refdbg.log /tests/twisted/with-session-bus*.address /tests/twisted/with-session-bus*.pid diff --git a/configure.ac b/configure.ac index 3723b1ec..47699d50 100644 --- a/configure.ac +++ b/configure.ac @@ -8,8 +8,8 @@ AC_PREREQ([2.60]) # set salut_nano_version to 0. m4_define([salut_major_version], [0]) -m4_define([salut_minor_version], [99]) -m4_define([salut_micro_version], [2]) +m4_define([salut_minor_version], [8]) +m4_define([salut_micro_version], [999]) m4_define([salut_nano_version], [1]) # Some magic @@ -114,6 +114,17 @@ AC_MSG_RESULT([$TEST_PYTHON]) AC_SUBST(TEST_PYTHON) AM_CONDITIONAL([WANT_TWISTED_TESTS], test false != "$TEST_PYTHON") +dnl olpc specific code switch +AC_ARG_ENABLE(olpc, + AC_HELP_STRING([--enable-olpc],[compile with olpc specific code]), + enable_olpc=$enableval, enable_olpc=no ) + +AS_IF([test x$enable_olpc = xyes], + [AC_DEFINE(ENABLE_OLPC, [], [Enable olpc code])]) +AM_CONDITIONAL(ENABLE_OLPC, test "x$enable_olpc" = "xyes") + +AC_SUBST(ENABLE_OLPC) + AC_HEADER_STDC([]) AC_C_INLINE @@ -184,16 +195,11 @@ PKG_CHECK_MODULES(LIBXML2, [libxml-2.0]) AC_SUBST(LIBXML2_CFLAGS) AC_SUBST(LIBXML2_LIBS) -TELEPATHY_GLIB_REQUIRED=0.99.2 AC_DEFINE([TP_SEAL_ENABLE], [], [Prevent to use sealed variables]) AC_DEFINE([TP_DISABLE_SINGLE_INCLUDE], [], [Disable single header include]) -AC_DEFINE([TP_VERSION_MIN_REQUIRED], [TP_VERSION_1_0], [Ignore post 1.0 deprecations]) -AC_DEFINE([TP_VERSION_MAX_ALLOWED], [TP_VERSION_1_0], [Prevent post 1.0 APIs]) -PKG_CHECK_MODULES([TELEPATHY_GLIB], - [ - telepathy-glib-1 >= $TELEPATHY_GLIB_REQUIRED - telepathy-glib-1-dbus >= $TELEPATHY_GLIB_REQUIRED - ]) +AC_DEFINE([TP_VERSION_MIN_REQUIRED], [TP_VERSION_0_22], [Ignore post 0.22 deprecations]) +AC_DEFINE([TP_VERSION_MAX_ALLOWED], [TP_VERSION_0_22], [Prevent post 0.22 APIs]) +PKG_CHECK_MODULES([TELEPATHY_GLIB], [telepathy-glib >= 0.21.2]) AC_SUBST(TELEPATHY_GLIB_CFLAGS) AC_SUBST(TELEPATHY_GLIB_LIBS) @@ -385,4 +391,9 @@ Configure summary: Enable debug................: ${enable_debug} Python tests................: ${tests_enabled} Plugins.....................: ${enable_plugins} + +Features: + + OLPC support................: ${enable_olpc} + " diff --git a/data/Makefile.am b/data/Makefile.am index 049070c6..e0002ab6 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -4,12 +4,12 @@ managerdir = $(datadir)/telepathy/managers manager_DATA = salut.manager servicedir = $(datadir)/dbus-1/services -service_DATA = im.telepathy1.ConnectionManager.salut.service +service_DATA = org.freedesktop.Telepathy.ConnectionManager.salut.service # We don't use the full filename for the .in because > 99 character filenames # in tarballs are non-portable (and automake 1.8 doesn't let us build # non-archaic tarballs) -im.telepathy1.ConnectionManager.salut.service: salut.service.in \ +org.freedesktop.Telepathy.ConnectionManager.salut.service: salut.service.in \ Makefile $(AM_V_GEN)sed -e "s|[@]libexecdir[@]|$(libexecdir)|" $< > $@ diff --git a/data/salut.service.in b/data/salut.service.in index 60303685..322aea76 100644 --- a/data/salut.service.in +++ b/data/salut.service.in @@ -1,3 +1,3 @@ [D-BUS Service] -Name=im.telepathy1.ConnectionManager.salut +Name=org.freedesktop.Telepathy.ConnectionManager.salut Exec=@libexecdir@/telepathy-salut diff --git a/extensions/Connection_Future.xml b/extensions/Connection_Future.xml index a578a888..11047983 100644 --- a/extensions/Connection_Future.xml +++ b/extensions/Connection_Future.xml @@ -20,9 +20,9 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

- - + @@ -75,12 +75,12 @@ USA.

in a dictionary, build a proxy object from the value). More “plural” plugins are likely to want to implement new types of Channel + namespace="org.freedesktop.Telepathy">Channel instead.

- + The requested sidecar is not implemented by this connection manager, or a necessary server-side component does not exist. (FIXME: split @@ -91,7 +91,7 @@ USA.

- + A server-side component needed by the requested sidecar reported it is currently too busy, or did not respond for some @@ -99,7 +99,7 @@ USA.

- + The connection was disconnected while the sidecar was being set up. diff --git a/extensions/Makefile.am b/extensions/Makefile.am index 5edd07d0..39f94e96 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -1,6 +1,8 @@ tools_dir = $(top_srcdir)/tools EXTRA_DIST = \ + OLPC_Buddy_Info.xml \ + OLPC_Activity_Properties.xml \ connection.xml \ Salut_Plugin_Test.xml \ Connection_Future.xml \ diff --git a/extensions/OLPC_Activity_Properties.xml b/extensions/OLPC_Activity_Properties.xml new file mode 100644 index 00000000..d1ea2afa --- /dev/null +++ b/extensions/OLPC_Activity_Properties.xml @@ -0,0 +1,124 @@ + + + Copyright (C) 2007 Collabora Limited + +

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.1 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, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

+
+ + + + + + + An integer handle representing the room of the activity + + + + + A dictionary mapping properties names to the desired values + + + + Set the properties of the activity associated to the given room for this connection. + You have to be the owner of this activity. + + + + + + + + + + + + An integer handle for the activity's room to request his properties for + + + + + A dictionary mapping properties names to their values + + + + Get the properties of a particular activity. + + + + + + + + + + + An activity id + + + + + A room handle + + + + Returns the handle of the room associated with this activity + +

When an activity starts up, it knows its activity_id but doesn't + know yet if it's shared or not, much less the room.

+
+
+ + + + +
+ + + + + An integer handle representing the room of the activity + + + + + A dictionary mapping properties names to their new values + + + + Signal emitted when the properties of an activity are changed. + + + + +

An interface on connections to associate OLPC activity properties + with rooms.

+ +

The following types and names are used to request and set properties:

+
+
s:color
+
The color of the activity. Format used is #RRGGBB,#RRGGBB (stroke,fill).
+ +
s:name
+
The name of the activity.
+ +
s:type
+
The type of the activity.
+
+ +
+
+
+ diff --git a/extensions/OLPC_Buddy_Info.xml b/extensions/OLPC_Buddy_Info.xml new file mode 100644 index 00000000..7caf35c0 --- /dev/null +++ b/extensions/OLPC_Buddy_Info.xml @@ -0,0 +1,285 @@ + + + Copyright (C) 2007 Collabora Limited + +

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.1 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, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

+
+ + + + + + + A dictionary mapping information names to the desired values. + This replaces any existing buddy properties completely: any keys + which were previously present, but are not present in this dictionary, + are deleted. + + + +

Set the information of the local user for this connection.

+

This method may be called before Connect(), in which case the given + properties will be advertised as soon as possible after connection + (possibly immediately).

+
+ + + +
+ + + + + An integer handle for the contact to request his properties for + + + + + A dictionary mapping information names to their values + + + + Get the properties of a particular contact. + + + + + + + + + + + An integer handle representing the contact + + + + + A dictionary mapping property names to their new values. All + properties are included, not just those that have changed. + + + + Signal emitted when the properties of a contact from your 'subscribe' + contact list are changed. + + + + + + A struct containing: +
    +
  • the identifier of the activity
  • +
  • the room handle of the activity channel
  • +
+
+ + +
+ + + + + An array of structs containing: +
    +
  • the identifier of the activity
  • +
  • the room handle of the activity channel
  • +
+
+
+ + Set the activities of the local user for this connection. + + + + + +
+ + + + + An activity id + + + + + A room handle + + + + Advertise an activity associated to a muc room + +

Once an activity shares itself, needs to be advertised if it's not + private. SetActivities could be used for this but it would mean that + the activity would need to call GetActivities then add itself.

+
+
+ + + + +
+ + + + + An integer handle for the contact whose activities are to be returned + + + + + An array of structs containing: +
    +
  • the identifier of the activity
  • +
  • the room handle of the activity channel
  • +
+
+
+ + Get the activities of a particular contact. + + + + + +
+ + + + + An integer handle representing the contact + + + + + An array of structs containing: +
    +
  • the identifier of the activity
  • +
  • the handle of the activity channel
  • +
+
+
+ + Signal emitted when the activities of a contact from your 'subscribe' + contact list are changed. + +
+ + + + + The identifier of the activity, or the empty string if there is no + current activity + + + + + The room handle of the activity channel, or 0 if there is no current + activity + + + + Set the current activity of the local user for this connection. + + + + + + + + + + + An integer handle for the contact whose current activity is to be + returned + + + + + The identifier of the activity, or "" if there is no current activity + + + + + The room handle of the activity, or 0 if there is no current activity + + + + Get the current activity of a particular contact. + + + + + + + + + + + An integer handle representing the contact + + + + + The identifier of the activity, or "" if there is no current activity + + + + + The room handle of the activity channel, or 0 if there is no current + activity + + + + Signal emitted when the current activity of a contact from your 'subscribe' + contact list is changed. + + + + +

An interface on connections to associate OLPC buddy information + with contacts, providing methods for the user to set their own + information and retrieve information of contacts. + The user is automatically notified when information of contacts that + are in his 'subscribe' contact list change.

+ +

The following types and names are used to request and set information + (except for activities):

+
+
s:color
+
The color name of the buddy. Format used is #RRGGBB,#RRGGBB + (stroke,fill). +
+ +
ay:key
+
The public key of the buddy.
+ +
s:jid
+
For link-local connections, the JID of the buddy's server account.
+
+ +

Activities are represented by a struct containing:

+
    +
  • the identifier of the activity
  • +
  • the handle of the activity channel
  • +
+
+
+
+ diff --git a/extensions/Salut_Plugin_Test.xml b/extensions/Salut_Plugin_Test.xml index d62c26b9..265c2a84 100644 --- a/extensions/Salut_Plugin_Test.xml +++ b/extensions/Salut_Plugin_Test.xml @@ -17,7 +17,7 @@ Lesser General Public License for more details.

License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

- +

A sidecar interface implemented by a plugin used by the test suite.

diff --git a/extensions/connection.xml b/extensions/connection.xml index cfaafbbd..8e72821c 100644 --- a/extensions/connection.xml +++ b/extensions/connection.xml @@ -22,4 +22,7 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

+ + + diff --git a/plugins/test.c b/plugins/test.c index 6ab15135..70e231b8 100644 --- a/plugins/test.c +++ b/plugins/test.c @@ -14,7 +14,7 @@ static void plugin_iface_init ( gpointer g_iface, gpointer data); -#define IFACE_TEST "im.telepathy1.Salut.Plugin.Test" +#define IFACE_TEST "org.freedesktop.Telepathy.Salut.Plugin.Test" static const gchar * const sidecar_interfaces[] = { IFACE_TEST, diff --git a/salut/capabilities.h b/salut/capabilities.h index 0dcbaee5..faecc64f 100644 --- a/salut/capabilities.h +++ b/salut/capabilities.h @@ -43,6 +43,7 @@ const GabbleCapabilitySet *gabble_capabilities_get_any_google_av (void); const GabbleCapabilitySet *gabble_capabilities_get_any_jingle_av (void); const GabbleCapabilitySet *gabble_capabilities_get_any_transport (void); const GabbleCapabilitySet *gabble_capabilities_get_geoloc_notify (void); +const GabbleCapabilitySet *gabble_capabilities_get_olpc_notify (void); /* XEP-0115 version 1.3: * diff --git a/src/Makefile.am b/src/Makefile.am index 52fabb1f..e1d10e0d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -106,6 +106,20 @@ BONJOUR_BACKEND_SOURCES = \ bonjour-discovery-client.h \ bonjour-discovery-client.c +if ENABLE_OLPC + CORE_SOURCES += \ + olpc-activity.h \ + olpc-activity.c \ + olpc-activity-manager.h \ + olpc-activity-manager.c + + AVAHI_BACKEND_SOURCES += \ + avahi-olpc-activity-manager.h \ + avahi-olpc-activity-manager.c \ + avahi-olpc-activity.h \ + avahi-olpc-activity.c +endif + DUMMY_BACKEND_SOURCES = \ dummy-discovery-client.h \ dummy-discovery-client.c diff --git a/src/avahi-contact.c b/src/avahi-contact.c index 385b8516..dd15ed4e 100644 --- a/src/avahi-contact.c +++ b/src/avahi-contact.c @@ -649,6 +649,11 @@ contact_resolved_cb (GaServiceResolver *resolver, char *nick, *first, *last; /* node, hash and ver as defined by XEP-0115 */ char *node, *hash, *ver; +#ifdef ENABLE_OLPC + char *activity_id, *room_id; + char *olpc_key_part; + gsize size; +#endif DEBUG_RESOLVER (self, resolver, "contact %s resolved", contact->name); @@ -718,6 +723,73 @@ contact_resolved_cb (GaServiceResolver *resolver, salut_contact_change_jid (contact, s); avahi_free (s); +#ifdef ENABLE_OLPC + /* OLPC color */ + s = _avahi_txt_get_keyval (txt, "olpc-color"); + salut_contact_change_olpc_color (contact, s); + avahi_free (s); + + /* current activity */ + activity_id = _avahi_txt_get_keyval (txt, "olpc-current-activity"); + room_id = _avahi_txt_get_keyval (txt, "olpc-current-activity-room"); + + salut_contact_change_current_activity (contact, room_id, activity_id); + avahi_free (activity_id); + avahi_free (room_id); + + /* OLPC key */ + olpc_key_part = _avahi_txt_get_keyval_with_size (txt, + "olpc-key-part0", &size); + + if (olpc_key_part != NULL) + { + guint i = 0; + gchar *olpc_key_part_name = NULL; + GArray *olpc_key; + + /* FIXME: how big are OLPC keys anyway? */ + olpc_key = g_array_sized_new (FALSE, FALSE, sizeof (guint8), 512); + + do + { + g_array_append_vals (olpc_key, olpc_key_part, size); + avahi_free (olpc_key_part); + + i++; + olpc_key_part_name = g_strdup_printf ("olpc-key-part%u", i); + olpc_key_part = _avahi_txt_get_keyval_with_size (txt, + olpc_key_part_name, &size); + g_free (olpc_key_part_name); + } + while (olpc_key_part != NULL); + + salut_contact_change_olpc_key (contact, olpc_key); + g_array_unref (olpc_key); + } + + /* address */ + if (address != NULL) + { + gchar* saddr = g_malloc0 (AVAHI_ADDRESS_STR_MAX); + + if (avahi_address_snprint (saddr, AVAHI_ADDRESS_STR_MAX, address)) + { + switch (address->proto) + { + case AVAHI_PROTO_INET: + salut_contact_change_ipv4_addr (contact, saddr); + break; + case AVAHI_PROTO_INET6: + salut_contact_change_ipv6_addr (contact, saddr); + break; + default: + break; + } + } + g_free (saddr); + } +#endif + salut_contact_found (contact); salut_contact_thaw (contact); } diff --git a/src/avahi-discovery-client.c b/src/avahi-discovery-client.c index b557d331..72cf9203 100644 --- a/src/avahi-discovery-client.c +++ b/src/avahi-discovery-client.c @@ -41,6 +41,9 @@ #include "avahi-contact-manager.h" #include "avahi-roomlist-manager.h" #include "avahi-self.h" +#ifdef ENABLE_OLPC +#include "avahi-olpc-activity-manager.h" +#endif #include "presence.h" @@ -302,6 +305,25 @@ salut_avahi_discovery_client_create_contact_manager ( self)); } +#ifdef ENABLE_OLPC +/* + * salut_avahi_discovery_client_create_olpc_activity_manager + * + * Implements salut_discovery_client_create_olpc_activity_manager on + * SalutDiscoveryClient + */ +static SalutOlpcActivityManager * +salut_avahi_discovery_client_create_olpc_activity_manager ( + SalutDiscoveryClient *client, + SalutConnection *connection) +{ + SalutAvahiDiscoveryClient *self = SALUT_AVAHI_DISCOVERY_CLIENT (client); + + return SALUT_OLPC_ACTIVITY_MANAGER (salut_avahi_olpc_activity_manager_new ( + connection, self)); +} +#endif + /* * salut_avahi_discovery_client_create_self * @@ -315,12 +337,14 @@ salut_avahi_discovery_client_create_self (SalutDiscoveryClient *client, const gchar *last_name, const gchar *jid, const gchar *email, - const gchar *published_name) + const gchar *published_name, + const GArray *olpc_key, + const gchar *olpc_color) { SalutAvahiDiscoveryClient *self = SALUT_AVAHI_DISCOVERY_CLIENT (client); return SALUT_SELF (salut_avahi_self_new (connection, self, nickname, first_name, - last_name, jid, email, published_name)); + last_name, jid, email, published_name, olpc_key, olpc_color)); } static const gchar * @@ -354,6 +378,10 @@ discovery_client_init (gpointer g_iface, salut_avahi_discovery_client_create_roomlist_manager; klass->create_contact_manager = salut_avahi_discovery_client_create_contact_manager; +#ifdef ENABLE_OLPC + klass->create_olpc_activity_manager = + salut_avahi_discovery_client_create_olpc_activity_manager; +#endif klass->create_self = salut_avahi_discovery_client_create_self; klass->get_host_name_fqdn = salut_avahi_discovery_client_get_host_name_fqdn; } diff --git a/src/avahi-olpc-activity-manager.c b/src/avahi-olpc-activity-manager.c new file mode 100644 index 00000000..f7ae90fd --- /dev/null +++ b/src/avahi-olpc-activity-manager.c @@ -0,0 +1,376 @@ +/* + * avahi-avahi_olpc-activity-manager.c - Source for + * SalutAvahiOlpcActivityManager + * Copyright (C) 2008 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "avahi-olpc-activity-manager.h" + +#include +#include +#include +#include + +#include + +#include "avahi-olpc-activity.h" +#include "avahi-discovery-client.h" +#include "olpc-activity.h" + +#define DEBUG_FLAG DEBUG_OLPC_ACTIVITY +#include "debug.h" + +G_DEFINE_TYPE (SalutAvahiOlpcActivityManager, salut_avahi_olpc_activity_manager, + SALUT_TYPE_OLPC_ACTIVITY_MANAGER); + +/* properties */ +enum { + PROP_CLIENT = 1, + LAST_PROP +}; + +/* private structure */ +typedef struct _SalutAvahiOlpcActivityManagerPrivate SalutAvahiOlpcActivityManagerPrivate; + +struct _SalutAvahiOlpcActivityManagerPrivate +{ + SalutAvahiDiscoveryClient *discovery_client; + GaServiceBrowser *browser; + + gboolean dispose_has_run; +}; + +#define SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SALUT_TYPE_AVAHI_OLPC_ACTIVITY_MANAGER, SalutAvahiOlpcActivityManagerPrivate)) + +static void +salut_avahi_olpc_activity_manager_init (SalutAvahiOlpcActivityManager *self) +{ + SalutAvahiOlpcActivityManagerPrivate *priv = + SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + priv->browser = ga_service_browser_new (SALUT_DNSSD_OLPC_ACTIVITY); +} + +static void +salut_avahi_olpc_activity_manager_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SalutAvahiOlpcActivityManager *self = SALUT_AVAHI_OLPC_ACTIVITY_MANAGER (object); + SalutAvahiOlpcActivityManagerPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + switch (property_id) + { + case PROP_CLIENT: + g_value_set_object (value, priv->discovery_client); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +salut_avahi_olpc_activity_manager_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + SalutAvahiOlpcActivityManager *self = SALUT_AVAHI_OLPC_ACTIVITY_MANAGER (object); + SalutAvahiOlpcActivityManagerPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + switch (property_id) + { + case PROP_CLIENT: + priv->discovery_client = g_value_get_object (value); + g_object_ref (priv->discovery_client); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +split_activity_name (const gchar *name, + gchar **room_name, + gchar **contact_name) +{ + gchar **tmp; + + tmp = g_strsplit (name, ":", 2); + if (tmp[0] == NULL || tmp[1] == NULL) + { + DEBUG ("Ignoring invalid OLPC activity DNS-SD with no ':': %s", name); + return FALSE; + } + + if (room_name != NULL) + *room_name = g_strdup (tmp[0]); + if (contact_name != NULL) + *contact_name = g_strdup (tmp[1]); + + g_strfreev (tmp); + return TRUE; +} + +static void +browser_found (GaServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + GaLookupResultFlags flags, + SalutAvahiOlpcActivityManager *self) +{ + SalutOlpcActivityManager *mgr = SALUT_OLPC_ACTIVITY_MANAGER (self); + SalutOlpcActivity *activity; + gchar *room_name = NULL; + gchar *contact_name = NULL; + TpBaseConnection *base_conn = (TpBaseConnection *) mgr->connection; + TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( + base_conn, TP_HANDLE_TYPE_ROOM); + TpHandle room; + GError *error = NULL; + SalutContactManager *contact_manager; + SalutContact *contact; + + if (flags & AVAHI_LOOKUP_RESULT_OUR_OWN) + return; + + if (tp_base_connection_is_destroyed (base_conn)) + return; + + if (!split_activity_name (name, &room_name, &contact_name)) + return; + + room = tp_handle_ensure (room_repo, room_name, NULL, &error); + if (room == 0) + { + DEBUG ("invalid room name %s: %s", room_name, error->message); + g_free (room_name); + g_free (contact_name); + return; + } + + activity = salut_olpc_activity_manager_ensure_activity_by_room (mgr, + room); + + salut_avahi_olpc_activity_add_service (SALUT_AVAHI_OLPC_ACTIVITY (activity), + interface, protocol, name, type, domain); + + g_object_get (mgr->connection, + "contact-manager", &contact_manager, NULL); + g_assert (contact_manager != NULL); + + contact = salut_contact_manager_ensure_contact (contact_manager, + contact_name); + salut_olpc_activity_manager_contact_joined (mgr, contact, activity); + + g_object_unref (activity); + g_free (contact_name); + g_free (room_name); + g_object_unref (contact); + g_object_unref (contact_manager); +} + +static void +browser_removed (GaServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain, + GaLookupResultFlags flags, + SalutAvahiOlpcActivityManager *self) +{ + SalutOlpcActivityManager *mgr = SALUT_OLPC_ACTIVITY_MANAGER (self); + SalutOlpcActivity *activity; + gchar *room_name = NULL; + gchar *contact_name = NULL; + TpHandleRepoIface *room_repo = tp_base_connection_get_handles + ((TpBaseConnection *) mgr->connection, TP_HANDLE_TYPE_ROOM); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles + ((TpBaseConnection *) mgr->connection, TP_HANDLE_TYPE_CONTACT); + TpHandle room; + TpHandle contact_handle; + GError *error = NULL; + SalutContactManager *contact_manager; + SalutContact *contact; + + if (!split_activity_name (name, &room_name, &contact_name)) + return; + + room = tp_handle_ensure (room_repo, room_name, NULL, &error); + g_free (room_name); + if (room == 0) + { + DEBUG ("invalid room name %s: %s", room_name, error->message); + g_free (contact_name); + g_error_free (error); + return; + } + + contact_handle = tp_handle_ensure (contact_repo, contact_name, NULL, &error); + if (contact_handle == 0) + { + DEBUG ("Invalid contact name %s: %s", contact_name, error->message); + g_error_free (error); + g_free (contact_name); + return; + } + g_free (contact_name); + + activity = salut_olpc_activity_manager_get_activity_by_room (mgr, room); + if (activity == NULL) + return; + + salut_avahi_olpc_activity_remove_service (SALUT_AVAHI_OLPC_ACTIVITY (activity), + interface, protocol, name, type, domain); + + g_object_get (mgr->connection, + "contact-manager", &contact_manager, NULL); + g_assert (contact_manager != NULL); + + contact = salut_contact_manager_get_contact (contact_manager, + contact_handle); + g_object_unref (contact_manager); + if (contact == NULL) + return; + + salut_olpc_activity_manager_contact_left (mgr, contact, activity); + g_object_unref (contact); +} + +static void +browser_failed (GaServiceBrowser *browser, + GError *error, + SalutAvahiOlpcActivityManager *self) +{ + g_warning ("browser failed -> %s", error->message); +} + +static gboolean +salut_avahi_olpc_activity_manager_start (SalutOlpcActivityManager *mgr, + GError **error) +{ + SalutAvahiOlpcActivityManager *self = SALUT_AVAHI_OLPC_ACTIVITY_MANAGER (mgr); + SalutAvahiOlpcActivityManagerPrivate *priv = + SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + g_signal_connect (priv->browser, "new-service", + G_CALLBACK (browser_found), self); + g_signal_connect (priv->browser, "removed-service", + G_CALLBACK (browser_removed), self); + g_signal_connect (priv->browser, "failure", + G_CALLBACK (browser_failed), self); + + if (!ga_service_browser_attach (priv->browser, + priv->discovery_client->avahi_client, error)) + { + DEBUG ("browser attach failed"); + return FALSE; + } + + return TRUE; +} + +static SalutOlpcActivity * +salut_avahi_olpc_activity_manager_create_activity ( + SalutOlpcActivityManager *mgr) +{ + SalutAvahiOlpcActivityManager *self = SALUT_AVAHI_OLPC_ACTIVITY_MANAGER (mgr); + SalutAvahiOlpcActivityManagerPrivate *priv = + SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + return SALUT_OLPC_ACTIVITY (salut_avahi_olpc_activity_new ( + mgr->connection, priv->discovery_client)); +} + +static void salut_avahi_olpc_activity_manager_dispose (GObject *object); + +static void +salut_avahi_olpc_activity_manager_class_init (SalutAvahiOlpcActivityManagerClass *salut_avahi_olpc_activity_manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (salut_avahi_olpc_activity_manager_class); + SalutOlpcActivityManagerClass *activity_manager_class = SALUT_OLPC_ACTIVITY_MANAGER_CLASS ( + salut_avahi_olpc_activity_manager_class); + + GParamSpec *param_spec; + + g_type_class_add_private (salut_avahi_olpc_activity_manager_class, + sizeof (SalutAvahiOlpcActivityManagerPrivate)); + + object_class->get_property = salut_avahi_olpc_activity_manager_get_property; + object_class->set_property = salut_avahi_olpc_activity_manager_set_property; + + object_class->dispose = salut_avahi_olpc_activity_manager_dispose; + + activity_manager_class->start = salut_avahi_olpc_activity_manager_start; + activity_manager_class->create_activity = + salut_avahi_olpc_activity_manager_create_activity; + + param_spec = g_param_spec_object ( + "discovery-client", + "SalutAvahiDiscoveryClient object", + "The Salut Avahi Discovery client associated with this manager", + SALUT_TYPE_AVAHI_DISCOVERY_CLIENT, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CLIENT, + param_spec); +} + +static void +salut_avahi_olpc_activity_manager_dispose (GObject *object) +{ + SalutAvahiOlpcActivityManager *self = SALUT_AVAHI_OLPC_ACTIVITY_MANAGER (object); + SalutAvahiOlpcActivityManagerPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + if (priv->discovery_client != NULL) + { + g_object_unref (priv->discovery_client); + priv->discovery_client = NULL; + } + + if (priv->browser != NULL) + { + g_object_unref (priv->browser); + priv->browser = NULL; + } + + if (G_OBJECT_CLASS (salut_avahi_olpc_activity_manager_parent_class)->dispose) + G_OBJECT_CLASS (salut_avahi_olpc_activity_manager_parent_class)->dispose (object); +} + +SalutAvahiOlpcActivityManager * +salut_avahi_olpc_activity_manager_new (SalutConnection *connection, + SalutAvahiDiscoveryClient *discovery_client) +{ + return g_object_new (SALUT_TYPE_AVAHI_OLPC_ACTIVITY_MANAGER, + "connection", connection, + "discovery-client", discovery_client, + NULL); +} diff --git a/src/avahi-olpc-activity-manager.h b/src/avahi-olpc-activity-manager.h new file mode 100644 index 00000000..88c94db3 --- /dev/null +++ b/src/avahi-olpc-activity-manager.h @@ -0,0 +1,68 @@ +/* + * avahi-olpc-activity-managere.h - Header for + * SalutAvahiOlpcActivityManager + * Copyright (C) 2008 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_H__ +#define __SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_H__ + +#include + +#include + +#include "olpc-activity-manager.h" +#include "connection.h" +#include "avahi-discovery-client.h" + +G_BEGIN_DECLS + +typedef struct _SalutAvahiOlpcActivityManager SalutAvahiOlpcActivityManager; +typedef struct _SalutAvahiOlpcActivityManagerClass SalutAvahiOlpcActivityManagerClass; + +struct _SalutAvahiOlpcActivityManagerClass { + SalutOlpcActivityManagerClass parent_class; +}; + +struct _SalutAvahiOlpcActivityManager { + SalutOlpcActivityManager parent; +}; + +GType salut_avahi_olpc_activity_manager_get_type (void); + +/* TYPE MACROS */ +#define SALUT_TYPE_AVAHI_OLPC_ACTIVITY_MANAGER \ + (salut_avahi_olpc_activity_manager_get_type ()) +#define SALUT_AVAHI_OLPC_ACTIVITY_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), SALUT_TYPE_AVAHI_OLPC_ACTIVITY_MANAGER, \ + SalutAvahiOlpcActivityManager)) +#define SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SALUT_TYPE_AVAHI_OLPC_ACTIVITY_MANAGER, \ + SalutAvahiOlpcActivityManagerClass)) +#define SALUT_IS_AVAHI_OLPC_ACTIVITY_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), SALUT_TYPE_AVAHI_OLPC_ACTIVITY_MANAGER)) +#define SALUT_IS_AVAHI_OLPC_ACTIVITY_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SALUT_TYPE_AVAHI_OLPC_ACTIVITY_MANAGER)) +#define SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), SALUT_TYPE_AVAHI_OLPC_ACTIVITY_MANAGER, SalutAvahiOlpcActivityManagerClass)) + +SalutAvahiOlpcActivityManager * salut_avahi_olpc_activity_manager_new ( + SalutConnection *connection, SalutAvahiDiscoveryClient *discovery_client); + +G_END_DECLS + +#endif /* #ifndef __SALUT_AVAHI_OLPC_ACTIVITY_MANAGER_H__*/ diff --git a/src/avahi-olpc-activity.c b/src/avahi-olpc-activity.c new file mode 100644 index 00000000..8dba37af --- /dev/null +++ b/src/avahi-olpc-activity.c @@ -0,0 +1,546 @@ +/* + * avahi-olpc-activity.c - Source for SalutAvahiOlpcActivity + * Copyright (C) 2008 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "avahi-olpc-activity.h" + +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_FLAG DEBUG_OLPC_ACTIVITY +#include "debug.h" + +G_DEFINE_TYPE (SalutAvahiOlpcActivity, salut_avahi_olpc_activity, + SALUT_TYPE_OLPC_ACTIVITY); + +/* properties */ +enum { + PROP_CLIENT = 1, + LAST_PROP +}; + +/* private structure */ +typedef struct _SalutAvahiOlpcActivityPrivate SalutAvahiOlpcActivityPrivate; + +struct _SalutAvahiOlpcActivityPrivate +{ + SalutAvahiDiscoveryClient *discovery_client; + GSList *resolvers; + /* group and service can be NULL if we are not announcing this activity */ + GaEntryGroup *group; + GaEntryGroupService *service; + + gboolean dispose_has_run; +}; + +#define SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SALUT_TYPE_AVAHI_OLPC_ACTIVITY, SalutAvahiOlpcActivityPrivate)) + +static void +salut_avahi_olpc_activity_init (SalutAvahiOlpcActivity *obj) +{ + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + obj); + + priv->resolvers = NULL; +} + +static void +salut_avahi_olpc_activity_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SalutAvahiOlpcActivity *self = SALUT_AVAHI_OLPC_ACTIVITY (object); + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + + switch (property_id) + { + case PROP_CLIENT: + g_value_set_object (value, priv->discovery_client); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +salut_avahi_olpc_activity_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + SalutAvahiOlpcActivity *self = SALUT_AVAHI_OLPC_ACTIVITY (object); + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + + switch (property_id) + { + case PROP_CLIENT: + priv->discovery_client = g_value_get_object (value); + g_object_ref (priv->discovery_client); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +activity_is_announced (SalutAvahiOlpcActivity *self) +{ + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + + return (priv->group != NULL && priv->service != NULL); +} + +static gboolean +update_activity_service (SalutAvahiOlpcActivity *self, + GError **error) +{ + SalutOlpcActivity *activity = SALUT_OLPC_ACTIVITY (self); + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + GError *err = NULL; + + if (!activity_is_announced (self)) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Trying to update an activity that's not announced"); + return FALSE; + } + + ga_entry_group_service_freeze (priv->service); + + if (activity->name != NULL) + ga_entry_group_service_set (priv->service, "name", + activity->name, NULL); + + if (activity->color != NULL) + ga_entry_group_service_set (priv->service, "color", + activity->color, NULL); + + if (activity->type != NULL) + ga_entry_group_service_set (priv->service, "type", + activity->type, NULL); + + if (activity->tags != NULL) + ga_entry_group_service_set (priv->service, "tags", + activity->tags, NULL); + + return ga_entry_group_service_thaw (priv->service, &err); +} + +static gboolean +salut_avahi_olpc_activity_announce (SalutOlpcActivity *activity, + GError **error) +{ + SalutAvahiOlpcActivity *self = SALUT_AVAHI_OLPC_ACTIVITY (activity); + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + const gchar *room_name; + gchar *name; + AvahiStringList *txt_record; + TpHandleRepoIface *room_repo; + gchar *published_name; + + g_return_val_if_fail (!activity->is_private, FALSE); + g_return_val_if_fail (!activity_is_announced (self), FALSE); + + room_repo = tp_base_connection_get_handles ( + (TpBaseConnection *) activity->connection, TP_HANDLE_TYPE_ROOM); + + room_name = tp_handle_inspect (room_repo, activity->room); + /* caller should already have validated this */ + g_return_val_if_fail (room_name != NULL, FALSE); + + priv->group = ga_entry_group_new (); + if (!ga_entry_group_attach (priv->group, priv->discovery_client->avahi_client, + error)) + return FALSE; + + g_object_get (activity->connection, "published-name", &published_name, NULL); + + name = g_strdup_printf ("%s:%s@%s", room_name, published_name, + avahi_client_get_host_name ( + priv->discovery_client->avahi_client->avahi_client)); + + g_free (published_name); + + txt_record = avahi_string_list_new ("txtvers=0", NULL); + txt_record = avahi_string_list_add_printf (txt_record, "room=%s", room_name); + if (activity->id != NULL) + txt_record = avahi_string_list_add_printf (txt_record, "activity-id=%s", + activity->id); + + priv->service = ga_entry_group_add_service_strlist (priv->group, name, + SALUT_DNSSD_OLPC_ACTIVITY, 0, error, txt_record); + + if (priv->service == NULL) + return FALSE; + + DEBUG ("announce activity %s", name); + g_free (name); + avahi_string_list_free (txt_record); + + if (!ga_entry_group_commit (priv->group, error)) + return FALSE; + + /* announce activities properties */ + if (!update_activity_service (self, error)) + return FALSE; + + return TRUE; +} + +static void +salut_avahi_olpc_activity_stop_announce (SalutOlpcActivity *activity) +{ + SalutAvahiOlpcActivity *self = SALUT_AVAHI_OLPC_ACTIVITY (activity); + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + + /* Announcing the activity could have failed, so check if we're actually + * announcing it */ + if (!activity_is_announced (self)) + return; + + g_object_unref (priv->group); + priv->group = NULL; + priv->service = NULL; + + DEBUG ("stop announce activity %s", activity->id); +} + +static gboolean +salut_avahi_update (SalutOlpcActivity *activity, + GError **error) +{ + SalutAvahiOlpcActivity *self = SALUT_AVAHI_OLPC_ACTIVITY (activity); + + return update_activity_service (self, error); +} + +static void salut_avahi_olpc_activity_dispose (GObject *object); +static void salut_avahi_olpc_activity_finalize (GObject *object); + +static void +salut_avahi_olpc_activity_class_init ( + SalutAvahiOlpcActivityClass *salut_avahi_olpc_activity_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (salut_avahi_olpc_activity_class); + SalutOlpcActivityClass *activity_class = SALUT_OLPC_ACTIVITY_CLASS ( + salut_avahi_olpc_activity_class); + GParamSpec *param_spec; + + g_type_class_add_private (salut_avahi_olpc_activity_class, + sizeof (SalutAvahiOlpcActivityPrivate)); + + object_class->get_property = salut_avahi_olpc_activity_get_property; + object_class->set_property = salut_avahi_olpc_activity_set_property; + + object_class->dispose = salut_avahi_olpc_activity_dispose; + object_class->finalize = salut_avahi_olpc_activity_finalize; + + activity_class->announce = salut_avahi_olpc_activity_announce; + activity_class->stop_announce = salut_avahi_olpc_activity_stop_announce; + activity_class->update = salut_avahi_update; + + param_spec = g_param_spec_object ( + "discovery-client", + "SalutAvahiDiscoveryClient object", + "The Salut Avahi Discovery client associated with this manager", + SALUT_TYPE_AVAHI_DISCOVERY_CLIENT, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CLIENT, + param_spec); +} + +void +salut_avahi_olpc_activity_dispose (GObject *object) +{ + SalutAvahiOlpcActivity *self = SALUT_AVAHI_OLPC_ACTIVITY (object); + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + g_slist_foreach (priv->resolvers, (GFunc) g_object_unref, NULL); + g_slist_free (priv->resolvers); + priv->resolvers = NULL; + + if (priv->group != NULL) + { + g_object_unref (priv->group); + priv->group = NULL; + } + + if (priv->discovery_client != NULL) + { + g_object_unref (priv->discovery_client); + priv->discovery_client = NULL; + } + + if (G_OBJECT_CLASS (salut_avahi_olpc_activity_parent_class)->dispose) + G_OBJECT_CLASS (salut_avahi_olpc_activity_parent_class)->dispose (object); +} + +void +salut_avahi_olpc_activity_finalize (GObject *object) +{ + G_OBJECT_CLASS (salut_avahi_olpc_activity_parent_class)->finalize (object); +} + +SalutAvahiOlpcActivity * +salut_avahi_olpc_activity_new (SalutConnection *connection, + SalutAvahiDiscoveryClient *discovery_client) +{ + return g_object_new (SALUT_TYPE_AVAHI_OLPC_ACTIVITY, + "connection", connection, + "discovery-client", discovery_client, + NULL); +} + +struct resolverinfo +{ + AvahiIfIndex interface; + AvahiProtocol protocol; + const gchar *name; + const gchar *type; + const gchar *domain; +}; + +static gint +compare_resolver (GaServiceResolver *resolver, + struct resolverinfo *info) +{ + AvahiIfIndex interface; + AvahiProtocol protocol; + gchar *name; + gchar *type; + gchar *domain; + gint result; + + g_object_get (resolver, + "interface", &interface, + "protocol", &protocol, + "name", &name, + "type", &type, + "domain", &domain, + NULL); + + if (interface == info->interface + && protocol == info->protocol + && !tp_strdiff (name, info->name) + && !tp_strdiff (type, info->type) + && !tp_strdiff (domain, info->domain)) + { + result = 0; + } + else + { + result = 1; + } + + g_free (name); + g_free (type); + g_free (domain); + return result; +} + +static GaServiceResolver * +find_resolver (SalutAvahiOlpcActivity *self, + AvahiIfIndex interface, + AvahiProtocol protocol, + const gchar *name, + const gchar *type, + const gchar *domain) +{ + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + struct resolverinfo info; + GSList *ret; + + info.interface = interface; + info.protocol = protocol; + info.name = name; + info.type = type; + info.domain = domain; + ret = g_slist_find_custom (priv->resolvers, &info, + (GCompareFunc) compare_resolver); + + return ret ? GA_SERVICE_RESOLVER (ret->data) : NULL; +} + +static void +activity_resolved_cb (GaServiceResolver *resolver, + AvahiIfIndex interface, + AvahiProtocol protocol, + gchar *name, + gchar *type, + gchar *domain, + gchar *host_name, + AvahiAddress *a, + gint port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + SalutAvahiOlpcActivity *self) +{ + SalutOlpcActivity *act = SALUT_OLPC_ACTIVITY (self); + AvahiStringList *t; + char *activity_id = NULL; + char *color = NULL; + char *activity_name = NULL; + char *activity_type = NULL; + char *tags = NULL; + char *room_name = NULL; + TpHandle room = 0; + TpHandleRepoIface *room_repo = tp_base_connection_get_handles + ((TpBaseConnection *) act->connection, TP_HANDLE_TYPE_ROOM); + + DEBUG ("called: \"%s\".%s. on %s port %u", name, domain, host_name, port); + + if ((t = avahi_string_list_find (txt, "txtvers")) != NULL) + { + char *txtvers; + + avahi_string_list_get_pair (t, NULL, &txtvers, NULL); + if (tp_strdiff (txtvers, "0")) + { + DEBUG ("Ignoring record with txtvers not 0: %s", + txtvers ? txtvers : "(no value)"); + avahi_free (txtvers); + return; + } + avahi_free (txtvers); + } + + if ((t = avahi_string_list_find (txt, "room")) != NULL) + { + avahi_string_list_get_pair (t, NULL, &room_name, NULL); + + room = tp_handle_ensure (room_repo, room_name, NULL, NULL); + avahi_free (room_name); + if (room == 0) + { + DEBUG ("Ignoring record with invalid room name: %s", room_name); + return; + } + } + + if ((t = avahi_string_list_find (txt, "activity-id")) != NULL) + { + avahi_string_list_get_pair (t, NULL, &activity_id, NULL); + } + + if ((t = avahi_string_list_find (txt, "color")) != NULL) + { + avahi_string_list_get_pair (t, NULL, &color, NULL); + } + + if ((t = avahi_string_list_find (txt, "name")) != NULL) + { + avahi_string_list_get_pair (t, NULL, &activity_name, NULL); + } + + if ((t = avahi_string_list_find (txt, "type")) != NULL) + { + avahi_string_list_get_pair (t, NULL, &activity_type, NULL); + } + + if ((t = avahi_string_list_find (txt, "tags")) != NULL) + { + avahi_string_list_get_pair (t, NULL, &tags, NULL); + } + + salut_olpc_activity_update (SALUT_OLPC_ACTIVITY (self), room, + activity_id, activity_name, activity_type, color, tags, FALSE); + + avahi_free (activity_id); + avahi_free (activity_type); + avahi_free (activity_name); + avahi_free (color); + avahi_free (tags); +} + +void +salut_avahi_olpc_activity_add_service (SalutAvahiOlpcActivity *self, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain) +{ + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + GaServiceResolver *resolver; + GError *error = NULL; + + resolver = find_resolver (self, interface, protocol, name, type, domain); + if (resolver != NULL) + return; + + resolver = ga_service_resolver_new (interface, protocol, name, type, domain, + protocol, 0); + + g_signal_connect (resolver, "found", G_CALLBACK (activity_resolved_cb), + self); + + if (!ga_service_resolver_attach (resolver, + priv->discovery_client->avahi_client, &error)) + { + g_warning ("Failed to attach resolver: %s", error->message); + g_error_free (error); + } + + /* DEBUG_RESOLVER (contact, resolver, "added"); */ + priv->resolvers = g_slist_prepend (priv->resolvers, resolver); +} + +void +salut_avahi_olpc_activity_remove_service (SalutAvahiOlpcActivity *self, + AvahiIfIndex interface, + AvahiProtocol protocol, + const char *name, + const char *type, + const char *domain) +{ + SalutAvahiOlpcActivityPrivate *priv = SALUT_AVAHI_OLPC_ACTIVITY_GET_PRIVATE ( + self); + GaServiceResolver *resolver; + + resolver = find_resolver (self, interface, protocol, name, type, domain); + + if (resolver == NULL) + return; + + priv->resolvers = g_slist_remove (priv->resolvers, resolver); + g_object_unref (resolver); +} diff --git a/src/avahi-olpc-activity.h b/src/avahi-olpc-activity.h new file mode 100644 index 00000000..6343261a --- /dev/null +++ b/src/avahi-olpc-activity.h @@ -0,0 +1,70 @@ +/* + * avahi-olpc-activity.h - Header for SalutAvahiOlpcActivity + * Copyright (C) 2008 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SALUT_AVAHI_OLPC_ACTIVITY_H__ +#define __SALUT_AVAHI_OLPC_ACTIVITY_H__ + +#include + +#include "olpc-activity.h" +#include "avahi-discovery-client.h" + +G_BEGIN_DECLS + +typedef struct _SalutAvahiOlpcActivity SalutAvahiOlpcActivity; +typedef struct _SalutAvahiOlpcActivityClass SalutAvahiOlpcActivityClass; + +struct _SalutAvahiOlpcActivityClass { + SalutOlpcActivityClass parent_class; +}; + +struct _SalutAvahiOlpcActivity { + SalutOlpcActivity parent; +}; + +GType salut_avahi_olpc_activity_get_type (void); + +/* TYPE MACROS */ +#define SALUT_TYPE_AVAHI_OLPC_ACTIVITY \ + (salut_avahi_olpc_activity_get_type ()) +#define SALUT_AVAHI_OLPC_ACTIVITY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), SALUT_TYPE_AVAHI_OLPC_ACTIVITY, SalutAvahiOlpcActivity)) +#define SALUT_AVAHI_OLPC_ACTIVITY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SALUT_TYPE_AVAHI_OLPC_ACTIVITY, SalutAvahiOlpcActivityClass)) +#define SALUT_IS_AVAHI_OLPC_ACTIVITY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), SALUT_TYPE_AVAHI_OLPC_ACTIVITY)) +#define SALUT_IS_AVAHI_OLPC_ACTIVITY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SALUT_TYPE_AVAHI_OLPC_ACTIVITY)) +#define SALUT_AVAHI_OLPC_ACTIVITY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), SALUT_TYPE_AVAHI_OLPC_ACTIVITY, SalutAvahiOlpcActivityClass)) + +SalutAvahiOlpcActivity * salut_avahi_olpc_activity_new ( + SalutConnection *connection, SalutAvahiDiscoveryClient *discovery_client); + +void salut_avahi_olpc_activity_add_service (SalutAvahiOlpcActivity *activity, + AvahiIfIndex interface, AvahiProtocol protocol, + const char *name, const char *type, const char *domain); + +void salut_avahi_olpc_activity_remove_service (SalutAvahiOlpcActivity *activity, + AvahiIfIndex interface, AvahiProtocol protocol, + const char *name, const char *type, const char *domain); + +G_END_DECLS + +#endif /* #ifndef __SALUT_AVAHI_OLPC_ACTIVITY_H__*/ diff --git a/src/avahi-self.c b/src/avahi-self.c index 8c6c7495..0b752868 100644 --- a/src/avahi-self.c +++ b/src/avahi-self.c @@ -32,6 +32,10 @@ #include "sha1/sha1-util.h" +#ifdef ENABLE_OLPC +#define KEY_SEGMENT_SIZE 200 +#endif + G_DEFINE_TYPE (SalutAvahiSelf, salut_avahi_self, SALUT_TYPE_SELF); /* properties */ @@ -140,6 +144,30 @@ create_txt_record (SalutAvahiSelf *self, if (_self->jid != NULL) ret = avahi_string_list_add_printf (ret, "jid=%s", _self->jid); +#ifdef ENABLE_OLPC + if (_self->olpc_color) + ret = avahi_string_list_add_printf (ret, "olpc-color=%s", + _self->olpc_color); + + if (_self->olpc_key != NULL) + { + uint8_t *key = (uint8_t *) _self->olpc_key->data; + size_t key_len = _self->olpc_key->len; + guint i = 0; + + while (key_len > 0) + { + size_t step = MIN (key_len, KEY_SEGMENT_SIZE); + gchar *name = g_strdup_printf ("olpc-key-part%u", i); + + ret = avahi_string_list_add_pair_arbitrary (ret, name, key, step); + key += step; + key_len -= step; + i++; + } + } +#endif + ret = avahi_string_list_add_printf (ret, "status=%s", salut_presence_status_txt_names[_self->status]); @@ -334,6 +362,99 @@ salut_avahi_self_set_avatar (SalutSelf *_self, _self->avatar_token, error); } +#ifdef ENABLE_OLPC +static gboolean +salut_avahi_self_update_current_activity (SalutSelf *_self, + const gchar *room_name, + GError **error) +{ + SalutAvahiSelf *self = SALUT_AVAHI_SELF (_self); + SalutAvahiSelfPrivate *priv = self->priv; + + ga_entry_group_service_freeze (priv->presence); + + ga_entry_group_service_set (priv->presence, + "olpc-current-activity", _self->olpc_cur_act, NULL); + + ga_entry_group_service_set (priv->presence, + "olpc-current-activity-room", room_name, NULL); + + return ga_entry_group_service_thaw (priv->presence, error); +} + +static gboolean +salut_avahi_self_set_olpc_properties (SalutSelf *_self, + const GArray *key, + const gchar *color, + const gchar *jid, + GError **error) +{ + SalutAvahiSelf *self = SALUT_AVAHI_SELF (_self); + SalutAvahiSelfPrivate *priv = self->priv; + + ga_entry_group_service_freeze (priv->presence); + + if (key != NULL) + { + size_t key_len = key->len; + const guint8 *key_data = (const guint8 *) key->data; + guint i; + guint to_remove; + + if (_self->olpc_key == NULL) + { + to_remove = 0; + } + else + { + to_remove = (_self->olpc_key->len + KEY_SEGMENT_SIZE - 1) / + KEY_SEGMENT_SIZE; + } + + i = 0; + while (key_len > 0) + { + size_t step = MIN (key_len, KEY_SEGMENT_SIZE); + gchar *name = g_strdup_printf ("olpc-key-part%u", i); + + ga_entry_group_service_set_arbitrary (priv->presence, name, + key_data, step, NULL); + g_free (name); + + key_data += step; + key_len -= step; + i++; + } + + /* if the new key is shorter than the old, clean up any stray segments */ + while (i < to_remove) + { + gchar *name = g_strdup_printf ("olpc-key-part%u", i); + + ga_entry_group_service_remove_key (priv->presence, name, + NULL); + g_free (name); + + i++; + } + } + + if (color != NULL) + { + ga_entry_group_service_set (priv->presence, "olpc-color", + color, NULL); + } + + if (jid != NULL) + { + ga_entry_group_service_set (priv->presence, "jid", + jid, NULL); + } + + return ga_entry_group_service_thaw (priv->presence, error); +} +#endif + static void salut_avahi_self_dispose (GObject *object); static void @@ -358,6 +479,11 @@ salut_avahi_self_class_init ( self_class->set_alias = salut_avahi_self_set_alias; self_class->remove_avatar = salut_avahi_self_remove_avatar; self_class->set_avatar = salut_avahi_self_set_avatar; +#ifdef ENABLE_OLPC + self_class->update_current_activity = + salut_avahi_self_update_current_activity; + self_class->set_olpc_properties = salut_avahi_self_set_olpc_properties; +#endif param_spec = g_param_spec_object ( "discovery-client", @@ -411,7 +537,9 @@ salut_avahi_self_new (SalutConnection *connection, const gchar *last_name, const gchar *jid, const gchar *email, - const gchar *published_name) + const gchar *published_name, + const GArray *olpc_key, + const gchar *olpc_color) { return g_object_new (SALUT_TYPE_AVAHI_SELF, "connection", connection, @@ -422,5 +550,9 @@ salut_avahi_self_new (SalutConnection *connection, "jid", jid, "email", email, "published-name", published_name, +#ifdef ENABLE_OLPC + "olpc-key", olpc_key, + "olpc-color", olpc_color, +#endif NULL); } diff --git a/src/avahi-self.h b/src/avahi-self.h index b6785e86..4ebe942c 100644 --- a/src/avahi-self.h +++ b/src/avahi-self.h @@ -63,6 +63,7 @@ GType salut_avahi_self_get_type (void); SalutAvahiSelf * salut_avahi_self_new (SalutConnection *connection, SalutAvahiDiscoveryClient *discovery_client, const gchar *nickname, const gchar *first_name, const gchar *last_name, const gchar *jid, - const gchar *email, const gchar *published_name); + const gchar *email, const gchar *published_name, const GArray *olpc_key, + const gchar *olpc_color); #endif /* #ifndef __SALUT_AVAHI_SELF_H__*/ diff --git a/src/bonjour-discovery-client.c b/src/bonjour-discovery-client.c index b6dfe1d6..66a69669 100644 --- a/src/bonjour-discovery-client.c +++ b/src/bonjour-discovery-client.c @@ -254,12 +254,14 @@ salut_bonjour_discovery_client_create_self (SalutDiscoveryClient *client, const gchar *last_name, const gchar *jid, const gchar *email, - const gchar *published_name) + const gchar *published_name, + const GArray *olpc_key, + const gchar *olpc_color) { SalutBonjourDiscoveryClient *self = SALUT_BONJOUR_DISCOVERY_CLIENT (client); return SALUT_SELF (salut_bonjour_self_new (connection, self, nickname, - first_name, last_name, jid, email, published_name); + first_name, last_name, jid, email, published_name, olpc_key, olpc_color)); } static const gchar * diff --git a/src/bonjour-self.c b/src/bonjour-self.c index 08d22a71..330bcae4 100644 --- a/src/bonjour-self.c +++ b/src/bonjour-self.c @@ -30,6 +30,10 @@ #include "sha1/sha1-util.h" +#ifdef ENABLE_OLPC +#define KEY_SEGMENT_SIZE 200 +#endif + #define RETURN_FALSE_IF_FAIL(error_type) \ if (error_type != kDNSServiceErr_NoError) return FALSE; @@ -563,7 +567,9 @@ salut_bonjour_self_new (SalutConnection *connection, const gchar *last_name, const gchar *jid, const gchar *email, - const gchar *published_name) + const gchar *published_name, + const GArray *olpc_key, + const gchar *olpc_color) { return g_object_new (SALUT_TYPE_BONJOUR_SELF, "connection", connection, @@ -574,5 +580,9 @@ salut_bonjour_self_new (SalutConnection *connection, "jid", jid, "email", email, "published-name", published_name, +#ifdef ENABLE_OLPC + "olpc-key", olpc_key, + "olpc-color", olpc_color, +#endif NULL); } diff --git a/src/bonjour-self.h b/src/bonjour-self.h index 56b03b60..31010366 100644 --- a/src/bonjour-self.h +++ b/src/bonjour-self.h @@ -63,6 +63,7 @@ GType salut_bonjour_self_get_type (void); SalutBonjourSelf * salut_bonjour_self_new (SalutConnection *connection, SalutBonjourDiscoveryClient *discovery_client, const gchar *nickname, const gchar *first_name, const gchar *last_name, const gchar *jid, - const gchar *email, const gchar *published_name); + const gchar *email, const gchar *published_name, const GArray *olpc_key, + const gchar *olpc_color); #endif /* #ifndef __SALUT_BONJOUR_SELF_H__*/ diff --git a/src/capability-set.c b/src/capability-set.c index ffdb0f36..f8632691 100644 --- a/src/capability-set.c +++ b/src/capability-set.c @@ -38,6 +38,7 @@ struct _Feature enum { FEATURE_FIXED, FEATURE_OPTIONAL, + FEATURE_OLPC } feature_type; gchar *ns; }; @@ -69,6 +70,11 @@ static const Feature self_advertised_features[] = { FEATURE_OPTIONAL, NS_JINGLE_RTP_AUDIO }, { FEATURE_OPTIONAL, NS_JINGLE_RTP_VIDEO }, + { FEATURE_OLPC, NS_OLPC_BUDDY_PROPS "+notify" }, + { FEATURE_OLPC, NS_OLPC_ACTIVITIES "+notify" }, + { FEATURE_OLPC, NS_OLPC_CURRENT_ACTIVITY "+notify" }, + { FEATURE_OLPC, NS_OLPC_ACTIVITY_PROPS "+notify" }, + { FEATURE_OPTIONAL, NS_GEOLOC "+notify" }, { 0, NULL } @@ -91,6 +97,7 @@ static GabbleCapabilitySet *any_jingle_av_caps = NULL; static GabbleCapabilitySet *any_transport_caps = NULL; static GabbleCapabilitySet *fixed_caps = NULL; static GabbleCapabilitySet *geoloc_caps = NULL; +static GabbleCapabilitySet *olpc_caps = NULL; const GabbleCapabilitySet * gabble_capabilities_get_legacy (void) @@ -164,6 +171,12 @@ gabble_capabilities_get_geoloc_notify (void) return geoloc_caps; } +const GabbleCapabilitySet * +gabble_capabilities_get_olpc_notify (void) +{ + return olpc_caps; +} + static gboolean omits_content_creators (WockyNode *identity) { @@ -279,6 +292,14 @@ gabble_capabilities_init (gpointer conn) geoloc_caps = gabble_capability_set_new (); gabble_capability_set_add (geoloc_caps, NS_GEOLOC "+notify"); + + olpc_caps = gabble_capability_set_new (); + + for (feat = self_advertised_features; feat->ns != NULL; feat++) + { + if (feat->feature_type == FEATURE_OLPC) + gabble_capability_set_add (olpc_caps, feat->ns); + } } g_assert (feature_handles != NULL); @@ -305,6 +326,7 @@ gabble_capabilities_finalize (gpointer conn) gabble_capability_set_free (any_transport_caps); gabble_capability_set_free (fixed_caps); gabble_capability_set_free (geoloc_caps); + gabble_capability_set_free (olpc_caps); legacy_caps = NULL; share_v1_caps = NULL; @@ -318,6 +340,7 @@ gabble_capabilities_finalize (gpointer conn) any_transport_caps = NULL; fixed_caps = NULL; geoloc_caps = NULL; + olpc_caps = NULL; tp_clear_object (&feature_handles); } diff --git a/src/connection-contact-info.c b/src/connection-contact-info.c index c49963fe..cf1f0cb6 100644 --- a/src/connection-contact-info.c +++ b/src/connection-contact-info.c @@ -252,6 +252,54 @@ salut_conn_contact_info_changed ( g_boxed_free (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, contact_info); } +static void +salut_conn_contact_info_get_contact_info ( + TpSvcConnectionInterfaceContactInfo *iface, + const GArray *contacts, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + TpBaseConnection *base = (TpBaseConnection *) self; + TpHandleRepoIface *contacts_repo = + tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); + SalutContactManager *contact_manager; + guint i; + GHashTable *ret; + GError *error = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (TP_BASE_CONNECTION (iface), + context); + + if (!tp_handles_are_valid (contacts_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + g_object_get (self, "contact-manager", &contact_manager, NULL); + ret = dbus_g_type_specialized_construct (TP_HASH_TYPE_CONTACT_INFO_MAP); + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + SalutContact *contact = salut_contact_manager_get_contact ( + contact_manager, handle); + + if (contact != NULL) + { + g_hash_table_insert (ret, GUINT_TO_POINTER (handle), + build_contact_info_for_contact (contact)); + g_object_unref (contact); + } + } + + tp_svc_connection_interface_contact_info_return_from_get_contact_info ( + context, ret); + g_boxed_free (TP_HASH_TYPE_CONTACT_INFO_MAP, ret); + g_object_unref (contact_manager); +} + static void salut_conn_contact_info_request_contact_info ( TpSvcConnectionInterfaceContactInfo *iface, @@ -319,6 +367,7 @@ salut_conn_contact_info_iface_init ( #define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x \ (klass, salut_conn_contact_info_##x) + IMPLEMENT (get_contact_info); IMPLEMENT (request_contact_info); IMPLEMENT (refresh_contact_info); #undef IMPLEMENT diff --git a/src/connection.c b/src/connection.c index 2ab791a0..a0e19e4d 100644 --- a/src/connection.c +++ b/src/connection.c @@ -61,11 +61,31 @@ #include "plugin-loader.h" +#ifdef ENABLE_OLPC +#include "olpc-activity-manager.h" +#endif + #include #define DEBUG_FLAG DEBUG_CONNECTION #include "debug.h" +#ifdef ENABLE_OLPC + +#define ACTIVITY_PAIR_TYPE \ + (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, \ + G_TYPE_INVALID)) + +static void +salut_connection_olpc_buddy_info_iface_init (gpointer g_iface, + gpointer iface_data); + +static void +salut_connection_olpc_activity_properties_iface_init (gpointer g_iface, + gpointer iface_data); + +#endif + static void salut_connection_aliasing_service_iface_init (gpointer g_iface, gpointer iface_data); @@ -96,8 +116,8 @@ G_DEFINE_TYPE_WITH_CODE(SalutConnection, tp_contacts_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST, tp_base_contact_list_mixin_list_iface_init); - G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE, - tp_presence_mixin_iface_init); + G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE, + tp_presence_mixin_simple_presence_iface_init); G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS, salut_connection_avatar_service_iface_init); G_IMPLEMENT_INTERFACE @@ -109,8 +129,19 @@ G_DEFINE_TYPE_WITH_CODE(SalutConnection, salut_conn_future_iface_init); G_IMPLEMENT_INTERFACE (SALUT_TYPE_PLUGIN_CONNECTION, salut_plugin_connection_iface_init); +#ifdef ENABLE_OLPC + G_IMPLEMENT_INTERFACE (SALUT_TYPE_SVC_OLPC_BUDDY_INFO, + salut_connection_olpc_buddy_info_iface_init); + G_IMPLEMENT_INTERFACE (SALUT_TYPE_SVC_OLPC_ACTIVITY_PROPERTIES, + salut_connection_olpc_activity_properties_iface_init); +#endif ) +#ifdef ENABLE_OLPC +static gboolean uninvite_stanza_callback (WockyPorter *porter, + WockyStanza *stanza, gpointer user_data); +#endif + /* properties */ enum { PROP_NICKNAME = 1, @@ -127,6 +158,9 @@ enum { PROP_SELF, PROP_XCM, PROP_SI_BYTESTREAM_MANAGER, +#ifdef ENABLE_OLPC + PROP_OLPC_ACTIVITY_MANAGER, +#endif PROP_BACKEND, PROP_DNSSD_NAME, LAST_PROP @@ -143,6 +177,10 @@ struct _SalutConnectionPrivate gchar *last_name; gchar *jid; gchar *email; +#ifdef ENABLE_OLPC + gchar *olpc_color; + GArray *olpc_key; +#endif /* Discovery client for browsing and resolving */ SalutDiscoveryClient *discovery_client; @@ -185,6 +223,11 @@ struct _SalutConnectionPrivate /* gchar *interface → GList */ GHashTable *pending_sidecars; +#ifdef ENABLE_OLPC + SalutOlpcActivityManager *olpc_activity_manager; + guint uninvite_handler_id; +#endif + /* timer used when trying to properly disconnect */ guint disconnect_timer; @@ -212,6 +255,9 @@ static void salut_connection_create_handle_repos (TpBaseConnection *self, TpHandleRepoIface *repos[TP_NUM_HANDLE_TYPES]); +static GPtrArray * +salut_connection_create_channel_factories (TpBaseConnection *self); + static GPtrArray * salut_connection_create_channel_managers (TpBaseConnection *self); @@ -261,21 +307,6 @@ static TpDBusPropertiesMixinPropImpl conn_avatars_properties[] = { { NULL } }; -static TpDBusPropertiesMixinPropImpl conn_aliasing_properties[] = { - { "AliasFlags", GUINT_TO_POINTER (0), NULL }, - { NULL } -}; - -static void -conn_aliasing_properties_getter (GObject *object, - GQuark interface, - GQuark name, - GValue *value, - gpointer getter_data) -{ - g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); -} - static void salut_connection_init (SalutConnection *obj) { @@ -303,6 +334,10 @@ salut_connection_init (SalutConnection *obj) priv->last_name = NULL; priv->jid = NULL; priv->email = NULL; +#ifdef ENABLE_OLPC + priv->olpc_color = NULL; + priv->olpc_key = NULL; +#endif priv->discovery_client = NULL; priv->self = NULL; @@ -319,9 +354,6 @@ static void sidecars_conn_status_changed_cb (SalutConnection *conn, guint status, guint reason, gpointer unused); -static void _contact_manager_contact_change_cb (SalutContactManager *mgr, - SalutContact *contact, int changes, gpointer data); - static void salut_connection_constructed (GObject *obj) { @@ -337,9 +369,8 @@ salut_connection_constructed (GObject *obj) G_STRUCT_OFFSET (SalutConnection, contacts_mixin)); tp_base_connection_register_with_contacts_mixin (base); - tp_presence_mixin_register_with_contacts_mixin (obj); - tp_base_contact_list_mixin_register_with_contacts_mixin ( - TP_BASE_CONTACT_LIST (self->priv->contact_manager), base); + tp_presence_mixin_simple_presence_register_with_contacts_mixin (obj); + tp_base_contact_list_mixin_register_with_contacts_mixin (base); tp_contacts_mixin_add_contact_attributes_iface (obj, TP_IFACE_CONNECTION_INTERFACE_AVATARS, @@ -420,6 +451,11 @@ salut_connection_get_property (GObject *object, case PROP_SI_BYTESTREAM_MANAGER: g_value_set_object (value, priv->si_bytestream_manager); break; +#endif +#ifdef ENABLE_OLPC + case PROP_OLPC_ACTIVITY_MANAGER: + g_value_set_object (value, priv->olpc_activity_manager); + break; #endif case PROP_BACKEND: g_value_set_gtype (value, priv->backend_type); @@ -547,15 +583,23 @@ make_presence_opt_args (SalutPresenceId presence, const gchar *message) static GHashTable * get_contact_statuses (GObject *obj, - const GArray *handles) + const GArray *handles, + GError **error) { SalutConnection *self = SALUT_CONNECTION (obj); SalutConnectionPrivate *priv = self->priv; TpBaseConnection *base = (TpBaseConnection *) self; TpHandle self_handle = tp_base_connection_get_self_handle (base); + TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); GHashTable *ret; guint i; + if (!tp_handles_are_valid (handle_repo, handles, FALSE, error)) + { + return NULL; + } + ret = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) tp_presence_status_free); @@ -672,12 +716,16 @@ static const gchar *interfaces [] = { TP_IFACE_CONNECTION_INTERFACE_ALIASING, TP_IFACE_CONNECTION_INTERFACE_AVATARS, TP_IFACE_CONNECTION_INTERFACE_CONTACTS, - TP_IFACE_CONNECTION_INTERFACE_PRESENCE, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_REQUESTS, TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, TP_IFACE_CONNECTION_INTERFACE_CONTACT_LIST, SALUT_IFACE_CONNECTION_FUTURE, +#ifdef ENABLE_OLPC + SALUT_IFACE_OLPC_BUDDY_INFO, + SALUT_IFACE_OLPC_ACTIVITY_PROPERTIES, +#endif NULL }; static GPtrArray * @@ -714,11 +762,6 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class) NULL, conn_avatars_properties, }, - { TP_IFACE_CONNECTION_INTERFACE_ALIASING, - conn_aliasing_properties_getter, - NULL, - conn_aliasing_properties, - }, { NULL } }; @@ -735,6 +778,8 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class) tp_connection_class->create_handle_repos = salut_connection_create_handle_repos; + tp_connection_class->create_channel_factories = + salut_connection_create_channel_factories; tp_connection_class->create_channel_managers = salut_connection_create_channel_managers; tp_connection_class->get_unique_connection_name = @@ -754,7 +799,7 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class) is_presence_status_available, get_contact_statuses, set_own_status, presence_statuses); - tp_presence_mixin_init_dbus_properties (object_class); + tp_presence_mixin_simple_presence_init_dbus_properties (object_class); tp_contacts_mixin_class_init (object_class, G_STRUCT_OFFSET (SalutConnectionClass, contacts_mixin)); @@ -857,6 +902,17 @@ salut_connection_class_init (SalutConnectionClass *salut_connection_class) g_object_class_install_property (object_class, PROP_SI_BYTESTREAM_MANAGER, param_spec); +#ifdef ENABLE_OLPC + param_spec = g_param_spec_object ( + "olpc-activity-manager", + "SalutOlpcActivityManager object", + "The OLPC activity Manager associated with this Salut Connection", + SALUT_TYPE_OLPC_ACTIVITY_MANAGER, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_OLPC_ACTIVITY_MANAGER, + param_spec); +#endif + param_spec = g_param_spec_gtype ( "backend-type", "backend type", @@ -921,6 +977,19 @@ salut_connection_dispose (GObject *object) priv->self = NULL; } +#ifdef ENABLE_OLPC + { + wocky_porter_unregister_handler (self->porter, priv->uninvite_handler_id); + priv->uninvite_handler_id = 0; + } + + if (priv->olpc_activity_manager != NULL) + { + g_object_unref (priv->olpc_activity_manager); + priv->olpc_activity_manager = NULL; + } +#endif + if (self->session != NULL) { g_object_unref (self->session); @@ -969,6 +1038,11 @@ salut_connection_finalize (GObject *object) g_free (priv->last_name); g_free (priv->email); g_free (priv->jid); +#ifdef ENABLE_OLPC + if (priv->olpc_key != NULL) + g_array_unref (priv->olpc_key); + g_free (priv->olpc_color); +#endif g_free (priv->dnssd_name); tp_contacts_mixin_finalize (G_OBJECT(self)); @@ -1074,6 +1148,19 @@ _self_established_cb (SalutSelf *s, gpointer data) } #endif +#ifdef ENABLE_OLPC + if (!salut_olpc_activity_manager_start (priv->olpc_activity_manager, &error)) + { + DEBUG ("failed to start olpc activity manager: %s", error->message); + g_clear_error (&error); + + tp_base_connection_change_status ( TP_BASE_CONNECTION (base), + TP_CONNECTION_STATUS_DISCONNECTED, + TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); + return; + } +#endif + tp_base_connection_change_status (base, TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED); } @@ -1099,7 +1186,13 @@ discovery_client_running (SalutConnection *self) priv->self = salut_discovery_client_create_self (priv->discovery_client, self, priv->nickname, priv->first_name, priv->last_name, priv->jid, - priv->email, priv->published_name); + priv->email, priv->published_name, +#ifdef ENABLE_OLPC + priv->olpc_key, priv->olpc_color +#else + NULL, NULL +#endif + ); if (priv->pre_connect_caps != NULL) { @@ -1196,6 +1289,23 @@ _salut_connection_disconnect (SalutConnection *self) /* Aliasing interface */ +/** + * salut_connection_get_alias_flags + * + * Implements D-Bus method GetAliasFlags + * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing + * + */ +static void +salut_connection_get_alias_flags (TpSvcConnectionInterfaceAliasing *self, + DBusGMethodInvocation *context) +{ + /* Aliases are set by the contacts + * Actually we concat the first and lastname property */ + + tp_svc_connection_interface_aliasing_return_from_get_alias_flags (context, + 0); +} static const gchar * salut_connection_get_alias (SalutConnection *self, TpHandle handle) @@ -1234,7 +1344,7 @@ salut_connection_get_alias (SalutConnection *self, TpHandle handle) * salut_connection_request_aliases * * Implements D-Bus method RequestAliases - * on interface im.telepathy1.Connection.Interface.Aliasing + * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing * */ static void @@ -1275,6 +1385,40 @@ salut_connection_request_aliases (TpSvcConnectionInterfaceAliasing *iface, return; } +static void +salut_connection_get_aliases (TpSvcConnectionInterfaceAliasing *iface, + const GArray *contacts, DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + TpBaseConnection *base = TP_BASE_CONNECTION (self); + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + guint i; + GError *error = NULL; + GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, NULL); + + if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + for (i = 0; i < contacts->len; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + + g_hash_table_insert (result, GUINT_TO_POINTER (handle), + (gchar *) salut_connection_get_alias (self, handle)); + } + + tp_svc_connection_interface_aliasing_return_from_get_aliases (context, + result); + + g_hash_table_unref (result); +} + static void salut_connection_aliasing_fill_contact_attributes (GObject *obj, const GArray *contacts, GHashTable *attributes_hash) @@ -1415,18 +1559,24 @@ static void _contact_manager_contact_alias_changed (SalutConnection *self, SalutContact *contact, TpHandle handle) { - GHashTable *aliases; + GPtrArray *aliases; + GValue entry = {0, }; - DEBUG("Emitting AliasesChanged"); + g_value_init (&entry, TP_STRUCT_TYPE_ALIAS_PAIR); + g_value_take_boxed (&entry, + dbus_g_type_specialized_construct (TP_STRUCT_TYPE_ALIAS_PAIR)); + + dbus_g_type_struct_set (&entry, + 0, handle, 1, salut_contact_get_alias (contact), G_MAXUINT); + aliases = g_ptr_array_sized_new (1); + g_ptr_array_add (aliases, g_value_get_boxed (&entry)); - aliases = g_hash_table_new (NULL, NULL); - g_hash_table_insert (aliases, - GUINT_TO_POINTER (handle), - (gchar *) salut_contact_get_alias (contact)); + DEBUG("Emitting AliasesChanged"); tp_svc_connection_interface_aliasing_emit_aliases_changed (self, aliases); - g_hash_table_unref (aliases); + g_value_unset (&entry); + g_ptr_array_unref (aliases); } static void @@ -1438,7 +1588,9 @@ salut_connection_aliasing_service_iface_init (gpointer g_iface, #define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x \ (klass, salut_connection_##x) + IMPLEMENT (get_alias_flags); IMPLEMENT (request_aliases); + IMPLEMENT (get_aliases); IMPLEMENT (set_aliases); #undef IMPLEMENT } @@ -1500,6 +1652,62 @@ salut_connection_set_avatar (TpSvcConnectionInterfaceAvatars *iface, } +static void +salut_connection_get_avatar_tokens (TpSvcConnectionInterfaceAvatars *iface, + const GArray *contacts, DBusGMethodInvocation *context) +{ + guint i; + gchar **ret; + GError *err = NULL; + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = TP_BASE_CONNECTION (self); + TpHandle self_handle = tp_base_connection_get_self_handle (base); + TpHandleRepoIface *handle_repo; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + handle_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + + if (!tp_handles_are_valid (handle_repo, contacts, FALSE, &err)) + { + dbus_g_method_return_error (context, err); + g_error_free (err); + return; + } + + ret = g_new0(gchar *, contacts->len + 1); + + for (i = 0; i < contacts->len ; i++) + { + TpHandle handle = g_array_index (contacts, TpHandle, i); + if (self_handle == handle) + { + ret[i] = priv->self->avatar_token; + } + else + { + SalutContact *contact; + + contact = salut_contact_manager_get_contact (priv->contact_manager, + handle); + if (contact != NULL) + { + ret[i] = contact->avatar_token; + g_object_unref (contact); + } + } + if (ret[i] == NULL) + ret[i] = ""; + } + + tp_svc_connection_interface_avatars_return_from_get_avatar_tokens (context, + (const gchar **)ret); + + g_free (ret); +} + static void salut_connection_get_known_avatar_tokens ( TpSvcConnectionInterfaceAvatars *iface, const GArray *contacts, @@ -1693,6 +1901,78 @@ salut_connection_request_avatars ( tp_svc_connection_interface_avatars_return_from_request_avatars (context); } +static void +_request_avatar_cb (SalutContact *contact, guint8 *avatar, gsize size, + gpointer user_data) +{ + DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; + + GError *err = NULL; + GArray *arr; + + if (size == 0) + { + err = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "Unable to get avatar"); + dbus_g_method_return_error (context, err); + g_error_free (err); + return; + } + + arr = g_array_sized_new (FALSE, FALSE, sizeof (guint8), size); + arr = g_array_append_vals (arr, avatar, size); + tp_svc_connection_interface_avatars_return_from_request_avatar (context, + arr, ""); + g_array_unref (arr); +} + +static void +salut_connection_request_avatar (TpSvcConnectionInterfaceAvatars *iface, + guint handle, DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = TP_BASE_CONNECTION (self); + TpHandle self_handle = tp_base_connection_get_self_handle (base); + SalutContact *contact; + GError *err = NULL; + TpHandleRepoIface *handle_repo; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + handle_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + + if (!tp_handle_is_valid (handle_repo, handle, &err)) + { + dbus_g_method_return_error (context, err); + g_error_free (err); + return; + } + + if (handle == self_handle) + { + _request_avatar_cb (NULL, priv->self->avatar, priv->self->avatar_size, + context); + return; + } + + contact = salut_contact_manager_get_contact (priv->contact_manager, handle); + if (contact == NULL || contact->avatar_token == NULL) + { + err = g_error_new (TP_ERROR, TP_ERROR_NOT_AVAILABLE, "No known avatar"); + dbus_g_method_return_error (context, err); + g_error_free (err); + if (contact != NULL) + { + g_object_unref (contact); + } + return; + } + salut_contact_get_avatar (contact, _request_avatar_cb, context); + g_object_unref (contact); +} + static void conn_avatars_properties_getter (GObject *object, GQuark interface, @@ -1713,6 +1993,15 @@ conn_avatars_properties_getter (GObject *object, } } +static void +salut_connection_get_avatar_requirements ( + TpSvcConnectionInterfaceAvatars *iface, DBusGMethodInvocation *context) +{ + tp_svc_connection_interface_avatars_return_from_get_avatar_requirements ( + context, mimetypes, AVATAR_MIN_PX, AVATAR_MIN_PX, AVATAR_MAX_PX, + AVATAR_MAX_PX, AVATAR_MAX_BYTES); +} + static void salut_connection_avatar_service_iface_init (gpointer g_iface, gpointer iface_data) @@ -1722,7 +2011,10 @@ salut_connection_avatar_service_iface_init (gpointer g_iface, #define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x \ (klass, salut_connection_##x) + IMPLEMENT (get_avatar_requirements); + IMPLEMENT (get_avatar_tokens); IMPLEMENT (get_known_avatar_tokens); + IMPLEMENT (request_avatar); IMPLEMENT (request_avatars); IMPLEMENT (set_avatar); IMPLEMENT (clear_avatar); @@ -1746,6 +2038,56 @@ salut_free_enhanced_contact_capabilities (GPtrArray *caps) g_ptr_array_unref (caps); } +/** + * salut_connection_get_contact_capabilities + * + * Implements D-Bus method GetContactCapabilities + * on interface + * org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities + */ +static void +salut_connection_get_contact_capabilities ( + TpSvcConnectionInterfaceContactCapabilities *iface, + const GArray *handles, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + TpBaseConnection *base = (TpBaseConnection *) self; + TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_CONTACT); + guint i; + GHashTable *ret; + GError *error = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handles_are_valid (contact_handles, handles, FALSE, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + ret = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) salut_free_enhanced_contact_capabilities); + + for (i = 0; i < handles->len; i++) + { + GPtrArray *arr = g_ptr_array_new (); + TpHandle handle = g_array_index (handles, TpHandle, i); + + salut_connection_get_handle_contact_capabilities (self, handle, arr); + + g_hash_table_insert (ret, GINT_TO_POINTER (handle), arr); + } + + tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities + (context, ret); + + g_hash_table_unref (ret); +} + + static void _emit_contact_capabilities_changed (SalutConnection *conn, TpHandle handle) @@ -1827,7 +2169,7 @@ data_forms_equal (GPtrArray *one, * * Implements D-Bus method UpdateCapabilities * on interface - * im.telepathy1.Connection.Interface.ContactCapabilities + * org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities */ static void salut_connection_update_capabilities ( @@ -1959,24 +2301,1055 @@ salut_conn_contact_caps_iface_init (gpointer g_iface, gpointer iface_data) #define IMPLEMENT(x) \ tp_svc_connection_interface_contact_capabilities_implement_##x (\ klass, salut_connection_##x) + IMPLEMENT(get_contact_capabilities); IMPLEMENT(update_capabilities); #undef IMPLEMENT } -gchar * -salut_normalize_non_empty (const gchar *id, - GError **error) + +#ifdef ENABLE_OLPC +static GValue * +new_gvalue (GType type) { - g_return_val_if_fail (id != NULL, NULL); + GValue *result = g_slice_new0 (GValue); + g_value_init (result, type); + return result; +} - if (*id == '\0') +static GHashTable * +get_properties_hash (const GArray *key, const gchar *color, const gchar *jid, + const gchar *ip4, const gchar *ip6) +{ + GHashTable *properties; + GValue *gvalue; + + properties = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify) tp_g_value_slice_free); + if (key != NULL) { - g_set_error (error, TP_ERROR, TP_ERROR_INVALID_HANDLE, - "Salut handle names may not be the empty string"); - return NULL; + gvalue = new_gvalue (DBUS_TYPE_G_UCHAR_ARRAY); + g_value_set_boxed (gvalue, key); + g_hash_table_insert (properties, "key", gvalue); } - return g_strdup (id); + if (color != NULL) + { + gvalue = new_gvalue (G_TYPE_STRING); + g_value_set_string (gvalue, color); + g_hash_table_insert (properties, "color", gvalue); + } + + if (jid != NULL) + { + gvalue = new_gvalue (G_TYPE_STRING); + g_value_set_string (gvalue, jid); + g_hash_table_insert (properties, "jid", gvalue); + } + + if (ip4 != NULL) + { + gvalue = new_gvalue (G_TYPE_STRING); + g_value_set_string (gvalue, ip4); + g_hash_table_insert (properties, "ip4-address", gvalue); + } + + if (ip6 != NULL) + { + gvalue = new_gvalue (G_TYPE_STRING); + g_value_set_string (gvalue, ip6); + g_hash_table_insert (properties, "ip6-address", gvalue); + } + + return properties; +} + +static void +emit_properties_changed (SalutConnection *connection, + TpHandle handle, + const GArray *key, + const gchar *color, + const gchar *jid, + const gchar *ip4, + const gchar *ip6) +{ + GHashTable *properties; + properties = get_properties_hash (key, color, jid, ip4, ip6); + + salut_svc_olpc_buddy_info_emit_properties_changed (connection, + handle, properties); + + g_hash_table_unref (properties); +} + +static void +append_activity (SalutOlpcActivity *activity, + gpointer user_data) +{ + GPtrArray *arr = user_data; + GType type = ACTIVITY_PAIR_TYPE; + GValue gvalue = {0}; + + g_value_init (&gvalue, type); + g_value_take_boxed (&gvalue, + dbus_g_type_specialized_construct (type)); + + dbus_g_type_struct_set (&gvalue, + 0, activity->id, + 1, activity->room, + G_MAXUINT); + g_ptr_array_add (arr, g_value_get_boxed (&gvalue)); +} + +static void +free_olpc_activities (GPtrArray *arr) +{ + GType type = ACTIVITY_PAIR_TYPE; + guint i; + + for (i = 0; i < arr->len; i++) + g_boxed_free (type, arr->pdata[i]); + + g_ptr_array_unref (arr); +} + +static void +_contact_manager_contact_olpc_activities_changed (SalutConnection *self, + SalutContact *contact, + TpHandle handle) +{ + GPtrArray *activities = g_ptr_array_new (); + + DEBUG ("called for %u", handle); + + salut_contact_foreach_olpc_activity (contact, append_activity, activities); + salut_svc_olpc_buddy_info_emit_activities_changed (self, + handle, activities); + free_olpc_activities (activities); +} + +static void +_contact_manager_contact_olpc_properties_changed (SalutConnection *self, + SalutContact *contact, + TpHandle handle) +{ + emit_properties_changed (self, handle, contact->olpc_key, + contact->olpc_color, contact->jid, contact->olpc_ip4, contact->olpc_ip6); +} + +static gboolean +check_handle (TpHandleRepoIface *handle_repo, + TpHandle handle, + DBusGMethodInvocation *context) +{ + GError *error = NULL; + + if (!tp_handle_is_valid (handle_repo, handle, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +static gboolean +check_contact (TpBaseConnection *base, + TpHandle contact, + DBusGMethodInvocation *context) +{ + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( + base, TP_HANDLE_TYPE_CONTACT); + + return check_handle (contact_repo, contact, context); +} + +static gboolean +check_room (TpBaseConnection *base, + TpHandle contact, + DBusGMethodInvocation *context) +{ + TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( + base, TP_HANDLE_TYPE_ROOM); + + return check_handle (room_repo, contact, context); +} + +static void +salut_connection_olpc_get_properties (SalutSvcOLPCBuddyInfo *iface, + TpHandle handle, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = TP_BASE_CONNECTION (self); + TpHandle self_handle = tp_base_connection_get_self_handle (base); + GHashTable *properties = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!check_contact (base, handle, context)) + return; + + if (handle == self_handle) + { + properties = get_properties_hash (priv->self->olpc_key, + priv->self->olpc_color, priv->self->jid, NULL, NULL); + } + else + { + SalutContact *contact; + contact = salut_contact_manager_get_contact (priv->contact_manager, + handle); + if (contact == NULL) + { + /* FIXME: should this be InvalidHandle? */ + GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Unknown contact" }; + dbus_g_method_return_error (context, &e); + return; + } + properties = get_properties_hash (contact->olpc_key, contact->olpc_color, + contact->jid, contact->olpc_ip4, contact->olpc_ip6); + g_object_unref (contact); + } + + salut_svc_olpc_buddy_info_return_from_get_properties (context, properties); + g_hash_table_unref (properties); +} + + +static gboolean +find_unknown_properties (gpointer key, + gpointer value, + gpointer user_data) +{ + gchar **valid_props = (gchar **) user_data; + int i; + for (i = 0; valid_props[i] != NULL; i++) + { + if (!tp_strdiff (key, valid_props[i])) + return FALSE; + } + return TRUE; +} + +static void +salut_connection_olpc_set_properties (SalutSvcOLPCBuddyInfo *iface, + GHashTable *properties, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + + GError *error = NULL; + /* Only a few known properties, so handle it quite naively */ + const gchar *known_properties[] = { "color", "key", "jid", "ip4-address", + "ip6-address", NULL }; + const gchar *color = NULL; + const GArray *key = NULL; + const gchar *jid = NULL; + const GValue *val; + + /* this function explicitly supports being called when DISCONNECTED + * or CONNECTING */ + + if (g_hash_table_find (properties, find_unknown_properties, known_properties) + != NULL) + { + error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Unknown property given"); + goto error; + } + + val = (const GValue *) g_hash_table_lookup (properties, "color"); + if (val != NULL) + { + if (G_VALUE_TYPE (val) != G_TYPE_STRING) + { + error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Color value should be of type s"); + goto error; + } + else + { + int len; + gboolean correct = TRUE; + + color = g_value_get_string (val); + + /* be very anal about the color format */ + len = strlen (color); + if (len != 15) + { + correct = FALSE; + } + else + { + int i; + for (i = 0 ; i < len ; i++) + { + switch (i) + { + case 0: + case 8: + correct = (color[i] == '#'); + break; + case 7: + correct = (color[i] == ','); + break; + default: + correct = isxdigit (color[i]); + break; + } + } + } + + if (!correct) + { + error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Color value has an incorrect format"); + goto error; + } + } + } + + if ((val = (const GValue *) g_hash_table_lookup (properties, "key")) != NULL) + { + if (G_VALUE_TYPE (val) != DBUS_TYPE_G_UCHAR_ARRAY) + { + error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Key value should be of type ay"); + goto error; + } + else + { + key = g_value_get_boxed (val); + if (key->len == 0) + { + error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Key value of length 0 not allowed"); + goto error; + } + } + } + + val = g_hash_table_lookup (properties, "jid"); + if (val != NULL) + { + if (G_VALUE_TYPE (val) != G_TYPE_STRING) + { + error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "JID value should be of type s"); + goto error; + } + + jid = g_value_get_string (val); + + if (strchr (jid, '@') == NULL) + { + error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "JID value has an incorrect format"); + goto error; + } + } + + if (priv->self) + { + if (!salut_self_set_olpc_properties (priv->self, key, color, jid, + &error)) + goto error; + } + else + { + /* queue it up for later */ + if (key) + { + if (priv->olpc_key == NULL) + { + priv->olpc_key = g_array_sized_new (FALSE, FALSE, sizeof (guint8), + key->len); + } + else + { + g_array_remove_range (priv->olpc_key, 0, priv->olpc_key->len); + } + g_array_append_vals (priv->olpc_key, key->data, key->len); + } + if (color) + { + g_free (priv->olpc_color); + priv->olpc_color = g_strdup (color); + } + if (jid) + { + g_free (priv->jid); + priv->jid = g_strdup (jid); + } + } + + salut_svc_olpc_buddy_info_return_from_set_properties (context); + return; + +error: + dbus_g_method_return_error (context, error); + g_error_free (error); +} + +static void +salut_connection_olpc_get_current_activity (SalutSvcOLPCBuddyInfo *iface, + TpHandle handle, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + TpBaseConnection *base = (TpBaseConnection *) self; + TpHandle self_handle = tp_base_connection_get_self_handle (base); + SalutConnectionPrivate *priv = self->priv; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + DEBUG ("called for %u", handle); + + if (!check_contact (base, handle, context)) + return; + + if (handle == self_handle) + { + DEBUG ("Returning my own cur.act.: %s -> %u", + priv->self->olpc_cur_act ? priv->self->olpc_cur_act : "", + priv->self->olpc_cur_act_room); + salut_svc_olpc_buddy_info_return_from_get_current_activity (context, + priv->self->olpc_cur_act ? priv->self->olpc_cur_act : "", + priv->self->olpc_cur_act_room); + } + else + { + SalutContact *contact = salut_contact_manager_get_contact + (priv->contact_manager, handle); + + if (contact == NULL) + { + /* FIXME: should this be InvalidHandle? */ + GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Unknown contact" }; + DEBUG ("Returning error: unknown contact"); + dbus_g_method_return_error (context, &e); + return; + } + + DEBUG ("Returning buddy %u cur.act.: %s -> %u", handle, + contact->olpc_cur_act ? contact->olpc_cur_act : "", + contact->olpc_cur_act_room); + salut_svc_olpc_buddy_info_return_from_get_current_activity (context, + contact->olpc_cur_act ? contact->olpc_cur_act : "", + contact->olpc_cur_act_room); + g_object_unref (contact); + } +} + +static void +salut_connection_olpc_set_current_activity (SalutSvcOLPCBuddyInfo *iface, + const gchar *activity_id, + TpHandle room_handle, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = (TpBaseConnection *) self; + GError *error = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + DEBUG ("called"); + + if (activity_id[0] == '\0') + { + if (room_handle != 0) + { + GError e = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "If activity ID is empty, room handle must be 0" }; + + dbus_g_method_return_error (context, &e); + return; + } + } + else + { + if (!check_room (base, room_handle, context)) + return; + } + + if (!salut_self_set_olpc_current_activity (priv->self, activity_id, + room_handle, &error)) + { + dbus_g_method_return_error (context, error); + g_error_free (error); + return; + } + + salut_svc_olpc_buddy_info_return_from_set_current_activity (context); +} + +static void +salut_connection_olpc_get_activities (SalutSvcOLPCBuddyInfo *iface, + TpHandle handle, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = (TpBaseConnection *) self; + TpHandle self_handle = tp_base_connection_get_self_handle (base); + GPtrArray *arr; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + DEBUG ("called for %u", handle); + + if (!check_contact (base, handle, context)) + return; + + if (handle == self_handle) + { + arr = g_ptr_array_new (); + salut_self_foreach_olpc_activity (priv->self, append_activity, arr); + } + else + { + SalutContact *contact = salut_contact_manager_get_contact + (priv->contact_manager, handle); + + if (contact == NULL) + { + /* FIXME: should this be InvalidHandle? */ + GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Unknown contact" }; + DEBUG ("Returning error: unknown contact"); + dbus_g_method_return_error (context, &e); + return; + } + + arr = g_ptr_array_new (); + salut_contact_foreach_olpc_activity (contact, append_activity, arr); + g_object_unref (contact); + } + + salut_svc_olpc_buddy_info_return_from_get_activities (context, arr); + free_olpc_activities (arr); +} + +static void +salut_connection_olpc_set_activities (SalutSvcOLPCBuddyInfo *iface, + const GPtrArray *activities, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = (TpBaseConnection *) self; + TpHandleRepoIface *room_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_ROOM); + GHashTable *room_to_act_id = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) g_free); + GError *error = NULL; + guint i; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + for (i = 0; i < activities->len; i++) + { + GValue pair = {0}; + gchar *activity; + guint room_handle; + + g_value_init (&pair, ACTIVITY_PAIR_TYPE); + g_value_set_static_boxed (&pair, g_ptr_array_index (activities, i)); + dbus_g_type_struct_get (&pair, + 0, &activity, + 1, &room_handle, + G_MAXUINT); + + if (activity[0] == '\0') + { + GError e = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Invalid empty activity ID" }; + + DEBUG ("%s", e.message); + dbus_g_method_return_error (context, &e); + g_free (activity); + goto finally; + } + + if (!tp_handle_is_valid (room_repo, room_handle, &error)) + { + DEBUG ("Invalid room handle %u: %s", room_handle, error->message); + dbus_g_method_return_error (context, error); + g_error_free (error); + g_free (activity); + goto finally; + } + + g_hash_table_insert (room_to_act_id, GUINT_TO_POINTER (room_handle), + activity); + } + + if (!salut_self_set_olpc_activities (priv->self, room_to_act_id, &error)) + { + dbus_g_method_return_error (context, error); + } + else + { + salut_svc_olpc_buddy_info_return_from_set_activities (context); + } + +finally: + g_hash_table_unref (room_to_act_id); +} + +static void +salut_connection_olpc_add_activity (SalutSvcOLPCBuddyInfo *iface, + const gchar *id, + TpHandle handle, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = (TpBaseConnection *) self; + GError *error = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!salut_self_add_olpc_activity (priv->self, id, handle, &error)) + { + dbus_g_method_return_error (context, error); + } + else + { + salut_svc_olpc_buddy_info_return_from_set_activities (context); + } +} + +static void +salut_connection_olpc_buddy_info_iface_init (gpointer g_iface, + gpointer iface_data) +{ + SalutSvcOLPCBuddyInfoClass *klass = + (SalutSvcOLPCBuddyInfoClass *) g_iface; +#define IMPLEMENT(x) salut_svc_olpc_buddy_info_implement_##x (klass, \ + salut_connection_olpc_##x) + IMPLEMENT(set_properties); + IMPLEMENT(get_properties); + IMPLEMENT(set_activities); + IMPLEMENT(add_activity); + IMPLEMENT(get_activities); + IMPLEMENT(set_current_activity); + IMPLEMENT(get_current_activity); +#undef IMPLEMENT +} + +static void +salut_connection_act_get_properties (SalutSvcOLPCActivityProperties *iface, + TpHandle handle, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = (TpBaseConnection *) self; + TpHandleRepoIface *room_repo = tp_base_connection_get_handles (base, + TP_HANDLE_TYPE_ROOM); + GHashTable *properties = NULL; + GError *error = NULL; + SalutOlpcActivity *activity; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!tp_handle_is_valid (room_repo, handle, &error)) + goto error; + + activity = salut_olpc_activity_manager_get_activity_by_room ( + priv->olpc_activity_manager, handle); + if (activity == NULL) + { + g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "Activity unknown: %u", handle); + goto error; + } + + properties = salut_olpc_activity_create_properties_table (activity); + + salut_svc_olpc_buddy_info_return_from_get_properties (context, properties); + g_hash_table_unref (properties); + + return; + +error: + dbus_g_method_return_error (context, error); + g_error_free (error); +} + +static gboolean +check_color (const gchar *color) +{ + int len, i; + + /* be very anal about the color format */ + len = strlen (color); + if (len != 15) + return FALSE; + + for (i = 0 ; i < len ; i++) + { + switch (i) + { + case 0: + case 8: + if (color[i] != '#') + return FALSE; + break; + case 7: + if (color[i] != ',') + return FALSE; + break; + default: + if (!isxdigit (color[i])) + return FALSE; + break; + } + } + + return TRUE; +} + +/* returned strings are only valid as long as the hash table isn't modified */ +static gboolean +extract_properties_from_hash (GHashTable *properties, + const gchar **id, + const gchar **color, + const gchar **name, + const gchar **type, + const gchar **tags, + gboolean *is_private, + GError **error) +{ + GValue *activity_id_val, *color_val, *activity_name_val, *activity_type_val, + *tags_val, *is_private_val; + + /* activity ID */ + activity_id_val = g_hash_table_lookup (properties, "id"); + if (activity_id_val != NULL) + { + if (G_VALUE_TYPE (activity_id_val) != G_TYPE_STRING) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Activity ID value should be of type s"); + return FALSE; + } + + if (id != NULL) + *id = g_value_get_string (activity_id_val); + } + + /* color */ + color_val = g_hash_table_lookup (properties, "color"); + if (color_val != NULL) + { + if (G_VALUE_TYPE (color_val) != G_TYPE_STRING) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Color value should be of type s"); + return FALSE; + } + + if (color != NULL) + { + *color = g_value_get_string (color_val); + + if (!check_color (*color)) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Color value has an incorrect format"); + return FALSE; + } + } + } + + /* name */ + activity_name_val = g_hash_table_lookup (properties, "name"); + if (activity_name_val != NULL) + { + if (G_VALUE_TYPE (activity_name_val) != G_TYPE_STRING) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "name value should be of type s"); + return FALSE; + } + + if (name != NULL) + *name = g_value_get_string (activity_name_val); + } + + /* type */ + activity_type_val = g_hash_table_lookup (properties, "type"); + if (activity_type_val != NULL) + { + if (G_VALUE_TYPE (activity_type_val) != G_TYPE_STRING) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "type value should be of type s"); + return FALSE; + } + + if (type != NULL) + { + *type = g_value_get_string (activity_type_val); + + if (*type[0] == '\0') + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "type value must be non-empty"); + return FALSE; + } + } + } + + /* tags */ + tags_val = g_hash_table_lookup (properties, "tags"); + if (tags_val != NULL) + { + if (G_VALUE_TYPE (activity_type_val) != G_TYPE_STRING) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "tags value should be of type s"); + return FALSE; + } + + if (type != NULL) + *tags = g_value_get_string (tags_val); + } + + /* is_private */ + is_private_val = g_hash_table_lookup (properties, "private"); + if (is_private_val != NULL) + { + if (G_VALUE_TYPE (is_private_val) != G_TYPE_BOOLEAN) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "private value should be of type b"); + return FALSE; + } + + if (is_private != NULL) + *is_private = g_value_get_boolean (is_private_val); + } + + return TRUE; +} + +static void +salut_connection_act_set_properties (SalutSvcOLPCActivityProperties *iface, + TpHandle handle, + GHashTable *properties, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = (TpBaseConnection *) self; + GError *error = NULL; + const gchar *known_properties[] = { "color", "name", "type", "private", + "tags", NULL }; + const gchar *color = NULL, *name = NULL, *type = NULL, *tags = NULL; + gboolean is_private = TRUE; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + if (!check_room (base, handle, context)) + return; + + if (g_hash_table_find (properties, find_unknown_properties, known_properties) + != NULL) + { + error = g_error_new (TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Unknown property given"); + goto error; + } + + if (!extract_properties_from_hash (properties, NULL, &color, &name, &type, + &tags, &is_private, &error)) + goto error; + + if (!salut_self_set_olpc_activity_properties (priv->self, handle, color, + name, type, tags, is_private, &error)) + goto error; + + salut_svc_olpc_activity_properties_return_from_set_properties (context); + return; + +error: + dbus_g_method_return_error (context, error); + g_error_free (error); +} + +typedef struct +{ + SalutContact *inviter; + SalutOlpcActivity *activity; +} muc_ready_ctx; + +static muc_ready_ctx * +muc_ready_ctx_new (SalutContact *inviter, + SalutOlpcActivity *activity) +{ + muc_ready_ctx *ctx = g_slice_new (muc_ready_ctx); + ctx->inviter = inviter; + g_object_ref (inviter); + ctx->activity = activity; + g_object_ref (activity); + return ctx; +} + +static void +muc_ready_ctx_free (muc_ready_ctx *ctx) +{ + if (ctx == NULL) + return; + + g_object_unref (ctx->inviter); + g_object_unref (ctx->activity); + g_slice_free (muc_ready_ctx, ctx); +} + +static void +muc_ready_cb (SalutMucChannel *muc, + muc_ready_ctx *ctx) +{ + /* We joined the muc so have to forget about invites */ + salut_contact_left_activity (ctx->inviter, ctx->activity); + + DEBUG ("forget invite received from %s", ctx->inviter->name); + g_signal_handlers_disconnect_matched (muc, G_SIGNAL_MATCH_DATA, 0, 0, NULL, + NULL, ctx); + muc_ready_ctx_free (ctx); +} + +static void +muc_closed_cb (SalutMucChannel *muc, + muc_ready_ctx *ctx) +{ + /* FIXME: should we call left_private_activity here too ? */ + + g_signal_handlers_disconnect_matched (muc, G_SIGNAL_MATCH_DATA, 0, 0, NULL, + NULL, ctx); + muc_ready_ctx_free (ctx); +} + +void +salut_connection_olpc_observe_invitation (SalutConnection *self, + TpHandle room, + TpHandle inviter_handle, + WockyNode *invite_node) +{ + SalutConnectionPrivate *priv = self->priv; + WockyNode *props_node; + GHashTable *properties; + const gchar *activity_id, *color = NULL, *activity_name = NULL, + *activity_type = NULL, *tags = NULL; + SalutContact *inviter; + SalutOlpcActivity *activity; + SalutMucChannel *muc; + muc_ready_ctx *ctx; + + props_node = wocky_node_get_child_ns (invite_node, "properties", + NS_OLPC_ACTIVITY_PROPS); + + if (props_node == NULL) + return; + + inviter = salut_contact_manager_get_contact (priv->contact_manager, + inviter_handle); + if (inviter == NULL) + return; + + properties = salut_wocky_node_extract_properties (props_node, + "property"); + + if (!extract_properties_from_hash (properties, &activity_id, &color, + &activity_name, &activity_type, &tags, NULL, NULL)) + return; + + activity = salut_olpc_activity_manager_got_invitation ( + priv->olpc_activity_manager, + room, inviter, activity_id, activity_name, activity_type, + color, tags); +#ifndef USE_BACKEND_BONJOUR + muc = salut_muc_manager_get_text_channel (priv->muc_manager, room); + g_assert (muc != NULL); + + ctx = muc_ready_ctx_new (inviter, activity); + g_signal_connect (muc, "ready", G_CALLBACK (muc_ready_cb), ctx); + g_signal_connect (muc, "closed", G_CALLBACK (muc_closed_cb), ctx); + + g_object_unref (muc); +#endif + g_hash_table_unref (properties); + g_object_unref (inviter); +} + +static void +salut_connection_act_get_activity (SalutSvcOLPCActivityProperties *iface, + const gchar *activity_id, + DBusGMethodInvocation *context) +{ + SalutConnection *self = SALUT_CONNECTION (iface); + SalutConnectionPrivate *priv = self->priv; + TpBaseConnection *base = (TpBaseConnection *) self; + GError *error = NULL; + SalutOlpcActivity *activity; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); + + activity = salut_olpc_activity_manager_get_activity_by_id ( + priv->olpc_activity_manager, activity_id); + if (activity == NULL) + { + g_set_error (&error, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "Activity unknown: %s", activity_id); + goto error; + } + + salut_svc_olpc_activity_properties_return_from_get_activity (context, + activity->room); + + return; + +error: + dbus_g_method_return_error (context, error); + g_error_free (error); +} + +static void +salut_connection_olpc_activity_properties_iface_init (gpointer g_iface, + gpointer iface_data) +{ + SalutSvcOLPCActivityPropertiesClass *klass = + (SalutSvcOLPCActivityPropertiesClass *) g_iface; +#define IMPLEMENT(x) salut_svc_olpc_activity_properties_implement_##x \ + (klass, salut_connection_act_##x) + IMPLEMENT(set_properties); + IMPLEMENT(get_properties); + IMPLEMENT(get_activity); +#undef IMPLEMENT +} +#endif + +gchar * +salut_normalize_non_empty (const gchar *id, + GError **error) +{ + g_return_val_if_fail (id != NULL, NULL); + + if (*id == '\0') + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_HANDLE, + "Salut handle names may not be the empty string"); + return NULL; + } + + return g_strdup (id); } static gchar * @@ -2033,7 +3406,234 @@ _contact_manager_contact_change_cb (SalutContactManager *mgr, { salut_conn_contact_info_changed (self, contact, handle); } + +#ifdef ENABLE_OLPC + if (changes & SALUT_CONTACT_OLPC_PROPERTIES) + _contact_manager_contact_olpc_properties_changed (self, contact, handle); + + if (changes & SALUT_CONTACT_OLPC_CURRENT_ACTIVITY) + salut_svc_olpc_buddy_info_emit_current_activity_changed (self, + handle, contact->olpc_cur_act, contact->olpc_cur_act_room); + + if (changes & SALUT_CONTACT_OLPC_ACTIVITIES) + _contact_manager_contact_olpc_activities_changed (self, contact, handle); +#endif +} + +#ifdef ENABLE_OLPC +static void +_olpc_activity_manager_activity_modified_cb (SalutOlpcActivityManager *mgr, + SalutOlpcActivity *activity, SalutConnection *self) +{ + GHashTable *properties; + + properties = salut_olpc_activity_create_properties_table (activity); + salut_svc_olpc_activity_properties_emit_activity_properties_changed ( + self, activity->room, properties); + + g_hash_table_unref (properties); +} + +gboolean +salut_connection_olpc_observe_muc_stanza (SalutConnection *self, + TpHandle room, TpHandle sender, WockyStanza *stanza) +{ + WockyNode *node = wocky_stanza_get_top_node (stanza); + SalutConnectionPrivate *priv = self->priv; + WockyNode *props_node; + GHashTable *properties; + const gchar *activity_id, *color = NULL, *activity_name = NULL, + *activity_type = NULL, *tags = NULL; + gboolean is_private = FALSE; + SalutOlpcActivity *activity; + + props_node = wocky_node_get_child_ns (node, "properties", + NS_OLPC_ACTIVITY_PROPS); + + if (props_node == NULL) + return FALSE; + + activity = salut_olpc_activity_manager_get_activity_by_room ( + priv->olpc_activity_manager, room); + + if (activity == NULL) + { + DEBUG ("no activity in room %d", room); + return FALSE; + } + + properties = salut_wocky_node_extract_properties (props_node, + "property"); + + if (!extract_properties_from_hash (properties, &activity_id, &color, + &activity_name, &activity_type, &tags, &is_private, NULL)) + return TRUE; + + salut_olpc_activity_update (activity, room, activity_id, activity_name, + activity_type, color, tags, is_private); + + g_hash_table_unref (properties); + + return TRUE; +} + +static gboolean +uninvite_stanza_callback (WockyPorter *porter, + WockyStanza *stanza, + gpointer user_data) +{ + SalutConnection *self = SALUT_CONNECTION (user_data); + SalutConnectionPrivate *priv = self->priv; + TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( + (TpBaseConnection *) self, TP_HANDLE_TYPE_ROOM); + WockyNode *node; + TpHandle room_handle; + const gchar *room, *activity_id; + SalutOlpcActivity *activity; + WockyNode *top_node = wocky_stanza_get_top_node (stanza); + SalutContact *contact = SALUT_CONTACT (wocky_stanza_get_from_contact (stanza)); + + node = wocky_node_get_child_ns (top_node, "uninvite", + NS_OLPC_ACTIVITY_PROPS); + g_assert (node != NULL); + + room = wocky_node_get_attribute (node, "room"); + if (room == NULL) + { + DEBUG ("No room attribute"); + return FALSE; + } + + room_handle = tp_handle_lookup (room_repo, room, NULL, NULL); + if (room_handle == 0) + { + DEBUG ("room %s unknown", room); + return FALSE; + } + + activity_id = wocky_node_get_attribute (node, "id"); + if (activity_id == NULL) + { + DEBUG ("No id attribute"); + return FALSE; + } + + DEBUG ("received uninvite from %s", contact->name); + + activity = salut_olpc_activity_manager_get_activity_by_room ( + priv->olpc_activity_manager, room_handle); + + if (activity == NULL) + return FALSE; + + salut_contact_left_activity (contact, activity); + + return TRUE; +} + +#endif + +static GPtrArray * +salut_connection_create_channel_factories (TpBaseConnection *base) +{ + SalutConnection *self = SALUT_CONNECTION (base); + SalutConnectionPrivate *priv = self->priv; + GPtrArray *factories = g_ptr_array_sized_new (4); + + /* Create the contact manager */ + priv->contact_manager = salut_discovery_client_create_contact_manager ( + priv->discovery_client, self); + g_signal_connect (priv->contact_manager, "contact-change", + G_CALLBACK (_contact_manager_contact_change_cb), self); + +#ifdef ENABLE_OLPC + priv->uninvite_handler_id = wocky_porter_register_handler_from_anyone ( + self->porter, + WOCKY_STANZA_TYPE_MESSAGE, WOCKY_STANZA_SUB_TYPE_NONE, + WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, + uninvite_stanza_callback, self, + '(', "uninvite", + ':', NS_OLPC_ACTIVITY_PROPS, + ')', NULL); + + /* create the OLPC activity manager */ + priv->olpc_activity_manager = + salut_discovery_client_create_olpc_activity_manager ( + priv->discovery_client, self); + g_signal_connect (priv->olpc_activity_manager, "activity-modified", + G_CALLBACK (_olpc_activity_manager_activity_modified_cb), self); +#endif + + return factories; +} + +#ifdef ENABLE_OLPC +static void +muc_channel_closed_cb (SalutMucChannel *chan, + SalutOlpcActivity *activity) +{ + SalutConnection *conn; + SalutConnectionPrivate *priv; + TpBaseConnection *base; + TpHandle self_handle; + GPtrArray *activities = g_ptr_array_new (); + + g_signal_handlers_disconnect_by_func (chan, + G_CALLBACK (muc_channel_closed_cb), activity); + + g_object_get (activity, + "connection", &conn, + NULL); + + priv = conn->priv; + base = (TpBaseConnection *) conn; + self_handle = tp_base_connection_get_self_handle (base); + + salut_self_remove_olpc_activity (priv->self, activity); + + salut_self_foreach_olpc_activity (priv->self, append_activity, activities); + salut_svc_olpc_buddy_info_emit_activities_changed (conn, self_handle, + activities); + free_olpc_activities (activities); + + /* we were holding a ref since the channel was opened */ + g_object_unref (activity); + + g_object_unref (conn); +} + +static void +muc_manager_new_channels_cb (TpChannelManager *channel_manager, + GHashTable *channels, + SalutConnection *conn) +{ + SalutConnectionPrivate *priv = conn->priv; + GHashTableIter iter; + gpointer chan; + + g_hash_table_iter_init (&iter, channels); + while (g_hash_table_iter_next (&iter, &chan, NULL)) + { + SalutOlpcActivity *activity; + TpHandle room_handle; + + if (!SALUT_IS_MUC_CHANNEL (chan)) + return; + + g_object_get (chan, + "handle", &room_handle, + NULL); + + /* ref the activity as long as we have a channel open */ + activity = salut_olpc_activity_manager_ensure_activity_by_room ( + priv->olpc_activity_manager, + room_handle); + + g_signal_connect (chan, "closed", G_CALLBACK (muc_channel_closed_cb), + activity); + } } +#endif static void add_to_array (gpointer data, @@ -2052,16 +3652,12 @@ salut_connection_create_channel_managers (TpBaseConnection *base) GPtrArray *tmp; SalutPluginLoader *loader; - /* Create the contact manager. This is not a channel manager anymore, - * but still needs to be created from here because others channel managers use - * it and TpBaseConnection calls ::create_channel_managers() before - * ::constructed() */ - self->priv->contact_manager = salut_discovery_client_create_contact_manager ( - self->priv->discovery_client, self); - g_signal_connect (self->priv->contact_manager, "contact-change", - G_CALLBACK (_contact_manager_contact_change_cb), self); - + /* FIXME: The second and third arguments depend on create_channel_factories + * being called before this; should telepathy-glib guarantee that or + * should we be defensive? + */ priv->im_manager = salut_im_manager_new (self, priv->contact_manager); + priv->ft_manager = salut_ft_manager_new (self, priv->contact_manager); #ifndef USE_BACKEND_BONJOUR @@ -2077,6 +3673,7 @@ salut_connection_create_channel_managers (TpBaseConnection *base) #endif g_ptr_array_add (managers, priv->im_manager); + g_ptr_array_add (managers, priv->contact_manager); g_ptr_array_add (managers, priv->ft_manager); #ifndef USE_BACKEND_BONJOUR g_ptr_array_add (managers, priv->muc_manager); @@ -2087,6 +3684,11 @@ salut_connection_create_channel_managers (TpBaseConnection *base) g_ptr_array_add (managers, priv->tubes_manager); #endif +#ifdef ENABLE_OLPC + g_signal_connect (TP_CHANNEL_MANAGER (priv->muc_manager), "new-channels", + G_CALLBACK (muc_manager_new_channels_cb), self); +#endif + /* plugin channel managers */ loader = salut_plugin_loader_dup (); tmp = salut_plugin_loader_create_channel_managers (loader, plugin_connection); diff --git a/src/connection.h b/src/connection.h index d965587e..dcc0128d 100644 --- a/src/connection.h +++ b/src/connection.h @@ -91,6 +91,16 @@ typedef enum { LIST_HANDLE_LAST = LIST_HANDLE_KNOWN } SalutConnectionListHandle; +#ifdef ENABLE_OLPC +void +salut_connection_olpc_observe_invitation (SalutConnection *connection, + TpHandle room, TpHandle invitor_handle, WockyNode *invite_node); + +gboolean +salut_connection_olpc_observe_muc_stanza (SalutConnection *self, TpHandle room, + TpHandle sender, WockyStanza *stanza); +#endif + const gchar * const *salut_connection_get_implemented_interfaces (void); gchar *salut_normalize_non_empty (const gchar *id, GError **error); diff --git a/src/contact-manager.c b/src/contact-manager.c index 8efd6f83..f15eda01 100644 --- a/src/contact-manager.c +++ b/src/contact-manager.c @@ -40,8 +40,9 @@ static void salut_contact_manager_close_all (SalutContactManager *mgr); static void _contact_finalized_cb (gpointer data, GObject *old_object); -G_DEFINE_TYPE(SalutContactManager, salut_contact_manager, - TP_TYPE_BASE_CONTACT_LIST) +G_DEFINE_TYPE_WITH_CODE(SalutContactManager, salut_contact_manager, + TP_TYPE_BASE_CONTACT_LIST, + G_IMPLEMENT_INTERFACE (GABBLE_TYPE_CAPS_CHANNEL_MANAGER, NULL)) /* signal enum */ enum diff --git a/src/contact.c b/src/contact.c index 558b6ff0..109c8db8 100644 --- a/src/contact.c +++ b/src/contact.c @@ -73,6 +73,10 @@ struct _SalutContactPrivate gboolean dispose_has_run; gchar *alias; GList *avatar_requests; +#ifdef ENABLE_OLPC + /* room handle owned by the SalutOlpcActivity -> SalutOlpcActivity */ + GHashTable *olpc_activities; +#endif gboolean found; gboolean frozen; guint pending_changes; @@ -142,6 +146,16 @@ salut_contact_init (SalutContact *obj) obj->status_message = NULL; obj->avatar_token = NULL; obj->jid = NULL; +#ifdef ENABLE_OLPC + obj->olpc_key = NULL; + obj->olpc_color = NULL; + obj->olpc_cur_act = NULL; + obj->olpc_cur_act_room = 0; + obj->olpc_ip4 = NULL; + obj->olpc_ip6 = NULL; + priv->olpc_activities = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) g_object_unref); +#endif priv->found = FALSE; priv->alias = NULL; } @@ -255,6 +269,17 @@ salut_contact_class_init (SalutContactClass *salut_contact_class) param_spec); } +#ifdef ENABLE_OLPC +static void +disconnect_activity_signal_foreach (TpHandle room, + SalutOlpcActivity *activity, + SalutContact *self) +{ + g_signal_handlers_disconnect_matched (activity, G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, self); +} +#endif + void salut_contact_dispose (GObject *object) { @@ -268,6 +293,12 @@ salut_contact_dispose (GObject *object) priv->dispose_has_run = TRUE; +#ifdef ENABLE_OLPC + g_hash_table_foreach (priv->olpc_activities, + (GHFunc) disconnect_activity_signal_foreach, self); + g_hash_table_unref (priv->olpc_activities); +#endif + salut_contact_avatar_request_flush (self, NULL, 0); /* release any references held by the object here */ @@ -299,6 +330,17 @@ salut_contact_finalize (GObject *object) g_free (self->email); g_free (self->jid); +#ifdef ENABLE_OLPC + if (self->olpc_key != NULL) + { + g_array_unref (self->olpc_key); + } + g_free (self->olpc_color); + g_free (self->olpc_cur_act); + g_free (self->olpc_ip4); + g_free (self->olpc_ip6); +#endif + G_OBJECT_CLASS (salut_contact_parent_class)->finalize (object); } @@ -317,6 +359,45 @@ purge_cached_avatar (SalutContact *self, SALUT_CONTACT_GET_CLASS (self)->retrieve_avatar (self); } +#ifdef ENABLE_OLPC +typedef struct +{ + SalutContactOLPCActivityFunc foreach; + gpointer user_data; +} foreach_olpc_activity_ctx; + +static void +foreach_olpc_activity (gpointer key, gpointer value, gpointer user_data) +{ + foreach_olpc_activity_ctx *ctx = user_data; + SalutOlpcActivity *activity = value; + + /* ignore activity without ID */ + if (activity->id == NULL) + return; + + DEBUG ("%s => %u", activity->id, activity->room); + (ctx->foreach) (activity, ctx->user_data); +} + +void +salut_contact_foreach_olpc_activity (SalutContact *self, + SalutContactOLPCActivityFunc foreach, + gpointer user_data) +{ + SalutContactPrivate *priv = self->priv; + foreach_olpc_activity_ctx ctx = { foreach, user_data }; + + DEBUG ("called"); + + g_hash_table_foreach (priv->olpc_activities, foreach_olpc_activity, + &ctx); + + DEBUG ("end"); +} + +#endif + GArray * salut_contact_get_addresses (SalutContact *self) { @@ -535,6 +616,9 @@ salut_contact_change_jid (SalutContact *self, gchar *jid) self->jid = g_strdup (jid); salut_contact_change (self, SALUT_CONTACT_JID_CHANGED +#ifdef ENABLE_OLPC + | SALUT_CONTACT_OLPC_PROPERTIES +#endif ); } } @@ -551,6 +635,115 @@ void salut_contact_change_capabilities (SalutContact *self, hash, node, ver); } +#ifdef ENABLE_OLPC +void +salut_contact_change_olpc_color (SalutContact *self, const gchar *olpc_color) +{ + if (tp_strdiff (self->olpc_color, olpc_color)) + { + g_free (self->olpc_color); + self->olpc_color = g_strdup (olpc_color); + salut_contact_change (self, SALUT_CONTACT_OLPC_PROPERTIES); + } +} + +void +salut_contact_change_olpc_key (SalutContact *self, GArray *olpc_key) +{ + if (olpc_key != NULL) + { + if (self->olpc_key == NULL || self->olpc_key->len != olpc_key->len || + memcmp (self->olpc_key->data, olpc_key->data, olpc_key->len) != 0) + { + if (self->olpc_key != NULL) + { + g_array_unref (self->olpc_key); + } + self->olpc_key = g_array_sized_new (FALSE, FALSE, + sizeof (guint8), olpc_key->len); + g_array_append_vals (self->olpc_key, olpc_key->data, + olpc_key->len); + salut_contact_change (self, SALUT_CONTACT_OLPC_PROPERTIES); + } + } +} + +void +salut_contact_change_ipv4_addr (SalutContact *self, const gchar *ipv4_addr) +{ + if (tp_strdiff (ipv4_addr, self->olpc_ip4)) + { + g_free (self->olpc_ip4); + self->olpc_ip4 = g_strdup (ipv4_addr); + salut_contact_change (self, SALUT_CONTACT_OLPC_PROPERTIES); + } + +} + +void +salut_contact_change_ipv6_addr (SalutContact *self, const gchar *ipv6_addr) +{ + if (tp_strdiff (ipv6_addr, self->olpc_ip6)) + { + g_free (self->olpc_ip6); + self->olpc_ip6 = g_strdup (ipv6_addr); + salut_contact_change (self, SALUT_CONTACT_OLPC_PROPERTIES); + } +} + +void +salut_contact_change_current_activity (SalutContact *self, + const gchar *current_activity_id, + const gchar *current_activity_room) +{ + TpHandleRepoIface *room_repo; + TpHandle room_handle = 0; + + if (self->connection == NULL) + return; + + room_repo = tp_base_connection_get_handles + ((TpBaseConnection *) self->connection, TP_HANDLE_TYPE_ROOM); + + if (current_activity_room != NULL && *current_activity_room != '\0') + { + room_handle = tp_handle_ensure (room_repo, current_activity_room, + NULL, NULL); + if (room_handle == 0) + { + DEBUG ("Invalid room \"%s\" for current activity \"%s\": " + "ignoring", current_activity_room, current_activity_id); + } + } + + if (current_activity_id == NULL || room_handle == 0) + { + DEBUG ("Unsetting current activity"); + if (self->olpc_cur_act != NULL || self->olpc_cur_act_room != 0) + { + g_free (self->olpc_cur_act); + self->olpc_cur_act = NULL; + self->olpc_cur_act_room = 0; + salut_contact_change (self, SALUT_CONTACT_OLPC_CURRENT_ACTIVITY); + } + } + else + { + DEBUG ("Current activity %s, room handle %d", current_activity_id, + room_handle); + if (tp_strdiff (self->olpc_cur_act, current_activity_id) || + self->olpc_cur_act_room != room_handle) + { + g_free (self->olpc_cur_act); + self->olpc_cur_act_room = room_handle; + self->olpc_cur_act = g_strdup (current_activity_id); + salut_contact_change (self, SALUT_CONTACT_OLPC_CURRENT_ACTIVITY); + } + } +} + +#endif + void salut_contact_found (SalutContact *self) { @@ -606,6 +799,68 @@ salut_contact_thaw (SalutContact *self) salut_contact_change (self, 0); } +#ifdef ENABLE_OLPC +static void +activity_valid_cb (SalutOlpcActivity *activity, + SalutContact *self) +{ + /* Now we can emit the ActivitiesChanged signal */ + DEBUG ("activity in room %d (%s) is now valid", activity->room, activity->id); + g_signal_emit (self, signals[CONTACT_CHANGE], 0, + SALUT_CONTACT_OLPC_ACTIVITIES); +} + +gboolean +salut_contact_joined_activity (SalutContact *self, + SalutOlpcActivity *activity) +{ + SalutContactPrivate *priv = self->priv; + + if (g_hash_table_lookup (priv->olpc_activities, + GUINT_TO_POINTER (activity->room)) != NULL) + return FALSE; + + DEBUG_CONTACT (self, "joined activity %s", activity->id); + g_hash_table_insert (priv->olpc_activities, GUINT_TO_POINTER (activity->room), + activity); + g_object_ref (activity); + + if (activity->id == NULL) + { + /* we can't emit the ActivitiesChanged signal right now as we don't have + * the activity ID. Thanks OLPC interface */ + DEBUG ("activity in room %d isn't valid yet", activity->room); + g_signal_connect (activity, "valid", G_CALLBACK (activity_valid_cb), + self); + } + else + { + g_signal_emit (self, signals[CONTACT_CHANGE], 0, + SALUT_CONTACT_OLPC_ACTIVITIES); + } + + return TRUE; +} + +void +salut_contact_left_activity (SalutContact *self, + SalutOlpcActivity *activity) +{ + SalutContactPrivate *priv = self->priv; + + g_signal_handlers_disconnect_matched (activity, G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, self); + + DEBUG_CONTACT (self, "left activity %s", activity->id); + if (!g_hash_table_remove (priv->olpc_activities, + GUINT_TO_POINTER (activity->room))) + return; + + g_signal_emit (self, signals[CONTACT_CHANGE], 0, + SALUT_CONTACT_OLPC_ACTIVITIES); +} +#endif + static const GPtrArray * salut_contact_get_data_forms (WockyXep0115Capabilities *caps) { diff --git a/src/contact.h b/src/contact.h index 33244f2c..d301e293 100644 --- a/src/contact.h +++ b/src/contact.h @@ -28,6 +28,10 @@ #include "presence.h" #include "connection.h" +#ifdef ENABLE_OLPC +#include "olpc-activity.h" +#endif + #include #include @@ -38,6 +42,11 @@ enum { SALUT_CONTACT_ALIAS_CHANGED = 0x1, SALUT_CONTACT_STATUS_CHANGED = 0x2, SALUT_CONTACT_AVATAR_CHANGED = 0x4, +#ifdef ENABLE_OLPC + SALUT_CONTACT_OLPC_PROPERTIES = 0x8, + SALUT_CONTACT_OLPC_CURRENT_ACTIVITY = 0x10, + SALUT_CONTACT_OLPC_ACTIVITIES = 0x20, +#endif /* ENABLE_OLPC */ SALUT_CONTACT_JID_CHANGED = 0x40, SALUT_CONTACT_EMAIL_CHANGED = 0x80, SALUT_CONTACT_REAL_NAME_CHANGED = 0x100, @@ -80,6 +89,14 @@ struct _SalutContact { GPtrArray *data_forms; /* of owned WockyDataForm*s */ TpHandle handle; +#ifdef ENABLE_OLPC + GArray *olpc_key; + gchar *olpc_color; + gchar *olpc_cur_act; + TpHandle olpc_cur_act_room; + gchar *olpc_ip4; + gchar *olpc_ip6; +#endif /* ENABLE_OLPC */ /* private */ SalutConnection *connection; @@ -127,6 +144,20 @@ void salut_contact_set_capabilities (SalutContact *contact, const GabbleCapabilitySet *caps, const GPtrArray *data_forms); +#ifdef ENABLE_OLPC +typedef void (*SalutContactOLPCActivityFunc) + (SalutOlpcActivity *activity, gpointer user_data); + +void salut_contact_foreach_olpc_activity (SalutContact *self, + SalutContactOLPCActivityFunc foreach, gpointer user_data); + +gboolean salut_contact_joined_activity (SalutContact *self, + SalutOlpcActivity *activity); + +void salut_contact_left_activity (SalutContact *self, + SalutOlpcActivity *activity); +#endif + /* restricted methods */ void salut_contact_change_real_name (SalutContact *self, const gchar *first, const gchar *last); @@ -141,6 +172,18 @@ void salut_contact_change_jid (SalutContact *self, gchar *jid); void salut_contact_change_capabilities (SalutContact *self, const gchar *hash, const gchar *node, const gchar *ver); +#ifdef ENABLE_OLPC +void salut_contact_change_olpc_color (SalutContact *self, + const gchar *olpc_color); +void salut_contact_change_olpc_key (SalutContact *self, GArray *olpc_key); +void salut_contact_change_ipv4_addr (SalutContact *self, + const gchar *ipv4_addr); +void salut_contact_change_ipv6_addr (SalutContact *self, + const gchar *ipv4_addr); +void salut_contact_change_current_activity (SalutContact *self, + const gchar *current_activity_id, const gchar *current_activity_room); +#endif + void salut_contact_avatar_request_flush (SalutContact *self, guint8 *data, gsize size); diff --git a/src/debug.c b/src/debug.c index 53a7c60e..365cb7d8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -35,6 +35,7 @@ GDebugKey keys[] = { { "xmpp-connection-manager", DEBUG_XCM }, { "si-bytestream-manager", DEBUG_SI_BYTESTREAM_MGR }, { "discovery", DEBUG_DISCOVERY }, + { "olpc-activity", DEBUG_OLPC_ACTIVITY }, { "ft", DEBUG_FT }, { "plugin", DEBUG_PLUGIN }, { 0, }, diff --git a/src/debug.h b/src/debug.h index 135ec385..9e97ed3d 100644 --- a/src/debug.h +++ b/src/debug.h @@ -31,8 +31,9 @@ typedef enum DEBUG_TUBES = 1 << 16, DEBUG_XCM = 1 << 17, DEBUG_DISCOVERY = 1 << 18, - DEBUG_FT = 1 << 19, - DEBUG_PLUGIN = 1 << 20, + DEBUG_OLPC_ACTIVITY = 1 << 19, + DEBUG_FT = 1 << 20, + DEBUG_PLUGIN = 1 << 21, } DebugFlags; void debug_set_flags_from_env (void); diff --git a/src/discovery-client.c b/src/discovery-client.c index 75f48248..7add6fa8 100644 --- a/src/discovery-client.c +++ b/src/discovery-client.c @@ -65,6 +65,19 @@ salut_discovery_client_create_contact_manager (SalutDiscoveryClient *self, return virtual_method (self, connection); } +#ifdef ENABLE_OLPC +SalutOlpcActivityManager * +salut_discovery_client_create_olpc_activity_manager (SalutDiscoveryClient *self, + SalutConnection *connection) +{ + SalutOlpcActivityManager * (*virtual_method)(SalutDiscoveryClient *, + SalutConnection *) = + SALUT_DISCOVERY_CLIENT_GET_CLASS (self)->create_olpc_activity_manager; + g_assert (virtual_method != NULL); + return virtual_method (self, connection); +} +#endif + SalutSelf * salut_discovery_client_create_self (SalutDiscoveryClient *self, SalutConnection *connection, @@ -73,15 +86,17 @@ salut_discovery_client_create_self (SalutDiscoveryClient *self, const gchar *last_name, const gchar *jid, const gchar *email, - const gchar *published_name) + const gchar *published_name, + const GArray *olpc_key, + const gchar *olpc_color) { SalutSelf * (*virtual_method)(SalutDiscoveryClient *, SalutConnection *, const gchar *, const gchar *, const gchar *, const gchar *, - const gchar *, const gchar *) = + const gchar *, const gchar *, const GArray *, const gchar *) = SALUT_DISCOVERY_CLIENT_GET_CLASS (self)->create_self; g_assert (virtual_method != NULL); return virtual_method (self, connection, nickname, first_name, last_name, - jid, email, published_name); + jid, email, published_name, olpc_key, olpc_color); } const gchar * diff --git a/src/discovery-client.h b/src/discovery-client.h index 60bfd63d..e741a275 100644 --- a/src/discovery-client.h +++ b/src/discovery-client.h @@ -26,6 +26,9 @@ #include "contact-manager.h" #include "roomlist-manager.h" #include "self.h" +#ifdef ENABLE_OLPC +#include "olpc-activity-manager.h" +#endif G_BEGIN_DECLS @@ -52,9 +55,14 @@ struct _SalutDiscoveryClientClass SalutConnection *connection); SalutContactManager * (*create_contact_manager) (SalutDiscoveryClient *clt, SalutConnection *connection); +#ifdef ENABLE_OLPC + SalutOlpcActivityManager * (*create_olpc_activity_manager) ( + SalutDiscoveryClient *clt, SalutConnection *connection); +#endif SalutSelf * (*create_self) (SalutDiscoveryClient *clt, SalutConnection *conn, const gchar *nickname, const gchar *first_name, const gchar *last_name, - const gchar *jid, const gchar *email, const gchar *published_name); + const gchar *jid, const gchar *email, const gchar *published_name, + const GArray *olpc_key, const gchar *olpc_color); const gchar * (*get_host_name_fqdn) (SalutDiscoveryClient *clt); }; @@ -85,10 +93,16 @@ SalutRoomlistManager * salut_discovery_client_create_roomlist_manager ( SalutContactManager * salut_discovery_client_create_contact_manager ( SalutDiscoveryClient *clt, SalutConnection *connection); +#ifdef ENABLE_OLPC +SalutOlpcActivityManager * salut_discovery_client_create_olpc_activity_manager ( + SalutDiscoveryClient *clt, SalutConnection *connection); +#endif + SalutSelf * salut_discovery_client_create_self ( SalutDiscoveryClient *clt, SalutConnection *connection, const gchar *nickname, const gchar *first_name, const gchar *last_name, - const gchar *jid, const gchar *email, const gchar *published_name); + const gchar *jid, const gchar *email, const gchar *published_name, + const GArray *olpc_key, const gchar *olpc_color); const gchar * salut_discovery_client_get_host_name_fqdn ( SalutDiscoveryClient *clt); diff --git a/src/dummy-discovery-client.c b/src/dummy-discovery-client.c index d7d279e2..5399f260 100644 --- a/src/dummy-discovery-client.c +++ b/src/dummy-discovery-client.c @@ -112,7 +112,9 @@ salut_dummy_discovery_client_create_self (SalutDiscoveryClient *client, const gchar *last_name, const gchar *jid, const gchar *email, - const gchar *published_name) + const gchar *published_name, + const GArray *olpc_key, + const gchar *olpc_color) { return NULL; } @@ -126,5 +128,8 @@ discovery_client_init (gpointer g_iface, klass->start = NULL; klass->create_muc_manager = NULL; klass->create_contact_manager = NULL; +#ifdef ENABLE_OLPC + klass->create_olpc_activity_manager = NULL; +#endif klass->create_self = salut_dummy_discovery_client_create_self; } diff --git a/src/file-transfer-channel.c b/src/file-transfer-channel.c index efc28802..78271d63 100644 --- a/src/file-transfer-channel.c +++ b/src/file-transfer-channel.c @@ -1188,7 +1188,7 @@ salut_file_transfer_channel_offer_file (SalutFileTransferChannel *self, * salut_file_transfer_channel_accept_file * * Implements D-Bus method AcceptFile - * on interface im.telepathy1.Channel.Type.FileTransfer + * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer */ static void salut_file_transfer_channel_accept_file (TpSvcChannelTypeFileTransfer *iface, @@ -1264,7 +1264,7 @@ salut_file_transfer_channel_accept_file (TpSvcChannelTypeFileTransfer *iface, * salut_file_transfer_channel_provide_file * * Implements D-Bus method ProvideFile - * on interface im.telepathy1.Channel.Type.FileTransfer + * on interface org.freedesktop.Telepathy.Channel.Type.FileTransfer */ static void salut_file_transfer_channel_provide_file ( diff --git a/src/im-channel.c b/src/im-channel.c index 46ecc32f..d7a409be 100644 --- a/src/im-channel.c +++ b/src/im-channel.c @@ -47,7 +47,9 @@ G_DEFINE_TYPE_WITH_CODE (SalutImChannel, salut_im_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, - tp_message_mixin_iface_init) + tp_message_mixin_text_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MESSAGES, + tp_message_mixin_messages_iface_init); ); /* properties */ @@ -208,10 +210,10 @@ salut_im_channel_fill_immutable_properties (TpBaseChannel *chan, tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, - TP_IFACE_CHANNEL_TYPE_TEXT, "MessagePartSupportFlags", - TP_IFACE_CHANNEL_TYPE_TEXT, "DeliveryReportingSupport", - TP_IFACE_CHANNEL_TYPE_TEXT, "SupportedContentTypes", - TP_IFACE_CHANNEL_TYPE_TEXT, "MessageTypes", + TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessagePartSupportFlags", + TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "DeliveryReportingSupport", + TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "SupportedContentTypes", + TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessageTypes", NULL); } @@ -222,6 +224,16 @@ salut_im_channel_get_object_path_suffix (TpBaseChannel *chan) tp_base_channel_get_target_handle (chan)); } +static GPtrArray * +salut_im_channel_get_interfaces (TpBaseChannel *chan) +{ + GPtrArray *interfaces = TP_BASE_CHANNEL_CLASS (salut_im_channel_parent_class) + ->get_interfaces (chan); + + g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_MESSAGES); + return interfaces; +} + static void salut_im_channel_class_init (SalutImChannelClass *salut_im_channel_class) { @@ -239,6 +251,7 @@ salut_im_channel_class_init (SalutImChannelClass *salut_im_channel_class) object_class->set_property = salut_im_channel_set_property; base_class->channel_type = TP_IFACE_CHANNEL_TYPE_TEXT; + base_class->get_interfaces = salut_im_channel_get_interfaces; base_class->target_handle_type = TP_HANDLE_TYPE_CONTACT; base_class->close = salut_im_channel_close; base_class->fill_immutable_properties = diff --git a/src/muc-channel.c b/src/muc-channel.c index c0eb63e6..091e60a2 100644 --- a/src/muc-channel.c +++ b/src/muc-channel.c @@ -54,13 +54,14 @@ #include "text-helper.h" #include "tube-stream.h" #include "tube-dbus.h" -#include "util.h" G_DEFINE_TYPE_WITH_CODE(SalutMucChannel, salut_muc_channel, TP_TYPE_BASE_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_group_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, - tp_message_mixin_iface_init); + tp_message_mixin_text_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MESSAGES, + tp_message_mixin_messages_iface_init); ) /* signal enum */ @@ -122,7 +123,7 @@ static void salut_muc_channel_close (TpBaseChannel *base); static void update_tube_info (SalutMucChannel *self); static SalutTubeIface * create_new_tube (SalutMucChannel *self, - SalutTubeType type, + TpTubeType type, TpHandle initiator, const gchar *service, GHashTable *parameters, @@ -182,34 +183,6 @@ salut_muc_channel_set_property (GObject *object, } } -/* This is helper function with the signature of tp_group_mixin_change_members() - * from tp-glib 0.x */ -static void -change_members (GObject *obj, - const gchar *message, - const TpIntset *add, - const TpIntset *del, - const TpIntset *add_local_pending, - const TpIntset *add_remote_pending, - TpHandle actor, - TpChannelGroupChangeReason reason) -{ - GHashTable *details; - - details = tp_asv_new ( - "actor", G_TYPE_UINT, actor, - "change-reason", G_TYPE_UINT, reason, - NULL); - - if (message != NULL) - tp_asv_set_string (details, "message", message); - - tp_group_mixin_change_members (obj, - add, del, add_local_pending, add_remote_pending, details); - - g_hash_table_unref (details); -} - static void salut_muc_channel_add_self_to_members (SalutMucChannel *self) { @@ -233,7 +206,7 @@ salut_muc_channel_add_self_to_members (SalutMucChannel *self) add = tp_intset_new (); tp_intset_add (add, tp_base_connection_get_self_handle (base_conn)); - change_members (G_OBJECT (self), + tp_group_mixin_change_members (G_OBJECT (self), "", add, empty, empty, empty, tp_base_connection_get_self_handle (base_conn), TP_CHANNEL_GROUP_CHANGE_REASON_NONE); @@ -331,6 +304,7 @@ salut_muc_channel_constructed (GObject *obj) contact_repo, tp_base_connection_get_self_handle (base_conn)); tp_group_mixin_change_flags (obj, + TP_CHANNEL_GROUP_FLAG_PROPERTIES | TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD, 0); @@ -405,6 +379,12 @@ create_invitation (SalutMucChannel *self, priv->muc_connection), invitation_append_parameter, invite_node); +#ifdef ENABLE_OLPC + salut_self_olpc_augment_invitation (priv->self, + tp_base_channel_get_target_handle (base_chan), contact->handle, + invite_node); +#endif + return msg; } @@ -450,7 +430,7 @@ send_invite_cb (GObject *source_object, removed = tp_intset_new (); tp_intset_add (removed, handle); - change_members (G_OBJECT (data->self), "", empty, removed, empty, + tp_group_mixin_change_members (G_OBJECT (data->self), "", empty, removed, empty, empty, tp_base_connection_get_self_handle (base_connection), TP_CHANNEL_GROUP_CHANGE_REASON_ERROR); @@ -542,7 +522,7 @@ salut_muc_channel_add_member (GObject *iface, { /* We are considered as remote-pending while the muc connection * is not connected */ - change_members (G_OBJECT (self), + tp_group_mixin_change_members (G_OBJECT (self), message, empty_, empty_, empty_, add, tp_base_connection_get_self_handle (base_connection), TP_CHANNEL_GROUP_CHANGE_REASON_NONE); @@ -571,7 +551,7 @@ salut_muc_channel_add_member (GObject *iface, empty = tp_intset_new (); remote_pending = tp_intset_new (); tp_intset_add (remote_pending, handle); - change_members (G_OBJECT(self), "", empty, empty, empty, + tp_group_mixin_change_members (G_OBJECT(self), "", empty, empty, empty, remote_pending, tp_base_connection_get_self_handle (base_connection), TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); tp_intset_destroy (empty); @@ -638,10 +618,10 @@ salut_muc_channel_fill_immutable_properties (TpBaseChannel *chan, tp_dbus_properties_mixin_fill_properties_hash ( G_OBJECT (chan), properties, - TP_IFACE_CHANNEL_TYPE_TEXT, "MessagePartSupportFlags", - TP_IFACE_CHANNEL_TYPE_TEXT, "DeliveryReportingSupport", - TP_IFACE_CHANNEL_TYPE_TEXT, "SupportedContentTypes", - TP_IFACE_CHANNEL_TYPE_TEXT, "MessageTypes", + TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessagePartSupportFlags", + TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "DeliveryReportingSupport", + TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "SupportedContentTypes", + TP_IFACE_CHANNEL_INTERFACE_MESSAGES, "MessageTypes", NULL); } @@ -652,7 +632,7 @@ salut_muc_channel_get_interfaces (TpBaseChannel *chan) ->get_interfaces (chan); g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_GROUP); - + g_ptr_array_add (interfaces, TP_IFACE_CHANNEL_INTERFACE_MESSAGES); return interfaces; } @@ -848,9 +828,11 @@ salut_muc_channel_invited (SalutMucChannel *self, TpHandle inviter, g_assert (stanza != NULL); tp_intset_add (local_pending, self_handle); - change_members (G_OBJECT(self), stanza, - empty, empty, local_pending, empty, - inviter, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); + tp_group_mixin_change_members (G_OBJECT(self), stanza, + empty, empty, + local_pending, empty, + inviter, + TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); tp_intset_destroy (local_pending); tp_intset_destroy (empty); } @@ -907,8 +889,13 @@ salut_muc_channel_add_members (SalutMucChannel *self, tp_intset_add (changes, contact->handle); } - change_members (G_OBJECT(self), "", changes, empty, empty, empty, - 0, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); + tp_group_mixin_change_members (G_OBJECT(self), + "", + changes, + empty, + empty, empty, + 0, + TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (changes); tp_intset_destroy (empty); g_object_unref (contact_mgr); @@ -947,8 +934,13 @@ salut_muc_channel_remove_members (SalutMucChannel *self, tp_intset_add (changes, handle); } - change_members (G_OBJECT(self), "", empty, changes, empty, empty, - 0, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); + tp_group_mixin_change_members (G_OBJECT(self), + "", + empty, + changes, + empty, empty, + 0, + TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (changes); tp_intset_destroy (empty); } @@ -957,7 +949,7 @@ salut_muc_channel_remove_members (SalutMucChannel *self, static gboolean extract_tube_information (SalutMucChannel *self, WockyNode *tube_node, - SalutTubeType *type, + TpTubeType *type, TpHandle *initiator_handle, const gchar **service, GHashTable **parameters, @@ -977,11 +969,11 @@ extract_tube_information (SalutMucChannel *self, if (!tp_strdiff (_type, "stream")) { - *type = SALUT_TUBE_TYPE_STREAM; + *type = TP_TUBE_TYPE_STREAM; } else if (!tp_strdiff (_type, "dbus")) { - *type = SALUT_TUBE_TYPE_DBUS; + *type = TP_TUBE_TYPE_DBUS; } else { @@ -1096,13 +1088,13 @@ muc_channel_handle_tubes (SalutMucChannel *self, { guint tube_id = GPOINTER_TO_UINT (key); SalutTubeIface *tube = value; - SalutTubeType type; + TpTubeType type; g_object_get (tube, "type", &type, NULL); - if (type != SALUT_TUBE_TYPE_DBUS) + if (type != TP_TUBE_TYPE_DBUS) return; if (salut_tube_dbus_handle_in_names (SALUT_TUBE_DBUS (tube), @@ -1118,7 +1110,7 @@ muc_channel_handle_tubes (SalutMucChannel *self, const gchar *stream_id; SalutTubeIface *tube; guint tube_id; - SalutTubeType type; + TpTubeType type; GibberBytestreamIface *bytestream; stream_id = wocky_node_get_attribute (tube_node, "stream-id"); @@ -1142,7 +1134,7 @@ muc_channel_handle_tubes (SalutMucChannel *self, { switch (type) { - case SALUT_TUBE_TYPE_DBUS: + case TP_TUBE_TYPE_DBUS: { if (initiator_handle == 0) { @@ -1151,7 +1143,7 @@ muc_channel_handle_tubes (SalutMucChannel *self, } } break; - case SALUT_TUBE_TYPE_STREAM: + case TP_TUBE_TYPE_STREAM: initiator_handle = contact; break; default: @@ -1180,7 +1172,7 @@ muc_channel_handle_tubes (SalutMucChannel *self, "type", &type, NULL); - if (type == SALUT_TUBE_TYPE_DBUS + if (type == TP_TUBE_TYPE_DBUS && !salut_tube_dbus_handle_in_names (SALUT_TUBE_DBUS (tube), contact)) { @@ -1283,6 +1275,14 @@ salut_muc_channel_received_stanza (GibberMucConnection *conn, /* let's not autoclose now */ priv->autoclose = FALSE; +#ifdef ENABLE_OLPC + if (salut_connection_olpc_observe_muc_stanza ( + SALUT_CONNECTION (base_connection), + tp_base_channel_get_target_handle (base_chan), + from_handle, stanza)) + return; +#endif + tubes_node = wocky_node_get_child_ns (node, "tubes", WOCKY_TELEPATHY_NS_TUBES); if (tubes_node != NULL) @@ -1479,7 +1479,7 @@ publish_tube_in_node (SalutMucChannel *self, base_conn, TP_HANDLE_TYPE_CONTACT); WockyNode *parameters_node; GHashTable *parameters; - SalutTubeType type; + TpTubeType type; gchar *service, *id_str; guint64 tube_id; TpHandle initiator_handle; @@ -1501,7 +1501,7 @@ publish_tube_in_node (SalutMucChannel *self, switch (type) { - case SALUT_TUBE_TYPE_DBUS: + case TP_TUBE_TYPE_DBUS: { gchar *name, *stream_id; @@ -1523,7 +1523,7 @@ publish_tube_in_node (SalutMucChannel *self, } break; - case SALUT_TUBE_TYPE_STREAM: + case TP_TUBE_TYPE_STREAM: wocky_node_set_attribute (node, "type", "stream"); break; default: @@ -1577,7 +1577,7 @@ update_tube_info (SalutMucChannel *self) while (g_hash_table_iter_next (&iter, NULL, &value)) { TpTubeChannelState state; - SalutTubeType type; + TpTubeType type; TpHandle initiator; WockyNode *tube_node; @@ -1590,7 +1590,7 @@ update_tube_info (SalutMucChannel *self) if (state != TP_TUBE_CHANNEL_STATE_OPEN) continue; - if (type == SALUT_TUBE_TYPE_STREAM + if (type == TP_TUBE_TYPE_STREAM && initiator != TP_GROUP_MIXIN (self)->self_handle) /* We only announce stream tubes we initiated */ return; @@ -1674,7 +1674,7 @@ generate_tube_id (SalutMucChannel *self) static SalutTubeIface * create_new_tube (SalutMucChannel *self, - SalutTubeType type, + TpTubeType type, TpHandle initiator, const gchar *service, GHashTable *parameters, @@ -1693,12 +1693,12 @@ create_new_tube (SalutMucChannel *self, switch (type) { - case SALUT_TUBE_TYPE_DBUS: + case TP_TUBE_TYPE_DBUS: tube = SALUT_TUBE_IFACE (salut_tube_dbus_new (conn, handle, TP_HANDLE_TYPE_ROOM, self_handle, priv->muc_connection, initiator, service, parameters, tube_id, requested)); break; - case SALUT_TUBE_TYPE_STREAM: + case TP_TUBE_TYPE_STREAM: tube = SALUT_TUBE_IFACE (salut_tube_stream_new (conn, handle, TP_HANDLE_TYPE_ROOM, self_handle, initiator, FALSE, service, @@ -1729,7 +1729,7 @@ salut_muc_channel_tube_request (SalutMucChannel *self, const gchar *service; GHashTable *parameters = NULL; guint64 tube_id; - SalutTubeType type; + TpTubeType type; tube_id = generate_tube_id (self); @@ -1738,14 +1738,14 @@ salut_muc_channel_tube_request (SalutMucChannel *self, if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) { - type = SALUT_TUBE_TYPE_STREAM; + type = TP_TUBE_TYPE_STREAM; service = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); } else if (! tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) { - type = SALUT_TUBE_TYPE_DBUS; + type = TP_TUBE_TYPE_DBUS; service = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); } diff --git a/src/muc-manager.c b/src/muc-manager.c index db4978e1..41cd3438 100644 --- a/src/muc-manager.c +++ b/src/muc-manager.c @@ -360,12 +360,12 @@ salut_muc_manager_type_foreach_channel_class (GType type, g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType", handle_type_value); - /* im.telepathy1.Channel.Type.Text */ + /* org.freedesktop.Telepathy.Channel.Type.Text */ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_TEXT); func (type, table, muc_channel_allowed_properties, user_data); - /* im.telepathy1.Channel.Type.StreamTube */ + /* org.freedesktop.Telepathy.Channel.Type.StreamTube */ g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE); func (type, table, salut_tube_stream_channel_get_allowed_properties (), @@ -1108,6 +1108,11 @@ invite_stanza_callback (WockyPorter *porter, /* FIXME handle properly */ g_assert (chan != NULL); +#ifdef ENABLE_OLPC + salut_connection_olpc_observe_invitation (priv->connection, room_handle, + inviter_handle, invite); +#endif + salut_muc_channel_invited (chan, inviter_handle, reason, NULL); return TRUE; diff --git a/src/muc-tube-dbus.c b/src/muc-tube-dbus.c index 3054885e..5d639f40 100644 --- a/src/muc-tube-dbus.c +++ b/src/muc-tube-dbus.c @@ -21,8 +21,6 @@ #include "muc-tube-dbus.h" -#include - G_DEFINE_TYPE (SalutMucTubeDBus, salut_muc_tube_dbus, SALUT_TYPE_TUBE_DBUS) diff --git a/src/muc-tube-stream.c b/src/muc-tube-stream.c index f66a96fe..8155b9c2 100644 --- a/src/muc-tube-stream.c +++ b/src/muc-tube-stream.c @@ -21,8 +21,6 @@ #include "muc-tube-stream.h" -#include - G_DEFINE_TYPE (SalutMucTubeStream, salut_muc_tube_stream, SALUT_TYPE_TUBE_STREAM) diff --git a/src/namespaces.h b/src/namespaces.h index fdc1bdd4..dd634185 100644 --- a/src/namespaces.h +++ b/src/namespaces.h @@ -86,6 +86,12 @@ #define NS_MUC_OWNER "http://jabber.org/protocol/muc#owner" #define NS_NICK "http://jabber.org/protocol/nick" #define NS_OOB "jabber:iq:oob" +#define NS_OLPC_BUDDY_PROPS "http://laptop.org/xmpp/buddy-properties" +#define NS_OLPC_ACTIVITIES "http://laptop.org/xmpp/activities" +#define NS_OLPC_CURRENT_ACTIVITY "http://laptop.org/xmpp/current-activity" +#define NS_OLPC_ACTIVITY_PROPS "http://laptop.org/xmpp/activity-properties" +#define NS_OLPC_BUDDY "http://laptop.org/xmpp/buddy" +#define NS_OLPC_ACTIVITY "http://laptop.org/xmpp/activity" #define NS_PUBSUB "http://jabber.org/protocol/pubsub" #define NS_PRESENCE_INVISIBLE "presence-invisible" #define NS_PRIVACY "jabber:iq:privacy" @@ -109,6 +115,8 @@ #define NS_TEMPPRES "urn:xmpp:temppres:0" #define NS_GOOGLE_SHARED_STATUS "google:shared-status" +#define NS_OLPC_ACTIVITY_PROPS "http://laptop.org/xmpp/activity-properties" + #define NS_TP_FT_METADATA_SERVICE "http://telepathy.freedesktop.org/xmpp/file-transfer-service" #define NS_TP_FT_METADATA "http://telepathy.freedesktop.org/xmpp/file-transfer-metadata" diff --git a/src/olpc-activity-manager.c b/src/olpc-activity-manager.c new file mode 100644 index 00000000..459d4c92 --- /dev/null +++ b/src/olpc-activity-manager.c @@ -0,0 +1,349 @@ +/* + * olpc-activity-manager.c - Source for SalutOlpcActivityManager + * Copyright (C) 2008 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "olpc-activity-manager.h" + +#include +#include +#include +#include + +#include "connection.h" + +#define DEBUG_FLAG DEBUG_OLPC_ACTIVITY +#include "debug.h" + +G_DEFINE_TYPE (SalutOlpcActivityManager, salut_olpc_activity_manager, + G_TYPE_OBJECT); + +/* properties */ +enum { + PROP_CONNECTION = 1, + LAST_PROP +}; + +/* signal enum */ +enum +{ + ACTIVITY_MODIFIED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/* private structure */ +typedef struct _SalutOlpcActivityManagerPrivate SalutOlpcActivityManagerPrivate; + +struct _SalutOlpcActivityManagerPrivate +{ + /* TpHandle (owned by the activity) => SalutOlpcActivity */ + GHashTable *activities_by_room; + + gboolean dispose_has_run; +}; + +#define SALUT_OLPC_ACTIVITY_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SALUT_TYPE_OLPC_ACTIVITY_MANAGER, SalutOlpcActivityManagerPrivate)) + +static void +salut_olpc_activity_manager_init (SalutOlpcActivityManager *self) +{ + SalutOlpcActivityManagerPrivate *priv = + SALUT_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + /* We just keep a weak reference on the activity object so we'll remove + * it from the hash when no one is using anymore */ + priv->activities_by_room = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, NULL); +} + +static void +salut_olpc_activity_manager_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SalutOlpcActivityManager *self = SALUT_OLPC_ACTIVITY_MANAGER (object); + + switch (property_id) + { + case PROP_CONNECTION: + g_value_set_object (value, self->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +salut_olpc_activity_manager_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + SalutOlpcActivityManager *self = SALUT_OLPC_ACTIVITY_MANAGER (object); + + switch (property_id) + { + case PROP_CONNECTION: + self->connection = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void salut_olpc_activity_manager_dispose (GObject *object); +static void salut_olpc_activity_manager_finalize (GObject *object); + +static void +salut_olpc_activity_manager_class_init (SalutOlpcActivityManagerClass *salut_olpc_activity_manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (salut_olpc_activity_manager_class); + GParamSpec *param_spec; + + g_type_class_add_private (salut_olpc_activity_manager_class, + sizeof (SalutOlpcActivityManagerPrivate)); + + object_class->get_property = salut_olpc_activity_manager_get_property; + object_class->set_property = salut_olpc_activity_manager_set_property; + + object_class->dispose = salut_olpc_activity_manager_dispose; + object_class->finalize = salut_olpc_activity_manager_finalize; + + param_spec = g_param_spec_object ( + "connection", + "SalutConnection object", + "The Salut Connection associated with this muc manager", + SALUT_TYPE_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CONNECTION, + param_spec); + + signals[ACTIVITY_MODIFIED] = g_signal_new ("activity-modified", + G_OBJECT_CLASS_TYPE (salut_olpc_activity_manager_class), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, SALUT_TYPE_OLPC_ACTIVITY); +} + +static gboolean +remove_activity_foreach (gpointer room, + gpointer act, + gpointer activity) +{ + return act == activity; +} + +static void +activity_finalized_cb (gpointer data, + GObject *activity) +{ + SalutOlpcActivityManager *self = SALUT_OLPC_ACTIVITY_MANAGER (data); + SalutOlpcActivityManagerPrivate *priv = + SALUT_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + g_hash_table_foreach_remove (priv->activities_by_room, + remove_activity_foreach, activity); +} + +static gboolean +dispose_activity_foreach (gpointer room, + gpointer activity, + gpointer user_data) +{ + SalutOlpcActivityManager *self = SALUT_OLPC_ACTIVITY_MANAGER (user_data); + + g_object_weak_unref (G_OBJECT (activity), activity_finalized_cb, self); + g_signal_handlers_disconnect_matched (activity, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, self); + + return TRUE; +} + +static void +salut_olpc_activity_manager_dispose (GObject *object) +{ + SalutOlpcActivityManager *self = SALUT_OLPC_ACTIVITY_MANAGER (object); + SalutOlpcActivityManagerPrivate *priv = SALUT_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + if (priv->activities_by_room != NULL) + { + g_hash_table_foreach_remove (priv->activities_by_room, + dispose_activity_foreach, self); + g_hash_table_unref (priv->activities_by_room); + priv->activities_by_room = NULL; + } + + if (G_OBJECT_CLASS (salut_olpc_activity_manager_parent_class)->dispose) + G_OBJECT_CLASS (salut_olpc_activity_manager_parent_class)->dispose (object); +} + +static void +salut_olpc_activity_manager_finalize (GObject *object) +{ + //SalutOlpcActivityManager *self = SALUT_OLPC_ACTIVITY_MANAGER (object); + + G_OBJECT_CLASS (salut_olpc_activity_manager_parent_class)->finalize (object); +} + +gboolean +salut_olpc_activity_manager_start (SalutOlpcActivityManager *self, + GError **error) +{ + return SALUT_OLPC_ACTIVITY_MANAGER_GET_CLASS (self)->start (self, error); +} + +SalutOlpcActivity * +salut_olpc_activity_manager_get_activity_by_room (SalutOlpcActivityManager *self, + TpHandle room) +{ + SalutOlpcActivityManagerPrivate *priv = + SALUT_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + return g_hash_table_lookup (priv->activities_by_room, + GUINT_TO_POINTER (room)); +} + +SalutOlpcActivity * +salut_olpc_activity_manager_get_activity_by_id (SalutOlpcActivityManager *self, + const gchar *activity_id) +{ + SalutOlpcActivityManagerPrivate *priv = + SALUT_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, priv->activities_by_room); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + SalutOlpcActivity *activity = value; + if (strcmp (activity->id, activity_id) == 0) + return activity; + } + + return NULL; +} + +SalutOlpcActivity * +salut_olpc_activity_manager_ensure_activity_by_room ( + SalutOlpcActivityManager *self, + TpHandle room) +{ + SalutOlpcActivity *activity; + SalutOlpcActivityManagerPrivate *priv = + SALUT_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + activity = g_hash_table_lookup (priv->activities_by_room, + GUINT_TO_POINTER (room)); + + if (activity != NULL) + { + return g_object_ref (activity); + } + else + { + activity = salut_olpc_activity_manager_create_activity (self, room); + return activity; + } +} + +static void +activity_modified_cb (SalutOlpcActivity *activity, + SalutOlpcActivityManager *self) +{ + g_signal_emit (self, signals[ACTIVITY_MODIFIED], 0, activity); +} + +SalutOlpcActivity * +salut_olpc_activity_manager_create_activity (SalutOlpcActivityManager *self, + TpHandle room) +{ + SalutOlpcActivity *activity; + SalutOlpcActivityManagerPrivate *priv = + SALUT_OLPC_ACTIVITY_MANAGER_GET_PRIVATE (self); + + g_assert (room != 0); + g_assert (g_hash_table_lookup (priv->activities_by_room, GUINT_TO_POINTER ( + room)) == NULL); + + activity = SALUT_OLPC_ACTIVITY_MANAGER_GET_CLASS (self)->create_activity ( + self); + salut_olpc_activity_update (activity, room, NULL, NULL, NULL, NULL, NULL, + TRUE); + + g_hash_table_insert (priv->activities_by_room, GUINT_TO_POINTER (room), + activity); + + g_signal_connect (activity, "modified", G_CALLBACK (activity_modified_cb), + self); + g_object_weak_ref (G_OBJECT (activity), activity_finalized_cb , self); + + return activity; +} + +SalutOlpcActivity * +salut_olpc_activity_manager_got_invitation (SalutOlpcActivityManager *self, + TpHandle room, + SalutContact *inviter, + const gchar *id, + const gchar *name, + const gchar *type, + const gchar *color, + const gchar *tags) +{ + SalutOlpcActivity *activity; + + activity = salut_olpc_activity_manager_ensure_activity_by_room (self, room); + + salut_olpc_activity_update (activity, room, id, name, type, color, tags, + activity->is_private); + + /* FIXME: we shouldn't add it if the local user is already in the activity + * as, for now, we don't manage private activity membership (it's PS job) */ + + /* add the inviter to the activity */ + salut_contact_joined_activity (inviter, activity); + + /* contact reffed the activity if it didn't hold a ref on it yet */ + g_object_unref (activity); + + return activity; +} + +void +salut_olpc_activity_manager_contact_joined (SalutOlpcActivityManager *self, + SalutContact *contact, + SalutOlpcActivity *activity) +{ + salut_contact_joined_activity (contact, activity); +} + +void +salut_olpc_activity_manager_contact_left (SalutOlpcActivityManager *mgr, + SalutContact *contact, + SalutOlpcActivity *activity) +{ + salut_contact_left_activity (contact, activity); +} diff --git a/src/olpc-activity-manager.h b/src/olpc-activity-manager.h new file mode 100644 index 00000000..1814d1cf --- /dev/null +++ b/src/olpc-activity-manager.h @@ -0,0 +1,98 @@ +/* + * olpc-activity-managere.h - Header for SalutOlpcActivityManager + * Copyright (C) 2008 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SALUT_OLPC_ACTIVITY_MANAGER_H__ +#define __SALUT_OLPC_ACTIVITY_MANAGER_H__ + +#include + +#include + +#include "connection.h" +#include "contact.h" +#include "olpc-activity.h" + +G_BEGIN_DECLS + +typedef struct _SalutOlpcActivityManager SalutOlpcActivityManager; +typedef struct _SalutOlpcActivityManagerClass SalutOlpcActivityManagerClass; + +struct _SalutOlpcActivityManagerClass { + GObjectClass parent_class; + + /* public abstract methods */ + gboolean (*start) (SalutOlpcActivityManager *self, GError **error); + + /* private abstract methods */ + SalutOlpcActivity * (*create_activity) (SalutOlpcActivityManager *self); +}; + +struct _SalutOlpcActivityManager { + GObject parent; + + /* private */ + SalutConnection *connection; +}; + +GType salut_olpc_activity_manager_get_type (void); + +/* TYPE MACROS */ +#define SALUT_TYPE_OLPC_ACTIVITY_MANAGER \ + (salut_olpc_activity_manager_get_type ()) +#define SALUT_OLPC_ACTIVITY_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), SALUT_TYPE_OLPC_ACTIVITY_MANAGER, SalutOlpcActivityManager)) +#define SALUT_OLPC_ACTIVITY_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SALUT_TYPE_OLPC_ACTIVITY_MANAGER, SalutOlpcActivityManagerClass)) +#define SALUT_IS_OLPC_ACTIVITY_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), SALUT_TYPE_OLPC_ACTIVITY_MANAGER)) +#define SALUT_IS_OLPC_ACTIVITY_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SALUT_TYPE_OLPC_ACTIVITY_MANAGER)) +#define SALUT_OLPC_ACTIVITY_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), SALUT_TYPE_OLPC_ACTIVITY_MANAGER, SalutOlpcActivityManagerClass)) + +gboolean salut_olpc_activity_manager_start (SalutOlpcActivityManager *mgr, + GError **error); + +SalutOlpcActivity * salut_olpc_activity_manager_get_activity_by_room ( + SalutOlpcActivityManager *mgr, TpHandle room); + +SalutOlpcActivity * salut_olpc_activity_manager_get_activity_by_id ( + SalutOlpcActivityManager *mgr, const gchar *activity_id); + +SalutOlpcActivity * salut_olpc_activity_manager_ensure_activity_by_room ( + SalutOlpcActivityManager *mgr, TpHandle room); + +SalutOlpcActivity * salut_olpc_activity_manager_got_invitation ( + SalutOlpcActivityManager *mgr, TpHandle room, SalutContact *inviter, + const gchar *id, const gchar *name, const gchar *type, const gchar *color, + const gchar *tags); + +/* restricted methods */ +SalutOlpcActivity * salut_olpc_activity_manager_create_activity ( + SalutOlpcActivityManager *mgr, TpHandle room); + +void salut_olpc_activity_manager_contact_joined (SalutOlpcActivityManager *mgr, + SalutContact *contact, SalutOlpcActivity *activity); + +void salut_olpc_activity_manager_contact_left (SalutOlpcActivityManager *mgr, + SalutContact *contact, SalutOlpcActivity *activity); + +G_END_DECLS + +#endif /* #ifndef __SALUT_OLPC_ACTIVITY_MANAGER_H__*/ diff --git a/src/olpc-activity.c b/src/olpc-activity.c new file mode 100644 index 00000000..b834d56f --- /dev/null +++ b/src/olpc-activity.c @@ -0,0 +1,680 @@ +/* + * olpc-activity.c - Source for SalutOlpcActivity + * Copyright (C) 2008 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "olpc-activity.h" + +#include +#include +#include +#include + +#include + +#include "contact-manager.h" +#include "muc-manager.h" +#include "util.h" +#include "namespaces.h" + +#define DEBUG_FLAG DEBUG_OLPC_ACTIVITY +#include "debug.h" + +G_DEFINE_TYPE (SalutOlpcActivity, salut_olpc_activity, G_TYPE_OBJECT); + +/* properties */ +enum { + PROP_CONNECTION = 1, + LAST_PROP +}; + +/* signal enum */ +enum +{ + MODIFIED, + VALID, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/* private structure */ +typedef struct _SalutOlpcActivityPrivate SalutOlpcActivityPrivate; + +struct _SalutOlpcActivityPrivate +{ + /* Handles of contacts we invited to join this activity */ + TpHandleSet *invited; + /* can be NULL if we are not in the activity */ + SalutMucChannel *muc; + + gboolean dispose_has_run; +}; + +#define SALUT_OLPC_ACTIVITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SALUT_TYPE_OLPC_ACTIVITY, SalutOlpcActivityPrivate)) + +static void +salut_olpc_activity_init (SalutOlpcActivity *self) +{ + self->connection = NULL; + + self->is_private = TRUE; +} + +static void +salut_olpc_activity_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + SalutOlpcActivity *self = SALUT_OLPC_ACTIVITY (object); + + switch (property_id) + { + case PROP_CONNECTION: + g_value_set_object (value, self->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +salut_olpc_activity_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + SalutOlpcActivity *self = SALUT_OLPC_ACTIVITY (object); + + switch (property_id) + { + case PROP_CONNECTION: + self->connection = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static GObject * +salut_olpc_activity_constructor (GType type, + guint n_props, + GObjectConstructParam *props) +{ + GObject *obj; + SalutOlpcActivity *self; + SalutOlpcActivityPrivate *priv; + TpHandleRepoIface *contact_repo; + + obj = G_OBJECT_CLASS (salut_olpc_activity_parent_class)-> + constructor (type, n_props, props); + + self = SALUT_OLPC_ACTIVITY (obj); + priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + + g_assert (self->connection != NULL); + contact_repo = tp_base_connection_get_handles ( + (TpBaseConnection *) self->connection, TP_HANDLE_TYPE_CONTACT); + + priv->invited = tp_handle_set_new (contact_repo); + + return obj; +} + +static void salut_olpc_activity_dispose (GObject *object); +static void salut_olpc_activity_finalize (GObject *object); + +static void +salut_olpc_activity_class_init (SalutOlpcActivityClass *salut_olpc_activity_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (salut_olpc_activity_class); + GParamSpec *param_spec; + + g_type_class_add_private (salut_olpc_activity_class, + sizeof (SalutOlpcActivityPrivate)); + + object_class->get_property = salut_olpc_activity_get_property; + object_class->set_property = salut_olpc_activity_set_property; + + object_class->constructor = salut_olpc_activity_constructor; + object_class->dispose = salut_olpc_activity_dispose; + object_class->finalize = salut_olpc_activity_finalize; + + param_spec = g_param_spec_object ( + "connection", + "SalutConnection object", + "The Salut Connection associated with this muc manager", + SALUT_TYPE_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_CONNECTION, + param_spec); + + signals[MODIFIED] = g_signal_new ("modified", + G_OBJECT_CLASS_TYPE (salut_olpc_activity_class), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + signals[VALID] = g_signal_new ("valid", + G_OBJECT_CLASS_TYPE (salut_olpc_activity_class), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +static void +salut_olpc_activity_dispose (GObject *object) +{ + SalutOlpcActivity *self = SALUT_OLPC_ACTIVITY (object); + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + if (priv->muc != NULL) + { + g_signal_handlers_disconnect_matched (priv->muc, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, self); + g_object_unref (priv->muc); + priv->muc = NULL; + } + + tp_handle_set_destroy (priv->invited); + + if (G_OBJECT_CLASS (salut_olpc_activity_parent_class)->dispose) + G_OBJECT_CLASS (salut_olpc_activity_parent_class)->dispose (object); +} + +static void +salut_olpc_activity_finalize (GObject *object) +{ + SalutOlpcActivity *self = SALUT_OLPC_ACTIVITY (object); + + g_free (self->id); + g_free (self->name); + g_free (self->type); + g_free (self->color); + g_free (self->tags); + + G_OBJECT_CLASS (salut_olpc_activity_parent_class)->finalize (object); +} + +GHashTable * +salut_olpc_activity_create_properties_table (SalutOlpcActivity *self) +{ + GHashTable *properties; + GValue *val; + + properties = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify) tp_g_value_slice_free); + + if (self->color != NULL) + { + val = tp_g_value_slice_new (G_TYPE_STRING); + g_value_set_static_string (val, self->color); + g_hash_table_insert (properties, "color", val); + } + + if (self->name != NULL) + { + val = tp_g_value_slice_new (G_TYPE_STRING); + g_value_set_static_string (val, self->name); + g_hash_table_insert (properties, "name", val); + } + + if (self->type != NULL) + { + val = tp_g_value_slice_new (G_TYPE_STRING); + g_value_set_static_string (val, self->type); + g_hash_table_insert (properties, "type", val); + } + + if (self->tags != NULL) + { + val = tp_g_value_slice_new (G_TYPE_STRING); + g_value_set_static_string (val, self->tags); + g_hash_table_insert (properties, "tags", val); + } + + val = tp_g_value_slice_new (G_TYPE_BOOLEAN); + g_value_set_boolean (val, self->is_private); + g_hash_table_insert (properties, "private", val); + + return properties; +} + +static gboolean +send_properties_change_msg (SalutOlpcActivity *self, + GError **error) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + GHashTable *properties; + GValue *activity_id_val; + WockyStanza *stanza; + WockyNode *top_node; + WockyNode *properties_node; + gchar *muc_name; + GibberMucConnection *muc_connection; + gboolean result; + GError *err = NULL; + + if (priv->muc == NULL) + /* we are not in the muc */ + return TRUE; + + g_object_get (priv->muc, + "name", &muc_name, + "muc-connection", &muc_connection, + NULL); + + if (muc_connection->state != GIBBER_MUC_CONNECTION_CONNECTED) + { + DEBUG ("Muc connection not connected yet. Drop activity change message"); + g_object_unref (muc_connection); + g_free (muc_name); + return TRUE; + } + + properties = salut_olpc_activity_create_properties_table (self); + + /* add the activity id */ + activity_id_val = g_slice_new0 (GValue); + g_value_init (activity_id_val, G_TYPE_STRING); + g_value_set_static_string (activity_id_val, self->id); + g_hash_table_insert (properties, "id", activity_id_val); + + stanza = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, + WOCKY_STANZA_SUB_TYPE_GROUPCHAT, + self->connection->name, muc_name, + WOCKY_NODE_START, "properties", + WOCKY_NODE_XMLNS, NS_OLPC_ACTIVITY_PROPS, + WOCKY_NODE_END, NULL); + top_node = wocky_stanza_get_top_node (stanza); + + properties_node = wocky_node_get_child_ns (top_node, "properties", + NS_OLPC_ACTIVITY_PROPS); + + salut_wocky_node_add_children_from_properties (properties_node, + properties, "property"); + + result = gibber_muc_connection_send (muc_connection, stanza, &err); + if (!result) + { + g_set_error (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "%s", + err->message); + g_error_free (err); + } + + g_object_unref (stanza); + g_object_unref (muc_connection); + g_free (muc_name); + g_hash_table_unref (properties); + + return result; +} + +static void +resend_invite_foreach (TpHandleSet *set, + TpHandle handle, + SalutOlpcActivity *self) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + GError *error = NULL; + + if (!salut_muc_channel_send_invitation (priv->muc, handle, + "OLPC activity properties update", &error)) + { + DEBUG ("failed to re-invite contact %d to activity %s", handle, + self->id); + } +} + +static void +resend_invite (SalutOlpcActivity *self) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + + if (priv->muc == NULL) + /* we are not in the muc */ + return; + + /* Resend pending invitations so contacts will know about new properties */ + tp_handle_set_foreach (priv->invited, + (TpHandleSetMemberFunc) resend_invite_foreach, self); +} + +static void +activity_changed (SalutOlpcActivity *self) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + GError *error = NULL; + + if (!send_properties_change_msg (self, &error)) + { + DEBUG ("send properties changes msg failed: %s", error->message); + g_error_free (error); + error = NULL; + } + + if (!self->is_private && priv->muc != NULL) + { + /* update announcement */ + if (!SALUT_OLPC_ACTIVITY_GET_CLASS (self)->update (self, &error)) + { + DEBUG ("update activity failed: %s", error->message); + g_error_free (error); + } + } + else + { + resend_invite (self); + } +} + +static gboolean +salut_olpc_activity_announce (SalutOlpcActivity *self, + GError **error) +{ + GError *err = NULL; + + if (!SALUT_OLPC_ACTIVITY_GET_CLASS (self)->announce (self, &err)) + { + g_set_error (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "%s", + err->message); + g_error_free (err); + return FALSE; + } + + return TRUE; +} + +static void +salut_olpc_activity_stop_announce (SalutOlpcActivity *self) +{ + SALUT_OLPC_ACTIVITY_GET_CLASS (self)->stop_announce (self); +} + +gboolean +salut_olpc_activity_update (SalutOlpcActivity *self, + TpHandle room, + const gchar *id, + const gchar *name, + const gchar *type, + const gchar *color, + const gchar *tags, + gboolean is_private) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + gboolean changed = FALSE; + GError *error = NULL; + gboolean become_valid = FALSE; + + if (room != 0 && room != self->room) + { + self->room = room; + } + + if (id != NULL && tp_strdiff (id, self->id)) + { + if (self->id == NULL) + become_valid = TRUE; + + g_free (self->id); + self->id = g_strdup (id); + changed = TRUE; + } + + if (name != NULL && tp_strdiff (name, self->name)) + { + g_free (self->name); + self->name = g_strdup (name); + changed = TRUE; + } + + if (type != NULL && tp_strdiff (type, self->type)) + { + g_free (self->type); + self->type = g_strdup (type); + changed = TRUE; + } + + if (color != NULL && tp_strdiff (color, self->color)) + { + g_free (self->color); + self->color = g_strdup (color); + changed = TRUE; + } + + if (tp_strdiff (tags, self->tags)) + { + g_free (self->tags); + self->tags = g_strdup (tags); + changed = TRUE; + } + + if (is_private != self->is_private) + { + self->is_private = is_private; + changed = TRUE; + + if (priv->muc != NULL) + { + if (is_private) + { + DEBUG ("activity is not public anymore. Stop to announce it"); + salut_olpc_activity_stop_announce (self); + } + else + { + DEBUG ("activity becomes public. Announce it"); + if (!salut_olpc_activity_announce (self, &error)) + { + DEBUG ("activity announce failed: %s", error->message); + g_error_free (error); + error = NULL; + } + } + } + } + + if (become_valid) + { + g_signal_emit (self, signals[VALID], 0); + } + + if (changed) + { + activity_changed (self); + + g_signal_emit (self, signals[MODIFIED], 0); + } + + return changed; +} + +static void +muc_channel_closed_cb (SalutMucChannel *chan, + SalutOlpcActivity *self) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + + g_object_unref (priv->muc); + priv->muc = NULL; +} + +gboolean +salut_olpc_activity_joined (SalutOlpcActivity *self, + GError **error) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + TpHandleRepoIface *room_repo; + SalutMucManager *muc_manager; + + if (priv->muc != NULL) + return TRUE; + + room_repo = tp_base_connection_get_handles ( + (TpBaseConnection *) self->connection, TP_HANDLE_TYPE_ROOM); + + g_object_get (self->connection, + "muc-manager", &muc_manager, + NULL); + + priv->muc = salut_muc_manager_get_text_channel (muc_manager, self->room); + g_object_unref (muc_manager); + + if (priv->muc == NULL) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Can't find muc channel for room %s", tp_handle_inspect ( + room_repo, self->room)); + return FALSE; + } + + if (!self->is_private) + { + /* This might fail but that doesn't prevent us from joining the + * activity.. */ + salut_olpc_activity_announce (self, NULL); + } + + g_signal_connect (priv->muc, "closed", G_CALLBACK (muc_channel_closed_cb), + self); + + return TRUE; +} + +void +salut_olpc_activity_left (SalutOlpcActivity *self) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + + if (priv->muc == NULL) + return; + + if (!self->is_private) + salut_olpc_activity_stop_announce (self); + + g_object_unref (priv->muc); + g_signal_handlers_disconnect_matched (priv->muc, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, self); + priv->muc = NULL; +} + +void +salut_olpc_activity_revoke_invitations (SalutOlpcActivity *self) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + WockyStanza *msg; + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( + (TpBaseConnection *) self->connection, TP_HANDLE_TYPE_CONTACT); + TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( + (TpBaseConnection *) self->connection, TP_HANDLE_TYPE_CONTACT); + TpIntsetFastIter iter; + guint contact_handle; + SalutContactManager *contact_mgr; + WockyNode *top_node; + + if (tp_handle_set_size (priv->invited) <= 0) + return; + + msg = wocky_stanza_build (WOCKY_STANZA_TYPE_MESSAGE, + WOCKY_STANZA_SUB_TYPE_NONE, + self->connection->name, NULL, + WOCKY_NODE_START, "uninvite", + WOCKY_NODE_XMLNS, NS_OLPC_ACTIVITY_PROPS, + WOCKY_NODE_ATTRIBUTE, "room", tp_handle_inspect (room_repo, + self->room), + WOCKY_NODE_ATTRIBUTE, "id", self->id, + WOCKY_NODE_END, NULL); + top_node = wocky_stanza_get_top_node (msg); + + g_object_get (self->connection, + "contact-manager", &contact_mgr, + NULL); + g_assert (contact_mgr != NULL); + + tp_intset_fast_iter_init (&iter, tp_handle_set_peek (priv->invited)); + + DEBUG ("revoke invitations for activity %s", self->id); + while (tp_intset_fast_iter_next (&iter, &contact_handle)) + { + SalutContact *contact; + const gchar *to; + + contact = salut_contact_manager_get_contact (contact_mgr, contact_handle); + if (contact == NULL) + { + DEBUG ("Can't find contact %d", contact_handle); + continue; + } + + to = tp_handle_inspect (contact_repo, contact_handle); + wocky_node_set_attribute (top_node, "to", to); + + wocky_stanza_set_to_contact (msg, WOCKY_CONTACT (contact)); + wocky_porter_send (self->connection->porter, msg); + + g_object_unref (contact); + } + + g_object_unref (msg); + g_object_unref (contact_mgr); +} + +void +salut_olpc_activity_augment_invitation (SalutOlpcActivity *self, + TpHandle contact, + WockyNode *invite_node) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + WockyNode *properties_node; + GHashTable *properties; + GValue *activity_id_val; + + properties = salut_olpc_activity_create_properties_table (self); + + properties_node = wocky_node_add_child_ns (invite_node, "properties", + NS_OLPC_ACTIVITY_PROPS); + + /* add the activity id */ + activity_id_val = g_slice_new0 (GValue); + g_value_init (activity_id_val, G_TYPE_STRING); + g_value_set_static_string (activity_id_val, self->id); + g_hash_table_insert (properties, "id", activity_id_val); + + salut_wocky_node_add_children_from_properties (properties_node, + properties, "property"); + + tp_handle_set_add (priv->invited, contact); + + g_hash_table_unref (properties); +} + +gboolean +salut_olpc_activity_remove_invited (SalutOlpcActivity *self, + TpHandle contact) +{ + SalutOlpcActivityPrivate *priv = SALUT_OLPC_ACTIVITY_GET_PRIVATE (self); + + return tp_handle_set_remove (priv->invited, contact); +} diff --git a/src/olpc-activity.h b/src/olpc-activity.h new file mode 100644 index 00000000..25e231af --- /dev/null +++ b/src/olpc-activity.h @@ -0,0 +1,98 @@ +/* + * olpc-activity.h - Header for SalutOlpcActivity + * Copyright (C) 2008 Collabora Ltd. + * + * 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.1 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SALUT_OLPC_ACTIVITY_H__ +#define __SALUT_OLPC_ACTIVITY_H__ + +#include + +#include + +#include "connection.h" + +G_BEGIN_DECLS + +typedef struct _SalutOlpcActivity SalutOlpcActivity; +typedef struct _SalutOlpcActivityClass SalutOlpcActivityClass; + +struct _SalutOlpcActivityClass { + GObjectClass parent_class; + + /* private abstract methods */ + gboolean (*announce) (SalutOlpcActivity *activity, GError **error); + void (*stop_announce) (SalutOlpcActivity *activity); + + gboolean (*update) (SalutOlpcActivity *activity, GError **error); +}; + +struct _SalutOlpcActivity { + GObject parent; + + TpHandle room; + gchar *id; + gchar *name; + gchar *type; + gchar *color; + gchar *tags; + gboolean is_private; + + /* private */ + SalutConnection *connection; +}; + +GType salut_olpc_activity_get_type (void); + +/* TYPE MACROS */ +#define SALUT_TYPE_OLPC_ACTIVITY \ + (salut_olpc_activity_get_type ()) +#define SALUT_OLPC_ACTIVITY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), SALUT_TYPE_OLPC_ACTIVITY, SalutOlpcActivity)) +#define SALUT_OLPC_ACTIVITY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), SALUT_TYPE_OLPC_ACTIVITY, SalutOlpcActivityClass)) +#define SALUT_IS_OLPC_ACTIVITY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), SALUT_TYPE_OLPC_ACTIVITY)) +#define SALUT_IS_OLPC_ACTIVITY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), SALUT_TYPE_OLPC_ACTIVITY)) +#define SALUT_OLPC_ACTIVITY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), SALUT_TYPE_OLPC_ACTIVITY, SalutOlpcActivityClass)) + +G_END_DECLS + +gboolean salut_olpc_activity_update (SalutOlpcActivity *activity, + TpHandle room, const gchar *id, const gchar *name, + const gchar *type, const gchar *color, const gchar *tags, + gboolean is_private); + +gboolean salut_olpc_activity_joined (SalutOlpcActivity *activity, + GError **error); + +void salut_olpc_activity_left (SalutOlpcActivity *activity); + +void salut_olpc_activity_revoke_invitations (SalutOlpcActivity *activity); + +GHashTable * salut_olpc_activity_create_properties_table ( + SalutOlpcActivity *activity); + +void salut_olpc_activity_augment_invitation (SalutOlpcActivity *activity, + TpHandle contact, WockyNode *invite_node); + +gboolean salut_olpc_activity_remove_invited (SalutOlpcActivity *activity, + TpHandle contact); + +#endif /* #ifndef __SALUT_OLPC_ACTIVITY_H__*/ diff --git a/src/presence.h b/src/presence.h index 466af9b4..993fdf8a 100644 --- a/src/presence.h +++ b/src/presence.h @@ -27,6 +27,7 @@ G_BEGIN_DECLS #define SALUT_DNSSD_CLIQUE "_clique._udp" +#define SALUT_DNSSD_OLPC_ACTIVITY "_olpc-activity1._udp" #define SALUT_DNSSD_PRESENCE "_presence._tcp" /* private structure */ diff --git a/src/protocol.c b/src/protocol.c index 1223734e..2b805c85 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -22,7 +22,7 @@ #include #include -#include +#include #include "connection.h" #include "contact-manager.h" @@ -156,6 +156,7 @@ get_connection_details (TpBaseProtocol *self, if (channel_managers != NULL) { GType types[] = { + SALUT_TYPE_CONTACT_MANAGER, SALUT_TYPE_FT_MANAGER, SALUT_TYPE_IM_MANAGER, SALUT_TYPE_MUC_MANAGER, diff --git a/src/protocol.h b/src/protocol.h index 9b99d538..8e3b4d5b 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -63,9 +63,9 @@ GType salut_protocol_get_type (void); SALUT_TYPE_PROTOCOL, \ SalutProtocolClass)) -#define SALUT_PROTOCOL_LOCAL_XMPP_NAME "local_xmpp" +#define SALUT_PROTOCOL_LOCAL_XMPP_NAME "local-xmpp" #define SALUT_PROTOCOL_LOCAL_XMPP_ENGLISH_NAME "Link-local XMPP" -#define SALUT_PROTOCOL_LOCAL_XMPP_ICON_NAME "im-local-xmpp" +#define SALUT_PROTOCOL_LOCAL_XMPP_ICON_NAME "im-" SALUT_PROTOCOL_LOCAL_XMPP_NAME /** * salut_protocol_new: diff --git a/src/roomlist-channel.c b/src/roomlist-channel.c index c09a643c..bdaa5c99 100644 --- a/src/roomlist-channel.c +++ b/src/roomlist-channel.c @@ -247,7 +247,7 @@ salut_roomlist_channel_add_room (SalutRoomlistChannel *self, dbus_g_type_specialized_construct (TP_STRUCT_TYPE_ROOM_INFO)); dbus_g_type_struct_set (&room, 0, handle, - 1, "im.telepathy1.Channel.Type.Text", + 1, "org.freedesktop.Telepathy.Channel.Type.Text", 2, keys, G_MAXUINT); g_ptr_array_add (priv->rooms, g_value_get_boxed (&room)); @@ -299,7 +299,7 @@ salut_roomlist_channel_remove_room (SalutRoomlistChannel *self, * salut_roomlist_channel_get_listing_rooms * * Implements D-Bus method GetListingRooms - * on interface im.telepathy1.Channel.Type.RoomList + * on interface org.freedesktop.Telepathy.Channel.Type.RoomList * * @error: Used to return a pointer to a GError detailing any error * that occurred, D-Bus will throw the error only if this @@ -324,7 +324,7 @@ salut_roomlist_channel_get_listing_rooms (TpSvcChannelTypeRoomList *iface, * salut_roomlist_channel_list_rooms * * Implements D-Bus method ListRooms - * on interface im.telepathy1.Channel.Type.RoomList + * on interface org.freedesktop.Telepathy.Channel.Type.RoomList * * @error: Used to return a pointer to a GError detailing any error * that occurred, D-Bus will throw the error only if this @@ -350,7 +350,7 @@ salut_roomlist_channel_list_rooms (TpSvcChannelTypeRoomList *iface, * salut_roomlist_channel_stop_listing * * Implements D-Bus method StopListing - * on interface im.telepathy1.Channel.Type.RoomList + * on interface org.freedesktop.Telepathy.Channel.Type.RoomList */ static void salut_roomlist_channel_stop_listing (TpSvcChannelTypeRoomList *iface, diff --git a/src/self.c b/src/self.c index 775d4cc5..aeafa9bf 100644 --- a/src/self.c +++ b/src/self.c @@ -44,6 +44,11 @@ #include "util.h" #include "muc-manager.h" +#ifdef ENABLE_OLPC +#include "olpc-activity.h" +#include "olpc-activity-manager.h" +#endif + #define DEBUG_FLAG DEBUG_SELF #include @@ -64,6 +69,10 @@ enum PROP_JID, PROP_EMAIL, PROP_PUBLISHED_NAME, +#ifdef ENABLE_OLPC + PROP_OLPC_KEY, + PROP_OLPC_COLOR +#endif }; /* signal enum */ @@ -82,16 +91,30 @@ struct _SalutSelfPrivate { SalutContactManager *contact_manager; TpHandleRepoIface *room_repo; +#ifdef ENABLE_OLPC + SalutOlpcActivityManager *olpc_activity_manager; +#endif GIOChannel *listener; guint io_watch_in; +#ifdef ENABLE_OLPC + /* room handle owned by the SalutOlpcActivity -> SalutOlpcActivity */ + GHashTable *olpc_activities; +#endif + GabbleCapabilitySet *caps; GPtrArray *data_forms; gboolean dispose_has_run; }; +#ifdef ENABLE_OLPC +void +contact_manager_contact_change_cb (SalutContactManager *mgr, + SalutContact *contact, int changes, gpointer user_data); +#endif + static void salut_self_init (SalutSelf *obj) { @@ -104,12 +127,22 @@ salut_self_init (SalutSelf *obj) obj->status = SALUT_PRESENCE_AVAILABLE; obj->status_message = NULL; obj->jid = NULL; +#ifdef ENABLE_OLPC + obj->olpc_key = NULL; + obj->olpc_color = NULL; + obj->olpc_cur_act = NULL; + obj->olpc_cur_act_room = 0; +#endif obj->first_name = NULL; obj->last_name = NULL; obj->email = NULL; obj->published_name = NULL; +#ifdef ENABLE_OLPC + priv->olpc_activities = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) g_object_unref); +#endif priv->listener = NULL; } @@ -144,6 +177,14 @@ salut_self_get_property (GObject *object, case PROP_PUBLISHED_NAME: g_value_set_string (value, self->published_name); break; +#ifdef ENABLE_OLPC + case PROP_OLPC_KEY: + g_value_set_pointer (value, self->olpc_key); + break; + case PROP_OLPC_COLOR: + g_value_set_string (value, self->olpc_color); + break; +#endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -157,6 +198,9 @@ salut_self_set_property (GObject *object, GParamSpec *pspec) { SalutSelf *self = SALUT_SELF (object); +#ifdef ENABLE_OLPC + GArray *arr; +#endif switch (property_id) { @@ -187,6 +231,24 @@ salut_self_set_property (GObject *object, g_free (self->published_name); self->published_name = g_value_dup_string (value); break; +#ifdef ENABLE_OLPC + case PROP_OLPC_KEY: + arr = g_value_get_pointer (value); + if (arr != NULL) + { + if (self->olpc_key != NULL) + g_array_unref (self->olpc_key); + + self->olpc_key = g_array_sized_new (FALSE, FALSE, sizeof (guint8), + arr->len); + g_array_append_vals (self->olpc_key, arr->data, arr->len); + } + break; + case PROP_OLPC_COLOR: + g_free (self->olpc_color); + self->olpc_color = g_value_dup_string (value); + break; +#endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -211,8 +273,14 @@ salut_self_constructor (GType type, g_assert (self->connection != NULL); g_object_get (self->connection, "contact-manager", &(priv->contact_manager), +#ifdef ENABLE_OLPC + "olpc-activity-manager", &(priv->olpc_activity_manager), +#endif NULL); g_assert (priv->contact_manager != NULL); +#ifdef ENABLE_OLPC + g_assert (priv->olpc_activity_manager != NULL); +#endif priv->room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) self->connection, TP_HANDLE_TYPE_ROOM); @@ -238,6 +306,11 @@ salut_self_constructor (GType type, } } +#ifdef ENABLE_OLPC + g_signal_connect (priv->contact_manager, "contact-change", + G_CALLBACK (contact_manager_contact_change_cb), self); +#endif + priv->caps = salut_dup_self_advertised_caps (); priv->data_forms = g_ptr_array_new (); @@ -332,6 +405,27 @@ salut_self_class_init (SalutSelfClass *salut_self_class) g_object_class_install_property (object_class, PROP_PUBLISHED_NAME, param_spec); +#ifdef ENABLE_OLPC + param_spec = g_param_spec_pointer ( + "olpc-key", + "the OLPC key", + "A pointer to a GArray containing the OLPC key", + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_OLPC_KEY, + param_spec); + + param_spec = g_param_spec_string ( + "olpc-color", + "the OLPC color", + "The OLPC color of the self user", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_OLPC_COLOR, + param_spec); +#endif + signals[ESTABLISHED] = g_signal_new ("established", G_OBJECT_CLASS_TYPE (salut_self_class), @@ -372,6 +466,17 @@ salut_self_dispose (GObject *object) priv->contact_manager = NULL; } +#ifdef ENABLE_OLPC + if (priv->olpc_activity_manager != NULL) + { + g_object_unref (priv->olpc_activity_manager); + priv->olpc_activity_manager = NULL; + } + + if (priv->olpc_activities != NULL) + g_hash_table_unref (priv->olpc_activities); +#endif + priv->room_repo = NULL; if (priv->listener) @@ -407,6 +512,12 @@ salut_self_finalize (GObject *object) g_free (self->email); g_free (self->published_name); g_free (self->alias); +#ifdef ENABLE_OLPC + if (self->olpc_key != NULL) + g_array_unref (self->olpc_key); + g_free (self->olpc_color); + g_free (self->olpc_cur_act); +#endif g_free (self->node); g_free (self->hash); g_free (self->ver); @@ -539,6 +650,375 @@ salut_self_set_avatar (SalutSelf *self, guint8 *data, return ret; } +#ifdef ENABLE_OLPC + +gboolean +salut_self_add_olpc_activity (SalutSelf *self, const gchar *activity_id, + TpHandle room, GError **error) +{ + SalutOlpcActivity *activity; + + g_return_val_if_fail (SALUT_IS_SELF (self), FALSE); + g_return_val_if_fail (activity_id != NULL, FALSE); + g_return_val_if_fail (room != 0, FALSE); + + if (strchr (activity_id, ':') != NULL) + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "Activity IDs may not contain ':'"); + return FALSE; + } + + activity = salut_olpc_activity_manager_ensure_activity_by_room ( + self->priv->olpc_activity_manager, room); + + if (!salut_olpc_activity_joined (activity, error)) + { + g_object_unref (activity); + return FALSE; + } + + salut_olpc_activity_update (activity, room, activity_id, NULL, NULL, NULL, + NULL, activity->is_private); + + g_hash_table_insert (self->priv->olpc_activities, GUINT_TO_POINTER (room), + activity); + + return TRUE; +} + +gboolean +salut_self_remove_olpc_activity (SalutSelf *self, SalutOlpcActivity *activity) +{ + SalutSelfPrivate *priv = self->priv; + + g_return_val_if_fail (activity != NULL, FALSE); + + g_hash_table_remove (priv->olpc_activities, + GUINT_TO_POINTER (activity->room)); + + salut_olpc_activity_left (activity); + salut_olpc_activity_revoke_invitations (activity); + + return TRUE; +} + +struct _set_olpc_activities_ctx +{ + SalutSelf *self; + TpHandleRepoIface *room_repo; + GHashTable *olpc_activities; + GHashTable *room_to_act_id; + GError **error; +}; + +static void +_set_olpc_activities_add (gpointer key, gpointer value, gpointer user_data) +{ + struct _set_olpc_activities_ctx *data = user_data; + SalutOlpcActivity *activity; + const gchar *id = (const gchar *) value; + TpHandle room = GPOINTER_TO_UINT (key); + + if (*(data->error) != NULL) + { + /* we already lost */ + return; + } + + activity = g_hash_table_lookup (data->olpc_activities, key); + if (activity == NULL) + { + /* add the activity service if it's not in data->olpc_activities */ + if (!salut_self_add_olpc_activity (data->self, id, room, data->error)) + return; + } + else + { + /* activity was already known */ + salut_olpc_activity_update (activity, room, id, NULL, NULL, NULL, + NULL, activity->is_private); + } +} + +static gboolean +_set_olpc_activities_delete (gpointer key, gpointer value, gpointer user_data) +{ + SalutOlpcActivity *activity = (SalutOlpcActivity *) value; + struct _set_olpc_activities_ctx *data = user_data; + gboolean remove_activity; + + /* delete the activity service if it's not in data->room_to_act_id */ + remove_activity = (g_hash_table_lookup (data->room_to_act_id, key) == NULL); + + if (remove_activity) + { + salut_olpc_activity_left (activity); + salut_olpc_activity_revoke_invitations (activity); + } + + return remove_activity; +} + +gboolean +salut_self_set_olpc_activities (SalutSelf *self, + GHashTable *room_to_act_id, + GError **error) +{ + GError *e = NULL; + struct _set_olpc_activities_ctx data = { self, self->priv->room_repo, + self->priv->olpc_activities, room_to_act_id, &e }; + + g_return_val_if_fail (SALUT_IS_SELF (self), FALSE); + + /* delete any which aren't in room_to_act_id. Can't fail */ + g_hash_table_foreach_remove (self->priv->olpc_activities, + _set_olpc_activities_delete, &data); + + /* add any which aren't in olpc_activities */ + g_hash_table_foreach (room_to_act_id, _set_olpc_activities_add, &data); + + if (error != NULL) + *error = e; + return (e == NULL); +} + +gboolean +salut_self_set_olpc_current_activity (SalutSelf *self, + const gchar *id, + TpHandle room, + GError **error) +{ + GError *err = NULL; + const gchar *room_name; + + g_return_val_if_fail (SALUT_IS_SELF (self), FALSE); + g_return_val_if_fail (id != NULL, FALSE); + + /* if one of id and room is empty, require the other to be */ + if (room == 0) + { + room_name = ""; + + if (id[0] != '\0') + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "In SetCurrentActivity, activity ID must be \"\" if room handle " + "is 0"); + return FALSE; + } + } + else + { + room_name = tp_handle_inspect (self->priv->room_repo, room); + + if (id[0] == '\0') + { + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "In SetCurrentActivity, activity ID must not be \"\" if room " + "handle is non-zero"); + return FALSE; + } + } + + g_free (self->olpc_cur_act); + self->olpc_cur_act = g_strdup (id); + self->olpc_cur_act_room = room; + + if (!SALUT_SELF_GET_CLASS (self)->update_current_activity (self, room_name, + &err)) + { + g_set_error (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "%s", + err->message); + g_error_free (err); + return FALSE; + } + + return TRUE; +} + +gboolean +salut_self_set_olpc_activity_properties (SalutSelf *self, + TpHandle handle, + const gchar *color, + const gchar *name, + const gchar *type, + const gchar *tags, + gboolean is_private, + GError **error) +{ + SalutSelfPrivate *priv; + SalutOlpcActivity *activity; + + g_return_val_if_fail (SALUT_IS_SELF (self), FALSE); + + priv = self->priv; + + activity = g_hash_table_lookup (priv->olpc_activities, + GUINT_TO_POINTER (handle)); + + if (activity == NULL) + { + /* User have to call org.laptop.Telepathy.BuddyInfo.SetActivities + * to create the activity */ + g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, + "No activity associated with room having handle %d", handle); + return FALSE; + } + + salut_olpc_activity_update (activity, handle, activity->id, + name, type, color, tags, is_private); + + return TRUE; +} + +gboolean +salut_self_set_olpc_properties (SalutSelf *self, + const GArray *key, + const gchar *color, + const gchar *jid, + GError **error) +{ + GError *err = NULL; + + if (key != NULL) + { + if (self->olpc_key == NULL) + { + self->olpc_key = g_array_sized_new (FALSE, FALSE, sizeof (guint8), + key->len); + } + else + { + g_array_remove_range (self->olpc_key, 0, self->olpc_key->len); + } + + g_array_append_vals (self->olpc_key, key->data, key->len); + } + + if (color != NULL) + { + g_free (self->olpc_color); + self->olpc_color = g_strdup (color); + } + + if (jid != NULL) + { + g_free (self->jid); + self->jid = g_strdup (jid); + } + + if (!SALUT_SELF_GET_CLASS (self)->set_olpc_properties (self, key, color, jid, + &err)) + { + g_set_error (error, TP_ERROR, TP_ERROR_NETWORK_ERROR, "%s", + err->message); + g_error_free (err); + return FALSE; + } + return TRUE; +} + +typedef struct +{ + SalutSelfOLPCActivityFunc foreach; + gpointer user_data; +} foreach_olpc_activity_ctx; + +static void +foreach_olpc_activity (gpointer key, gpointer value, gpointer user_data) +{ + foreach_olpc_activity_ctx *ctx = user_data; + SalutOlpcActivity *activity = value; + + DEBUG ("%s -> %u", activity->id, GPOINTER_TO_UINT (key)); + (ctx->foreach) (activity, ctx->user_data); +} + +void +salut_self_foreach_olpc_activity (SalutSelf *self, + SalutSelfOLPCActivityFunc foreach, + gpointer user_data) +{ + foreach_olpc_activity_ctx ctx = { foreach, user_data }; + + g_return_if_fail (SALUT_IS_SELF (self)); + + DEBUG ("called"); + + g_hash_table_foreach (self->priv->olpc_activities, foreach_olpc_activity, + &ctx); + + DEBUG ("end"); +} + +void +salut_self_olpc_augment_invitation (SalutSelf *self, + TpHandle room, + TpHandle contact, + WockyNode *invite_node) +{ + SalutOlpcActivity *activity; + + g_return_if_fail (SALUT_IS_SELF (self)); + + activity = g_hash_table_lookup (self->priv->olpc_activities, + GUINT_TO_POINTER (room)); + if (activity == NULL) + return; + + salut_olpc_activity_augment_invitation (activity, contact, invite_node); +} + +typedef struct +{ + GHashTable *olpc_activities; + TpHandle contact_handle; +} remove_from_invited_ctx; + +static void +remove_from_invited (SalutOlpcActivity *act, + gpointer user_data) +{ + SalutOlpcActivity *activity; + remove_from_invited_ctx *data = (remove_from_invited_ctx *) user_data; + + activity = g_hash_table_lookup (data->olpc_activities, + GUINT_TO_POINTER (act->room)); + if (activity == NULL) + return; + + if (salut_olpc_activity_remove_invited (activity, data->contact_handle)) + DEBUG ("contact %d joined activity %s. Remove it from the invited list", + data->contact_handle, activity->id); +} + +/* when a buddy changes his activity list, check if we invited him + * to this activity and remove him from the invited set */ +void +contact_manager_contact_change_cb (SalutContactManager *mgr, + SalutContact *contact, + int changes, + gpointer user_data) +{ + SalutSelf *self = SALUT_SELF (user_data); + SalutSelfPrivate *priv = self->priv; + TpHandleRepoIface *handle_repo = tp_base_connection_get_handles ( + TP_BASE_CONNECTION (self->connection), TP_HANDLE_TYPE_CONTACT); + TpHandle handle; + remove_from_invited_ctx data; + + if (!(changes & SALUT_CONTACT_OLPC_ACTIVITIES)) + return; + + handle = tp_handle_lookup (handle_repo, contact->name, NULL, NULL); + + data.olpc_activities = priv->olpc_activities; + data.contact_handle = handle; + salut_contact_foreach_olpc_activity (contact, remove_from_invited, &data); +} +#endif /* ENABLE_OLPC */ + void salut_self_established (SalutSelf *self) { diff --git a/src/self.h b/src/self.h index 84a7eeec..4c1b51ab 100644 --- a/src/self.h +++ b/src/self.h @@ -32,6 +32,9 @@ #include "connection.h" #include "presence.h" +#ifdef ENABLE_OLPC +#include "olpc-activity.h" +#endif G_BEGIN_DECLS @@ -49,6 +52,10 @@ struct _SalutSelfClass { gboolean (*set_alias) (SalutSelf *self, GError **error); gboolean (*set_avatar) (SalutSelf *self, guint8 *data, gsize size, GError **error); +#ifdef ENABLE_OLPC + gboolean (*set_olpc_properties) (SalutSelf *self, const GArray *key, + const gchar *color, const gchar *jid, GError **error); +#endif /* private abstract methods */ void (*remove_avatar) (SalutSelf *self); @@ -65,6 +72,12 @@ struct _SalutSelf { guint8 *avatar; gsize avatar_size; gchar *jid; +#ifdef ENABLE_OLPC + GArray *olpc_key; + gchar *olpc_cur_act; + TpHandle olpc_cur_act_room; + gchar *olpc_color; +#endif gchar *node; gchar *hash; gchar *ver; @@ -114,6 +127,37 @@ gboolean salut_self_set_alias (SalutSelf *self, const gchar *alias, const gchar *salut_self_get_alias (SalutSelf *self); +#ifdef ENABLE_OLPC +gboolean salut_self_set_olpc_properties (SalutSelf *self, + const GArray *key, const gchar *color, const gchar *jid, GError **error); + +gboolean salut_self_set_olpc_activity_properties (SalutSelf *self, + TpHandle handle, + const gchar *color, const gchar *name, const gchar *type, + const gchar *tags, gboolean is_private, GError **error); + +gboolean salut_self_set_olpc_activities (SalutSelf *self, + GHashTable *act_id_to_room, GError **error); + +gboolean salut_self_add_olpc_activity (SalutSelf *self, + const gchar *activity_id, TpHandle room, GError **error); + +gboolean salut_self_remove_olpc_activity (SalutSelf *self, + SalutOlpcActivity *activity); + +gboolean salut_self_set_olpc_current_activity (SalutSelf *self, + const gchar *id, TpHandle room, GError **error); + +typedef void (*SalutSelfOLPCActivityFunc) + (SalutOlpcActivity *activity, gpointer user_data); + +void salut_self_foreach_olpc_activity (SalutSelf *self, + SalutSelfOLPCActivityFunc foreach, gpointer user_data); + +void salut_self_olpc_augment_invitation (SalutSelf *self, + TpHandle room, TpHandle contact, WockyNode *invite_node); +#endif + const GabbleCapabilitySet *salut_self_get_caps (SalutSelf *self); void salut_self_take_caps (SalutSelf *self, GabbleCapabilitySet *caps); diff --git a/src/text-helper.h b/src/text-helper.h index c9c2fcde..d1ab7c6c 100644 --- a/src/text-helper.h +++ b/src/text-helper.h @@ -21,7 +21,7 @@ #ifndef __TEXT_HELPER_H__ #define __TEXT_HELPER_H__ -#include +#include #include diff --git a/src/tube-dbus.c b/src/tube-dbus.c index c48421d6..08cb0016 100644 --- a/src/tube-dbus.c +++ b/src/tube-dbus.c @@ -43,7 +43,6 @@ #include "muc-tube-dbus.h" #include "tube-iface.h" #include "sha1/sha1-util.h" -#include "util.h" /* When we receive D-Bus messages to be delivered to the application and the * application is not yet connected to the D-Bus tube, theses D-Bus messages @@ -637,7 +636,7 @@ salut_tube_dbus_get_property (GObject *object, g_value_set_string (value, priv->stream_id); break; case PROP_TYPE: - g_value_set_uint (value, SALUT_TUBE_TYPE_DBUS); + g_value_set_uint (value, TP_TUBE_TYPE_DBUS); break; case PROP_SERVICE: g_value_set_string (value, priv->service); @@ -1536,7 +1535,7 @@ salut_tube_dbus_check_access_control (SalutTubeDBus *self, * salut_tube_dbus_offer_async * * Implement D-Bus method Offer on interface - * im.telepathy1.Channel.Type.DBusTube + * org.freedesktop.Telepathy.Channel.Type.DBusTube */ static void salut_tube_dbus_offer_async (TpSvcChannelTypeDBusTube *self, @@ -1576,7 +1575,7 @@ salut_tube_dbus_offer_async (TpSvcChannelTypeDBusTube *self, * salut_tube_dbus_accept_async * * Implements D-Bus method Accept on interface - * im.telepathy1.Channel.Type.DBusTube + * org.freedesktop.Telepathy.Channel.Type.DBusTube */ static void salut_tube_dbus_accept_async (TpSvcChannelTypeDBusTube *self, diff --git a/src/tube-iface.c b/src/tube-iface.c index 60b9012a..5381c989 100644 --- a/src/tube-iface.c +++ b/src/tube-iface.c @@ -144,7 +144,7 @@ salut_tube_iface_base_init (gpointer klass) param_spec = g_param_spec_uint ( "type", "Tube type", - "The SalutTubeType this D-Bus tube object.", + "The TpTubeType this D-Bus tube object.", 0, G_MAXUINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); @@ -179,7 +179,7 @@ salut_tube_iface_base_init (gpointer klass) "state", "Tube state", "The TpTubeChannelState of this DBUS tube object", - 0, G_MAXUINT32, TP_TUBE_CHANNEL_STATE_REMOTE_PENDING, + 0, G_MAXUINT32, TP_TUBE_STATE_REMOTE_PENDING, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (klass, param_spec); diff --git a/src/tube-stream.c b/src/tube-stream.c index c55c4db1..fea5be7c 100644 --- a/src/tube-stream.c +++ b/src/tube-stream.c @@ -65,7 +65,6 @@ #include "tube-iface.h" #include "si-bytestream-manager.h" #include "contact-manager.h" -#include "util.h" static void tube_iface_init (gpointer g_iface, gpointer iface_data); static void streamtube_iface_init (gpointer g_iface, gpointer iface_data); @@ -718,8 +717,7 @@ local_new_connection_cb (GibberListener *listener, static void fire_new_remote_connection (SalutTubeStream *self, GibberTransport *transport, - TpHandle contact, - const gchar *contact_id) + TpHandle contact) { SalutTubeStreamPrivate *priv = SALUT_TUBE_STREAM_GET_PRIVATE (self); GValue access_control_param = {0,}; @@ -736,7 +734,7 @@ fire_new_remote_connection (SalutTubeStream *self, g_assert (connection_id != 0); tp_svc_channel_type_stream_tube_emit_new_remote_connection (self, - contact, contact_id, &access_control_param, connection_id); + contact, &access_control_param, connection_id); g_value_unset (&access_control_param); } @@ -1090,7 +1088,7 @@ salut_tube_stream_get_property (GObject *object, g_value_set_uint64 (value, priv->id); break; case PROP_TYPE: - g_value_set_uint (value, SALUT_TUBE_TYPE_STREAM); + g_value_set_uint (value, TP_TUBE_TYPE_STREAM); break; case PROP_SERVICE: g_value_set_string (value, priv->service); @@ -1859,7 +1857,7 @@ salut_tube_stream_add_bytestream (SalutTubeIface *tube, g_signal_emit (G_OBJECT (self), signals[NEW_CONNECTION], 0, contact); - fire_new_remote_connection (self, transport, contact, peer_id); + fire_new_remote_connection (self, transport, contact); g_free (peer_id); } @@ -2053,7 +2051,7 @@ salut_tube_stream_check_params (TpSocketAddressType address_type, * salut_tube_stream_offer_async * * Implements D-Bus method Offer - * on im.telepathy1.Channel.Type.StreamTube + * on org.freedesktop.Telepathy.Channel.Type.StreamTube */ static void salut_tube_stream_offer_async (TpSvcChannelTypeStreamTube *iface, @@ -2110,7 +2108,7 @@ salut_tube_stream_offer_async (TpSvcChannelTypeStreamTube *iface, * salut_tube_stream_accept_async * * Implements D-Bus method Accept - * on im.telepathy1.Channel.Type.StreamTube + * on org.freedesktop.Telepathy.Channel.Type.StreamTube */ static void salut_tube_stream_accept_async (TpSvcChannelTypeStreamTube *iface, diff --git a/src/tubes-manager.c b/src/tubes-manager.c index 45b65669..998296cc 100644 --- a/src/tubes-manager.c +++ b/src/tubes-manager.c @@ -57,7 +57,7 @@ static void gabble_caps_channel_manager_iface_init ( GabbleCapsChannelManagerIface *); static SalutTubeIface * create_new_tube (SalutTubesManager *self, - SalutTubeType type, + TpTubeType type, TpHandle handle, const gchar *service, GHashTable *parameters, @@ -120,7 +120,7 @@ static gboolean extract_tube_information (TpHandleRepoIface *contact_repo, WockyStanza *stanza, gboolean *close_out, - SalutTubeType *type, + TpTubeType *type, TpHandle *initiator_handle, const gchar **service, GHashTable **parameters, @@ -220,9 +220,9 @@ extract_tube_information (TpHandleRepoIface *contact_repo, tube_type = wocky_node_get_attribute (tube_node, "type"); if (!tp_strdiff (tube_type, "stream")) - *type = SALUT_TUBE_TYPE_STREAM; + *type = TP_TUBE_TYPE_STREAM; else if (!tp_strdiff (tube_type, "dbus")) - *type = SALUT_TUBE_TYPE_DBUS; + *type = TP_TUBE_TYPE_DBUS; else { g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT, @@ -293,7 +293,7 @@ iq_tube_request_cb (WockyPorter *porter, /* tube informations */ const gchar *service; - SalutTubeType tube_type; + TpTubeType tube_type; TpHandle initiator_handle; GHashTable *parameters; guint64 tube_id; @@ -706,7 +706,7 @@ generate_tube_id (SalutTubesManager *self) static SalutTubeIface * create_new_tube (SalutTubesManager *self, - SalutTubeType type, + TpTubeType type, TpHandle handle, const gchar *service, GHashTable *parameters, @@ -720,14 +720,14 @@ create_new_tube (SalutTubesManager *self, TpHandle self_handle = tp_base_connection_get_self_handle (base_conn); SalutTubeIface *tube; - if (type == SALUT_TUBE_TYPE_STREAM) + if (type == TP_TUBE_TYPE_STREAM) { tube = SALUT_TUBE_IFACE (salut_tube_stream_new (priv->conn, handle, TP_HANDLE_TYPE_CONTACT, self_handle, self_handle, FALSE, service, parameters, tube_id, portnum, iq_req, TRUE)); } - else if (type == SALUT_TUBE_TYPE_DBUS) + else if (type == TP_TUBE_TYPE_DBUS) { tube = SALUT_TUBE_IFACE (salut_tube_dbus_new (priv->conn, handle, TP_HANDLE_TYPE_CONTACT, self_handle, NULL, @@ -757,7 +757,7 @@ new_channel_from_request (SalutTubesManager *self, { SalutTubeIface *tube; - SalutTubeType type; + TpTubeType type; const gchar *ctype, *service; TpHandle handle; guint64 tube_id; @@ -778,14 +778,14 @@ new_channel_from_request (SalutTubesManager *self, service = tp_asv_get_string (request, TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); - type = SALUT_TUBE_TYPE_STREAM; + type = TP_TUBE_TYPE_STREAM; } else if (!tp_strdiff (ctype, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) { service = tp_asv_get_string (request, TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); - type = SALUT_TUBE_TYPE_DBUS; + type = TP_TUBE_TYPE_DBUS; } else { @@ -991,7 +991,7 @@ salut_tubes_manager_iface_init (gpointer g_iface, static void add_service_to_array (const gchar *service, GPtrArray *arr, - SalutTubeType type) + TpTubeType type) { GValue monster = {0, }; GHashTable *fixed_properties; @@ -1003,7 +1003,7 @@ add_service_to_array (const gchar *service, NULL }; - g_assert (type == SALUT_TUBE_TYPE_STREAM || type == SALUT_TUBE_TYPE_DBUS); + g_assert (type == TP_TUBE_TYPE_STREAM || type == TP_TUBE_TYPE_DBUS); g_value_init (&monster, TP_STRUCT_TYPE_REQUESTABLE_CHANNEL_CLASS); g_value_take_boxed (&monster, @@ -1014,7 +1014,7 @@ add_service_to_array (const gchar *service, (GDestroyNotify) tp_g_value_slice_free); channel_type_value = tp_g_value_slice_new (G_TYPE_STRING); - if (type == SALUT_TUBE_TYPE_STREAM) + if (type == TP_TUBE_TYPE_STREAM) g_value_set_static_string (channel_type_value, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE); else @@ -1030,7 +1030,7 @@ add_service_to_array (const gchar *service, target_handle_type_value = tp_g_value_slice_new (G_TYPE_STRING); g_value_set_string (target_handle_type_value, service); - if (type == SALUT_TUBE_TYPE_STREAM) + if (type == TP_TUBE_TYPE_STREAM) g_hash_table_insert (fixed_properties, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service", target_handle_type_value); @@ -1142,10 +1142,10 @@ get_contact_caps_foreach (gpointer data, if (g_str_has_prefix (ns, STREAM_CAP_PREFIX)) add_service_to_array (ns + strlen (STREAM_CAP_PREFIX), closure->arr, - SALUT_TUBE_TYPE_STREAM); + TP_TUBE_TYPE_STREAM); else if (g_str_has_prefix (ns, DBUS_CAP_PREFIX)) add_service_to_array (ns + strlen (DBUS_CAP_PREFIX), closure->arr, - SALUT_TUBE_TYPE_DBUS); + TP_TUBE_TYPE_DBUS); } static void diff --git a/src/util.h b/src/util.h index 1f4876f5..03a6e8dd 100644 --- a/src/util.h +++ b/src/util.h @@ -32,10 +32,4 @@ void salut_wocky_node_add_children_from_properties (WockyNode *node, GHashTable *properties, const gchar *prop); gchar *salut_generate_id (void); -typedef enum -{ - SALUT_TUBE_TYPE_STREAM, - SALUT_TUBE_TYPE_DBUS, -} SalutTubeType; - #endif /* __SALUT_UTIL_H__ */ diff --git a/src/write-mgr-file.c b/src/write-mgr-file.c index fd52820c..6a02fff8 100644 --- a/src/write-mgr-file.c +++ b/src/write-mgr-file.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include "protocol.h" @@ -206,6 +206,14 @@ generate_group_name (GHashTable *props) handle_type_name = "-multi"; break; + case TP_HANDLE_TYPE_GROUP: + handle_type_name = "-group"; + break; + + case TP_HANDLE_TYPE_LIST: + handle_type_name = "-list"; + break; + default: handle_type_name = ""; } @@ -272,7 +280,7 @@ write_presence (GKeyFile *f, statuses = tp_asv_get_boxed (props, TP_PROP_PROTOCOL_INTERFACE_PRESENCE_STATUSES, - TP_HASH_TYPE_STATUS_SPEC_MAP); + TP_HASH_TYPE_SIMPLE_STATUS_SPEC_MAP); g_return_if_fail (statuses != NULL); g_hash_table_iter_init (&iter, statuses); diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index b3b833de..a83c1432 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -51,10 +51,17 @@ TWISTED_AVAHI_TESTS = \ avahi/tubes/two-muc-stream-tubes.py \ avahi/tubes/two-muc-dbus-tubes.py +TWISTED_AVAHI_OLPC_TESTS = \ + avahi/olpc-activity-announcements.py + if WANT_TWISTED_TESTS TWISTED_TESTS += $(TWISTED_BASIC_TESTS) endif +if ENABLE_OLPC + TWISTED_AVAHI_TESTS += $(TWISTED_AVAHI_OLPC_TESTS) +endif + if USE_BACKEND_AVAHI TWISTED_TESTS += $(TWISTED_AVAHI_TESTS) endif @@ -110,6 +117,7 @@ run-test.sh: run-test.sh.in Makefile EXTRA_DIST = \ $(TWISTED_AVAHI_TESTS) \ + $(TWISTED_AVAHI_OLPC_TESTS) \ $(TWISTED_BASIC_TESTS) \ constants.py \ run-test.sh.in \ diff --git a/tests/twisted/avahi/aliases.py b/tests/twisted/avahi/aliases.py index 799bd54f..41c82857 100644 --- a/tests/twisted/avahi/aliases.py +++ b/tests/twisted/avahi/aliases.py @@ -13,8 +13,8 @@ import time def wait_for_aliases_changed(q, handle): e = q.expect('dbus-signal', signal='AliasesChanged', - predicate=lambda e: handle in e.args[0]) - alias = e.args[0][handle] + predicate=lambda e: e.args[0][0][0] == handle) + _, alias = e.args[0][0] return alias def wait_for_contact_info_changed(q, handle): @@ -57,7 +57,7 @@ def check_contact_info(info, txt): def check_all_contact_info_methods(conn, handle, keys): attrs = conn.Contacts.GetContactAttributes([handle], - [cs.CONN_IFACE_CONTACT_INFO])[handle] + [cs.CONN_IFACE_CONTACT_INFO], True)[handle] info = attrs[cs.CONN_IFACE_CONTACT_INFO + "/info"] check_contact_info(info, keys) @@ -69,9 +69,6 @@ def test(q, bus, conn): q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED]) - flags = conn.Properties.Get(cs.CONN_IFACE_ALIASING, "AliasFlags") - assertEquals(flags, 0) - assertContains(cs.CONN_IFACE_CONTACT_INFO, conn.Properties.Get(cs.CONN, "Interfaces")) ci_props = conn.Properties.GetAll(cs.CONN_IFACE_CONTACT_INFO) @@ -130,7 +127,7 @@ def test(q, bus, conn): check_contact_info(info, dict) attrs = conn.Contacts.GetContactAttributes([handle], - [cs.CONN_IFACE_ALIASING])[handle] + [cs.CONN_IFACE_ALIASING], True)[handle] assertEquals(alias, attrs[cs.CONN_IFACE_ALIASING + "/alias"]) check_all_contact_info_methods(conn, handle, dict) diff --git a/tests/twisted/avahi/caps-file-transfer.py b/tests/twisted/avahi/caps-file-transfer.py index 13425c26..c239fdfd 100644 --- a/tests/twisted/avahi/caps-file-transfer.py +++ b/tests/twisted/avahi/caps-file-transfer.py @@ -140,7 +140,7 @@ def test_ft_caps_from_contact(q, bus, conn, client): # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( - [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface @@ -194,7 +194,7 @@ def test_ft_caps_from_contact(q, bus, conn, client): # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( - [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface @@ -215,7 +215,7 @@ def test_ft_caps_from_contact(q, bus, conn, client): # check the Contacts interface give the same caps caps_via_contacts_iface = conn_contacts_iface.GetContactAttributes( - [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assert caps_via_contacts_iface == caps, caps_via_contacts_iface @@ -231,7 +231,7 @@ def test(q, bus, conn): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") conn_contacts_iface = dbus.Interface(conn, cs.CONN_IFACE_CONTACTS) caps = conn_contacts_iface.GetContactAttributes( - [self_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.CONN_IFACE_CONTACT_CAPS + '/capabilities'] assertContains(ft_metadata_caps, caps) diff --git a/tests/twisted/avahi/caps-self.py b/tests/twisted/avahi/caps-self.py index c176691b..4438d937 100644 --- a/tests/twisted/avahi/caps-self.py +++ b/tests/twisted/avahi/caps-self.py @@ -1,6 +1,6 @@ """ Basic test of SetSelfCapabilities on interface -im.telepathy1.Connection.Interface.ContactCapabilities +org.freedesktop.Telepathy.Connection.Interface.ContactCapabilities """ from saluttest import exec_test diff --git a/tests/twisted/avahi/close-local-pending-room.py b/tests/twisted/avahi/close-local-pending-room.py index 5066fcfd..1e04fa37 100644 --- a/tests/twisted/avahi/close-local-pending-room.py +++ b/tests/twisted/avahi/close-local-pending-room.py @@ -61,18 +61,18 @@ def test(q, bus, conn): xmpp_connection.send(message) # group channel is created - e = q.expect('dbus-signal', signal='NewChannels', + e = q.expect('dbus-signal', signal='NewChannel', predicate=lambda e: - e.args[0][0][1][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT and - e.args[0][0][1][cs.TARGET_HANDLE_TYPE] == cs.HT_ROOM) - path = e.args[0][0][0] + e.args[1] == cs.CHANNEL_TYPE_TEXT and + e.args[2] == cs.HT_ROOM) + path = e.args[0] channel = make_channel_proxy(conn, path, 'Channel') props_iface = dbus.Interface(bus.get_object(conn.object.bus_name, path), dbus.PROPERTIES_IFACE) q.expect('dbus-signal', signal='MembersChanged', path=path) - lp_members = props_iface.Get('im.telepathy1.Channel.Interface.Group1', + lp_members = props_iface.Get('org.freedesktop.Telepathy.Channel.Interface.Group', 'LocalPendingMembers') assert len(lp_members) == 1 diff --git a/tests/twisted/avahi/file-transfer/file_transfer_helper.py b/tests/twisted/avahi/file-transfer/file_transfer_helper.py index edfb1989..cb10c6b8 100644 --- a/tests/twisted/avahi/file-transfer/file_transfer_helper.py +++ b/tests/twisted/avahi/file-transfer/file_transfer_helper.py @@ -133,7 +133,7 @@ class FileTransferTest(object): def create_ft_channel(self): self.channel = make_channel_proxy(self.conn, self.ft_path, 'Channel') - self.ft_channel = make_channel_proxy(self.conn, self.ft_path, 'Channel.Type.FileTransfer1') + self.ft_channel = make_channel_proxy(self.conn, self.ft_path, 'Channel.Type.FileTransfer') self.ft_props = dbus.Interface(self.bus.get_object( self.conn.object.bus_name, self.ft_path), PROPERTIES_IFACE) @@ -246,7 +246,7 @@ class ReceiveFileTest(FileTransferTest): path, props = channels[0] # check channel properties - # im.telepathy1.Channel D-Bus properties + # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER assert props[cs.INTERFACES] == [] assert props[cs.TARGET_HANDLE] == self.handle @@ -256,7 +256,7 @@ class ReceiveFileTest(FileTransferTest): assert props[cs.INITIATOR_HANDLE] == self.handle assert props[cs.INITIATOR_ID] == self.contact_name - # im.telepathy1.Channel.Type.FileTransfer D-Bus properties + # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING assert props[cs.FT_CONTENT_TYPE] == self.file.content_type assert props[cs.FT_FILENAME] == self.file.name @@ -410,7 +410,7 @@ class SendFileTest(FileTransferTest): self.ft_path, props = self.conn.Requests.CreateChannel(request) - # im.telepathy1.Channel D-Bus properties + # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_FILE_TRANSFER assert props[cs.INTERFACES] == [] assert props[cs.TARGET_HANDLE] == self.handle @@ -420,7 +420,7 @@ class SendFileTest(FileTransferTest): assert props[cs.INITIATOR_HANDLE] == self.self_handle assert props[cs.INITIATOR_ID] == self.self_handle_name - # im.telepathy1.Channel.Type.FileTransfer D-Bus properties + # org.freedesktop.Telepathy.Channel.Type.FileTransfer D-Bus properties assert props[cs.FT_STATE] == cs.FT_STATE_PENDING assert props[cs.FT_CONTENT_TYPE] == self.file.content_type assert props[cs.FT_FILENAME] == self.file.name diff --git a/tests/twisted/avahi/file-transfer/ft-client-caps.py b/tests/twisted/avahi/file-transfer/ft-client-caps.py index 8d6bb8df..3cb6aca3 100644 --- a/tests/twisted/avahi/file-transfer/ft-client-caps.py +++ b/tests/twisted/avahi/file-transfer/ft-client-caps.py @@ -131,7 +131,7 @@ def receive_caps(q, bus, conn, service, contact, contact_handle, features, # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( - [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(expected_caps, caps_via_contacts_iface) @@ -151,7 +151,7 @@ def test_ft_caps_from_contact(q, bus, conn, service, contact): # Check that we don't crash if we haven't seen any caps/presence for this # contact yet. caps = conn.Contacts.GetContactAttributes( - [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [contact_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [contact_handle] basic_caps = [(text_fixed_properties, text_allowed_properties)] @@ -243,7 +243,7 @@ def advertise_caps(q, bus, conn, service, filters, expected_features, unexpected # check the Contacts interface give the same caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( - [self_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertSameElements(expected_caps, caps_via_contacts_iface) @@ -276,7 +276,7 @@ def test_ft_caps_to_contact(q, bus, conn, service): # Check our own caps # caps_via_contacts_iface = conn.Contacts.GetContactAttributes( - [self_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(basic_caps, caps_via_contacts_iface) @@ -288,7 +288,7 @@ def test_ft_caps_to_contact(q, bus, conn, service): # Check our own caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( - [self_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(basic_caps, caps_via_contacts_iface) @@ -302,7 +302,7 @@ def test_ft_caps_to_contact(q, bus, conn, service): # Check our own caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( - [self_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(basic_caps, caps_via_contacts_iface) @@ -316,7 +316,7 @@ def test_ft_caps_to_contact(q, bus, conn, service): # Check our own caps caps_via_contacts_iface = conn.Contacts.GetContactAttributes( - [self_handle], [cs.CONN_IFACE_CONTACT_CAPS]) \ + [self_handle], [cs.CONN_IFACE_CONTACT_CAPS], False) \ [self_handle][cs.ATTR_CONTACT_CAPABILITIES] assertEquals(basic_caps, caps_via_contacts_iface) diff --git a/tests/twisted/avahi/ichat-composing.py b/tests/twisted/avahi/ichat-composing.py index c2057fac..ae48d8a8 100644 --- a/tests/twisted/avahi/ichat-composing.py +++ b/tests/twisted/avahi/ichat-composing.py @@ -66,11 +66,9 @@ def test(q, bus, conn): event.addElement('composing') xmpp_connection.send(message) - e = q.expect('dbus-signal', signal='MessageReceived') - assert len(e.args[0]) == 2 - assert e.args[0][0]['message-sender-id'] == contact_name - assert e.args[0][0]['message-sender'] == handle - assert e.args[0][1]['content'] == OUTGOING_MESSAGE + e = q.expect('dbus-signal', signal='Received') + assert e.args[2] == handle + assert e.args[5] == OUTGOING_MESSAGE if __name__ == '__main__': exec_test(test) diff --git a/tests/twisted/avahi/ichat-incoming-msg.py b/tests/twisted/avahi/ichat-incoming-msg.py index bbf3bf06..71812230 100644 --- a/tests/twisted/avahi/ichat-incoming-msg.py +++ b/tests/twisted/avahi/ichat-incoming-msg.py @@ -53,12 +53,10 @@ def test(q, bus, conn): boddy = msg.addElement('body', content='hi') outbound.send(msg) - e = q.expect('dbus-signal', signal='MessageReceived') - assert len(e.args[0]) == 2 - assert e.args[0][0]['message-sender-id'] == contact_name - assert e.args[0][0]['message-sender'] == handle - assert e.args[0][0]['message-type'] == cs.MT_NORMAL - assert e.args[0][1]['content'] == "hi" + e = q.expect('dbus-signal', signal='Received') + assert e.args[2] == handle + assert e.args[3] == cs.MT_NORMAL + assert e.args[5] == "hi" if __name__ == '__main__': skip_if_another_llxmpp() diff --git a/tests/twisted/avahi/muc-invite.py b/tests/twisted/avahi/muc-invite.py index 6f0a6116..abfc8f28 100644 --- a/tests/twisted/avahi/muc-invite.py +++ b/tests/twisted/avahi/muc-invite.py @@ -80,9 +80,10 @@ def test(q, bus, conn): channel_group = make_channel_proxy(conn, path, "Channel.Interface.Group") # check channel properties - # im.telepathy1.Channel D-Bus properties + # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assertContains(cs.CHANNEL_IFACE_GROUP, props[cs.INTERFACES]) + assertContains(cs.CHANNEL_IFACE_MESSAGES, props[cs.INTERFACES]) assert props[cs.TARGET_ID] == 'my-room' assert props[cs.TARGET_HANDLE_TYPE] == HT_ROOM assert props[cs.REQUESTED] == False @@ -91,14 +92,14 @@ def test(q, bus, conn): # we are added to local pending e = q.expect('dbus-signal', signal='MembersChanged') - added, removed, lp, rp, details = e.args - assert details['message'] == 'Inviting to this room' + msg, added, removed, lp, rp, actor, reason = e.args + assert msg == 'Inviting to this room' assert added == [] assert removed == [] assert lp == [self_handle] assert rp == [] - assert details['actor'] == handle - assert details['change-reason'] == 4 # invited + assert actor == handle + assert reason == 4 # invited # TODO: join the muc, check if we are added to remote-pending and then # to members. This would need some tweak in Salut and/or the test framework diff --git a/tests/twisted/avahi/olpc-activity-announcements.py b/tests/twisted/avahi/olpc-activity-announcements.py new file mode 100644 index 00000000..6b4d01a2 --- /dev/null +++ b/tests/twisted/avahi/olpc-activity-announcements.py @@ -0,0 +1,115 @@ +from saluttest import exec_test, wait_for_contact_in_publish +from avahitest import AvahiAnnouncer, AvahiRecordAnnouncer, AvahiListener +from avahitest import get_host_name, get_domain_name +import avahi + +from xmppstream import setup_stream_listener, connect_to_stream +from servicetest import make_channel_proxy, format_event, EventPattern + +from twisted.words.xish import xpath, domish +import constants as cs + +import time +import dbus +import socket + +CHANNEL_TYPE_TEXT = "org.freedesktop.Telepathy.Channel.Type.Text" +HT_CONTACT = 1 +HT_ROOM = 2 +HT_CONTACT_LIST = 3 + +PUBLISHED_NAME = "acttest" +TESTSUITE_PUBLISHED_NAME = "salutacttest" +ACTIVITY_ID = str(time.time()) + +def announce_address(hostname, address): + "Announce IN A record, address is assume to be ipv4" + + data = reduce (lambda x, y: (x << 8) + int(y), address.split("."), 0) + ndata = socket.htonl(data) + rdata = [ (ndata >> (24 - x)) & 0xff for x in xrange(0, 32, 8)] + + AvahiRecordAnnouncer(hostname, 0x1, 0x01, rdata) + +def test(q, bus, conn): + conn.Connect() + q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) + + + activity_txt = { "type": "org.laptop.HelloMesh", + "name": "HelloMesh", + "color": "#7b83c1,#260993", + "txtvers": "0", + "activity-id": ACTIVITY_ID, + "room": ACTIVITY_ID + } + + # Listen for announcements + l = AvahiListener(q).listen_for_service("_olpc-activity1._udp") + + # Assert that the testsuite doesn't announce the activity + service_name = ACTIVITY_ID + ":" + TESTSUITE_PUBLISHED_NAME + "@" + get_host_name() + forbiden_event = EventPattern('service-added', name=service_name) + q.forbid_events([forbiden_event]) + + contact_name = PUBLISHED_NAME + "@" + get_host_name() + + activity_name = ACTIVITY_ID + ":" + PUBLISHED_NAME + "@" + get_host_name() + + AvahiAnnouncer(contact_name, "_presence._tcp", 1234, {}) + + act_hostname = ACTIVITY_ID + ":" + PUBLISHED_NAME + \ + "._clique._udp." + get_domain_name() + act_address = "239.253.70.70" + + announce_address(act_hostname, act_address) + + # FIXME, if we use the same name as the running salut will MembersChanged + # isn't signalled later on, needs to be fixed. + AvahiAnnouncer(ACTIVITY_ID + ":" + PUBLISHED_NAME, + "_clique._udp", 12345, {}, hostname = act_hostname) + AvahiAnnouncer(activity_name, "_olpc-activity1._udp", + 0, activity_txt) + + # Publish a contact, now get it's handle + handle = wait_for_contact_in_publish(q, bus, conn, contact_name) + + # Assert that the remote handles signals it joined the activity + while True: + e = q.expect('dbus-signal', signal = 'ActivitiesChanged') + if e.args[0] == handle and e.args[1] != []: + assert len(e.args[1]) == 1 + assert e.args[1][0][0] == ACTIVITY_ID + activity_handle = e.args[1][0][1] + break + + act_prop_iface = dbus.Interface(conn, cs.ACTIVITY_PROPERTIES) + act_properties = act_prop_iface.GetProperties(activity_handle) + assert act_properties['private'] == False + assert act_properties['color'] == activity_txt['color'] + assert act_properties['name'] == activity_txt['name'] + assert act_properties['type'] == activity_txt['type'] + + room_channel = conn.RequestChannel(CHANNEL_TYPE_TEXT, + HT_ROOM, activity_handle, True) + + q.expect('dbus-signal', signal='MembersChanged', path=room_channel, + args = [u'', [1L], [], [], [], 1L, 0L]) + + # Make it public that we joined the activity + q.unforbid_events([forbiden_event]) + buddy_info_iface = dbus.Interface(conn, cs.BUDDY_INFO) + buddy_info_iface.SetActivities([(ACTIVITY_ID, activity_handle)]) + + q.expect('service-added', + name = ACTIVITY_ID + ":" + TESTSUITE_PUBLISHED_NAME + + "@" + get_host_name()) + + buddy_info_iface.SetActivities([]) + + q.expect('service-removed', + name = ACTIVITY_ID + ":" + TESTSUITE_PUBLISHED_NAME + + "@" + get_host_name()) + +if __name__ == '__main__': + exec_test(test, { "published-name": TESTSUITE_PUBLISHED_NAME}, timeout=15) diff --git a/tests/twisted/avahi/request-im.py b/tests/twisted/avahi/request-im.py index 93e3abf7..d7d2141e 100644 --- a/tests/twisted/avahi/request-im.py +++ b/tests/twisted/avahi/request-im.py @@ -5,11 +5,13 @@ Test requesting of text 1-1 channels using the old and new request API. import dbus -from saluttest import (exec_test, wait_for_contact_in_publish) +from saluttest import (exec_test, wait_for_contact_list, + wait_for_contact_in_publish) from servicetest import call_async, EventPattern, \ tp_name_prefix, make_channel_proxy from avahitest import get_host_name, AvahiAnnouncer from xmppstream import setup_stream_listener + import constants as cs def test(q, bus, conn): @@ -22,6 +24,10 @@ def test(q, bus, conn): contact_name = "test-request-im@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) + # FIXME: this is a hack to be sure to have all the contact list channels + # announced so they won't interfere with the muc ones announces. + wait_for_contact_list(q, conn) + AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt) handle = wait_for_contact_in_publish(q, bus, conn, contact_name) diff --git a/tests/twisted/avahi/request-muc.py b/tests/twisted/avahi/request-muc.py index 9ef67770..a6283b2e 100644 --- a/tests/twisted/avahi/request-muc.py +++ b/tests/twisted/avahi/request-muc.py @@ -8,7 +8,7 @@ import avahitest from twisted.words.xish import domish -from saluttest import exec_test +from saluttest import exec_test, wait_for_contact_list from servicetest import call_async, EventPattern, \ tp_name_prefix, tp_path_prefix, make_channel_proxy @@ -21,6 +21,10 @@ def test(q, bus, conn): q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) + # FIXME: this is a hack to be sure to have all the contact list channels + # announced so they won't interfere with the muc ones announces. + wait_for_contact_list(q, conn) + # check if we can request roomlist channels properties = conn.GetAll( tp_name_prefix + '.Connection.Interface.Requests', diff --git a/tests/twisted/avahi/roomlist.py b/tests/twisted/avahi/roomlist.py index 21fb1824..7e84a1be 100644 --- a/tests/twisted/avahi/roomlist.py +++ b/tests/twisted/avahi/roomlist.py @@ -8,7 +8,7 @@ import avahitest from twisted.words.xish import domish -from saluttest import exec_test +from saluttest import exec_test, wait_for_contact_list from servicetest import call_async, EventPattern, \ tp_name_prefix, tp_path_prefix, wrap_channel import constants as cs @@ -20,11 +20,14 @@ def test(q, bus, conn): q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) + # FIXME: this is a hack to be sure to have all the contact list channels + # announced so they won't interfere with the roomlist ones announces. + wait_for_contact_list(q, conn) + # check if we can request roomlist channels properties = conn.GetAll( tp_name_prefix + '.Connection.Interface.Requests', dbus_interface='org.freedesktop.DBus.Properties') - assert ({tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_ROOM_LIST, tp_name_prefix + '.Channel.TargetHandleType': 0, @@ -48,7 +51,7 @@ def test(q, bus, conn): EventPattern('dbus-signal', signal='NewChannels'), ) path2 = ret.value[0] - chan2 = wrap_channel(bus.get_object(conn.bus_name, path2), "RoomList1") + chan2 = wrap_channel(bus.get_object(conn.bus_name, path2), "RoomList") props = ret.value[1] assert props[tp_name_prefix + '.Channel.ChannelType'] ==\ @@ -61,7 +64,7 @@ def test(q, bus, conn): == conn.Properties.Get(cs.CONN, "SelfHandle") assert props[tp_name_prefix + '.Channel.InitiatorID'] \ == self_name - assert props[tp_name_prefix + '.Channel.Type.RoomList1.Server'] == '' + assert props[tp_name_prefix + '.Channel.Type.RoomList.Server'] == '' assert new_sig.args[0][0][0] == path2 assert new_sig.args[0][0][1] == props diff --git a/tests/twisted/avahi/set-presence.py b/tests/twisted/avahi/set-presence.py index b6f38f1e..82343c4b 100644 --- a/tests/twisted/avahi/set-presence.py +++ b/tests/twisted/avahi/set-presence.py @@ -32,13 +32,13 @@ def test(q, bus, conn): assert status == 'avail', status assert msg is None, msg - statuses = conn.Get(cs.CONN_IFACE_PRESENCE, 'Statuses', dbus_interface=dbus.PROPERTIES_IFACE) + statuses = conn.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses', dbus_interface=dbus.PROPERTIES_IFACE) assert 'available' in statuses assert 'dnd' in statuses assert 'away' in statuses - simple_presence = dbus.Interface(conn, cs.CONN_IFACE_PRESENCE) + simple_presence = dbus.Interface(conn, cs.CONN_IFACE_SIMPLE_PRESENCE) # set your status to away simple_presence.SetPresence('away', 'At the pub') diff --git a/tests/twisted/avahi/text-channel.py b/tests/twisted/avahi/text-channel.py index acaed6a2..b37b1c2f 100644 --- a/tests/twisted/avahi/text-channel.py +++ b/tests/twisted/avahi/text-channel.py @@ -34,9 +34,7 @@ def test(q, bus, conn): cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: handle})[0] text_channel = make_channel_proxy(conn, t, "Channel.Type.Text") - text_channel.SendMessage([{'message-type': cs.MT_NORMAL}, - {'content-type': 'text/plain', - 'content': INCOMING_MESSAGE}], 0) + text_channel.Send(cs.MT_NORMAL, INCOMING_MESSAGE) e = q.expect('incoming-connection', listener = listener) incoming = e.connection @@ -78,12 +76,11 @@ def test(q, bus, conn): e.connection.send(message) - e = q.expect('dbus-signal', signal='MessageReceived') - assert len(e.args[0]) == 2 - assert e.args[0][0]['message-sender-id'] == contact_name - assert e.args[0][0]['message-sender'] == handle - assert e.args[0][0]['message-type'] == cs.MT_NORMAL - assert e.args[0][1]['content'] == OUTGOING_MESSAGE + e = q.expect('dbus-signal', signal='Received') + assert e.args[2] == handle + assert e.args[3] == cs.MT_NORMAL + assert e.args[5] == OUTGOING_MESSAGE + if __name__ == '__main__': exec_test(test) diff --git a/tests/twisted/avahi/tubes/request-muc-tubes.py b/tests/twisted/avahi/tubes/request-muc-tubes.py index 206f3384..21100f15 100644 --- a/tests/twisted/avahi/tubes/request-muc-tubes.py +++ b/tests/twisted/avahi/tubes/request-muc-tubes.py @@ -8,7 +8,7 @@ import avahitest from twisted.words.xish import domish -from saluttest import exec_test +from saluttest import exec_test, wait_for_contact_list from servicetest import call_async, EventPattern, wrap_channel, pretty import constants as cs @@ -19,6 +19,10 @@ def test(q, bus, conn): q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) + # FIXME: this is a hack to be sure to have all the contact list channels + # announced so they won't interfere with the muc ones announces. + wait_for_contact_list(q, conn) + # check if we can request tube channels properties = conn.Properties.GetAll(cs.CONN_IFACE_REQUESTS) assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, diff --git a/tests/twisted/avahi/tubes/tubetestutil.py b/tests/twisted/avahi/tubes/tubetestutil.py index 4e342f9e..d058ef62 100644 --- a/tests/twisted/avahi/tubes/tubetestutil.py +++ b/tests/twisted/avahi/tubes/tubetestutil.py @@ -1,4 +1,4 @@ -from saluttest import make_connection +from saluttest import make_connection, wait_for_contact_list from avahitest import get_host_name from servicetest import make_channel_proxy @@ -10,6 +10,10 @@ def connect_two_accounts(q, bus, conn): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED]) + # FIXME: this is a hack to be sure to have all the contact list channels + # announced so they won't interfere with other channels announces. + wait_for_contact_list(q, conn) + # second connection: connect conn2_params = { 'published-name': 'testsuite2', @@ -21,6 +25,8 @@ def connect_two_accounts(q, bus, conn): conn2.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED]) + wait_for_contact_list(q, conn2) + # first connection: get the contact list publish_handle = conn.RequestHandles(cs.HT_LIST, ["publish"])[0] conn1_publish = conn.RequestChannel(cs.CHANNEL_TYPE_CONTACT_LIST, @@ -56,7 +62,7 @@ def connect_two_accounts(q, bus, conn): # property first contact1_handle_on_conn2 = 0 conn2_members = conn2_publish_proxy.Get( - 'im.telepathy1.Channel.Interface.Group', 'Members', + 'org.freedesktop.Telepathy.Channel.Interface.Group', 'Members', dbus_interface='org.freedesktop.DBus.Properties') for h in conn2_members: name = conn2.InspectHandles(cs.HT_CONTACT, [h])[0] diff --git a/tests/twisted/avahi/tubes/two-muc-dbus-tubes.py b/tests/twisted/avahi/tubes/two-muc-dbus-tubes.py index ae8864c3..6fc81097 100644 --- a/tests/twisted/avahi/tubes/two-muc-dbus-tubes.py +++ b/tests/twisted/avahi/tubes/two-muc-dbus-tubes.py @@ -21,9 +21,9 @@ def check_dbus_names(tube, members): names = tube.Properties.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames') assert set(names.keys()) == set(members), names.keys() -SERVICE = "im.telepathy1.Tube.Test" +SERVICE = "org.freedesktop.Telepathy.Tube.Test" IFACE = SERVICE -PATH = "/im/telepathy1/Tube/Test" +PATH = "/org/freedesktop/Telepathy/Tube/Test" print "FIXME: MUC tubes tests are currently broken: fdo#69223" # exiting 77 causes automake to consider the test to have been skipped diff --git a/tests/twisted/cm/protocol.py b/tests/twisted/cm/protocol.py index 157c6b6f..6f62f1ec 100644 --- a/tests/twisted/cm/protocol.py +++ b/tests/twisted/cm/protocol.py @@ -16,9 +16,9 @@ def test(q, bus, conn): cm_prop_iface = dbus.Interface(cm, cs.PROPERTIES_IFACE) protocols = unwrap(cm_prop_iface.Get(cs.CM, 'Protocols')) - assertEquals(set(['local_xmpp']), set(protocols.keys())) + assertEquals(set(['local-xmpp']), set(protocols.keys())) - local_props = protocols['local_xmpp'] + local_props = protocols['local-xmpp'] local_params = local_props[cs.PROTOCOL + '.Parameters'] proto = bus.get_object(cm.bus_name, cm.object_path + '/local_xmpp') @@ -39,7 +39,7 @@ def test(q, bus, conn): assertContains(cs.CONN_IFACE_ALIASING, proto_props['ConnectionInterfaces']) assertContains(cs.CONN_IFACE_AVATARS, proto_props['ConnectionInterfaces']) assertContains(cs.CONN_IFACE_CONTACTS, proto_props['ConnectionInterfaces']) - assertContains(cs.CONN_IFACE_PRESENCE, + assertContains(cs.CONN_IFACE_SIMPLE_PRESENCE, proto_props['ConnectionInterfaces']) assertContains(cs.CONN_IFACE_REQUESTS, proto_props['ConnectionInterfaces']) diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py index e72c5928..8c601678 100644 --- a/tests/twisted/constants.py +++ b/tests/twisted/constants.py @@ -1,32 +1,10 @@ -# Copyright (C) 2009 Nokia Corporation -# Copyright (C) 2009-2013 Collabora Ltd. -# -# 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.1 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, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA - """ Some handy constants for other tests to share and enjoy. """ -from dbus import PROPERTIES_IFACE, INTROSPECTABLE_IFACE - -PREFIX = "im.telepathy1" -PATH_PREFIX = '/' + PREFIX.replace('.', '/') +from dbus import PROPERTIES_IFACE -tp_name_prefix = PREFIX -tp_path_prefix = PATH_PREFIX +PREFIX = "org.freedesktop.Telepathy" CM = PREFIX + ".ConnectionManager" @@ -38,31 +16,42 @@ HT_GROUP = 4 CHANNEL = PREFIX + ".Channel" -CHANNEL_IFACE_CHAT_STATE = CHANNEL + '.Interface.ChatState1' -CHANNEL_IFACE_DESTROYABLE = CHANNEL + ".Interface.Destroyable1" -CHANNEL_IFACE_DTMF = CHANNEL + ".Interface.DTMF1" -CHANNEL_IFACE_GROUP = CHANNEL + ".Interface.Group1" -CHANNEL_IFACE_HOLD = CHANNEL + ".Interface.Hold1" -CHANNEL_IFACE_PASSWORD = CHANNEL + ".Interface.Password1" -CHANNEL_IFACE_TUBE = CHANNEL + ".Interface.Tube1" -CHANNEL_IFACE_SASL_AUTH = CHANNEL + ".Interface.SASLAuthentication1" -CHANNEL_IFACE_CONFERENCE = CHANNEL + '.Interface.Conference1' -CHANNEL_IFACE_ROOM = CHANNEL + '.Interface.Room1' +CHANNEL_IFACE_CALL_STATE = CHANNEL + ".Interface.CallState" +CHANNEL_IFACE_CHAT_STATE = CHANNEL + '.Interface.ChatState' +CHANNEL_IFACE_DESTROYABLE = CHANNEL + ".Interface.Destroyable" +CHANNEL_IFACE_DTMF = CHANNEL + ".Interface.DTMF" +CHANNEL_IFACE_GROUP = CHANNEL + ".Interface.Group" +CHANNEL_IFACE_HOLD = CHANNEL + ".Interface.Hold" +CHANNEL_IFACE_MEDIA_SIGNALLING = CHANNEL + ".Interface.MediaSignalling" +CHANNEL_IFACE_MESSAGES = CHANNEL + ".Interface.Messages" +CHANNEL_IFACE_PASSWORD = CHANNEL + ".Interface.Password" +CHANNEL_IFACE_TUBE = CHANNEL + ".Interface.Tube" +CHANNEL_IFACE_SASL_AUTH = CHANNEL + ".Interface.SASLAuthentication" +CHANNEL_IFACE_CONFERENCE = CHANNEL + '.Interface.Conference' +CHANNEL_IFACE_ROOM = CHANNEL + '.Interface.Room2' CHANNEL_IFACE_ROOM_CONFIG = CHANNEL + '.Interface.RoomConfig1' -CHANNEL_IFACE_SUBJECT = CHANNEL + '.Interface.Subject1' -CHANNEL_IFACE_FILE_TRANSFER_METADATA = CHANNEL + '.Interface.FileTransfer.Metadata1' +CHANNEL_IFACE_SUBJECT = CHANNEL + '.Interface.Subject2' +CHANNEL_IFACE_FILE_TRANSFER_METADATA = CHANNEL + '.Interface.FileTransfer.Metadata' CHANNEL_TYPE_CALL = CHANNEL + ".Type.Call1" -CHANNEL_TYPE_CONTACT_SEARCH = CHANNEL + ".Type.ContactSearch1" +CHANNEL_TYPE_CONTACT_LIST = CHANNEL + ".Type.ContactList" +CHANNEL_TYPE_CONTACT_SEARCH = CHANNEL + ".Type.ContactSearch" CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" -CHANNEL_TYPE_STREAM_TUBE = CHANNEL + ".Type.StreamTube1" -CHANNEL_TYPE_DBUS_TUBE = CHANNEL + ".Type.DBusTube1" -CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer1" -CHANNEL_TYPE_ROOM_LIST = CHANNEL + ".Type.RoomList1" +CHANNEL_TYPE_TUBES = CHANNEL + ".Type.Tubes" +CHANNEL_TYPE_STREAM_TUBE = CHANNEL + ".Type.StreamTube" +CHANNEL_TYPE_DBUS_TUBE = CHANNEL + ".Type.DBusTube" +CHANNEL_TYPE_TEXT = CHANNEL + ".Type.Text" +CHANNEL_TYPE_FILE_TRANSFER = CHANNEL + ".Type.FileTransfer" +CHANNEL_TYPE_ROOM_LIST = CHANNEL + ".Type.RoomList" CHANNEL_TYPE_SERVER_AUTHENTICATION = \ - CHANNEL + ".Type.ServerAuthentication1" + CHANNEL + ".Type.ServerAuthentication" CHANNEL_TYPE_SERVER_TLS_CONNECTION = \ - CHANNEL + ".Type.ServerTLSConnection1" + CHANNEL + ".Type.ServerTLSConnection" + +TP_AWKWARD_PROPERTIES = PREFIX + ".Properties" +PROPERTY_FLAG_READ = 1 +PROPERTY_FLAG_WRITE = 2 +PROPERTY_FLAGS_RW = PROPERTY_FLAG_READ | PROPERTY_FLAG_WRITE CHANNEL_TYPE = CHANNEL + '.ChannelType' TARGET_HANDLE_TYPE = CHANNEL + '.TargetHandleType' @@ -81,7 +70,7 @@ CALL_MUTABLE_CONTENTS = CHANNEL_TYPE_CALL + '.MutableContents' CALL_CONTENT = PREFIX + '.Call1.Content' CALL_CONTENT_IFACE_MEDIA = CALL_CONTENT + '.Interface.Media' -CALL_CONTENT_IFACE_DTMF = CALL_CONTENT + '.Interface.DTMF1' +CALL_CONTENT_IFACE_DTMF = CALL_CONTENT + '.Interface.DTMF' CALL_CONTENT_MEDIADESCRIPTION = CALL_CONTENT + '.MediaDescription' @@ -176,30 +165,28 @@ CONTACT_LIST_STATE_FAILURE = 2 CONTACT_LIST_STATE_SUCCESS = 3 CONN = PREFIX + ".Connection" -CONN_IFACE_AVATARS = CONN + '.Interface.Avatars1' -CONN_IFACE_ALIASING = CONN + '.Interface.Aliasing1' +CONN_IFACE_AVATARS = CONN + '.Interface.Avatars' +CONN_IFACE_ALIASING = CONN + '.Interface.Aliasing' +CONN_IFACE_CAPS = CONN + '.Interface.Capabilities' CONN_IFACE_CONTACTS = CONN + '.Interface.Contacts' -CONN_IFACE_CONTACT_CAPS = CONN + '.Interface.ContactCapabilities1' -CONN_IFACE_CONTACT_INFO = CONN + ".Interface.ContactInfo1" -CONN_IFACE_PRESENCE = CONN + '.Interface.Presence1' +CONN_IFACE_CONTACT_CAPS = CONN + '.Interface.ContactCapabilities' +CONN_IFACE_CONTACT_INFO = CONN + ".Interface.ContactInfo" +CONN_IFACE_PRESENCE = CONN + '.Interface.Presence' +CONN_IFACE_SIMPLE_PRESENCE = CONN + '.Interface.SimplePresence' CONN_IFACE_REQUESTS = CONN + '.Interface.Requests' -CONN_IFACE_LOCATION = CONN + '.Interface.Location1' +CONN_IFACE_LOCATION = CONN + '.Interface.Location' CONN_IFACE_GABBLE_DECLOAK = CONN + '.Interface.Gabble.Decloak' -CONN_IFACE_MAIL_NOTIFICATION = CONN + '.Interface.MailNotification1' -CONN_IFACE_CONTACT_LIST = CONN + '.Interface.ContactList1' -CONN_IFACE_CONTACT_GROUPS = CONN + '.Interface.ContactGroups1' -CONN_IFACE_CLIENT_TYPES = CONN + '.Interface.ClientTypes1' -CONN_IFACE_POWER_SAVING = CONN + '.Interface.PowerSaving1' -CONN_IFACE_CONTACT_BLOCKING = CONN + '.Interface.ContactBlocking1' +CONN_IFACE_MAIL_NOTIFICATION = CONN + '.Interface.MailNotification' +CONN_IFACE_CONTACT_LIST = CONN + '.Interface.ContactList' +CONN_IFACE_CONTACT_GROUPS = CONN + '.Interface.ContactGroups' +CONN_IFACE_CLIENT_TYPES = CONN + '.Interface.ClientTypes' +CONN_IFACE_POWER_SAVING = CONN + '.Interface.PowerSaving' +CONN_IFACE_CONTACT_BLOCKING = CONN + '.Interface.ContactBlocking' CONN_IFACE_ADDRESSING = CONN + '.Interface.Addressing1' -CONN_IFACE_SERVICE_POINT = CONN + '.Interface.ServicePoint1' ATTR_CONTACT_ID = CONN + '/contact-id' ATTR_CONTACT_CAPABILITIES = CONN_IFACE_CONTACT_CAPS + '/capabilities' -ATTR_PRESENCE = CONN_IFACE_PRESENCE + '/presence' -ATTR_SUBSCRIBE = CONN_IFACE_CONTACT_LIST + '/subscribe' -ATTR_PUBLISH = CONN_IFACE_CONTACT_LIST + '/publish' -ATTR_GROUPS = CONN_IFACE_CONTACT_GROUPS + '/groups' +ATTR_PRESENCE = CONN_IFACE_SIMPLE_PRESENCE + '/presence' STREAM_HANDLER = PREFIX + '.Media.StreamHandler' @@ -214,7 +201,6 @@ CONNECTION_REFUSED = ERROR + '.ConnectionRefused' CONNECTION_FAILED = ERROR + '.ConnectionFailed' CONNECTION_LOST = ERROR + '.ConnectionLost' CANCELLED = ERROR + '.Cancelled' -NOT_YOURS = ERROR + '.NotYours' DISCONNECTED = ERROR + '.Disconnected' REGISTRATION_EXISTS = ERROR + '.RegistrationExists' AUTHENTICATION_FAILED = ERROR + '.AuthenticationFailed' @@ -226,12 +212,10 @@ INVALID_HANDLE = ERROR + '.InvalidHandle' CERT_UNTRUSTED = ERROR + '.Cert.Untrusted' SERVICE_BUSY = ERROR + '.ServiceBusy' SERVICE_CONFUSED = ERROR + '.ServiceConfused' -SOFTWARE_UPGRADE_REQUIRED = ERROR + '.SoftwareUpgradeRequired' BANNED = ERROR + '.Channel.Banned' -DBUS_ERROR_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod' -DBUS_ERROR_NO_REPLY = 'org.freedesktop.DBus.Error.NoReply' +UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod' TUBE_PARAMETERS = CHANNEL_IFACE_TUBE + '.Parameters' TUBE_STATE = CHANNEL_IFACE_TUBE + '.State' @@ -277,13 +261,17 @@ SOCKET_ADDRESS_TYPE_IPV6 = 3 SOCKET_ACCESS_CONTROL_LOCALHOST = 0 SOCKET_ACCESS_CONTROL_PORT = 1 -SOCKET_ACCESS_CONTROL_CREDENTIALS = 2 +SOCKET_ACCESS_CONTROL_NETMASK = 2 +SOCKET_ACCESS_CONTROL_CREDENTIALS = 3 TUBE_STATE_LOCAL_PENDING = 0 TUBE_STATE_REMOTE_PENDING = 1 TUBE_STATE_OPEN = 2 TUBE_STATE_NOT_OFFERED = 3 +TUBE_TYPE_DBUS = 0 +TUBE_TYPE_STREAM = 1 + MEDIA_STREAM_DIRECTION_NONE = 0 MEDIA_STREAM_DIRECTION_SEND = 1 MEDIA_STREAM_DIRECTION_RECEIVE = 2 @@ -295,6 +283,15 @@ MEDIA_STREAM_PENDING_REMOTE_SEND = 2 MEDIA_STREAM_TYPE_AUDIO = 0 MEDIA_STREAM_TYPE_VIDEO = 1 +MEDIA_STREAM_STATE_DISCONNECTED = 0 +MEDIA_STREAM_STATE_CONNECTING = 1 +MEDIA_STREAM_STATE_CONNECTED = 2 + +MEDIA_STREAM_DIRECTION_NONE = 0 +MEDIA_STREAM_DIRECTION_SEND = 1 +MEDIA_STREAM_DIRECTION_RECEIVE = 2 +MEDIA_STREAM_DIRECTION_BIDIRECTIONAL = 3 + FT_STATE_NONE = 0 FT_STATE_PENDING = 1 FT_STATE_ACCEPTED = 2 @@ -341,7 +338,8 @@ GF_MESSAGE_RESCIND = 128 GF_CHANNEL_SPECIFIC_HANDLES = 256 GF_ONLY_ONE_GROUP = 512 GF_HANDLE_OWNERS_NOT_AVAILABLE = 1024 -GF_MESSAGE_DEPART = 2048 +GF_PROPERTIES = 2048 +GF_MEMBERS_CHANGED_DETAILED = 4096 GC_REASON_NONE = 0 GC_REASON_OFFLINE = 1 @@ -403,7 +401,6 @@ MEDIA_CAP_IMMUTABLE_STREAMS = 32 CLIENT = PREFIX + '.Client' -PRESENCE_UNSET = 0 PRESENCE_OFFLINE = 1 PRESENCE_AVAILABLE = 2 PRESENCE_AWAY = 3 @@ -458,6 +455,12 @@ MT_NOTICE = 2 MT_AUTO_REPLY = 3 MT_DELIVERY_REPORT = 4 +class MessageFlag(object): + TRUNCATED = 1 + NON_TEXT_CONTENT = 2 + SCROLLBACK = 4 + RESCUED = 8 + class SendError(object): UNKNOWN = 0 OFFLINE = 1 @@ -467,9 +470,9 @@ class SendError(object): NOT_IMPLEMENTED = 5 PROTOCOL = PREFIX + '.Protocol' -PROTOCOL_IFACE_PRESENCES = PROTOCOL + '.Interface.Presence1' -PROTOCOL_IFACE_ADDRESSING = PROTOCOL + '.Interface.Addressing1' -PROTOCOL_IFACE_AVATARS = PROTOCOL + '.Interface.Avatars1' +PROTOCOL_IFACE_PRESENCES = PROTOCOL + '.Interface.Presence' +PROTOCOL_IFACE_ADDRESSING = PROTOCOL + '.Interface.Addressing' +PROTOCOL_IFACE_AVATARS = PROTOCOL + '.Interface.Avatars' PARAM_REQUIRED = 1 PARAM_REGISTER = 2 @@ -489,9 +492,9 @@ TLS_REJECT_REASON_UNTRUSTED = 1 # Channel.Interface.Messages -MESSAGE_PART_SUPPORT_FLAGS = CHANNEL_TYPE_TEXT + '.MessagePartSupportFlags' -DELIVERY_REPORTING_SUPPORT = CHANNEL_TYPE_TEXT + '.DeliveryReportingSupport' -SUPPORTED_CONTENT_TYPES = CHANNEL_TYPE_TEXT + '.SupportedContentTypes' +MESSAGE_PART_SUPPORT_FLAGS = CHANNEL_IFACE_MESSAGES + '.MessagePartSupportFlags' +DELIVERY_REPORTING_SUPPORT = CHANNEL_IFACE_MESSAGES + '.DeliveryReportingSupport' +SUPPORTED_CONTENT_TYPES = CHANNEL_IFACE_MESSAGES + '.SupportedContentTypes' MSG_SENDING_FLAGS_REPORT_DELIVERY = 1 MSG_SENDING_FLAGS_REPORT_READ = 2 @@ -510,7 +513,15 @@ DELIVERY_STATUS_ACCEPTED = 4 DELIVERY_STATUS_READ = 5 DELIVERY_STATUS_DELETED = 6 -PASSWORD_FLAG_HINT = 4 +MEDIA_STREAM_ERROR_UNKNOWN = 0 +MEDIA_STREAM_ERROR_EOS = 1 +MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED = 2 +MEDIA_STREAM_ERROR_CONNECTION_FAILED = 3 +MEDIA_STREAM_ERROR_NETWORK_ERROR = 4 +MEDIA_STREAM_ERROR_NO_CODECS = 5 +MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR = 6 +MEDIA_STREAM_ERROR_MEDIA_ERROR = 7 + PASSWORD_FLAG_PROVIDE = 8 # Channel.Interface.Room @@ -523,46 +534,4 @@ SUBJECT_PRESENT = 1 SUBJECT_CAN_SET = 2 DEBUG_IFACE = PREFIX + '.Debug' -DEBUG_PATH = PATH_PREFIX + '/debug' - -SERVICE_POINT_TYPE_NONE = 0 -SERVICE_POINT_TYPE_EMERGENCY = 1 -SERVICE_POINT_TYPE_COUNSELING = 2 - -CLIENT = PREFIX + '.Client' -CLIENT_PATH = PATH_PREFIX + '/Client' -OBSERVER = PREFIX + '.Client.Observer' -APPROVER = PREFIX + '.Client.Approver' -HANDLER = PREFIX + '.Client.Handler' -CLIENT_IFACE_REQUESTS = CLIENT + '.Interface.Requests' - -ACCOUNT = PREFIX + '.Account' -ACCOUNT_IFACE_AVATAR = ACCOUNT + '.Interface.Avatar1' -ACCOUNT_IFACE_ADDRESSING = ACCOUNT + '.Interface.Addressing1' -ACCOUNT_IFACE_HIDDEN = ACCOUNT + '.Interface.Hidden1' -ACCOUNT_IFACE_NOKIA_CONDITIONS = 'com.nokia.Account.Interface.Conditions' -ACCOUNT_PATH_PREFIX = PATH_PREFIX + '/Account/' - -AM = PREFIX + '.AccountManager' -AM_IFACE_HIDDEN = AM + '.Interface.Hidden1' -AM_PATH = PATH_PREFIX + '/AccountManager' - -CR = PREFIX + '.ChannelRequest' -CDO = PREFIX + '.ChannelDispatchOperation' - -CD = PREFIX + '.ChannelDispatcher' -CD_IFACE_OP_LIST = PREFIX + '.ChannelDispatcher.Interface.OperationList1' -CD_PATH = PATH_PREFIX + '/ChannelDispatcher' - -MC = PREFIX + '.MissionControl6' -MC_PATH = PATH_PREFIX + '/MissionControl6' - -TESTDOT = PREFIX + ".Test." -TESTSLASH = PATH_PREFIX + "/Test/" - -TEST_DBUS_ACCOUNT_SERVICE = TESTDOT + "DBusAccountService" -TEST_DBUS_ACCOUNT_SERVICE_PATH = TESTSLASH + "DBusAccountService" -TEST_DBUS_ACCOUNT_SERVICE_IFACE = TEST_DBUS_ACCOUNT_SERVICE - -TEST_DBUS_ACCOUNT_PLUGIN_PATH = TESTSLASH + "DBusAccountPlugin" -TEST_DBUS_ACCOUNT_PLUGIN_IFACE = TESTDOT + "DBusAccountPlugin" +DEBUG_PATH = '/' + PREFIX.replace('.', '/') + '/debug' diff --git a/tests/twisted/ns.py b/tests/twisted/ns.py index e9c24f80..b538fc4f 100644 --- a/tests/twisted/ns.py +++ b/tests/twisted/ns.py @@ -41,6 +41,16 @@ NS_XMPP_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl' NS_XMPP_BIND = 'urn:ietf:params:xml:ns:xmpp-bind' NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls' NS_XMPP_SESSION = 'urn:ietf:params:xml:ns:xmpp-session' +OLPC_ACTIVITIES = "http://laptop.org/xmpp/activities" +OLPC_ACTIVITIES_NOTIFY = "%s+notify" % OLPC_ACTIVITIES +OLPC_ACTIVITY = "http://laptop.org/xmpp/activity" +OLPC_ACTIVITY_PROPS = "http://laptop.org/xmpp/activity-properties" +OLPC_ACTIVITY_PROPS_NOTIFY = "%s+notify" % OLPC_ACTIVITY_PROPS +OLPC_BUDDY = "http://laptop.org/xmpp/buddy" +OLPC_BUDDY_PROPS = "http://laptop.org/xmpp/buddy-properties" +OLPC_BUDDY_PROPS_NOTIFY = "%s+notify" % OLPC_BUDDY_PROPS +OLPC_CURRENT_ACTIVITY = "http://laptop.org/xmpp/current-activity" +OLPC_CURRENT_ACTIVITY_NOTIFY = "%s+notify" % OLPC_CURRENT_ACTIVITY PUBSUB = "http://jabber.org/protocol/pubsub" PUBSUB_EVENT = "%s#event" % PUBSUB REGISTER = "jabber:iq:register" diff --git a/tests/twisted/saluttest.py b/tests/twisted/saluttest.py index 0517730e..fec5b1ed 100644 --- a/tests/twisted/saluttest.py +++ b/tests/twisted/saluttest.py @@ -54,7 +54,7 @@ def make_connection(bus, event_func, params=None): default_params.update(params) return servicetest.make_connection(bus, event_func, 'salut', - 'local_xmpp', default_params) + 'local-xmpp', default_params) def ensure_avahi_is_running(): bus = dbus.SystemBus() @@ -185,11 +185,31 @@ def exec_test(fun, params=None, protocol=None, timeout=None, make_conn) reactor.run() +def wait_for_contact_list(q, conn): + """Request contact list channels and wait for their NewChannel signals. + This is useful to avoid these signals to interfere with your test.""" + + #FIXME: this maybe racy if there are other contacts connected + requestotron = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS) + + # publish + requestotron.EnsureChannel({ + cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, + cs.TARGET_HANDLE_TYPE: cs.HT_LIST, + cs.TARGET_ID: 'publish'}) + q.expect('dbus-signal', signal='NewChannel') + # subscribe + requestotron.EnsureChannel({ + cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CONTACT_LIST, + cs.TARGET_HANDLE_TYPE: cs.HT_LIST, + cs.TARGET_ID: 'subscribe'}) + q.expect('dbus-signal', signal='NewChannel') + def wait_for_contact_in_publish(q, bus, conn, contact_name): handle = 0 # Wait until the record shows up in publish while handle == 0: - e = q.expect('dbus-signal', signal='ContactsChanged', + e = q.expect('dbus-signal', signal='ContactsChangedWithID', path=conn.object_path) for h, state in e.args[0].items(): name = e.args[1][h] diff --git a/tests/twisted/servicetest.py b/tests/twisted/servicetest.py index 8a813f50..bca3b13b 100644 --- a/tests/twisted/servicetest.py +++ b/tests/twisted/servicetest.py @@ -1,23 +1,6 @@ -# Copyright (C) 2009 Nokia Corporation -# Copyright (C) 2009-2013 Collabora Ltd. -# -# 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.1 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, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA """ -Infrastructure code for testing Telepathy services. +Infrastructure code for testing connection managers. """ from twisted.internet import glib2reactor @@ -25,22 +8,18 @@ from twisted.internet.protocol import Protocol, Factory, ClientFactory glib2reactor.install() import sys import time -import os import pprint import unittest -import dbus -import dbus.lowlevel -from dbus.mainloop.glib import DBusGMainLoop -DBusGMainLoop(set_as_default=True) +import dbus.glib from twisted.internet import reactor import constants as cs -tp_name_prefix = cs.PREFIX -tp_path_prefix = cs.PATH_PREFIX +tp_name_prefix = 'org.freedesktop.Telepathy' +tp_path_prefix = '/org/freedesktop/Telepathy' class DictionarySupersetOf (object): """Utility class for expecting "a dictionary with at least these keys".""" @@ -61,19 +40,16 @@ class DictionarySupersetOf (object): except TypeError: # other is not iterable return False -class Event(object): +class Event: def __init__(self, type, **kw): self.__dict__.update(kw) self.type = type (self.subqueue, self.subtype) = type.split ("-", 1) - def __str__(self): - return '\n'.join([ str(type(self)) ] + format_event(self)) - def format_event(event): ret = ['- type %s' % event.type] - for key in sorted(dir(event)): + for key in dir(event): if key != 'type' and not key.startswith('_'): ret.append('- %s: %s' % ( key, pprint.pformat(getattr(event, key)))) @@ -175,12 +151,6 @@ class BaseEventQueue: """ self.forbidden_events.difference_update(set(patterns)) - def unforbid_all(self): - """ - Remove all patterns from the set of forbidden events. - """ - self.forbidden_events.clear() - def _check_forbidden(self, event): for e in self.forbidden_events: if e.match(event): @@ -309,11 +279,6 @@ class IteratingEventQueue(BaseEventQueue): def __init__(self, timeout=None): BaseEventQueue.__init__(self, timeout) - self._dbus_method_impls = [] - self._buses = [] - # a message filter which will claim we handled everything - self._dbus_dev_null = \ - lambda bus, message: dbus.lowlevel.HANDLER_RESULT_HANDLED def wait(self, queues=None): stop = [False] @@ -338,127 +303,6 @@ class IteratingEventQueue(BaseEventQueue): else: raise TimeoutError - def add_dbus_method_impl(self, cb, bus=None, **kwargs): - if bus is None: - bus = self._buses[0] - - self._dbus_method_impls.append( - (EventPattern('dbus-method-call', **kwargs), cb)) - - def dbus_emit(self, path, iface, name, *a, **k): - bus = k.pop('bus', self._buses[0]) - assert 'signature' in k, k - message = dbus.lowlevel.SignalMessage(path, iface, name) - message.append(*a, **k) - bus.send_message(message) - - def dbus_return(self, in_reply_to, *a, **k): - bus = k.pop('bus', self._buses[0]) - assert 'signature' in k, k - reply = dbus.lowlevel.MethodReturnMessage(in_reply_to) - reply.append(*a, **k) - bus.send_message(reply) - - def dbus_raise(self, in_reply_to, name, message=None, bus=None): - if bus is None: - bus = self._buses[0] - - reply = dbus.lowlevel.ErrorMessage(in_reply_to, name, message) - bus.send_message(reply) - - def attach_to_bus(self, bus): - if not self._buses: - # first-time setup - self._dbus_filter_bound_method = self._dbus_filter - - self._buses.append(bus) - - # Only subscribe to messages on the first bus connection (assumed to - # be the shared session bus connection used by the simulated connection - # manager and most of the test suite), not on subsequent bus - # connections (assumed to represent extra clients). - # - # When we receive a method call on the other bus connections, ignore - # it - the eavesdropping filter installed on the first bus connection - # will see it too. - # - # This is highly counter-intuitive, but it means our messages are in - # a guaranteed order (we don't have races between messages arriving on - # various connections). - if len(self._buses) > 1: - bus.add_message_filter(self._dbus_dev_null) - return - - try: - # for dbus > 1.5 - bus.add_match_string("eavesdrop=true,type='signal'") - except dbus.DBusException: - bus.add_match_string("type='signal'") - bus.add_match_string("type='method_call'") - else: - bus.add_match_string("eavesdrop=true,type='method_call'") - - bus.add_message_filter(self._dbus_filter_bound_method) - - bus.add_signal_receiver( - lambda *args, **kw: - self.append( - Event('dbus-signal', - path=unwrap(kw['path']), - signal=kw['member'], - args=map(unwrap, args), - interface=kw['interface'])), - None, - None, - None, - path_keyword='path', - member_keyword='member', - interface_keyword='interface', - byte_arrays=True, - ) - - def cleanup(self): - if self._buses: - self._buses[0].remove_message_filter(self._dbus_filter_bound_method) - for bus in self._buses[1:]: - bus.remove_message_filter(self._dbus_dev_null) - - self._buses = [] - self._dbus_method_impls = [] - - def _dbus_filter(self, bus, message): - if isinstance(message, dbus.lowlevel.MethodCallMessage): - - destination = message.get_destination() - sender = message.get_sender() - - if (destination == 'org.freedesktop.DBus' or - sender == self._buses[0].get_unique_name()): - # suppress reply and don't make an Event - return dbus.lowlevel.HANDLER_RESULT_HANDLED - - e = Event('dbus-method-call', message=message, - interface=message.get_interface(), path=message.get_path(), - raw_args=message.get_args_list(byte_arrays=True), - args=map(unwrap, message.get_args_list(byte_arrays=True)), - destination=str(destination), - method=message.get_member(), - sender=message.get_sender(), - handled=False) - - for pair in self._dbus_method_impls: - pattern, cb = pair - if pattern.match(e): - cb(e) - e.handled = True - break - - self.append(e) - - return dbus.lowlevel.HANDLER_RESULT_HANDLED - - return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED - class TestEventQueue(BaseEventQueue): def __init__(self, events): BaseEventQueue.__init__(self) @@ -569,23 +413,20 @@ def call_async(test, proxy, method, *args, **kw): kw.update({'reply_handler': reply_func, 'error_handler': error_func}) method_proxy(*args, **kw) -def sync_dbus(bus, q, proxy): - # Dummy D-Bus method call. We can't use DBus.Peer.Ping() because libdbus - # replies to that message immediately, rather than handing it up to - # dbus-glib and thence the application, which means that Ping()ing the - # application doesn't ensure that it's processed all D-Bus messages prior - # to our ping. - call_async(q, dbus.Interface(proxy, 'org.freedesktop.Telepathy.Tests'), - 'DummySyncDBus') +def sync_dbus(bus, q, conn): + # Dummy D-Bus method call + # This won't do the right thing unless the proxy has a unique name. + assert conn.object.bus_name.startswith(':') + root_object = bus.get_object(conn.object.bus_name, '/') + call_async( + q, dbus.Interface(root_object, 'org.freedesktop.Telepathy.Tests'), 'DummySyncDBus') q.expect('dbus-error', method='DummySyncDBus') class ProxyWrapper: - def __init__(self, object, default, others={}): + def __init__(self, object, default, others): self.object = object self.default_interface = dbus.Interface(object, default) self.Properties = dbus.Interface(object, dbus.PROPERTIES_IFACE) - self.TpProperties = \ - dbus.Interface(object, tp_name_prefix + '.Properties') self.interfaces = dict([ (name, dbus.Interface(object, iface)) for name, iface in others.iteritems()]) @@ -599,47 +440,27 @@ class ProxyWrapper: return getattr(self.default_interface, name) -class ConnWrapper(ProxyWrapper): - def inspect_contact_sync(self, handle): - return self.inspect_contacts_sync([handle])[0] - - def inspect_contacts_sync(self, handles): - h2asv = self.Contacts.GetContactAttributes(handles, []) - ret = [] - for h in handles: - ret.append(h2asv[h][cs.ATTR_CONTACT_ID]) - return ret - - def get_contact_handle_sync(self, identifier): - return self.Contacts.GetContactByID(identifier, [])[0] - - def get_contact_handles_sync(self, ids): - return [self.get_contact_handle_sync(i) for i in ids] - def wrap_connection(conn): - return ConnWrapper(conn, tp_name_prefix + '.Connection', - dict( + return ProxyWrapper(conn, tp_name_prefix + '.Connection', + dict([ + (name, tp_name_prefix + '.Connection.Interface.' + name) + for name in ['Aliasing', 'Avatars', 'Capabilities', 'Contacts', + 'Presence', 'SimplePresence', 'Requests']] + [('Peer', 'org.freedesktop.DBus.Peer'), - ('Aliasing', cs.CONN_IFACE_ALIASING), - ('Avatars', cs.CONN_IFACE_AVATARS), - ('Contacts', cs.CONN_IFACE_CONTACTS), ('ContactCapabilities', cs.CONN_IFACE_CONTACT_CAPS), ('ContactInfo', cs.CONN_IFACE_CONTACT_INFO), ('Location', cs.CONN_IFACE_LOCATION), - ('Presence', cs.CONN_IFACE_PRESENCE), - ('Requests', cs.CONN_IFACE_REQUESTS), ('Future', tp_name_prefix + '.Connection.FUTURE'), ('MailNotification', cs.CONN_IFACE_MAIL_NOTIFICATION), ('ContactList', cs.CONN_IFACE_CONTACT_LIST), ('ContactGroups', cs.CONN_IFACE_CONTACT_GROUPS), ('PowerSaving', cs.CONN_IFACE_POWER_SAVING), - ('Addressing', cs.CONN_IFACE_ADDRESSING), ])) def wrap_channel(chan, type_, extra=None): interfaces = { type_: tp_name_prefix + '.Channel.Type.' + type_, - 'Group': cs.CHANNEL_IFACE_GROUP, + 'Group': tp_name_prefix + '.Channel.Interface.Group', } if extra: @@ -649,26 +470,14 @@ def wrap_channel(chan, type_, extra=None): return ProxyWrapper(chan, tp_name_prefix + '.Channel', interfaces) - -def wrap_content(chan, extra=None): - interfaces = { } - - if extra: - interfaces.update(dict([ - (name, tp_name_prefix + '.Call1.Content.Interface.' + name) - for name in extra])) - - return ProxyWrapper(chan, tp_name_prefix + '.Call1.Content', interfaces) - def make_connection(bus, event_func, name, proto, params): cm = bus.get_object( tp_name_prefix + '.ConnectionManager.%s' % name, - tp_path_prefix + '/ConnectionManager/%s' % name, - introspect=False) + tp_path_prefix + '/ConnectionManager/%s' % name) cm_iface = dbus.Interface(cm, tp_name_prefix + '.ConnectionManager') connection_name, connection_path = cm_iface.RequestConnection( - proto, dbus.Dictionary(params, signature='sv')) + proto, params) conn = wrap_connection(bus.get_object(connection_name, connection_path)) return conn @@ -815,16 +624,6 @@ def install_colourer(): sys.stdout = Colourer(sys.stdout, patterns) return sys.stdout -# this is just to shut up unittest. -class DummyStream(object): - def write(self, s): - if 'CHECK_TWISTED_VERBOSE' in os.environ: - print s, - - def flush(self): - pass - if __name__ == '__main__': - stream = DummyStream() - runner = unittest.TextTestRunner(stream=stream) - unittest.main(testRunner=runner) + unittest.main() + diff --git a/tests/twisted/sidecars.py b/tests/twisted/sidecars.py index 137a27f8..26a7b67c 100644 --- a/tests/twisted/sidecars.py +++ b/tests/twisted/sidecars.py @@ -9,7 +9,7 @@ from saluttest import exec_test import constants as cs from config import PLUGINS_ENABLED -TEST_PLUGIN_IFACE = "im.telepathy1.Salut.Plugin.Test" +TEST_PLUGIN_IFACE = "org.freedesktop.Telepathy.Salut.Plugin.Test" if not PLUGINS_ENABLED: print "NOTE: built without --enable-plugins, not testing plugins" diff --git a/tests/twisted/tools/Makefile.am b/tests/twisted/tools/Makefile.am index bab9999f..aaca6af3 100644 --- a/tests/twisted/tools/Makefile.am +++ b/tests/twisted/tools/Makefile.am @@ -8,12 +8,12 @@ exec-with-log.sh: exec-with-log.sh.in # We don't use the full filename for the .in because > 99 character filenames # in tarballs are non-portable (and automake 1.8 doesn't let us build # non-archaic tarballs) -im.telepathy1.ConnectionManager.%.service: %.service.in +org.freedesktop.Telepathy.ConnectionManager.%.service: %.service.in $(AM_V_GEN)sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ # D-Bus service file for testing service_in_files = salut.service.in -service_files = im.telepathy1.ConnectionManager.salut.service +service_files = org.freedesktop.Telepathy.ConnectionManager.salut.service # D-Bus config file for testing conf_in_files = tmp-session-bus.conf.in diff --git a/tests/twisted/tools/salut.service.in b/tests/twisted/tools/salut.service.in index c517ba10..93adb71d 100644 --- a/tests/twisted/tools/salut.service.in +++ b/tests/twisted/tools/salut.service.in @@ -1,3 +1,3 @@ [D-BUS Service] -Name=im.telepathy1.ConnectionManager.salut +Name=org.freedesktop.Telepathy.ConnectionManager.salut Exec=@abs_top_builddir@/tests/twisted/tools/exec-with-log.sh -- cgit v1.2.1