When removing the async calls I ended up wiping information on a callback, so moving...
[theonering] / src / channel / contact_list.py
index 0d20dc0..8f38fbd 100644 (file)
-import weakref
 import logging
 
 import telepathy
 
+import util.coroutines as coroutines
+import gtk_toolbox
 import handle
 
 
 _moduleLogger = logging.getLogger("channel.contact_list")
 
 
-def create_contact_list_channel(connection, h):
-       if h.get_name() == 'subscribe':
-               channel_class = SubscribeListChannel
-       elif h.get_name() == 'publish':
-               channel_class = PublishListChannel
-       elif h.get_name() == 'hide':
-               _moduleLogger.warn("Unsuported type %s" % h.get_name())
-       elif h.get_name() == 'allow':
-               _moduleLogger.warn("Unsuported type %s" % h.get_name())
-       elif h.get_name() == 'deny':
-               _moduleLogger.warn("Unsuported type %s" % h.get_name())
-       else:
-               raise TypeError("Unknown list type : " + h.get_name())
-       return channel_class(connection, h)
-
-
-class AbstractListChannel(
+class AllContactsListChannel(
                telepathy.server.ChannelTypeContactList,
                telepathy.server.ChannelInterfaceGroup,
        ):
-       "Abstract Contact List channels"
+       """
+       The group of contacts for whom you receive presence
+       """
 
-       def __init__(self, connection, h):
-               telepathy.server.ChannelTypeContactList.__init__(self, connection, h)
+       def __init__(self, connection, manager, props, h):
+               try:
+                       # HACK Older python-telepathy way
+                       telepathy.server.ChannelTypeContactList.__init__(self, connection, h)
+                       self._requested = props[telepathy.interfaces.CHANNEL_INTERFACE + '.Requested']
+                       self._implement_property_get(
+                               telepathy.interfaces.CHANNEL_INTERFACE,
+                               {"Requested": lambda: self._requested}
+                       )
+               except TypeError:
+                       # HACK Newer python-telepathy way
+                       telepathy.server.ChannelTypeContactList.__init__(self, connection, manager, props)
                telepathy.server.ChannelInterfaceGroup.__init__(self)
 
-               self._conn_ref = weakref.ref(connection)
-
-       def GetLocalPendingMembersWithInfo(self):
-               return []
+               self.__manager = manager
+               self.__props = props
+               self.__session = connection.session
 
+               self._callback = coroutines.func_sink(
+                       coroutines.expand_positional(
+                               self._on_contacts_refreshed
+                       )
+               )
+               self.__session.addressbook.updateSignalHandler.register_sink(
+                       self._callback
+               )
 
-class SubscribeListChannel(AbstractListChannel):
-       """
-       Subscribe List channel.
+               self.GroupFlagsChanged(0, 0)
 
-       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.
-       """
+               addressbook = connection.session.addressbook
+               contacts = addressbook.get_contacts()
+               self._process_refresh(addressbook, contacts, [])
+
+       def get_props(self):
+               # HACK Older python-telepathy doesn't provide this
+               _immutable_properties = {
+                       'ChannelType': telepathy.server.interfaces.CHANNEL_INTERFACE,
+                       'TargetHandle': telepathy.server.interfaces.CHANNEL_INTERFACE,
+                       'Interfaces': telepathy.server.interfaces.CHANNEL_INTERFACE,
+                       'TargetHandleType': telepathy.server.interfaces.CHANNEL_INTERFACE,
+                       'TargetID': telepathy.server.interfaces.CHANNEL_INTERFACE,
+                       'Requested': telepathy.server.interfaces.CHANNEL_INTERFACE
+               }
+               props = dict()
+               for prop, iface in _immutable_properties.items():
+                       props[iface + '.' + prop] = \
+                               self._prop_getters[iface][prop]()
+               return props
+
+       @gtk_toolbox.log_exception(_moduleLogger)
+       def Close(self):
+               self.close()
 
-       def __init__(self, connection, h):
-               AbstractListChannel.__init__(self, connection, h)
-               self.GroupFlagsChanged(
-                       telepathy.CHANNEL_GROUP_FLAG_CAN_ADD |
-                       telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE,
-                       0,
+       def close(self):
+               self.__session.addressbook.updateSignalHandler.unregister_sink(
+                       self._callback
+               )
+               self._callback = None
+
+               telepathy.server.ChannelTypeContactList.Close(self)
+               if self.__manager.channel_exists(self.__props):
+                       # HACK Older python-telepathy requires doing this manually
+                       self.__manager.remove_channel(self)
+               self.remove_from_connection()
+
+       @gtk_toolbox.log_exception(_moduleLogger)
+       def _on_contacts_refreshed(self, addressbook, added, removed, changed):
+               self._process_refresh(addressbook, added, removed)
+
+       def _process_refresh(self, addressbook, added, removed):
+               connection = self._conn
+               handlesAdded = [
+                       handle.create_handle(connection, "contact", contactId, phoneNumber)
+                       for contactId in added
+                       for (phoneType, phoneNumber) in addressbook.get_contact_details(contactId)
+               ]
+               handlesRemoved = [
+                       handle.create_handle(connection, "contact", contactId, phoneNumber)
+                       for contactId in removed
+                       for (phoneType, phoneNumber) in addressbook.get_contact_details(contactId)
+               ]
+               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
-               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 PublishListChannel(AbstractListChannel):
-
-       def __init__(self, connection, h):
-               AbstractListChannel.__init__(self, connection, h)
-               self.GroupFlagsChanged(0, 0)
 
-       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
-
-       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 GroupChannel(AbstractListChannel):
-
-       def __init__(self, connection, h):
-               self.__pending_add = []
-               self.__pending_remove = []
-               AbstractListChannel.__init__(self, connection, h)
-               self.GroupFlagsChanged(
-                       telepathy.CHANNEL_GROUP_FLAG_CAN_ADD | telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE,
-                       0,
-               )
+def create_contact_list_channel(connection, manager, props, h):
+       if h.get_name() == 'subscribe':
+               # The group of contacts for whom you receive presence
+               ChannelClass = AllContactsListChannel
+       elif h.get_name() == 'publish':
+               # The group of contacts who may receive your presence
+               ChannelClass = AllContactsListChannel
+       elif h.get_name() == 'hide':
+               # 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
+               _moduleLogger.warn("Unsuported type %s" % h.get_name())
+       elif h.get_name() == 'allow':
+               # A group of contacts who may send you messages
+               # @todo Allow-List would be cool to support
+               _moduleLogger.warn("Unsuported type %s" % h.get_name())
+       elif h.get_name() == 'deny':
+               # A group of contacts who may not send you messages
+               # @todo Deny-List would be cool to support
+               _moduleLogger.warn("Unsuported type %s" % h.get_name())
+       elif h.get_name() == 'stored':
+               # On protocols where the user's contacts are stored, this contact list
+               # contains all stored contacts regardless of subscription status.
+               ChannelClass = AllContactsListChannel
+       else:
+               raise TypeError("Unknown list type : " + h.get_name())
+       return ChannelClass(connection, manager, props, h)
 
-       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)
-                               _moduleLogger.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)
-                               _moduleLogger.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)
-                               _moduleLogger.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)
-                               _moduleLogger.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):
-               _moduleLogger.debug("Deleting group %s" % self._handle.name)
-               addressBook = self._conn.gvoice_client
-               group = self._handle.group
-               addressBook.delete_group(group)