diff options
Diffstat (limited to 'src/linklocal_plugin.py')
-rw-r--r-- | src/linklocal_plugin.py | 146 |
1 files changed, 140 insertions, 6 deletions
diff --git a/src/linklocal_plugin.py b/src/linklocal_plugin.py index b8f6445..7d2073c 100644 --- a/src/linklocal_plugin.py +++ b/src/linklocal_plugin.py @@ -1,3 +1,4 @@ +"""Link-local plugin for Presence Service""" # Copyright (C) 2007, Red Hat, Inc. # Copyright (C) 2007, Collabora Ltd. # @@ -15,13 +16,146 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# Standard library +import logging +from itertools import izip +from string import hexdigits + +# Other libraries import gobject +from telepathy.client import (ConnectionManager, Connection) +from telepathy.interfaces import (CONN_MGR_INTERFACE, CONN_INTERFACE, + CHANNEL_INTERFACE_GROUP) +from telepathy.constants import (HANDLE_TYPE_CONTACT, + CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_DISCONNECTED, + CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES) + +# Presence Service local modules +import psutils +from telepathy_plugin import TelepathyPlugin + + +CONN_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo' + +_logger = logging.getLogger('s-p-s.linklocal_plugin') + + +class LinkLocalPlugin(TelepathyPlugin): + """Telepathy-python-based presence server interface + + The ServerPlugin instance translates network events from + Telepathy Python into GObject events. It provides direct + python calls to perform the required network operations + to implement the PresenceService. + """ + + _TP_CONN_MANAGER = 'salut' + _PROTOCOL = 'salut' + _OBJ_PATH_PREFIX = "/org/freedesktop/Telepathy/Connection/salut/salut/" -class LinkLocalPlugin(gobject.GObject): def __init__(self, registry, owner): - gobject.GObject.__init__(self) - self._registry = registry - self._owner = owner + TelepathyPlugin.__init__(self, registry, owner) + + def _get_account_info(self): + """Retrieve connection manager parameters for this account + """ + server = self._owner.get_server() + khash = psutils.pubkey_to_keyid(self._owner.props.key) + + return { + 'nickname': '%s' % self._owner.props.nick, + 'first-name': ' ', + 'last-name': '%s' % self._owner.props.nick, + 'jid': '%s@%s' % (khash, server), + 'published-name': '%s' % self._owner.props.nick, + } + + def _find_existing_connection(self): + """Try to find an existing Telepathy connection to this server + + filters the set of connections from + telepathy.client.Connection.get_connections + to find a connection using our protocol with the + "self handle" of that connection being a handle + which matches our account (see _get_account_info) + + returns connection or None + """ + # Search existing connections, if any, that we might be able to use + connections = Connection.get_connections() + for item in connections: + if not item.object_path.startswith(self._OBJ_PATH_PREFIX): + continue + if item[CONN_INTERFACE].GetProtocol() != self._PROTOCOL: + continue + # Any Salut instance will do + return item + return None + + def identify_contacts(self, tp_chan, handles, identifiers=None): + """Work out the "best" unique identifier we can for the given handles, + in the context of the given channel (which may be None), using only + 'fast' connection manager API (that does not involve network + round-trips). + + For the XMPP server case, we proceed as follows: + + * Find the owners of the given handles, if the channel has + channel-specific handles + * If the owner (globally-valid JID) is on a trusted server, return + 'keyid/' plus the 'key fingerprint' (the user part of their JID, + currently implemented as the SHA-1 of the Base64 blob in + owner.key.pub) + * If the owner (globally-valid JID) cannot be found or is on an + untrusted server, return 'xmpp/' plus an escaped form of the JID + + The idea is that we identify buddies by key-ID (i.e. by key, assuming + no collisions) if we can find it without making network round-trips, + but if that's not possible we just use their JIDs. + + :Parameters: + `tp_chan` : telepathy.client.Channel or None + The channel in which the handles were found, or None if they + are known to be channel-specific handles + `handles` : iterable over (int or long) + The contacts' handles in that channel + :Returns: + A dict mapping the provided handles to the best available + unique identifier, which is a string that could be used as a + suffix to an object path + """ + # we need to be able to index into handles, so force them to + # be a sequence + if not isinstance(handles, (tuple, list)): + handles = tuple(handles) + + # we happen to know that Salut has no channel-specific handles + + if identifiers is None: + identifiers = self._conn[CONN_INTERFACE].InspectHandles( + HANDLE_TYPE_CONTACT, handles) + + ret = {} + for handle, ident in izip(handles, identifiers): + # special-case the Owner - we always know who we are + if handle == self.self_handle: + ret[handle] = self._owner.props.objid + continue + + # we also happen to know that on Salut, getting properties + # is immediate, and the key is (well, will be) trustworthy + + if CONN_INTERFACE_BUDDY_INFO in self._conn: + props = self._conn[CONN_INTERFACE_BUDDY_INFO].GetProperties( + handle) + key = props.get('key') + else: + key = None + + if key is not None: + khash = psutils.pubkey_to_keyid(key) + ret[handle] = 'keyid/' + khash + else: + ret[handle] = 'salut/' + psutils.escape_identifier(ident) - def cleanup(self): - pass + return ret |