From a840b976430685dbf23bee4e590d1ee23594728c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 14 Jan 2010 19:25:39 -0600 Subject: [PATCH] Switching to shipping parts of python-telepathy with The One Ring so random changes between versions doesn't break things --- src/aliasing.py | 9 +- src/capabilities.py | 88 +------ src/channel/call.py | 48 +--- src/channel/contact_list.py | 45 +--- src/channel/debug_prompt.py | 38 +-- src/channel/text.py | 38 +-- src/channel_manager.py | 90 +------ src/connection.py | 31 +-- src/connection_manager.py | 9 +- src/debug.py | 6 +- src/gvoice/addressbook.py | 8 +- src/handle.py | 11 +- src/location.py | 4 +- src/presence.py | 7 +- src/requests.py | 181 +------------- src/simple_presence.py | 9 +- src/tp/__init__.py | 43 ++++ src/tp/channel.py | 416 ++++++++++++++++++++++++++++++++ src/tp/channelhandler.py | 19 ++ src/tp/channelmanager.py | 99 ++++++++ src/tp/conn.py | 560 +++++++++++++++++++++++++++++++++++++++++++ src/tp/connmgr.py | 73 ++++++ src/tp/debug.py | 113 +++++++++ src/tp/handle.py | 45 ++++ src/tp/media.py | 28 +++ src/tp/properties.py | 70 ++++++ 26 files changed, 1538 insertions(+), 550 deletions(-) create mode 100644 src/tp/__init__.py create mode 100644 src/tp/channel.py create mode 100644 src/tp/channelhandler.py create mode 100644 src/tp/channelmanager.py create mode 100644 src/tp/conn.py create mode 100644 src/tp/connmgr.py create mode 100644 src/tp/debug.py create mode 100644 src/tp/handle.py create mode 100644 src/tp/media.py create mode 100644 src/tp/properties.py diff --git a/src/aliasing.py b/src/aliasing.py index f25c06f..2911b04 100644 --- a/src/aliasing.py +++ b/src/aliasing.py @@ -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() diff --git a/src/capabilities.py b/src/capabilities.py index c4a94cb..6c7ba15 100644 --- a/src/capabilities.py +++ b/src/capabilities.py @@ -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 diff --git a/src/channel/call.py b/src/channel/call.py index 0c39f7e..7155df2 100644 --- a/src/channel/call.py +++ b/src/channel/call.py @@ -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) diff --git a/src/channel/contact_list.py b/src/channel/contact_list.py index d247ec6..be99430 100644 --- a/src/channel/contact_list.py +++ b/src/channel/contact_list.py @@ -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) diff --git a/src/channel/debug_prompt.py b/src/channel/debug_prompt.py index 79b64be..6985725 100644 --- a/src/channel/debug_prompt.py +++ b/src/channel/debug_prompt.py @@ -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): diff --git a/src/channel/text.py b/src/channel/text.py index 64ee0be..b1a7aa9 100644 --- a/src/channel/text.py +++ b/src/channel/text.py @@ -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 diff --git a/src/channel_manager.py b/src/channel_manager.py index 18b682e..a696d90 100644 --- a/src/channel_manager.py +++ b/src/channel_manager.py @@ -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, diff --git a/src/connection.py b/src/connection.py index 7518e4a..b7d9b9b 100644 --- a/src/connection.py +++ b/src/connection.py @@ -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() diff --git a/src/connection_manager.py b/src/connection_manager.py index c4b5f21..207f773 100644 --- a/src/connection_manager.py +++ b/src/connection_manager.py @@ -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): diff --git a/src/debug.py b/src/debug.py index c52fc63..d556cf7 100644 --- a/src/debug.py +++ b/src/debug.py @@ -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) diff --git a/src/gvoice/addressbook.py b/src/gvoice/addressbook.py index 57b935a..b1ceb8f 100644 --- a/src/gvoice/addressbook.py +++ b/src/gvoice/addressbook.py @@ -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 diff --git a/src/handle.py b/src/handle.py index d063a77..2d27dd4 100644 --- a/src/handle.py +++ b/src/handle.py @@ -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): diff --git a/src/location.py b/src/location.py index 71868ba..59195fd 100644 --- a/src/location.py +++ b/src/location.py @@ -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 diff --git a/src/presence.py b/src/presence.py index 8c38252..ab6fc66 100644 --- a/src/presence.py +++ b/src/presence.py @@ -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) diff --git a/src/requests.py b/src/requests.py index 51da1f2..39a81f8 100644 --- a/src/requests.py +++ b/src/requests.py @@ -1,186 +1,11 @@ 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 diff --git a/src/simple_presence.py b/src/simple_presence.py index 7cad575..59bf641 100644 --- a/src/simple_presence.py +++ b/src/simple_presence.py @@ -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 index 0000000..d0f561b --- /dev/null +++ b/src/tp/__init__.py @@ -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 index 0000000..55ac675 --- /dev/null +++ b/src/tp/channel.py @@ -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 index 0000000..5bef170 --- /dev/null +++ b/src/tp/channelhandler.py @@ -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 index 0000000..aacde30 --- /dev/null +++ b/src/tp/channelmanager.py @@ -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 index 0000000..0498e64 --- /dev/null +++ b/src/tp/conn.py @@ -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 index 0000000..8167108 --- /dev/null +++ b/src/tp/connmgr.py @@ -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 index 0000000..3ddd409 --- /dev/null +++ b/src/tp/debug.py @@ -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 index 0000000..a7771b0 --- /dev/null +++ b/src/tp/handle.py @@ -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 index 0000000..7d1ce7b --- /dev/null +++ b/src/tp/media.py @@ -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 index 0000000..c42c737 --- /dev/null +++ b/src/tp/properties.py @@ -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 +# +# 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() -- 1.7.9.5