-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