summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLouis-Francis Ratté-Boulianne <louis-francis.ratte-boulianne@collabora.co.uk>2009-08-11 22:48:33 -0400
committerLouis-Francis Ratté-Boulianne <louis-francis.ratte-boulianne@collabora.co.uk>2009-08-11 22:48:33 -0400
commit2d8eaadde631e28bf251f4a7164a90c1301efd7c (patch)
treed816d7014ac183bd2c9a085a945574b86b04e855 /src
parent54ac81d1e44bdd24d27b40a4549ad3c530ca9c69 (diff)
parent5487b662f43c5cda757e7275a14116aa64538d82 (diff)
Merge branch 'master' of git+ssh://git.collabora.co.uk/git/user/lfrb/telepathy-python/
Diffstat (limited to 'src')
-rw-r--r--src/_version.py2
-rw-r--r--src/client/conn.py8
-rw-r--r--src/server/__init__.py1
-rw-r--r--src/server/channel.py75
-rw-r--r--src/server/channelmanager.py99
-rw-r--r--src/server/conn.py217
6 files changed, 370 insertions, 32 deletions
diff --git a/src/_version.py b/src/_version.py
index f087efe..d643b4f 100644
--- a/src/_version.py
+++ b/src/_version.py
@@ -1,4 +1,4 @@
__all__ = ('version', '__version__')
-version = (0, 15, 7, 1)
+version = (0, 15, 10, 1)
__version__ = '.'.join(str(x) for x in version)
diff --git a/src/client/conn.py b/src/client/conn.py
index a37a11d..e46c3ea 100644
--- a/src/client/conn.py
+++ b/src/client/conn.py
@@ -22,9 +22,11 @@ import dbus
from telepathy.client.channel import Channel
from telepathy.client.interfacefactory import (
InterfaceFactory, default_error_handler)
-from telepathy.interfaces import CONN_INTERFACE
+from telepathy.interfaces import (
+ CONN_INTERFACE, CONNECTION_INTERFACE_REQUESTS)
from telepathy.constants import CONNECTION_STATUS_CONNECTED
+
class Connection(InterfaceFactory):
def __init__(self, service_name, object_path=None, bus=None,
ready_handler=None, error_handler=default_error_handler):
@@ -99,6 +101,10 @@ class Connection(InterfaceFactory):
path = self.RequestChannel(type, handle_type, handle, suppress_handler)
return Channel(self.service_name, path, self.bus)
+ def create_channel(self, props):
+ object_path, props = self[CONNECTION_INTERFACE_REQUESTS].CreateChannel(props)
+ return Channel(self.service_name, object_path, self.bus)
+
def call_when_ready(self, handler):
if self._ready:
handler(self)
diff --git a/src/server/__init__.py b/src/server/__init__.py
index 52e804f..aa077be 100644
--- a/src/server/__init__.py
+++ b/src/server/__init__.py
@@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from telepathy.server.connmgr import *
from telepathy.server.conn import *
from telepathy.server.channel import *
+from telepathy.server.channelmanager import *
from telepathy.server.handle import *
from telepathy.server.media import *
from telepathy.server.properties import *
diff --git a/src/server/channel.py b/src/server/channel.py
index 9455235..b454823 100644
--- a/src/server/channel.py
+++ b/src/server/channel.py
@@ -44,30 +44,49 @@ from telepathy.server.properties import DBusProperties
class Channel(_Channel, DBusProperties):
- def __init__(self, connection, type, handle):
+ def __init__(self, connection, manager, props):
"""
Initialise the base channel object.
Parameters:
connection - the parent Connection object
- type - interface name for the type of this channel
- handle - the channels handle if applicable
+ 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 = type
- self._handle = handle
+ self._type = props[CHANNEL_INTERFACE + '.ChannelType']
+ self._requested = props[CHANNEL_INTERFACE + '.Requested']
+
+ self._immutable_properties = dict()
+
+ self._handle = self._conn.handle(
+ 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),
+ 'TargetHandle': lambda: dbus.UInt32(self._handle.get_id()),
'TargetHandleType': lambda: dbus.UInt32(self._get_handle_type()),
- 'TargetID': lambda: dbus.String(self._get_target_id())})
+ '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:
@@ -81,9 +100,17 @@ class Channel(_Channel, DBusProperties):
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')
@@ -117,14 +144,30 @@ from telepathy._generated.Channel_Type_Contact_List \
class ChannelTypeContactList(Channel, _ChannelTypeContactListIface):
__doc__ = _ChannelTypeContactListIface.__doc__
- def __init__(self, connection, handle):
+ def __init__(self, connection, manager, props):
"""
Initialise the channel.
Parameters:
connection - the parent Telepathy Connection object
"""
- Channel.__init__(self, connection, CHANNEL_TYPE_CONTACT_LIST, handle)
+ 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 \
@@ -149,14 +192,14 @@ from telepathy._generated.Channel_Type_Streamed_Media \
class ChannelTypeStreamedMedia(Channel, _ChannelTypeStreamedMediaIface):
__doc__ = _ChannelTypeStreamedMediaIface.__doc__
- def __init__(self, connection, handle):
+ def __init__(self, connection, manager, props):
"""
Initialise the channel.
Parameters:
connection - the parent Telepathy Connection object
"""
- Channel.__init__(self, connection, CHANNEL_TYPE_STREAMED_MEDIA, handle)
+ Channel.__init__(self, connection, manager, props)
from telepathy._generated.Channel_Type_Room_List \
@@ -165,17 +208,19 @@ from telepathy._generated.Channel_Type_Room_List \
class ChannelTypeRoomList(Channel, _ChannelTypeRoomListIface):
__doc__ = _ChannelTypeRoomListIface.__doc__
- def __init__(self, connection):
+ def __init__(self, connection, manager, props):
"""
Initialise the channel.
Parameters:
connection - the parent Telepathy Connection object
"""
- Channel.__init__(self, connection, CHANNEL_TYPE_ROOM_LIST, 0)
+ 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
@@ -191,14 +236,14 @@ from telepathy._generated.Channel_Type_Text \
class ChannelTypeText(Channel, _ChannelTypeTextIface):
__doc__ = _ChannelTypeTextIface.__doc__
- def __init__(self, connection, handle):
+ def __init__(self, connection, manager, props):
"""
Initialise the channel.
Parameters:
connection - the parent Telepathy Connection object
"""
- Channel.__init__(self, connection, CHANNEL_TYPE_TEXT, handle)
+ Channel.__init__(self, connection, manager, props)
self._pending_messages = {}
self._message_types = [CHANNEL_TEXT_MESSAGE_TYPE_NORMAL]
diff --git a/src/server/channelmanager.py b/src/server/channelmanager.py
new file mode 100644
index 0000000..aacde30
--- /dev/null
+++ b/src/server/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/server/conn.py b/src/server/conn.py
index 031e2db..0ef2206 100644
--- a/src/server/conn.py
+++ b/src/server/conn.py
@@ -26,15 +26,19 @@ 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)
+ InvalidHandle, NotAvailable,
+ NotImplemented)
from telepathy.interfaces import (CONN_INTERFACE,
CONN_INTERFACE_ALIASING,
CONN_INTERFACE_AVATARS,
CONN_INTERFACE_CAPABILITIES,
CONN_INTERFACE_PRESENCE,
- CONN_INTERFACE_RENAMING)
+ CONN_INTERFACE_RENAMING,
+ CONNECTION_INTERFACE_REQUESTS,
+ CHANNEL_INTERFACE)
from telepathy.server.handle import Handle
from telepathy.server.properties import DBusProperties
@@ -179,13 +183,37 @@ class Connection(_Connection, DBusProperties):
self._next_channel_id += 1
return ret
- def add_channel(self, channel, handle, suppress_handler):
- """ add a new channel and signal its creation"""
- self._channels.add(channel)
- self.NewChannel(channel._object_path, channel._type, handle.get_type(), handle.get_id(), suppress_handler)
+ 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):
@@ -281,7 +309,7 @@ class Connection(_Connection, DBusProperties):
self.check_connected()
ret = []
for channel in self._channels:
- chan = (channel._object_path, channel._type, channel._handle.get_type(), channel._handle)
+ chan = (channel._object_path, channel._type, channel._get_handle_type(), channel._handle)
ret.append(chan)
return ret
@@ -308,13 +336,15 @@ class ConnectionInterfaceCapabilities(_ConnectionInterfaceCapabilities):
@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 not in self._handles):
+ if (handle != 0 and (handle_type, handle) not in self._handles):
raise InvalidHandle
elif handle in self._caps:
- theirs = self._caps[handle]
- for type in theirs:
- ret.append([handle, type, theirs[0], theirs[1]])
+ 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):
@@ -345,11 +375,171 @@ class ConnectionInterfaceCapabilities(_ConnectionInterfaceCapabilities):
caps.append((self._self_handle, ctype, gen_old, gen_new,
spec_old, spec_new))
- self.CapabilitiesChanged(self._self_handle, caps)
+ 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)
+
+ altered_properties = props.copy()
+
+ if target_handle_type != HANDLE_TYPE_NONE:
+ if target_handle == None:
+ # Turn TargetID into TargetHandle.
+ for handle in self._handles.itervalues():
+ if handle.get_name() == target_id and handle.get_type() == target_handle_type:
+ target_handle = handle.get_id()
+ if not target_handle:
+ raise InvalidHandle('TargetID %s not valid for type %d' %
+ target_id, target_handle_type)
+
+ altered_properties[CHANNEL_INTERFACE + '.TargetHandle'] = \
+ target_handle
+ del altered_properties[CHANNEL_INTERFACE + '.TargetID']
+ else:
+ # Check the supplied TargetHandle is valid
+ self.check_handle(target_handle_type, target_handle)
+
+ 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
@@ -359,6 +549,3 @@ from telepathy._generated.Connection_Interface_Simple_Presence \
from telepathy._generated.Connection_Interface_Contacts \
import ConnectionInterfaceContacts
-
-from telepathy._generated.Connection_Interface_Requests \
- import ConnectionInterfaceRequests