diff --git a/services/presence/buddy.py b/services/presence/buddy.py index ab397ef2..eb072a18 100644 --- a/services/presence/buddy.py +++ b/services/presence/buddy.py @@ -80,7 +80,9 @@ class Buddy(ExportedGObject): 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) + ([gobject.TYPE_PYOBJECT])), + 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([])), } __gproperties__ = { @@ -236,6 +238,12 @@ class Buddy(ExportedGObject): full set of properties, just the changes. """ + def add_telepathy_handle(self, tp_client, handle): + """Add a Telepathy handle.""" + conn = tp_client.get_connection() + self.TelepathyHandleAdded(conn.service_name, conn.object_path, handle) + self.handles[tp_client] = handle + @dbus.service.signal(_BUDDY_INTERFACE, signature='sou') def TelepathyHandleAdded(self, tp_conn_name, tp_conn_path, handle): """Another Telepathy handle has become associated with the buddy. @@ -250,6 +258,21 @@ class Buddy(ExportedGObject): newly associated with the buddy """ + def remove_telepathy_handle(self, tp_client, handle): + """Remove a Telepathy handle.""" + conn = tp_client.get_connection() + my_handle = self.handles.get(tp_client, 0) + if my_handle == handle: + del self.handles[tp_client] + self.TelepathyHandleRemoved(conn.service_name, conn.object_path, + handle) + if not self.handles: + self.emit('disappeared') + else: + _logger.debug('Telepathy handle %u supposedly removed, but ' + 'my handle on that connection is %u - ignoring', + handle, my_handle) + @dbus.service.signal(_BUDDY_INTERFACE, signature='sou') def TelepathyHandleRemoved(self, tp_conn_name, tp_conn_path, handle): """A Telepathy handle has ceased to be associated with the buddy, diff --git a/services/presence/presenceservice.py b/services/presence/presenceservice.py index c554ce6e..6273a22c 100644 --- a/services/presence/presenceservice.py +++ b/services/presence/presenceservice.py @@ -126,12 +126,12 @@ class PresenceService(ExportedGObject): objid = self._get_next_object_id() buddy = Buddy(self._bus_name, objid, key=key) buddy.connect("validity-changed", self._buddy_validity_changed_cb) + buddy.connect("disappeared", self._buddy_disappeared_cb) self._buddies[key] = buddy - buddies = self._handles_buddies[tp] - buddies[handle] = buddy + self._handles_buddies[tp][handle] = buddy # store the handle of the buddy for this CM - buddy.handles[tp] = handle + buddy.add_telepathy_handle(tp, handle) buddy.set_properties(props) @@ -143,6 +143,12 @@ class PresenceService(ExportedGObject): self.BuddyDisappeared(buddy.object_path()) _logger.debug("Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)) + def _buddy_disappeared_cb(self, buddy): + if buddy.props.valid: + self.BuddyDisappeared(buddy.object_path()) + _logger.debug('Buddy left: %s (%s)' % (buddy.props.nick, buddy.props.color) + self._buddies.pop(buddy.props.key) + def _contact_offline(self, tp, handle): if not self._handles_buddies[tp].has_key(handle): return @@ -151,12 +157,7 @@ class PresenceService(ExportedGObject): key = buddy.props.key # the handle of the buddy for this CM is not valid anymore - buddy.handles.pop(tp) - if not buddy.handles: - if buddy.props.valid: - self.BuddyDisappeared(buddy.object_path()) - _logger.debug("Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)) - self._buddies.pop(key) + buddy.remove_telepathy_handle(tp, handle) def _get_next_object_id(self): """Increment and return the object ID counter.""" @@ -304,6 +305,42 @@ class PresenceService(ExportedGObject): return buddy.object_path() raise NotFoundError("The buddy was not found.") + @dbus.service.method(_PRESENCE_INTERFACE, in_signature='sou', + out_signature='o') + def GetBuddyByTelepathyHandle(self, tp_conn_name, tp_conn_path, handle): + """Get the buddy corresponding to a Telepathy handle. + + :Parameters: + `tp_conn_name` : str + The well-known bus name of a Telepathy connection + `tp_conn_path` : dbus.ObjectPath + The object path of the Telepathy connection + `handle` : int or long + The handle of a Telepathy contact on that connection, + of type HANDLE_TYPE_CONTACT. This may not be a + channel-specific handle. + :Returns: the object path of a Buddy + :Raises NotFoundError: if the buddy is not found. + """ + for tp, handles in self._handles_buddies.iteritems(): + conn = tp.get_connection() + if conn is None: + continue + if (conn.service_name == tp_conn_name + and conn.object_path == tp_conn_path): + buddy = handles.get(handle) + if buddy is not None and buddy.props.valid: + return buddy.object_path() + # either the handle is invalid, or we don't have a Buddy + # object for that buddy because we don't have all their + # details yet + raise NotFoundError("The buddy %u was not found on the " + "connection to %s:%s" + % (handle, tp_conn_name, tp_conn_path)) + raise NotFoundError("The buddy %u was not found: we have no " + "connection to %s:%s" % (handle, tp_conn_name, + tp_conn_path)) + @dbus.service.method(_PRESENCE_INTERFACE, out_signature="o") def GetOwner(self): if not self._owner: