X-Git-Url: http://git.maemo.org/git/?p=theonering;a=blobdiff_plain;f=src%2Fchannel%2Fcontact_list.py;h=e184d2d6ddb97092530d8697205138a86ae9c8dc;hp=62c2d67f4e8c51fd184c4abe5da7df0816313eab;hb=1c892d1b9bf14b28eb54ce3590ed2ee29d5e3d25;hpb=c36275efc363d3ef81f31e67526908e11a6ae030 diff --git a/src/channel/contact_list.py b/src/channel/contact_list.py index 62c2d67..e184d2d 100644 --- a/src/channel/contact_list.py +++ b/src/channel/contact_list.py @@ -1,173 +1,179 @@ -import weakref import logging import telepathy +import tp +import util.coroutines as coroutines +import util.misc as misc_utils import handle -def create_contact_list_channel(connection, h): - if h.get_name() == 'subscribe': - channel_class = TheOneRingSubscribeListChannel - elif h.get_name() == 'publish': - channel_class = TheOneRingPublishListChannel - elif h.get_name() == 'hide': - logging.warn("Unsuported type %s" % h.get_name()) - elif h.get_name() == 'allow': - logging.warn("Unsuported type %s" % h.get_name()) - elif h.get_name() == 'deny': - logging.warn("Unsuported type %s" % h.get_name()) - else: - raise TypeError("Unknown list type : " + h.get_name()) - return channel_class(connection, h) - - -class TheOneRingListChannel( - telepathy.server.ChannelTypeContactList, - telepathy.server.ChannelInterfaceGroup, +_moduleLogger = logging.getLogger("channel.contact_list") + + +class AllContactsListChannel( + tp.ChannelTypeContactList, + tp.ChannelInterfaceGroup, ): - "Abstract Contact List channels" + """ + The group of contacts for whom you receive presence + """ + + def __init__(self, connection, manager, props, listHandle): + tp.ChannelTypeContactList.__init__(self, connection, manager, props) + tp.ChannelInterfaceGroup.__init__(self) + + self.__manager = manager + self.__props = props + self.__session = connection.session + self.__listHandle = listHandle + self.__members = set() + + if self._conn.options.useGVContacts: + self._callback = coroutines.func_sink( + coroutines.expand_positional( + self._on_contacts_refreshed + ) + ) + self.__session.addressbook.updateSignalHandler.register_sink( + self._callback + ) + + addressbook = connection.session.addressbook + contacts = addressbook.get_numbers() + self._process_refresh(addressbook, set(contacts), set(), set()) + else: + self._callback = None + + self.GroupFlagsChanged(0, 0) - def __init__(self, connection, h): - telepathy.server.ChannelTypeContactList.__init__(self, connection, h) - telepathy.server.ChannelInterfaceGroup.__init__(self) - self._conn_ref = weakref.ref(connection) + @misc_utils.log_exception(_moduleLogger) + def Close(self): + self.close() + + def close(self): + _moduleLogger.debug("Closing contact list") + if self._callback is not None: + self.__session.addressbook.updateSignalHandler.unregister_sink( + self._callback + ) + self._callback = None + tp.ChannelTypeContactList.Close(self) + self.remove_from_connection() + + @misc_utils.log_exception(_moduleLogger) def GetLocalPendingMembersWithInfo(self): return [] + @misc_utils.log_exception(_moduleLogger) + def _on_contacts_refreshed(self, addressbook, added, removed, changed): + self._process_refresh(addressbook, added, removed, changed) -class TheOneRingSubscribeListChannel(TheOneRingListChannel): - """ - Subscribe List channel. + def _is_on_list(self, number): + return True - This channel contains the list of contact to whom the current used is - 'subscribed', basically this list contains the contact for whom you are - supposed to receive presence notification. - """ + def _is_on_list_changed(self, number): + return (number in self.__members) ^ (self._is_on_list(number)) - def __init__(self, connection, h): - TheOneRingListChannel.__init__(self, connection, h) - self.GroupFlagsChanged( - telepathy.CHANNEL_GROUP_FLAG_CAN_ADD | - telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE, - 0, - ) + def _is_now_on_list(self, number): + return number not in self.__members and self._is_on_list(number) - def AddMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - for h in contacts: - h = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, h) - contact = h.contact - if contact is None: - account = h.account - else: - account = contact.account - groups = list(h.pending_groups) - h.pending_groups = set() - addressBook.add_messenger_contact(account, - invite_message=message.encode('utf-8'), - groups=groups) - - def RemoveMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - for h in contacts: - h = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, h) - contact = h.contact - addressBook.delete_contact(contact) - - -class TheOneRingPublishListChannel(TheOneRingListChannel): - - def __init__(self, connection, h): - TheOneRingListChannel.__init__(self, connection, h) - self.GroupFlagsChanged(0, 0) + def _was_on_list(self, number): + return number in self.__members and not self._is_on_list(number) + + def _process_refresh(self, addressbook, added, removed, changed): + _moduleLogger.info( + "%s Added: %r, Removed: %r" % (self.__listHandle.get_name(), len(added), len(removed)) + ) + connection = self._conn - def AddMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, - contactHandleId) - contact = contactHandle.contact - addressBook.accept_contact_invitation(contact, False) - - def RemoveMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, - contactHandleId) - contact = contactHandle.contact + # convert changed into added/removed + alsoAdded = set( + number + for number in changed + if self._is_now_on_list(number) + ) + alsoRemoved = set( + number + for number in changed + if self._was_on_list(number) + ) - def GetLocalPendingMembersWithInfo(self): - addressBook = self._conn.gvoice_client - result = [] - for contact in addressBook.contacts: - h = handle.create_handle(self._conn_ref(), 'contact', - contact.account, contact.network_id) - result.append((h, h, - telepathy.CHANNEL_GROUP_CHANGE_REASON_INVITED, - contact.attributes.get('invite_message', ''))) - return result - - -class TheOneRingGroupChannel(TheOneRingListChannel): - - def __init__(self, connection, h): - self.__pending_add = [] - self.__pending_remove = [] - TheOneRingListChannel.__init__(self, connection, h) - self.GroupFlagsChanged( - telepathy.CHANNEL_GROUP_FLAG_CAN_ADD | telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE, - 0, + # Merge the added/removed with changed + added = [ + contactNumber + for contactNumber in added + if self._is_on_list(contactNumber) + ] + added.extend(alsoAdded) + added.sort() + handlesAdded = [ + handle.create_handle(connection, "contact", contactNumber) + for contactNumber in added + ] + self.__members.union(added) + + removed = list(removed) + removed.extend(alsoRemoved) + handlesRemoved = [ + handle.create_handle(connection, "contact", contactNumber) + for contactNumber in removed + ] + self.__members.difference(removed) + + message = "" + actor = 0 + reason = telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE + self.MembersChanged( + message, + handlesAdded, handlesRemoved, + (), (), + actor, + reason, ) - def AddMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - if self._handle.group is None: - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId) - logging.info("Adding contact %r to pending group %r" % (contactHandle, self._handle)) - if contactHandleId in self.__pending_remove: - self.__pending_remove.remove(contactHandleId) - else: - self.__pending_add.append(contactHandleId) - return - else: - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId) - logging.info("Adding contact %r to group %r" % (contactHandle, self._handle)) - contact = contactHandle.contact - group = self._handle.group - if contact is not None: - addressBook.add_contact_to_group(group, contact) - else: - contactHandle.pending_groups.add(group) - - def RemoveMembers(self, contacts, message): - addressBook = self._conn.gvoice_client - if self._handle.group is None: - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId) - logging.info("Adding contact %r to pending group %r" % (contactHandle, self._handle)) - if contactHandleId in self.__pending_add: - self.__pending_add.remove(contactHandleId) - else: - self.__pending_remove.append(contactHandleId) - return - else: - for contactHandleId in contacts: - contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId) - logging.info("Removing contact %r from pending group %r" % (contactHandle, self._handle)) - contact = contactHandle.contact - group = self._handle.group - if contact is not None: - addressBook.delete_contact_from_group(group, contact) - else: - contactHandle.pending_groups.discard(group) - def Close(self): - logging.debug("Deleting group %s" % self._handle.name) - addressBook = self._conn.gvoice_client - group = self._handle.group - addressBook.delete_group(group) +class DenyContactsListChannel(AllContactsListChannel): + + def _is_on_list(self, number): + return self._conn.session.addressbook.is_blocked(number) + + +_LIST_TO_FACTORY = { + # The group of contacts for whom you receive presence + 'subscribe': AllContactsListChannel, + # The group of contacts who may receive your presence + 'publish': AllContactsListChannel, + # A group of contacts who are on the publish list but are temporarily + # disallowed from receiving your presence + # This doesn't make sense to support + 'hide': None, + # A group of contacts who may send you messages + # Is this meant to serve as a definitive white list for all contact? + 'allow': None, + # A group of contacts who may not send you messages + 'deny': DenyContactsListChannel, + # On protocols where the user's contacts are stored, this contact list + # contains all stored contacts regardless of subscription status. + 'stored': AllContactsListChannel, +} + + +_SUPPORTED_LISTS = frozenset( + name + for name in _LIST_TO_FACTORY.iterkeys() + if name is not None +) + + +def create_contact_list_channel(connection, manager, props, h): + factory = _LIST_TO_FACTORY.get(h.get_name(), None) + if factory is None: + raise telepathy.errors.NotCapable("Unsuported type %s" % h.get_name()) + return factory(connection, manager, props, h) + + +def get_spported_lists(): + return _SUPPORTED_LISTS