Switching to shipping parts of python-telepathy with The One Ring so random changes...
authorEd Page <eopage@byu.net>
Fri, 15 Jan 2010 01:25:39 +0000 (19:25 -0600)
committerEd Page <eopage@byu.net>
Fri, 15 Jan 2010 01:25:39 +0000 (19:25 -0600)
26 files changed:
src/aliasing.py
src/capabilities.py
src/channel/call.py
src/channel/contact_list.py
src/channel/debug_prompt.py
src/channel/text.py
src/channel_manager.py
src/connection.py
src/connection_manager.py
src/debug.py
src/gvoice/addressbook.py
src/handle.py
src/location.py
src/presence.py
src/requests.py
src/simple_presence.py
src/tp/__init__.py [new file with mode: 0644]
src/tp/channel.py [new file with mode: 0644]
src/tp/channelhandler.py [new file with mode: 0644]
src/tp/channelmanager.py [new file with mode: 0644]
src/tp/conn.py [new file with mode: 0644]
src/tp/connmgr.py [new file with mode: 0644]
src/tp/debug.py [new file with mode: 0644]
src/tp/handle.py [new file with mode: 0644]
src/tp/media.py [new file with mode: 0644]
src/tp/properties.py [new file with mode: 0644]

index f25c06f..2911b04 100644 (file)
@@ -2,6 +2,7 @@ import logging
 
 import telepathy
 
+import tp
 import gtk_toolbox
 import util.misc as util_misc
 import handle
@@ -88,13 +89,13 @@ def make_pretty(phonenumber):
        return prettynumber.strip()
 
 
-class AliasingMixin(telepathy.server.ConnectionInterfaceAliasing):
+class AliasingMixin(tp.ConnectionInterfaceAliasing):
 
        USER_ALIAS_ACCOUNT = "account"
        USER_ALIAS_CALLBACK = "callback"
 
        def __init__(self):
-               telepathy.server.ConnectionInterfaceAliasing.__init__(self)
+               tp.ConnectionInterfaceAliasing.__init__(self)
 
        @property
        def session(self):
@@ -146,7 +147,7 @@ class AliasingMixin(telepathy.server.ConnectionInterfaceAliasing):
                # first validate that no other handle types are included
                userHandleAndAlias = None
                for handleId, alias in aliases.iteritems():
-                       h = self.handle(telepathy.HANDLE_TYPE_CONTACT, handleId)
+                       h = self.get_handle_by_id(telepathy.HANDLE_TYPE_CONTACT, handleId)
                        if not isinstance(h, handle.ConnectionHandle):
                                raise telepathy.errors.PermissionDenied("No user customizable aliases")
                        userHandleAndAlias = h, alias
@@ -163,7 +164,7 @@ class AliasingMixin(telepathy.server.ConnectionInterfaceAliasing):
                self.AliasesChanged(changedAliases)
 
        def _get_alias(self, handleId):
-               h = self.handle(telepathy.HANDLE_TYPE_CONTACT, handleId)
+               h = self.get_handle_by_id(telepathy.HANDLE_TYPE_CONTACT, handleId)
                if isinstance(h, handle.ConnectionHandle):
                        if self.userAliasType == self.USER_ALIAS_CALLBACK:
                                aliasNumber = self.session.backend.get_callback_number()
index c4a94cb..6c7ba15 100644 (file)
@@ -1,93 +1,11 @@
 import logging
 
-import telepathy
-
-import gtk_toolbox
+import tp
 
 
 _moduleLogger = logging.getLogger('capabilities')
 
 
-class CapabilitiesMixin(telepathy.server.ConnectionInterfaceCapabilities):
-
-       def __init__(self):
-               telepathy.server.ConnectionInterfaceCapabilities.__init__(self)
-               self._implement_property_get(
-                       telepathy.interfaces.CONN_INTERFACE_CAPABILITIES,
-                       {"caps": self.GetCapabilities},
-               )
-
-       @property
-       def session(self):
-               """
-               @abstract
-               """
-               raise NotImplementedError("Abstract property called")
-
-       def handle(self, handleType, handleId):
-               """
-               @abstract
-               """
-               raise NotImplementedError("Abstract function called")
-
-       def GetSelfHandle(self):
-               """
-               @abstract
-               """
-               raise NotImplementedError("Abstract function called")
-
-       @gtk_toolbox.log_exception(_moduleLogger)
-       def GetCapabilities(self, handleIds):
-               """
-               @todo HACK Remove this once we are building against a fixed version of python-telepathy
-               """
-               ret = []
-               for handleId in handleIds:
-                       h = self.handle(telepathy.HANDLE_TYPE_CONTACT, handleId)
-                       if handleId != 0 and (telepathy.HANDLE_TYPE_CONTACT, handleId) not in self._handles:
-                               raise telepathy.errors.InvalidHandle
-                       elif h in self._caps:
-                               types = self._caps[h]
-                               for type in types:
-                                       for ctype, specs in types.iteritems():
-                                               ret.append([handleId, type, specs[0], specs[1]])
-                       else:
-                               # No caps, so just default to the connection's caps
-                               types = self._caps[self.GetSelfHandle()]
-                               for type in types:
-                                       for ctype, specs in types.iteritems():
-                                               ret.append([handleId, type, specs[0], specs[1]])
-               return ret
-
-       @gtk_toolbox.log_exception(_moduleLogger)
-       def AdvertiseCapabilities(self, add, remove):
-               """
-               @todo HACK Remove this once we are building against a fixed version of python-telepathy
-               """
-               my_caps = self._caps.setdefault(self.GetSelfHandle(), {})
-
-               changed = {}
-               for ctype, spec_caps in add:
-                       changed[ctype] = spec_caps
-               for ctype in remove:
-                       changed[ctype] = None
-
-               caps = []
-               for ctype, spec_caps in changed.iteritems():
-                       gen_old, spec_old = my_caps.get(ctype, (0, 0))
-                       if spec_caps is None:
-                               # channel type no longer supported (provider has gone away)
-                               gen_new, spec_new = 0, 0
-                       else:
-                               # channel type supports new capabilities
-                               gen_new, spec_new = gen_old, spec_old | spec_caps
-                       if spec_old != spec_new or gen_old != gen_new:
-                               caps.append((self.GetSelfHandle(), ctype, gen_old, gen_new,
-                                                       spec_old, spec_new))
-
-               self.CapabilitiesChanged(caps)
-               _moduleLogger.info("CapsChanged %r" % self._caps)
+class CapabilitiesMixin(tp.ConnectionInterfaceCapabilities):
 
-               # return all my capabilities
-               ret = [(ctype, caps[1]) for ctype, caps in my_caps.iteritems()]
-               return ret
+       pass
index 0c39f7e..7155df2 100644 (file)
@@ -3,6 +3,7 @@ import logging
 import gobject
 import telepathy
 
+import tp
 import gtk_toolbox
 import handle
 
@@ -11,9 +12,9 @@ _moduleLogger = logging.getLogger("channel.call")
 
 
 class CallChannel(
-               telepathy.server.ChannelTypeStreamedMedia,
-               telepathy.server.ChannelInterfaceCallState,
-               telepathy.server.ChannelInterfaceGroup,
+               tp.ChannelTypeStreamedMedia,
+               tp.ChannelInterfaceCallState,
+               tp.ChannelInterfaceGroup,
        ):
 
        def __init__(self, connection, manager, props, contactHandle):
@@ -21,19 +22,9 @@ class CallChannel(
                self.__props = props
                self.__cancelId = None
 
-               try:
-                       # HACK Older python-telepathy way
-                       telepathy.server.ChannelTypeStreamedMedia.__init__(self, connection, contactHandle)
-                       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.ChannelTypeStreamedMedia.__init__(self, connection, manager, props)
-               telepathy.server.ChannelInterfaceCallState.__init__(self)
-               telepathy.server.ChannelInterfaceGroup.__init__(self)
+               tp.ChannelTypeStreamedMedia.__init__(self, connection, manager, props)
+               tp.ChannelInterfaceCallState.__init__(self)
+               tp.ChannelInterfaceGroup.__init__(self)
                self.__contactHandle = contactHandle
                self._implement_property_get(
                        telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA,
@@ -43,16 +34,6 @@ class CallChannel(
                        },
                )
 
-               # HACK Older python-telepathy doesn't provide this
-               self._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
-               }
-
                self.GroupFlagsChanged(0, 0)
                self.MembersChanged(
                        '', [self._conn.GetSelfHandle()], [], [], [contactHandle],
@@ -65,23 +46,12 @@ class CallChannel(
        def initial_video(self):
                return False
 
-       def get_props(self):
-               # HACK Older python-telepathy doesn't provide this
-               props = dict()
-               for prop, iface in self._immutable_properties.items():
-                       props[iface + '.' + prop] = \
-                               self._prop_getters[iface][prop]()
-               return props
-
        @gtk_toolbox.log_exception(_moduleLogger)
        def Close(self):
                self.close()
 
        def close(self):
-               telepathy.server.ChannelTypeStreamedMedia.Close(self)
-               if self.__manager.channel_exists(self.__props):
-                       # HACK Older python-telepathy requires doing this manually
-                       self.__manager.remove_channel(self)
+               tp.ChannelTypeStreamedMedia.Close(self)
                self.remove_from_connection()
                if self.__cancelId is not None:
                        gobject.source_remove(self.__cancelId)
@@ -118,7 +88,7 @@ class CallChannel(
 
                @returns [(Stream ID, contact, stream type, stream state, stream direction, pending send flags)]
                """
-               contact = self._conn.handle(telepathy.constants.HANDLE_TYPE_CONTACT, contactId)
+               contact = self._conn.get_handle_by_id(telepathy.constants.HANDLE_TYPE_CONTACT, contactId)
                assert self.__contactHandle == contact, "%r != %r" % (self.__contactHandle, contact)
                contactId, contactNumber = handle.ContactHandle.from_handle_name(contact.name)
 
index d247ec6..be99430 100644 (file)
@@ -2,6 +2,7 @@ import logging
 
 import telepathy
 
+import tp
 import util.coroutines as coroutines
 import gtk_toolbox
 import handle
@@ -11,41 +12,21 @@ _moduleLogger = logging.getLogger("channel.contact_list")
 
 
 class AllContactsListChannel(
-               telepathy.server.ChannelTypeContactList,
-               telepathy.server.ChannelInterfaceGroup,
+               tp.ChannelTypeContactList,
+               tp.ChannelInterfaceGroup,
        ):
        """
        The group of contacts for whom you receive presence
        """
 
        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)
+               tp.ChannelTypeContactList.__init__(self, connection, manager, props)
+               tp.ChannelInterfaceGroup.__init__(self)
 
                self.__manager = manager
                self.__props = props
                self.__session = connection.session
 
-               # HACK Older python-telepathy doesn't provide this
-               self._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
-               }
-
                self._callback = coroutines.func_sink(
                        coroutines.expand_positional(
                                self._on_contacts_refreshed
@@ -58,16 +39,9 @@ class AllContactsListChannel(
                self.GroupFlagsChanged(0, 0)
 
                addressbook = connection.session.addressbook
-               contacts = addressbook.get_contacts()
-               self._process_refresh(addressbook, contacts, [])
+               contacts = addressbook.get_contact_ids()
+               self._process_refresh(addressbook, set(contacts), set())
 
-       def get_props(self):
-               # HACK Older python-telepathy doesn't provide this
-               props = dict()
-               for prop, iface in self._immutable_properties.items():
-                       props[iface + '.' + prop] = \
-                               self._prop_getters[iface][prop]()
-               return props
 
        @gtk_toolbox.log_exception(_moduleLogger)
        def Close(self):
@@ -79,10 +53,7 @@ class AllContactsListChannel(
                )
                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)
+               tp.ChannelTypeContactList.Close(self)
                self.remove_from_connection()
 
        @gtk_toolbox.log_exception(_moduleLogger)
index 79b64be..6985725 100644 (file)
@@ -6,13 +6,14 @@ import logging
 
 import telepathy
 
+import tp
 import gtk_toolbox
 
 
 _moduleLogger = logging.getLogger("channel.text")
 
 
-class DebugPromptChannel(telepathy.server.ChannelTypeText, cmd.Cmd):
+class DebugPromptChannel(tp.ChannelTypeText, cmd.Cmd):
        """
        Look into implementing ChannelInterfaceMessages for rich text formatting
        """
@@ -23,40 +24,12 @@ class DebugPromptChannel(telepathy.server.ChannelTypeText, cmd.Cmd):
 
                cmd.Cmd.__init__(self, "Debug Prompt")
                self.use_rawinput = False
-               try:
-                       # HACK Older python-telepathy way
-                       telepathy.server.ChannelTypeText.__init__(self, connection, contactHandle)
-                       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.ChannelTypeText.__init__(self, connection, manager, props)
+               tp.ChannelTypeText.__init__(self, connection, manager, props)
                self.__nextRecievedId = 0
                self.__lastMessageTimestamp = datetime.datetime(1, 1, 1)
 
                self.__otherHandle = contactHandle
 
-               # HACK Older python-telepathy doesn't provide this
-               self._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
-               }
-
-       def get_props(self):
-               # HACK Older python-telepathy doesn't provide this
-               props = dict()
-               for prop, iface in self._immutable_properties.items():
-                       props[iface + '.' + prop] = \
-                               self._prop_getters[iface][prop]()
-               return props
-
        @gtk_toolbox.log_exception(_moduleLogger)
        def Send(self, messageType, text):
                if messageType != telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
@@ -79,10 +52,7 @@ class DebugPromptChannel(telepathy.server.ChannelTypeText, cmd.Cmd):
                self.close()
 
        def close(self):
-               telepathy.server.ChannelTypeText.Close(self)
-               if self.__manager.channel_exists(self.__props):
-                       # HACK Older python-telepathy requires doing this manually
-                       self.__manager.remove_channel(self)
+               tp.ChannelTypeText.Close(self)
                self.remove_from_connection()
 
        def _report_new_message(self, message):
index 64ee0be..b1a7aa9 100644 (file)
@@ -4,6 +4,7 @@ import logging
 
 import telepathy
 
+import tp
 import util.coroutines as coroutines
 import gtk_toolbox
 
@@ -11,7 +12,7 @@ import gtk_toolbox
 _moduleLogger = logging.getLogger("channel.text")
 
 
-class TextChannel(telepathy.server.ChannelTypeText):
+class TextChannel(tp.ChannelTypeText):
        """
        Look into implementing ChannelInterfaceMessages for rich text formatting
        """
@@ -20,32 +21,12 @@ class TextChannel(telepathy.server.ChannelTypeText):
                self.__manager = manager
                self.__props = props
 
-               try:
-                       # HACK Older python-telepathy way
-                       telepathy.server.ChannelTypeText.__init__(self, connection, contactHandle)
-                       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.ChannelTypeText.__init__(self, connection, manager, props)
+               tp.ChannelTypeText.__init__(self, connection, manager, props)
                self.__nextRecievedId = 0
                self.__lastMessageTimestamp = datetime.datetime(1, 1, 1)
 
                self.__otherHandle = contactHandle
 
-               # HACK Older python-telepathy doesn't provide this
-               self._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
-               }
-
                self.__callback = coroutines.func_sink(
                        coroutines.expand_positional(
                                self._on_conversations_updated
@@ -73,14 +54,6 @@ class TextChannel(telepathy.server.ChannelTypeText):
                else:
                        self._report_conversation(mergedConversations)
 
-       def get_props(self):
-               # HACK Older python-telepathy doesn't provide this
-               props = dict()
-               for prop, iface in self._immutable_properties.items():
-                       props[iface + '.' + prop] = \
-                               self._prop_getters[iface][prop]()
-               return props
-
        @gtk_toolbox.log_exception(_moduleLogger)
        def Send(self, messageType, text):
                if messageType != telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
@@ -105,10 +78,7 @@ class TextChannel(telepathy.server.ChannelTypeText):
                )
                self.__callback = None
 
-               telepathy.server.ChannelTypeText.Close(self)
-               if self.__manager.channel_exists(self.__props):
-                       # HACK Older python-telepathy requires doing this manually
-                       self.__manager.remove_channel(self)
+               tp.ChannelTypeText.Close(self)
                self.remove_from_connection()
 
        @property
index 18b682e..a696d90 100644 (file)
@@ -3,6 +3,7 @@ import logging
 import dbus
 import telepathy
 
+import tp
 import channel
 import util.misc as util_misc
 
@@ -10,95 +11,10 @@ import util.misc as util_misc
 _moduleLogger = logging.getLogger("channel_manager")
 
 
-class TelepathyChannelManager(object):
+class ChannelManager(tp.ChannelManager):
 
        def __init__(self, connection):
-               self._conn = connection
-
-               self._requestable_channel_classes = dict()
-               self._channels = dict()
-               self._fixed_properties = dict()
-               self._available_properties = dict()
-
-       def close(self):
-               for channel_type in self._requestable_channel_classes:
-                       for chan in self._channels[channel_type].values():
-                               try:
-                                       _moduleLogger.debug("Closing %s %s" % (channel_type, chan._object_path))
-                                       chan.Close()
-                               except Exception:
-                                       _moduleLogger.exception("Shutting down %r" % (chan, ))
-
-       def remove_channel(self, chan):
-               for channel_type in self._requestable_channel_classes:
-                       for handle, ichan in self._channels[channel_type].items():
-                               if chan == ichan:
-                                       del self._channels[channel_type][handle]
-
-       def _get_type_requested_handle(self, props):
-               type = props[telepathy.interfaces.CHANNEL_INTERFACE + '.ChannelType']
-               requested = props[telepathy.interfaces.CHANNEL_INTERFACE + '.Requested']
-               target_handle = props[telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle']
-               target_handle_type = props[telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandleType']
-
-               handle = self._conn._handles[target_handle_type, target_handle]
-
-               return (type, requested, handle)
-
-       def channel_exists(self, props):
-               type, _, handle = self._get_type_requested_handle(props)
-
-               if type in self._channels:
-                       if handle in self._channels[type]:
-                               return True
-
-               return False
-
-       def channel_for_props(self, props, signal=True, **args):
-               type, suppress_handler, handle = self._get_type_requested_handle(props)
-
-               if type not in self._requestable_channel_classes:
-                       raise NotImplemented('Unknown channel type "%s"' % type)
-
-               if self.channel_exists(props):
-                       return self._channels[type][handle]
-
-               chan = self._requestable_channel_classes[type](props, **args)
-
-               if hasattr(self._conn, "add_channels"):
-                       # HACK Newer python-telepathy
-                       self._conn.add_channels([chan], signal=signal)
-               elif hasattr(self._conn, "add_channel"):
-                       # HACK Older python-telepathy
-                       self._conn.NewChannels([(chan._object_path, chan.get_props())])
-                       self._conn.add_channel(chan, handle, suppress_handler)
-               else:
-                       raise RuntimeError("Uhh, what just happened with the connection")
-               self._channels[type][handle] = chan
-
-               return chan
-
-       def _implement_channel_class(self, type, make_channel, fixed, available):
-               self._requestable_channel_classes[type] = make_channel
-               self._channels.setdefault(type, {})
-
-               self._fixed_properties[type] = fixed
-               self._available_properties[type] = available
-
-       def get_requestable_channel_classes(self):
-               retval = []
-
-               for channel_type in self._requestable_channel_classes:
-                       retval.append((self._fixed_properties[channel_type],
-                               self._available_properties[channel_type]))
-
-               return retval
-
-
-class ChannelManager(TelepathyChannelManager):
-
-       def __init__(self, connection):
-               TelepathyChannelManager.__init__(self, connection)
+               tp.ChannelManager.__init__(self, connection)
 
                fixed = {
                        telepathy.CHANNEL_INTERFACE + '.ChannelType': telepathy.CHANNEL_TYPE_TEXT,
index 7518e4a..b7d9b9b 100644 (file)
@@ -19,6 +19,7 @@ except (ImportError, OSError):
        conic = None
 
 import constants
+import tp
 import util.coroutines as coroutines
 import gtk_toolbox
 
@@ -39,7 +40,7 @@ _moduleLogger = logging.getLogger("connection")
 
 
 class TheOneRingConnection(
-       telepathy.server.Connection,
+       tp.Connection,
        requests.RequestsMixin,
        contacts.ContactsMixin,
        aliasing.AliasingMixin,
@@ -72,7 +73,7 @@ class TheOneRingConnection(
                        raise telepathy.errors.InvalidArgument("User must specify what number GV forwards calls to")
 
                # Connection init must come first
-               telepathy.server.Connection.__init__(
+               tp.Connection.__init__(
                        self,
                        constants._telepathy_protocol_name_,
                        account,
@@ -122,10 +123,6 @@ class TheOneRingConnection(
        def userAliasType(self):
                return self.USER_ALIAS_ACCOUNT
 
-       def handle(self, handleType, handleId):
-               self.check_handle(handleType, handleId)
-               return self._handles[handleType, handleId]
-
        def handle_by_name(self, handleType, handleName):
                requestedHandleName = handleName.encode('utf-8')
                if handleType == telepathy.HANDLE_TYPE_CONTACT:
@@ -223,33 +220,15 @@ class TheOneRingConnection(
                self.check_connected()
                self.check_handle(handleType, handleId)
 
-               h = self.handle(handleType, handleId) if handleId != 0 else None
+               h = self.get_handle_by_id(handleType, handleId) if handleId != 0 else None
                props = self._generate_props(type, h, suppressHandler)
-               if hasattr(self, "_validate_handle"):
-                       # HACK Newer python-telepathy
-                       self._validate_handle(props)
+               self._validate_handle(props)
 
                chan = self.__channelManager.channel_for_props(props, signal=True)
                path = chan._object_path
                _moduleLogger.info("RequestChannel Object Path: %s" % path)
                return path
 
-       @gtk_toolbox.log_exception(_moduleLogger)
-       def RequestHandles(self, handleType, names, sender):
-               """
-               For org.freedesktop.telepathy.Connection
-               Overiding telepathy.server.Connecton to allow custom handles
-               """
-               self.check_connected()
-               self.check_handle_type(handleType)
-
-               handles = []
-               for name in names:
-                       h = self.handle_by_name(handleType, name)
-                       handles.append(h)
-                       self.add_client_handle(h, sender)
-               return handles
-
        def _generate_props(self, channelType, handle, suppressHandler, initiatorHandle=None):
                targetHandle = 0 if handle is None else handle.get_id()
                targetHandleType = telepathy.HANDLE_TYPE_NONE if handle is None else handle.get_type()
index c4b5f21..207f773 100644 (file)
@@ -12,6 +12,7 @@ import gobject
 import telepathy
 
 import constants
+import tp
 import gtk_toolbox
 import connection
 
@@ -19,10 +20,10 @@ import connection
 _moduleLogger = logging.getLogger("connection_manager")
 
 
-class TheOneRingConnectionManager(telepathy.server.ConnectionManager):
+class TheOneRingConnectionManager(tp.ConnectionManager):
 
        def __init__(self, shutdown_func=None):
-               telepathy.server.ConnectionManager.__init__(self, constants._telepathy_implementation_name_)
+               tp.ConnectionManager.__init__(self, constants._telepathy_implementation_name_)
 
                # self._protos is from super
                self._protos[constants._telepathy_protocol_name_] = connection.TheOneRingConnection
@@ -78,9 +79,9 @@ class TheOneRingConnectionManager(telepathy.server.ConnectionManager):
 
        def disconnected(self, conn):
                """
-               Overrides telepathy.server.ConnectionManager
+               Overrides tp.ConnectionManager
                """
-               result = telepathy.server.ConnectionManager.disconnected(self, conn)
+               result = tp.ConnectionManager.disconnected(self, conn)
                gobject.timeout_add(5000, self._shutdown)
 
        def quit(self):
index c52fc63..d556cf7 100644 (file)
@@ -1,7 +1,7 @@
-import telepathy
+import tp
 
 
-class Debug(telepathy.server.Debug):
+class Debug(tp.Debug):
 
        def __init__(self, connManager):
-               telepathy.server.Debug.__init__(self, connManager)
+               tp.Debug.__init__(self, connManager)
index 57b935a..b1ceb8f 100644 (file)
@@ -22,11 +22,11 @@ class Addressbook(object):
                if not force and self._contacts:
                        return
                oldContacts = self._contacts
-               oldContactIds = set(self.get_contacts())
+               oldContactIds = set(self.get_contact_ids())
 
                self._contacts = {}
                self._populate_contacts()
-               newContactIds = set(self.get_contacts())
+               newContactIds = set(self.get_contact_ids())
 
                addedContacts = newContactIds - oldContactIds
                removedContacts = oldContactIds - newContactIds
@@ -40,7 +40,7 @@ class Addressbook(object):
                        message = self, addedContacts, removedContacts, changedContacts
                        self.updateSignalHandler.stage.send(message)
 
-       def get_contacts(self):
+       def get_contact_ids(self):
                return self._contacts.iterkeys()
 
        def get_contact_name(self, contactId):
@@ -51,7 +51,7 @@ class Addressbook(object):
 
        def find_contacts_with_number(self, queryNumber):
                strippedQueryNumber = util_misc.strip_number(queryNumber)
-               for contactId, (contactName, contactDetails) in self.get_contacts():
+               for contactId, (contactName, contactDetails) in self.get_contact_ids():
                        for phoneType, number in contactDetails:
                                if number == strippedQueryNumber:
                                        yield contactId
index d063a77..2d27dd4 100644 (file)
@@ -3,19 +3,20 @@ import weakref
 
 import telepathy
 
+import tp
 import util.misc as util_misc
 
 
 _moduleLogger = logging.getLogger("handle")
 
 
-class TheOneRingHandle(telepathy.server.Handle):
+class TheOneRingHandle(tp.Handle):
        """
        Instances are memoized
        """
 
        def __init__(self, connection, id, handleType, name):
-               telepathy.server.Handle.__init__(self, id, handleType, name)
+               tp.Handle.__init__(self, id, handleType, name)
                self._conn = weakref.proxy(connection)
 
        def __repr__(self):
@@ -26,9 +27,9 @@ class TheOneRingHandle(telepathy.server.Handle):
        def is_same(self, handleType, handleName):
                return self.get_name() == handleName and self.get_type() == handleType
 
-       id = property(telepathy.server.Handle.get_id)
-       type = property(telepathy.server.Handle.get_type)
-       name = property(telepathy.server.Handle.get_name)
+       id = property(tp.Handle.get_id)
+       type = property(tp.Handle.get_type)
+       name = property(tp.Handle.get_name)
 
 
 class ConnectionHandle(TheOneRingHandle):
index 71868ba..59195fd 100644 (file)
@@ -8,11 +8,11 @@ import gtk_toolbox
 _moduleLogger = logging.getLogger('location')
 
 
-#class LocationMixin(telepathy.server.ConnectionInterfaceLocation):
+#class LocationMixin(tp.ConnectionInterfaceLocation):
 class LocationMixin(object):
 
        def __init__(self):
-               #telepathy.server.ConnectionInterfaceLocation.__init__(self)
+               #tp.ConnectionInterfaceLocation.__init__(self)
                pass
 
        @property
index 8c38252..ab6fc66 100644 (file)
@@ -1,7 +1,6 @@
 import logging
 
-import telepathy
-
+import tp
 import gtk_toolbox
 import simple_presence
 
@@ -9,10 +8,10 @@ import simple_presence
 _moduleLogger = logging.getLogger('presence')
 
 
-class PresenceMixin(telepathy.server.ConnectionInterfacePresence, simple_presence.TheOneRingPresence):
+class PresenceMixin(tp.ConnectionInterfacePresence, simple_presence.TheOneRingPresence):
 
        def __init__(self):
-               telepathy.server.ConnectionInterfacePresence.__init__(self)
+               tp.ConnectionInterfacePresence.__init__(self)
                simple_presence.TheOneRingPresence.__init__(self)
 
        @gtk_toolbox.log_exception(_moduleLogger)
index 51da1f2..39a81f8 100644 (file)
 import logging
 
-import dbus
-import telepathy
-import gtk_toolbox
+import tp
 
 
 _moduleLogger = logging.getLogger('requests')
 
 
-class RequestsMixin(
-       telepathy._generated.Connection_Interface_Requests.ConnectionInterfaceRequests,
-       telepathy.server.properties.DBusProperties
-):
-       """
-       HACK older python-telepathy doesn't provide an implementation but the new one does, ARGH
-       """
+class RequestsMixin(tp.ConnectionInterfaceRequests):
 
-       def __init__(self):
-               telepathy._generated.Connection_Interface_Requests.ConnectionInterfaceRequests.__init__(self)
-               telepathy.server.properties.DBusProperties.__init__(self)
-
-               self._implement_property_get(telepathy.interfaces.CONNECTION_INTERFACE_REQUESTS,
-                       {'Channels': lambda: dbus.Array(self._get_channels(),
-                               signature='(oa{sv})'),
-                       'RequestableChannelClasses': lambda: dbus.Array(
-                               self._channel_manager.get_requestable_channel_classes(),
-                               signature='(a{sv}as)')})
-
-       @property
-       def _channel_manager(self):
-               """
-               @abstract
-               """
-               raise NotImplementedError("Abstract property called")
-
-       @property
-       def handle_by_name(self, handleType, handleName):
-               """
-               @abstract
-               """
-               raise NotImplementedError("Abstract property called")
-
-       def _get_channels(self):
-               return [(c._object_path, c.get_props()) for c in self._channels]
-
-       def _check_basic_properties(self, props):
-               # ChannelType must be present and must be a string.
-               if telepathy.interfaces.CHANNEL_INTERFACE + '.ChannelType' not in props or \
-                               not isinstance(props[telepathy.interfaces.CHANNEL_INTERFACE + '.ChannelType'],
-                                       dbus.String):
-                       raise telepathy.errors.InvalidArgument('ChannelType is required')
-
-               def check_valid_type_if_exists(prop, fun):
-                       p = telepathy.interfaces.CHANNEL_INTERFACE + '.' + prop
-                       if p in props and not fun(props[p]):
-                               raise telepathy.errors.InvalidArgument('Invalid %s' % prop)
-
-               # Allow TargetHandleType to be missing, but not to be otherwise broken.
-               check_valid_type_if_exists('TargetHandleType',
-                       lambda p: p > 0 and p < (2**32)-1)
-
-               # Allow TargetType to be missing, but not to be otherwise broken.
-               check_valid_type_if_exists('TargetHandle',
-                       lambda p: p > 0 and p < (2**32)-1)
-               if props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle') == 0:
-                       raise telepathy.errors.InvalidArgument("TargetHandle may not be 0")
-
-               # Allow TargetID to be missing, but not to be otherwise broken.
-               check_valid_type_if_exists('TargetID',
-                       lambda p: isinstance(p, dbus.String))
-
-               # Disallow InitiatorHandle, InitiatorID and Requested.
-               check_valid_type_if_exists('InitiatorHandle', lambda p: False)
-               check_valid_type_if_exists('InitiatorID', lambda p: False)
-               check_valid_type_if_exists('Requested', lambda p: False)
-
-               type = props[telepathy.interfaces.CHANNEL_INTERFACE + '.ChannelType']
-               handle_type = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandleType',
-                               telepathy.constants.HANDLE_TYPE_NONE)
-               handle = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle', 0)
-
-               return (type, handle_type, handle)
-
-       def _validate_handle(self, props):
-               target_handle_type = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandleType',
-                       telepathy.constants.HANDLE_TYPE_NONE)
-               target_handle = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle', None)
-               target_id = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetID', None)
-
-               # Handle type 0 cannot have a handle.
-               if target_handle_type == telepathy.constants.HANDLE_TYPE_NONE and target_handle != None:
-                       raise telepathy.errors.InvalidArgument('When TargetHandleType is NONE, ' +
-                               'TargetHandle must be omitted')
-
-               # Handle type 0 cannot have a TargetID.
-               if target_handle_type == telepathy.constants.HANDLE_TYPE_NONE and target_id != None:
-                       raise telepathy.errors.InvalidArgument('When TargetHandleType is NONE, TargetID ' +
-                               'must be omitted')
-
-               if target_handle_type != telepathy.constants.HANDLE_TYPE_NONE:
-                       if target_handle == None and target_id == None:
-                               raise telepathy.errors.InvalidArgument('When TargetHandleType is not NONE, ' +
-                                       'either TargetHandle or TargetID must also be given')
-
-                       if target_handle != None and target_id != None:
-                               raise telepathy.errors.InvalidArgument('TargetHandle and TargetID must not ' +
-                                       'both be given')
-
-                       self.check_handle_type(target_handle_type)
-
-
-       def _alter_properties(self, props):
-               target_handle_type = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandleType',
-                       telepathy.constants.HANDLE_TYPE_NONE)
-               target_handle = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle', None)
-               target_id = props.get(telepathy.interfaces.CHANNEL_INTERFACE + '.TargetID', None)
-
-               altered_properties = props.copy()
-
-               if target_handle_type != telepathy.constants.HANDLE_TYPE_NONE:
-                       if target_handle == None:
-                               # Turn TargetID into TargetHandle.
-                               target_handle = self.handle_by_name(target_handle_type, target_id)
-                               altered_properties[telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle'] = \
-                                       target_handle
-                       else:
-                               # Check the supplied TargetHandle is valid
-                               self.check_handle(target_handle_type, target_handle)
-
-                               target_id = self._handles[target_handle_type,\
-                                                                                       target_handle].get_name()
-                               altered_properties[telepathy.interfaces.CHANNEL_INTERFACE + '.TargetID'] = \
-                                       target_id
-
-               altered_properties[telepathy.interfaces.CHANNEL_INTERFACE + '.Requested'] = True
-
-               return altered_properties
-
-       @dbus.service.method(telepathy.interfaces.CONNECTION_INTERFACE_REQUESTS,
-               in_signature='a{sv}', out_signature='oa{sv}',
-               async_callbacks=('_success', '_error'))
-       def CreateChannel(self, request, _success, _error):
-               _moduleLogger.info("CreateChannel")
-               type, handle_type, handle = self._check_basic_properties(request)
-               self._validate_handle(request)
-               props = self._alter_properties(request)
-
-               channel = self._channel_manager.channel_for_props(props, signal=False)
-
-               # Remove mutable properties
-               todel = []
-               for prop in props:
-                       iface, name = prop.rsplit('.', 1) # a bit of a hack
-                       if name in channel._immutable_properties:
-                               if channel._immutable_properties[name] != iface:
-                                       todel.append(prop)
-                       else:
-                               todel.append(prop)
-
-               for p in todel:
-                       del props[p]
-
-               _success(channel._object_path, props)
-
-               # CreateChannel MUST return *before* NewChannels is emitted.
-               # @bug On older python-telepathy, it doesn't exist
-               self.signal_new_channels([channel])
-
-       @dbus.service.method(telepathy.interfaces.CONNECTION_INTERFACE_REQUESTS,
-               in_signature='a{sv}', out_signature='boa{sv}',
-               async_callbacks=('_success', '_error'))
-       def EnsureChannel(self, request, _success, _error):
-               _moduleLogger.info("EnsureChannel")
-               type, handle_type, handle = self._check_basic_properties(request)
-               self._validate_handle(request)
-               props = self._alter_properties(request)
-
-               yours = not self._channel_manager.channel_exists(props)
-
-               channel = self._channel_manager.channel_for_props(props, signal=False)
-
-               _success(yours, channel._object_path, props)
-
-               # @bug On older python-telepathy, it doesn't exist
-               self.signal_new_channels([channel])
+       pass
index 7cad575..59bf641 100644 (file)
@@ -3,6 +3,7 @@ import logging
 import telepathy
 
 import gtk_toolbox
+import tp
 import handle
 import gvoice.state_machine as state_machine
 
@@ -50,7 +51,7 @@ class TheOneRingPresence(object):
                """
                presences = {}
                for handleId in contactIds:
-                       h = self.handle(telepathy.HANDLE_TYPE_CONTACT, handleId)
+                       h = self.get_handle_by_id(telepathy.HANDLE_TYPE_CONTACT, handleId)
                        if isinstance(h, handle.ConnectionHandle):
                                isDnd = self.session.backend.is_dnd()
                                if isDnd:
@@ -86,14 +87,14 @@ class TheOneRingPresence(object):
                _moduleLogger.info("Setting Presence to '%s'" % status)
 
 
-class SimplePresenceMixin(telepathy.server.ConnectionInterfaceSimplePresence, TheOneRingPresence):
+class SimplePresenceMixin(tp.ConnectionInterfaceSimplePresence, TheOneRingPresence):
 
        def __init__(self):
-               telepathy.server.ConnectionInterfaceSimplePresence.__init__(self)
+               tp.ConnectionInterfaceSimplePresence.__init__(self)
                TheOneRingPresence.__init__(self)
 
                self._implement_property_get(
-                       telepathy.server.CONNECTION_INTERFACE_SIMPLE_PRESENCE,
+                       tp.CONNECTION_INTERFACE_SIMPLE_PRESENCE,
                        {'Statuses' : self._get_statuses}
                )
 
diff --git a/src/tp/__init__.py b/src/tp/__init__.py
new file mode 100644 (file)
index 0000000..d0f561b
--- /dev/null
@@ -0,0 +1,43 @@
+"""
+telepathy-python - Base classes defining the interfaces of the Telepathy framework
+
+Copyright (C) 2005, 2006 Collabora Limited
+Copyright (C) 2005, 2006 Nokia Corporation
+Copyright (C) 2006 INdT
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+
+from connmgr import *
+from conn import *
+from channel import *
+from channelmanager import *
+try:
+    from debug import *
+except ImportError:
+    pass
+from handle import *
+from media import *
+from properties import *
+
+try:
+    from telepathy._generated.Client_Observer import ClientObserver as Observer
+    from telepathy._generated.Client_Approver import ClientApprover as Approver
+    from telepathy._generated.Client_Handler import ClientHandler as Handler
+    from telepathy._generated.Client_Interface_Requests import ClientInterfaceRequests
+except ImportError:
+    pass
+
+from telepathy import version, __version__
diff --git a/src/tp/channel.py b/src/tp/channel.py
new file mode 100644 (file)
index 0000000..55ac675
--- /dev/null
@@ -0,0 +1,416 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2005, 2006 Collabora Limited
+# Copyright (C) 2005, 2006 Nokia Corporation
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import dbus
+import dbus.service
+
+from telepathy.constants import (CONNECTION_HANDLE_TYPE_NONE,
+                                 CHANNEL_TEXT_MESSAGE_TYPE_NORMAL)
+
+from telepathy.errors import InvalidArgument
+
+from telepathy.interfaces import (CHANNEL_INTERFACE,
+                                  CHANNEL_INTERFACE_DTMF,
+                                  CHANNEL_INTERFACE_GROUP,
+                                  CHANNEL_INTERFACE_HOLD,
+                                  CHANNEL_INTERFACE_PASSWORD,
+                                  CHANNEL_TYPE_CONTACT_LIST,
+                                  CHANNEL_TYPE_FILE_TRANSFER,
+                                  CHANNEL_TYPE_ROOM_LIST,
+                                  CHANNEL_TYPE_STREAMED_MEDIA,
+                                  CHANNEL_TYPE_TEXT,
+                                  MEDIA_SESSION_HANDLER,
+                                  MEDIA_STREAM_HANDLER)
+
+from telepathy._generated.Channel import Channel as _Channel
+
+from properties import DBusProperties
+
+class Channel(_Channel, DBusProperties):
+
+    def __init__(self, connection, manager, props):
+        """
+        Initialise the base channel object.
+
+        Parameters:
+        connection - the parent Connection object
+        props - initial channel properties
+        """
+        self._conn = connection
+        self._chan_manager = manager
+        object_path = self._conn.get_channel_path()
+        _Channel.__init__(self, self._conn._name, object_path)
+
+        self._type = props[CHANNEL_INTERFACE + '.ChannelType']
+        self._requested = props[CHANNEL_INTERFACE + '.Requested']
+
+        self._immutable_properties = dict()
+
+        self._handle = self._conn.get_handle_by_id(
+            props[CHANNEL_INTERFACE + '.TargetHandleType'],
+            props[CHANNEL_INTERFACE + '.TargetHandle'])
+        self._interfaces = set()
+
+        DBusProperties.__init__(self)
+        self._implement_property_get(CHANNEL_INTERFACE,
+            {'ChannelType': lambda: dbus.String(self.GetChannelType()),
+             'Interfaces': lambda: dbus.Array(self.GetInterfaces(), signature='s'),
+             'TargetHandle': lambda: dbus.UInt32(self._handle.get_id()),
+             'TargetHandleType': lambda: dbus.UInt32(self._get_handle_type()),
+             'TargetID': lambda: dbus.String(self._get_target_id()),
+             'Requested': lambda: self._requested})
+
+        self._add_immutables({
+            'ChannelType': CHANNEL_INTERFACE,
+            'TargetHandle': CHANNEL_INTERFACE,
+            'Interfaces': CHANNEL_INTERFACE,
+            'TargetHandleType': CHANNEL_INTERFACE,
+            'TargetID': CHANNEL_INTERFACE,
+            'Requested': CHANNEL_INTERFACE
+            })
+
+    def _add_immutables(self, props):
+        self._immutable_properties.update(props)
+
+    def _get_handle_type(self):
+        if self._handle:
+            return self._handle.get_type()
+        else:
+            return CONNECTION_HANDLE_TYPE_NONE
+
+    def _get_target_id(self):
+        if self._handle:
+            return self._handle.get_name()
+        else:
+            return ''
+
+    def get_props(self):
+        props = dict()
+        for prop, iface in self._immutable_properties.items():
+            props[iface + '.' + prop] = \
+                self._prop_getters[iface][prop]()
+        return props
+
+    @dbus.service.method(CHANNEL_INTERFACE, in_signature='', out_signature='')
+    def Close(self):
+        self.Closed()
+        self._chan_manager.remove_channel(self)
+        self._conn.remove_channel(self)
+
+    @dbus.service.method(CHANNEL_INTERFACE, in_signature='', out_signature='s')
+    def GetChannelType(self):
+        """ Returns the interface name for the type of this channel. """
+        return self._type
+
+    @dbus.service.method(CHANNEL_INTERFACE, in_signature='', out_signature='uu')
+    def GetHandle(self):
+        """ Returns the handle type and number if this channel represents a
+        communication with a particular contact, room or server-stored list, or
+        zero if it is transient and defined only by its contents. """
+        if self._handle:
+            return self._handle.get_type(), self._handle
+        else:
+            return (CONNECTION_HANDLE_TYPE_NONE, 0)
+
+    @dbus.service.method(CHANNEL_INTERFACE, in_signature='', out_signature='as')
+    def GetInterfaces(self):
+        """
+        Get the optional interfaces implemented by the channel.
+
+        Returns:
+        an array of the D-Bus interface names
+        """
+        return self._interfaces
+
+from telepathy._generated.Channel_Type_Contact_List \
+        import ChannelTypeContactList as _ChannelTypeContactListIface
+
+class ChannelTypeContactList(Channel, _ChannelTypeContactListIface):
+    __doc__ = _ChannelTypeContactListIface.__doc__
+
+    def __init__(self, connection, manager, props):
+        """
+        Initialise the channel.
+
+        Parameters:
+        connection - the parent Telepathy Connection object
+        """
+        Channel.__init__(self, connection, manager, props)
+
+
+from telepathy._generated.Channel_Type_File_Transfer \
+        import ChannelTypeFileTransfer as _ChannelTypeFileTransferIface
+
+class ChannelTypeFileTransfer(Channel, _ChannelTypeFileTransferIface):
+    __doc__ = _ChannelTypeFileTransferIface.__doc__
+
+    def __init__(self, connection, manager, props):
+        """
+        Initialise the channel.
+
+        Parameters:
+        connection - the parent Telepathy Connection object
+        """
+        Channel.__init__(self, connection, manager, props)
+
+
+from telepathy._generated.Channel_Type_File_Transfer \
+        import ChannelTypeFileTransfer as _ChannelTypeFileTransferIface
+
+class ChannelTypeFileTransfer(Channel, _ChannelTypeFileTransferIface):
+    __doc__ = _ChannelTypeFileTransferIface.__doc__
+
+    def __init__(self, connection, manager, props):
+        """
+        Initialise the channel.
+
+        Parameters:
+        connection - the parent Telepathy Connection object
+        """
+        Channel.__init__(self, connection, manager, props)
+
+
+from telepathy._generated.Channel_Type_Streamed_Media \
+        import ChannelTypeStreamedMedia as _ChannelTypeStreamedMediaIface
+
+class ChannelTypeStreamedMedia(Channel, _ChannelTypeStreamedMediaIface):
+    __doc__ = _ChannelTypeStreamedMediaIface.__doc__
+
+    def __init__(self, connection, manager, props):
+        """
+        Initialise the channel.
+
+        Parameters:
+        connection - the parent Telepathy Connection object
+        """
+        Channel.__init__(self, connection, manager, props)
+
+
+from telepathy._generated.Channel_Type_Room_List \
+        import ChannelTypeRoomList as _ChannelTypeRoomListIface
+
+class ChannelTypeRoomList(Channel, _ChannelTypeRoomListIface):
+    __doc__ = _ChannelTypeRoomListIface.__doc__
+
+    def __init__(self, connection, manager, props):
+        """
+        Initialise the channel.
+
+        Parameters:
+        connection - the parent Telepathy Connection object
+        """
+        Channel.__init__(self, connection, manager, props)
+        self._listing_rooms = False
+        self._rooms = {}
+
+        self._add_immutables(self, {'Server': CHANNEL_TYPE_ROOM_LIST})
+
+    @dbus.service.method(CHANNEL_TYPE_ROOM_LIST, in_signature='', out_signature='b')
+    def GetListingRooms(self):
+        return self._listing_rooms
+
+    @dbus.service.signal(CHANNEL_TYPE_ROOM_LIST, signature='b')
+    def ListingRooms(self, listing):
+        self._listing_rooms = listing
+
+
+from telepathy._generated.Channel_Type_Text \
+        import ChannelTypeText as _ChannelTypeTextIface
+
+class ChannelTypeText(Channel, _ChannelTypeTextIface):
+    __doc__ = _ChannelTypeTextIface.__doc__
+
+    def __init__(self, connection, manager, props):
+        """
+        Initialise the channel.
+
+        Parameters:
+        connection - the parent Telepathy Connection object
+        """
+        Channel.__init__(self, connection, manager, props)
+
+        self._pending_messages = {}
+        self._message_types = [CHANNEL_TEXT_MESSAGE_TYPE_NORMAL]
+
+    @dbus.service.method(CHANNEL_TYPE_TEXT, in_signature='', out_signature='au')
+    def GetMessageTypes(self):
+        """
+        Return an array indicating which types of message may be sent on this
+        channel.
+
+        Returns:
+        an array of integer message types as defined above
+        """
+        return self._message_types
+
+    @dbus.service.method(CHANNEL_TYPE_TEXT, in_signature='au', out_signature='')
+    def AcknowledgePendingMessages(self, ids):
+        """
+        Inform the channel that you have handled messages by displaying them to
+        the user (or equivalent), so they can be removed from the pending queue.
+
+        Parameters:
+        ids - the message to acknowledge
+
+        Possible Errors:
+        InvalidArgument (a given message ID was not found, no action taken)
+        """
+        for id in ids:
+            if id not in self._pending_messages:
+                raise InvalidArgument("the given message ID was not found")
+
+        for id in ids:
+            del self._pending_messages[id]
+
+    @dbus.service.method(CHANNEL_TYPE_TEXT, in_signature='b', out_signature='a(uuuuus)')
+    def ListPendingMessages(self, clear):
+        """
+        List the messages currently in the pending queue, and optionally
+        remove then all.
+
+        Parameters:
+        clear - a boolean indicating whether the queue should be cleared
+
+        Returns:
+        an array of structs containing:
+            a numeric identifier
+            a unix timestamp indicating when the message was received
+            an integer handle of the contact who sent the message
+            an integer of the message type
+            a bitwise OR of the message flags
+            a string of the text of the message
+        """
+        messages = []
+        for id in self._pending_messages.keys():
+            (timestamp, sender, type, flags, text) = self._pending_messages[id]
+            message = (id, timestamp, sender, type, flags, text)
+            messages.append(message)
+            if clear:
+                del self._pending_messages[id]
+        messages.sort(cmp=lambda x,y:cmp(x[1], y[1]))
+        return messages
+
+    @dbus.service.signal(CHANNEL_TYPE_TEXT, signature='uuuuus')
+    def Received(self, id, timestamp, sender, type, flags, text):
+        self._pending_messages[id] = (timestamp, sender, type, flags, text)
+
+
+from telepathy._generated.Channel_Interface_Chat_State \
+        import ChannelInterfaceChatState
+
+
+from telepathy._generated.Channel_Interface_DTMF import ChannelInterfaceDTMF
+
+
+from telepathy._generated.Channel_Interface_Group \
+        import ChannelInterfaceGroup as _ChannelInterfaceGroup
+
+class ChannelInterfaceGroup(_ChannelInterfaceGroup, DBusProperties):
+
+    def __init__(self):
+        _ChannelInterfaceGroup.__init__(self)
+        DBusProperties.__init__(self)
+
+        self._implement_property_get(CHANNEL_INTERFACE_GROUP,
+            {'GroupFlags': lambda: dbus.UInt32(self.GetGroupFlags()),
+             'Members': lambda: dbus.Array(self.GetMembers(), signature='u'),
+             'RemotePendingMembers': lambda: dbus.Array(self.GetRemotePendingMembers(), signature='u'),
+             'SelfHandle': lambda: dbus.UInt32(self.GetSelfHandle())})
+
+        self._group_flags = 0
+        self._members = set()
+        self._local_pending = set()
+        self._remote_pending = set()
+
+    @dbus.service.method(CHANNEL_INTERFACE_GROUP, in_signature='', out_signature='u')
+    def GetGroupFlags(self):
+        return self._group_flags
+
+    @dbus.service.signal(CHANNEL_INTERFACE_GROUP, signature='uu')
+    def GroupFlagsChanged(self, added, removed):
+        self._group_flags |= added
+        self._group_flags &= ~removed
+
+    @dbus.service.method(CHANNEL_INTERFACE_GROUP, in_signature='', out_signature='au')
+    def GetMembers(self):
+        return self._members
+
+    @dbus.service.method(CHANNEL_INTERFACE_GROUP, in_signature='', out_signature='u')
+    def GetSelfHandle(self):
+        self_handle = self._conn.GetSelfHandle()
+        if (self_handle in self._members or
+            self_handle in self._local_pending or
+            self_handle in self._remote_pending):
+            return self_handle
+        else:
+            return 0
+
+    @dbus.service.method(CHANNEL_INTERFACE_GROUP, in_signature='', out_signature='au')
+    def GetLocalPendingMembers(self):
+        return self._local_pending
+
+    @dbus.service.method(CHANNEL_INTERFACE_GROUP, in_signature='', out_signature='au')
+    def GetRemotePendingMembers(self):
+        return self._remote_pending
+
+    @dbus.service.method(CHANNEL_INTERFACE_GROUP, in_signature='', out_signature='auauau')
+    def GetAllMembers(self):
+        return (self._members, self._local_pending, self._remote_pending)
+
+    @dbus.service.signal(CHANNEL_INTERFACE_GROUP, signature='sauauauauuu')
+    def MembersChanged(self, message, added, removed, local_pending, remote_pending, actor, reason):
+
+        self._members.update(added)
+        self._members.difference_update(removed)
+
+        self._local_pending.update(local_pending)
+        self._local_pending.difference_update(added)
+        self._local_pending.difference_update(removed)
+
+        self._remote_pending.update(remote_pending)
+        self._remote_pending.difference_update(added)
+        self._remote_pending.difference_update(removed)
+
+
+from telepathy._generated.Channel_Interface_Hold import ChannelInterfaceHold
+
+
+# ChannelInterfaceMediaSignalling is in telepathy.server.media
+
+
+from telepathy._generated.Channel_Interface_Password \
+        import ChannelInterfacePassword as _ChannelInterfacePassword
+
+class ChannelInterfacePassword(_ChannelInterfacePassword):
+    def __init__(self):
+        _ChannelInterfacePassword.__init__(self)
+        self._password_flags = 0
+        self._password = ''
+
+    @dbus.service.method(CHANNEL_INTERFACE_PASSWORD, in_signature='', out_signature='u')
+    def GetPasswordFlags(self):
+        return self._password_flags
+
+    @dbus.service.signal(CHANNEL_INTERFACE_PASSWORD, signature='uu')
+    def PasswordFlagsChanged(self, added, removed):
+        self._password_flags |= added
+        self._password_flags &= ~removed
+
+
+from telepathy._generated.Channel_Interface_Call_State import ChannelInterfaceCallState
diff --git a/src/tp/channelhandler.py b/src/tp/channelhandler.py
new file mode 100644 (file)
index 0000000..5bef170
--- /dev/null
@@ -0,0 +1,19 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2008 Collabora Limited
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+from telepathy._generated.Channel_Handler import ChannelHandler
diff --git a/src/tp/channelmanager.py b/src/tp/channelmanager.py
new file mode 100644 (file)
index 0000000..aacde30
--- /dev/null
@@ -0,0 +1,99 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2009 Collabora Limited
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+from telepathy.errors import NotImplemented
+
+from telepathy.interfaces import (CHANNEL_INTERFACE,
+                                 CHANNEL_TYPE_CONTACT_LIST,
+                                 CHANNEL_TYPE_TEXT)
+
+class ChannelManager(object):
+
+    def __init__(self, connection):
+        self._conn = connection
+
+        self._requestable_channel_classes = dict()
+        self._channels = dict()
+        self._fixed_properties = dict()
+        self._available_properties = dict()
+
+    def close(self):
+        for channel_type in self._requestable_channel_classes:
+            for channel in self._channels[channel_type].values():
+                if channel._type == CHANNEL_TYPE_CONTACT_LIST:
+                    channel.remove_from_connection()
+                else:
+                    channel.Close()
+
+    def remove_channel(self, channel):
+        for channel_type in self._requestable_channel_classes:
+            for handle, chan in self._channels[channel_type].items():
+                if channel == chan:
+                    del self._channels[channel_type][handle]
+
+    def _get_type_requested_handle(self, props):
+        type = props[CHANNEL_INTERFACE + '.ChannelType']
+        requested = props[CHANNEL_INTERFACE + '.Requested']
+        target_handle = props[CHANNEL_INTERFACE + '.TargetHandle']
+        target_handle_type = props[CHANNEL_INTERFACE + '.TargetHandleType']
+
+        handle = self._conn._handles[target_handle_type, target_handle]
+
+        return (type, requested, handle)
+
+    def channel_exists(self, props):
+        type, _, handle = self._get_type_requested_handle(props)
+
+        if type in self._channels:
+            if handle in self._channels[type]:
+                return True
+
+        return False
+
+    def channel_for_props(self, props, signal=True, **args):
+        type, _, handle = self._get_type_requested_handle(props)
+
+        if type not in self._requestable_channel_classes:
+            raise NotImplemented('Unknown channel type "%s"' % type)
+
+        if self.channel_exists(props):
+            return self._channels[type][handle]
+
+        channel = self._requestable_channel_classes[type](
+            props, **args)
+
+        self._conn.add_channels([channel], signal=signal)
+        self._channels[type][handle] = channel
+
+        return channel
+
+    def _implement_channel_class(self, type, make_channel, fixed, available):
+        self._requestable_channel_classes[type] = make_channel
+        self._channels.setdefault(type, {})
+
+        self._fixed_properties[type] = fixed
+        self._available_properties[type] = available
+
+    def get_requestable_channel_classes(self):
+        retval = []
+
+        for channel_type in self._requestable_channel_classes:
+            retval.append((self._fixed_properties[channel_type],
+                self._available_properties[channel_type]))
+
+        return retval
diff --git a/src/tp/conn.py b/src/tp/conn.py
new file mode 100644 (file)
index 0000000..0498e64
--- /dev/null
@@ -0,0 +1,560 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2005, 2006 Collabora Limited
+# Copyright (C) 2005, 2006 Nokia Corporation
+# Copyright (C) 2006 INdT
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import dbus
+import dbus.service
+import re
+import weakref
+
+from telepathy.constants import (CONNECTION_STATUS_DISCONNECTED,
+                                 CONNECTION_STATUS_CONNECTED,
+                                 HANDLE_TYPE_NONE,
+                                 HANDLE_TYPE_CONTACT,
+                                 LAST_HANDLE_TYPE)
+from telepathy.errors import (Disconnected, InvalidArgument,
+                              InvalidHandle, NotAvailable,
+                              NotImplemented)
+from telepathy.interfaces import (CONN_INTERFACE,
+                                  CONN_INTERFACE_ALIASING,
+                                  CONN_INTERFACE_AVATARS,
+                                  CONN_INTERFACE_CAPABILITIES,
+                                  CONN_INTERFACE_PRESENCE,
+                                  CONN_INTERFACE_RENAMING,
+                                  CONNECTION_INTERFACE_REQUESTS,
+                                  CHANNEL_INTERFACE)
+from handle import Handle
+from properties import DBusProperties
+
+from telepathy._generated.Connection import Connection as _Connection
+
+_BAD = re.compile(r'(?:^[0-9])|(?:[^A-Za-z0-9])')
+
+def _escape_as_identifier(name):
+    if isinstance(name, unicode):
+        name = name.encode('utf-8')
+    if not name:
+        return '_'
+    return _BAD.sub(lambda match: '_%02x' % ord(match.group(0)), name)
+
+class Connection(_Connection, DBusProperties):
+
+    _optional_parameters = {}
+    _mandatory_parameters = {}
+
+    def __init__(self, proto, account, manager=None):
+        """
+        Parameters:
+        proto - the name of the protcol this conection should be handling.
+        account - a protocol-specific account name
+        manager - the name of the connection manager
+        """
+
+        if manager is None:
+            import warnings
+            warnings.warn('The manager parameter to Connection.__init__ '
+                          'should be supplied', DeprecationWarning)
+            manager = 'python'
+
+        clean_account = _escape_as_identifier(account)
+        bus_name = u'org.freedesktop.Telepathy.Connection.%s.%s.%s' % \
+                (manager, proto, clean_account)
+        bus_name = dbus.service.BusName(bus_name, bus=dbus.SessionBus())
+
+        object_path = '/org/freedesktop/Telepathy/Connection/%s/%s/%s' % \
+                (manager, proto, clean_account)
+        _Connection.__init__(self, bus_name, object_path)
+
+        # monitor clients dying so we can release handles
+        dbus.SessionBus().add_signal_receiver(self.name_owner_changed_callback,
+                                              'NameOwnerChanged',
+                                              'org.freedesktop.DBus',
+                                              'org.freedesktop.DBus',
+                                              '/org/freedesktop/DBus')
+
+        self._interfaces = set()
+
+        DBusProperties.__init__(self)
+        self._implement_property_get(CONN_INTERFACE,
+                {'SelfHandle': lambda: dbus.UInt32(self.GetSelfHandle())})
+
+        self._proto = proto
+
+        self._status = CONNECTION_STATUS_DISCONNECTED
+
+        self._handles = weakref.WeakValueDictionary()
+        self._next_handle_id = 1
+        self._client_handles = {}
+
+        self._channels = set()
+        self._next_channel_id = 0
+
+    def check_parameters(self, parameters):
+        """
+        Uses the values of self._mandatory_parameters and
+        self._optional_parameters to validate and type check all of the
+        provided parameters, and check all mandatory parameters are present.
+        Sets defaults according to the defaults if the client has not
+        provided any.
+        """
+        for (parm, value) in parameters.iteritems():
+            if parm in self._mandatory_parameters.keys():
+                sig = self._mandatory_parameters[parm]
+            elif parm in self._optional_parameters.keys():
+                sig = self._optional_parameters[parm]
+            else:
+                raise InvalidArgument('unknown parameter name %s' % parm)
+
+            # we currently support strings, (u)int16/32 and booleans
+            if sig == 's':
+                if not isinstance(value, unicode):
+                    raise InvalidArgument('incorrect type to %s parameter, got %s, expected a string' % (parm, type(value)))
+            elif sig in 'iunq':
+                if not isinstance(value, (int, long)):
+                    raise InvalidArgument('incorrect type to %s parameter, got %s, expected an int' % (parm, type(value)))
+            elif sig == 'b':
+                if not isinstance(value, bool):
+                    raise InvalidArgument('incorrect type to %s parameter, got %s, expected an boolean' % (parm, type(value)))
+            else:
+                raise TypeError('unknown type signature %s in protocol parameters' % type)
+
+        for (parm, value) in self._parameter_defaults.iteritems():
+            if parm not in parameters:
+                parameters[parm] = value
+
+        missing = set(self._mandatory_parameters.keys()).difference(parameters.keys())
+        if missing:
+            raise InvalidArgument('required parameters %s not given' % missing)
+
+    def check_connected(self):
+        if self._status != CONNECTION_STATUS_CONNECTED:
+            raise Disconnected('method cannot be called unless status is CONNECTION_STATUS_CONNECTED')
+
+    def check_handle(self, handle_type, handle):
+        if (handle_type, handle) not in self._handles:
+            print "Connection.check_handle", handle, handle_type, self._handles.keys()
+            print str(list( [ self._handles[x] for x in self._handles.keys() ] ) )
+            raise InvalidHandle('handle number %d not valid for type %d' %
+                (handle, handle_type))
+
+    def check_handle_type(self, type):
+        if (type <= HANDLE_TYPE_NONE or type > LAST_HANDLE_TYPE):
+            raise InvalidArgument('handle type %s not known' % type)
+
+    def get_handle_id(self):
+        id = self._next_handle_id
+        self._next_handle_id += 1
+        return id
+
+    def add_client_handle(self, handle, sender):
+        if sender in self._client_handles:
+            self._client_handles[sender].add((handle.get_type(), handle))
+        else:
+            self._client_handles[sender] = set([(handle.get_type(), handle)])
+
+    def get_handle_by_id(self, handle_type, handle_id):
+        self.check_handle(handle_type, handle_id)
+        return self._handles[handle_type, handle_id]
+
+    def get_handle_by_name(self, handle_type, handle_name):
+        self.check_handle_type(handle_type)
+        handle = None
+
+        for candidate in self._handles.itervalues():
+            if candidate.get_name() == handle_name:
+                handle = candidate
+                break
+        else:
+            id = self.get_handle_id()
+            handle = Handle(id, handle_type, handle_name)
+            self._handles[handle_type, id] = handle
+
+        return handle
+
+    def name_owner_changed_callback(self, name, old_owner, new_owner):
+        # when name and old_owner are the same, and new_owner is
+        # blank, it is the client itself releasing its name... aka exiting
+        if (name == old_owner and new_owner == "" and name in self._client_handles):
+            print "deleting handles for", name
+            del self._client_handles[name]
+
+    def set_self_handle(self, handle):
+        self._self_handle = handle
+
+    def get_channel_path(self):
+        ret = '%s/channel%d' % (self._object_path, self._next_channel_id)
+        self._next_channel_id += 1
+        return ret
+
+    def add_channels(self, channels, signal=True):
+        """ add new channels and signal their creation"""
+        signal_channels = set()
+
+        for channel in channels:
+            if channel not in self._channels:
+                self._channels.add(channel)
+                signal_channels.add(channel)
+
+        if signal:
+            self.signal_new_channels(signal_channels)
+
+    def signal_new_channels(self, channels):
+        self.NewChannels([(channel._object_path, channel.get_props())
+            for channel in channels])
+
+        # Now NewChannel needs to be called for each new channel.
+        for channel in channels:
+            props = channel.get_props()
+
+            target_handle_type = props[CHANNEL_INTERFACE + '.TargetHandleType']
+            target_handle = props[CHANNEL_INTERFACE + '.TargetHandle']
+            suppress_handler = props[CHANNEL_INTERFACE + '.Requested']
+
+            self.NewChannel(channel._object_path, channel._type,
+                target_handle_type, target_handle,
+                suppress_handler)
+
+    def remove_channel(self, channel):
+        self._channels.remove(channel)
+        self.ChannelClosed(channel._object_path)
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='', out_signature='as')
+    def GetInterfaces(self):
+        self.check_connected()
+        return self._interfaces
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='', out_signature='s')
+    def GetProtocol(self):
+        return self._proto
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='uau', out_signature='as')
+    def InspectHandles(self, handle_type, handles):
+        self.check_connected()
+        self.check_handle_type(handle_type)
+
+        for handle in handles:
+            self.check_handle(handle_type, handle)
+
+        ret = []
+        for handle in handles:
+            ret.append(self._handles[handle_type, handle].get_name())
+
+        return ret
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='uas', out_signature='au', sender_keyword='sender')
+    def RequestHandles(self, handle_type, names, sender):
+        self.check_connected()
+        self.check_handle_type(handle_type)
+
+        ret = []
+        for name in names:
+            handle = self.get_handle_by_name(handle_type, name)
+            self.add_client_handle(handle, sender)
+            ret.append(handle.get_id())
+
+        return ret
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='uau', out_signature='', sender_keyword='sender')
+    def HoldHandles(self, handle_type, handles, sender):
+        self.check_connected()
+        self.check_handle_type(handle_type)
+
+        for handle in handles:
+            self.check_handle(handle_type, handle)
+
+        for handle in handles:
+            hand = self._handles[handle_type, handle]
+            self.add_client_handle(hand, sender)
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='uau', out_signature='', sender_keyword='sender')
+    def ReleaseHandles(self, handle_type, handles, sender):
+        self.check_connected()
+        self.check_handle_type(handle_type)
+
+        for handle in handles:
+            self.check_handle(handle_type, handle)
+            hand = self._handles[handle_type, handle]
+            if sender in self._client_handles:
+                if (handle_type, hand) not in self._client_handles[sender]:
+                    raise NotAvailable('client is not holding handle %s of type %s' % (handle, handle_type))
+            else:
+                raise NotAvailable('client does not hold any handles')
+
+        for handle in handles:
+            hand = self._handles[handle_type, handle]
+            self._client_handles[sender].remove((handle_type, hand))
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='', out_signature='u')
+    def GetSelfHandle(self):
+        self.check_connected()
+        return self._self_handle
+
+    @dbus.service.signal(CONN_INTERFACE, signature='uu')
+    def StatusChanged(self, status, reason):
+        self._status = status
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='', out_signature='u')
+    def GetStatus(self):
+        return self._status
+
+    @dbus.service.method(CONN_INTERFACE, in_signature='', out_signature='a(osuu)')
+    def ListChannels(self):
+        self.check_connected()
+        ret = []
+        for channel in self._channels:
+            chan = (channel._object_path, channel._type, channel._get_handle_type(), channel._handle)
+            ret.append(chan)
+        return ret
+
+
+from telepathy._generated.Connection_Interface_Aliasing \
+        import ConnectionInterfaceAliasing
+
+
+from telepathy._generated.Connection_Interface_Avatars \
+        import ConnectionInterfaceAvatars
+
+
+from telepathy._generated.Connection_Interface_Capabilities \
+        import ConnectionInterfaceCapabilities \
+        as _ConnectionInterfaceCapabilities
+
+class ConnectionInterfaceCapabilities(_ConnectionInterfaceCapabilities):
+    def __init__(self):
+        _ConnectionInterfaceCapabilities.__init__(self)
+        # { contact handle : { str channel type : [int, int] }}
+        # the first int is the generic caps, the second is the type-specific
+        self._caps = {}
+
+    @dbus.service.method(CONN_INTERFACE_CAPABILITIES, in_signature='au', out_signature='a(usuu)')
+    def GetCapabilities(self, handles):
+        ret = []
+        handle_type = HANDLE_TYPE_CONTACT
+        for handle in handles:
+            if (handle != 0 and (handle_type, handle) not in self._handles):
+                raise InvalidHandle
+            elif handle in self._caps:
+                types = self._caps[handle]
+                for ctype, specs in types.items():
+                    ret.append([handle, ctype, specs[0], specs[1]])
+        return ret
+
+    @dbus.service.signal(CONN_INTERFACE_CAPABILITIES, signature='a(usuuuu)')
+    def CapabilitiesChanged(self, caps):
+        for handle, ctype, gen_old, gen_new, spec_old, spec_new in caps:
+            self._caps.setdefault(handle, {})[ctype] = [gen_new, spec_new]
+
+    @dbus.service.method(CONN_INTERFACE_CAPABILITIES,
+                         in_signature='a(su)as', out_signature='a(su)')
+    def AdvertiseCapabilities(self, add, remove):
+        my_caps = self._caps.setdefault(self._self_handle, {})
+
+        changed = {}
+        for ctype, spec_caps in add:
+            changed[ctype] = spec_caps
+        for ctype in remove:
+            changed[ctype] = None
+
+        caps = []
+        for ctype, spec_caps in changed.iteritems():
+            gen_old, spec_old = my_caps.get(ctype, (0, 0))
+            if spec_caps is None:
+                # channel type no longer supported (provider has gone away)
+                gen_new, spec_new = 0, 0
+            else:
+                # channel type supports new capabilities
+                gen_new, spec_new = gen_old, spec_old | spec_caps
+            if spec_old != spec_new or gen_old != gen_new:
+                caps.append((self._self_handle, ctype, gen_old, gen_new,
+                            spec_old, spec_new))
+
+        self.CapabilitiesChanged(caps)
+
+        # return all my capabilities
+        return [(ctype, caps[1]) for ctype, caps in my_caps.iteritems()]
+
+from telepathy._generated.Connection_Interface_Requests \
+        import ConnectionInterfaceRequests \
+        as _ConnectionInterfaceRequests
+
+class ConnectionInterfaceRequests(
+    _ConnectionInterfaceRequests,
+    DBusProperties):
+
+    def __init__(self):
+        _ConnectionInterfaceRequests.__init__(self)
+        DBusProperties.__init__(self)
+
+        self._implement_property_get(CONNECTION_INTERFACE_REQUESTS,
+            {'Channels': lambda: dbus.Array(self._get_channels(),
+                signature='(oa{sv})'),
+            'RequestableChannelClasses': lambda: dbus.Array(
+                self._channel_manager.get_requestable_channel_classes(),
+                signature='(a{sv}as)')})
+
+    def _get_channels(self):
+        return [(c._object_path, c.get_props()) for c in self._channels]
+
+    def _check_basic_properties(self, props):
+        # ChannelType must be present and must be a string.
+        if CHANNEL_INTERFACE + '.ChannelType' not in props or \
+                not isinstance(props[CHANNEL_INTERFACE + '.ChannelType'],
+                    dbus.String):
+            raise InvalidArgument('ChannelType is required')
+
+        def check_valid_type_if_exists(prop, fun):
+            p = CHANNEL_INTERFACE + '.' + prop
+            if p in props and not fun(props[p]):
+                raise InvalidArgument('Invalid %s' % prop)
+
+        # Allow TargetHandleType to be missing, but not to be otherwise broken.
+        check_valid_type_if_exists('TargetHandleType',
+            lambda p: p > 0 and p < (2**32)-1)
+
+        # Allow TargetType to be missing, but not to be otherwise broken.
+        check_valid_type_if_exists('TargetHandle',
+            lambda p: p > 0 and p < (2**32)-1)
+        if props.get(CHANNEL_INTERFACE + '.TargetHandle') == 0:
+            raise InvalidArgument("TargetHandle may not be 0")
+
+        # Allow TargetID to be missing, but not to be otherwise broken.
+        check_valid_type_if_exists('TargetID',
+            lambda p: isinstance(p, dbus.String))
+
+        # Disallow InitiatorHandle, InitiatorID and Requested.
+        check_valid_type_if_exists('InitiatorHandle', lambda p: False)
+        check_valid_type_if_exists('InitiatorID', lambda p: False)
+        check_valid_type_if_exists('Requested', lambda p: False)
+
+        type = props[CHANNEL_INTERFACE + '.ChannelType']
+        handle_type = props.get(CHANNEL_INTERFACE + '.TargetHandleType',
+                HANDLE_TYPE_NONE)
+        handle = props.get(CHANNEL_INTERFACE + '.TargetHandle', 0)
+
+        return (type, handle_type, handle)
+
+    def _validate_handle(self, props):
+        target_handle_type = props.get(CHANNEL_INTERFACE + '.TargetHandleType',
+            HANDLE_TYPE_NONE)
+        target_handle = props.get(CHANNEL_INTERFACE + '.TargetHandle', None)
+        target_id = props.get(CHANNEL_INTERFACE + '.TargetID', None)
+
+        # Handle type 0 cannot have a handle.
+        if target_handle_type == HANDLE_TYPE_NONE and target_handle != None:
+            raise InvalidArgument('When TargetHandleType is NONE, ' +
+                'TargetHandle must be omitted')
+
+        # Handle type 0 cannot have a TargetID.
+        if target_handle_type == HANDLE_TYPE_NONE and target_id != None:
+            raise InvalidArgument('When TargetHandleType is NONE, TargetID ' +
+                'must be omitted')
+
+        if target_handle_type != HANDLE_TYPE_NONE:
+            if target_handle == None and target_id == None:
+                raise InvalidArgument('When TargetHandleType is not NONE, ' +
+                    'either TargetHandle or TargetID must also be given')
+
+            if target_handle != None and target_id != None:
+                raise InvalidArgument('TargetHandle and TargetID must not ' +
+                    'both be given')
+
+            self.check_handle_type(target_handle_type)
+
+
+    def _alter_properties(self, props):
+        target_handle_type = props.get(CHANNEL_INTERFACE + '.TargetHandleType',
+            HANDLE_TYPE_NONE)
+        target_handle = props.get(CHANNEL_INTERFACE + '.TargetHandle', None)
+        target_id = props.get(CHANNEL_INTERFACE + '.TargetID', None)
+               # Note: what the spec calls a handle, we call an ID
+               # What the spec calls an ID, we call a name
+               # What we call a handle is a handle object
+
+        altered_properties = props.copy()
+
+        if target_handle_type != HANDLE_TYPE_NONE:
+            if target_handle == None:
+                target_handle = self.get_handle_by_name(target_handle_type, target_id).get_id()
+                altered_properties[CHANNEL_INTERFACE + '.TargetHandle'] = \
+                    target_handle
+            else:
+                # Check the supplied TargetHandle is valid
+                self.check_handle(target_handle_type, target_handle)
+
+                target_id = self._handles[target_handle_type,\
+                                            target_handle].get_name()
+                altered_properties[CHANNEL_INTERFACE + '.TargetID'] = \
+                    target_id
+
+        altered_properties[CHANNEL_INTERFACE + '.Requested'] = True
+
+        return altered_properties
+
+    @dbus.service.method(CONNECTION_INTERFACE_REQUESTS,
+        in_signature='a{sv}', out_signature='oa{sv}',
+        async_callbacks=('_success', '_error'))
+    def CreateChannel(self, request, _success, _error):
+        type, handle_type, handle = self._check_basic_properties(request)
+        self._validate_handle(request)
+        props = self._alter_properties(request)
+
+        channel = self._channel_manager.channel_for_props(props, signal=False)
+
+        # Remove mutable properties
+        todel = []
+        for prop in props:
+            iface, name = prop.rsplit('.', 1) # a bit of a hack
+            if name in channel._immutable_properties:
+                if channel._immutable_properties[name] != iface:
+                    todel.append(prop)
+            else:
+                todel.append(prop)
+
+        for p in todel:
+            del props[p]
+
+        _success(channel._object_path, props)
+
+        # CreateChannel MUST return *before* NewChannels is emitted.
+        self.signal_new_channels([channel])
+
+    @dbus.service.method(CONNECTION_INTERFACE_REQUESTS,
+        in_signature='a{sv}', out_signature='boa{sv}',
+        async_callbacks=('_success', '_error'))
+    def EnsureChannel(self, request, _success, _error):
+        type, handle_type, handle = self._check_basic_properties(request)
+        self._validate_handle(request)
+        props = self._alter_properties(request)
+
+        yours = not self._channel_manager.channel_exists(props)
+
+        channel = self._channel_manager.channel_for_props(props, signal=False)
+
+        _success(yours, channel._object_path, props)
+
+        self.signal_new_channels([channel])
+
+from telepathy._generated.Connection_Interface_Presence \
+        import ConnectionInterfacePresence
+
+from telepathy._generated.Connection_Interface_Simple_Presence \
+        import ConnectionInterfaceSimplePresence
+
+from telepathy._generated.Connection_Interface_Contacts \
+        import ConnectionInterfaceContacts
diff --git a/src/tp/connmgr.py b/src/tp/connmgr.py
new file mode 100644 (file)
index 0000000..8167108
--- /dev/null
@@ -0,0 +1,73 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2005, 2006 Collabora Limited
+# Copyright (C) 2005, 2006 Nokia Corporation
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import dbus
+import dbus.service
+
+from telepathy.errors import NotImplemented
+from telepathy.interfaces import CONN_MGR_INTERFACE
+
+from telepathy._generated.Connection_Manager \
+        import ConnectionManager as _ConnectionManager
+
+class ConnectionManager(_ConnectionManager):
+    def __init__(self, name):
+        """
+        Initialise the connection manager.
+        """
+        bus_name = 'org.freedesktop.Telepathy.ConnectionManager.%s' % name
+        object_path = '/org/freedesktop/Telepathy/ConnectionManager/%s' % name
+        _ConnectionManager.__init__(self,
+                                    dbus.service.BusName(bus_name, dbus.Bus(), do_not_queue=True),
+                                    object_path)
+
+        self._connections = set()
+        self._protos = {}
+
+    def connected(self, conn):
+        """
+        Add a connection to the list of connections, emit the appropriate
+        signal.
+        """
+        self._connections.add(conn)
+        self.NewConnection(conn._name.get_name(), conn._object_path, conn._proto)
+
+    def disconnected(self, conn):
+        """
+        Remove a connection from the list of connections.
+        """
+        self._connections.remove(conn)
+        if hasattr(conn, 'remove_from_connection'):
+            # requires dbus-python >= 0.81.1
+            conn.remove_from_connection()
+        del conn
+
+        return False # when called in an idle callback
+
+    @dbus.service.method(CONN_MGR_INTERFACE, in_signature='', out_signature='as')
+    def ListProtocols(self):
+        return self._protos.keys()
+
+    def RequestConnection(self, proto, parameters):
+        if proto in self._protos:
+            conn = self._protos[proto](self, parameters)
+            self.connected(conn)
+            return (conn._name.get_name(), conn._object_path)
+        else:
+            raise NotImplemented('unknown protocol %s' % proto)
diff --git a/src/tp/debug.py b/src/tp/debug.py
new file mode 100644 (file)
index 0000000..3ddd409
--- /dev/null
@@ -0,0 +1,113 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2009 Collabora Limited
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+from telepathy.interfaces import DEBUG
+from telepathy.constants import (DEBUG_LEVEL_ERROR,
+                                 DEBUG_LEVEL_CRITICAL,
+                                 DEBUG_LEVEL_WARNING,
+                                 DEBUG_LEVEL_INFO,
+                                 DEBUG_LEVEL_DEBUG)
+
+from telepathy._generated.Debug import Debug as _Debug
+from properties import DBusProperties
+
+import dbus.service
+import logging
+import sys
+import time
+
+LEVELS = {
+        logging.ERROR:   DEBUG_LEVEL_ERROR,
+        logging.FATAL:   DEBUG_LEVEL_CRITICAL,
+        logging.WARNING: DEBUG_LEVEL_WARNING,
+        logging.INFO:    DEBUG_LEVEL_INFO,
+        logging.DEBUG:   DEBUG_LEVEL_DEBUG,
+        logging.NOTSET:  DEBUG_LEVEL_DEBUG
+}
+
+DEBUG_MESSAGE_LIMIT = 800
+
+class Debug(_Debug, DBusProperties, logging.Handler):
+
+    def __init__(self, conn_manager, root=''):
+        self.enabled = False
+        self._interfaces = set()
+        self._messages = []
+        object_path = '/org/freedesktop/Telepathy/debug'
+
+        _Debug.__init__(self, conn_manager._name, object_path)
+        DBusProperties.__init__(self)
+        logging.Handler.__init__(self)
+
+        self._implement_property_get(DEBUG, {'Enabled': lambda: self.enabled})
+        logging.getLogger(root).addHandler(self)
+        sys.stderr = StdErrWrapper(self, sys.stderr)
+
+    def GetMessages(self):
+        return self._messages
+
+    def add_message(self, timestamp, name, level, msg):
+        if len(self._messages) >= DEBUG_MESSAGE_LIMIT:
+            self._messages.pop()
+        self._messages.append((timestamp, name, level, msg))
+        if self.enabled:
+            self.NewDebugMessage(timestamp, name, level, msg)
+
+    # Handle logging module messages
+
+    def emit(self, record):
+        name = self.get_record_name(record)
+        level = self.get_record_level(record)
+        self.add_message(record.created, name, level, record.msg)
+
+    def get_record_level(self, record):
+        return LEVELS[record.levelno]
+
+    def get_record_name(self, record):
+        name = record.name
+        if name.contains("."):
+            domain, category = record.name.split('.', 1)
+            name = domain + "/" + category
+        return name
+
+# Wrapper around stderr so the exceptions are logged
+
+class StdErrWrapper(object):
+
+    def __init__(self, interface, stderr):
+        self._buffer = ""
+        self._interface = interface
+        self._stderr = stderr
+
+    def __getattr__(self, attr):
+        return getattr(self._stderr, attr)
+
+    def write(self, string):
+        self._stderr.write(string)
+        if '\n' not in string:
+            self._buffer += string
+            return
+
+        lines = string.split('\n')
+        lines[0] = self._buffer + lines[0]
+        self._buffer = lines[-1]
+        del lines[-1]
+
+        timestamp = time.time()
+        for line in lines:
+            self._interface.add_message(timestamp, "stderr", DEBUG_LEVEL_ERROR, line)
diff --git a/src/tp/handle.py b/src/tp/handle.py
new file mode 100644 (file)
index 0000000..a7771b0
--- /dev/null
@@ -0,0 +1,45 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2005,2006 Collabora Limited
+# Copyright (C) 2005,2006 Nokia Corporation
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+class Handle(object):
+    def __init__(self, id, handle_type, name):
+        self._id = id
+        self._type = handle_type
+        self._name = name
+
+    def get_id(self):
+        return self._id
+
+    def __int__(self):
+        return int(self._id)
+
+    def __long__(self):
+        return long(self._id)
+
+    def get_type(self):
+        return self._type
+
+    def get_name(self):
+        return self._name
+
+    def __eq__(self, other):
+        return (int(self) == int(other) and self.get_type() == other.get_type())
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
diff --git a/src/tp/media.py b/src/tp/media.py
new file mode 100644 (file)
index 0000000..7d1ce7b
--- /dev/null
@@ -0,0 +1,28 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2005,2006 Collabora Limited
+# Copyright (C) 2005,2006 Nokia Corporation
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import dbus.service
+
+from telepathy import *
+
+
+from telepathy._generated.Channel_Interface_Media_Signalling \
+        import ChannelInterfaceMediaSignalling
+from telepathy._generated.Media_Session_Handler import MediaSessionHandler
+from telepathy._generated.Media_Stream_Handler import MediaStreamHandler
diff --git a/src/tp/properties.py b/src/tp/properties.py
new file mode 100644 (file)
index 0000000..c42c737
--- /dev/null
@@ -0,0 +1,70 @@
+# telepathy-python - Base classes defining the interfaces of the Telepathy framework
+#
+# Copyright (C) 2005, 2006, 2008 Collabora Limited
+# Copyright (C) 2005, 2006 Nokia Corporation
+# Copyright (C) 2008 Olivier Le Thanh Duong <olivier@lethanh.be>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import dbus
+import dbus.service
+
+from telepathy.interfaces import PROPERTIES_INTERFACE
+import telepathy.errors
+
+from telepathy._generated.Properties_Interface import PropertiesInterface
+
+class DBusProperties(dbus.service.Interface):
+    def __init__(self):
+        if not getattr(self, '_interfaces', None):
+            self._interfaces = set()
+
+        self._interfaces.add(dbus.PROPERTIES_IFACE)
+
+        if not getattr(self, '_prop_getters', None):
+            self._prop_getters = {}
+            self._prop_setters = {}
+
+    def _implement_property_get(self, iface, dict):
+        self._prop_getters.setdefault(iface, {}).update(dict)
+
+    def _implement_property_set(self, iface, dict):
+        self._prop_setters.setdefault(iface, {}).update(dict)
+
+    @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v')
+    def Get(self, interface_name, property_name):
+        if interface_name in self._prop_getters \
+            and property_name in self._prop_getters[interface_name]:
+                return self._prop_getters[interface_name][property_name]()
+        else:
+            raise telepathy.errors.InvalidArgument()
+
+    @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ssv', out_signature='')
+    def Set(self, interface_name, property_name, value):
+        if interface_name in self._prop_setters \
+            and property_name in self._prop_setters[interface_name]:
+                return self._prop_setters[interface_name][property_name](value)
+        else:
+            raise telepathy.errors.PermissionDenied()
+
+    @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}')
+    def GetAll(self, interface_name):
+        if interface_name in self._prop_getters:
+            r = {}
+            for k, v in self._prop_getters[interface_name].items():
+                r[k] = v()
+            return r
+        else:
+            raise telepathy.errors.InvalidArgument()