diff --git a/src/presenceservice.py b/src/presenceservice.py index 30c6dab..1503dac 100644 --- a/src/presenceservice.py +++ b/src/presenceservice.py @@ -834,6 +834,11 @@ class PresenceService(ExportedGObject): else: activity.set_properties(props) + @dbus.service.method(PRESENCE_INTERFACE, in_signature="", + out_signature="") + def SyncFriends(self): + if self._server_plugin is not None: + self._server_plugin.sync_friends() def main(test_num=0, randomize=False): loop = gobject.MainLoop() diff --git a/src/server_plugin.py b/src/server_plugin.py index 834eedc..90a9ca2 100644 --- a/src/server_plugin.py +++ b/src/server_plugin.py @@ -17,7 +17,10 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Standard library +import base64 +import ConfigParser import logging +import os from itertools import izip from string import hexdigits @@ -31,6 +34,8 @@ from telepathy.constants import (HANDLE_TYPE_CONTACT, CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_DISCONNECTED, CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES, CONNECTION_STATUS_REASON_NAME_IN_USE) +import sugar.env +import sugar.profile # Presence Service local modules import psutils @@ -287,11 +292,13 @@ class ServerPlugin(TelepathyPlugin): local_pending, '') self._add_not_subscribed_to_subscribe_channel() + self.sync_friends() def _subscribe_channel_cb(self, channel): TelepathyPlugin._subscribe_channel_cb(self, channel) self._add_not_subscribed_to_subscribe_channel() + self.sync_friends() def _add_not_subscribed_to_subscribe_channel(self): if self._publish_channel is None or self._subscribe_channel is None: @@ -306,3 +313,76 @@ class ServerPlugin(TelepathyPlugin): not_subscribed -= self._subscribe_members self._subscribe_channel[CHANNEL_INTERFACE_GROUP].AddMembers( not_subscribed, '') + + def sync_friends(self): + """Synchornise our local friends list with our roster on the + server.""" + + try: + return self._sync_friends_ignore_dbus_error() + except dbus.DBusException, exc: + # something bad happened while using the connection; give up on + # syncing friend list + _logger.debug('error syncing friends: %s' % exc) + return False + + def _sync_friends_ignore_dbus_error(self): + if self._publish_channel is None or self._subscribe_channel is None: + # not ready yet; try again later + return True + + profile_path = sugar.env.get_profile_path() + friends_path = os.path.join(profile_path, 'friends') + config_path = os.path.join(profile_path, 'config') + parser = ConfigParser.SafeConfigParser() + + if parser.read([friends_path]) == []: + # friends list is invalid; try again later + _logger.debug('error reading friends list') + return True + + profile = sugar.profile.Profile(config_path) + + if not profile.is_valid(): + # profile is invalid; try again later + return True + + friends = set() + + for section in parser.sections: + try: + key = base64.b64decode(section) + except TypeError: + # key is invalid; skip this friend + _logger.debug('skipping friend with invalid key') + continue + + id = psutils.pubkey_to_keyid(key) + # this assumes that all our friends are on the same server as us + jid = '%s@%s' % (id, profile.jabber_server) + friends.add(jid) + + handles = self._conn[CONN_INTERFACE].RequestHandles( + HANDLE_TYPE_CONTACT, friends) + handles = set(handles) + + cur, local_p, remote_p = self._subscribe_channel[ + CHANNEL_INTERFACE_GROUP].GetAllMembers() + subscribe_handles = set(cur) + set(remote_p) + + # stop sending presence to people not on our friend list + _logger.debug('removing friends from roster: %r' % ( + subscribe_handles - handles)) + self._publish_channel[CHANNEL_INTERFACE_GROUP].RemoveMembers( + subscribe_handles - handles) + + # do send presence to people on our friend list + _logger.debug('adding friends to roster: %r' % ( + handles - subscribe_handles)) + self._subscribe_channel[CHANNEL_INTERFACE_GROUP].AddMembers( + handles - subscribe_handles) + + # do this again later + _logger.debug('friends list synced with server') + return True +